Propal::set_date_livraison()   A
last analyzed

Complexity

Conditions 1

Size

Total Lines 4
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nop 3
dl 0
loc 4
rs 10
c 0
b 0
f 0
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
namespace Dolibarr\Code\Comm\Classes;
41
42
/**
43
 *  \file       htdocs/comm/propal/class/propal.class.php
44
 *  \brief      File of class to manage proposals
45
 */
46
47
use Dolibarr\Code\Core\Classes\DiscountAbsolute;
48
use Dolibarr\Code\Core\Classes\WorkboardResponse;
49
use Dolibarr\Code\Core\Traits\CommonIncoterm;
50
use Dolibarr\Code\MultiCurrency\Classes\MultiCurrency;
51
use Dolibarr\Code\Product\Classes\Product;
52
use Dolibarr\Code\Product\Classes\ProductCustomerPrice;
53
use Dolibarr\Code\User\Classes\User;
54
use Dolibarr\Core\Base\CommonObject;
55
use DoliDB;
56
57
require_once constant('DOL_DOCUMENT_ROOT') . '/margin/lib/margins.lib.php';
58
59
/**
60
 *  Class to manage proposals
61
 */
62
class Propal extends CommonObject
63
{
64
    use CommonIncoterm;
65
66
    /**
67
     * Canceled status
68
     */
69
    const STATUS_CANCELED = -1;
70
    /**
71
     * Draft status
72
     */
73
    const STATUS_DRAFT = 0;
74
    /**
75
     * Validated status
76
     */
77
    const STATUS_VALIDATED = 1;
78
    /**
79
     * Signed quote
80
     */
81
    const STATUS_SIGNED = 2;
82
    /**
83
     * Not signed quote
84
     */
85
    const STATUS_NOTSIGNED = 3;
86
/**
87
     * Billed or processed quote
88
     */
89
    const STATUS_BILLED = 4;
90
    /**
91
     * @var string code
92
     */
93
    public $code = "";
94
    /**
95
     * @var string ID to identify managed object
96
     */
97
    public $element = 'propal';
98
    /**
99
     * @var string Name of table without prefix where object is stored
100
     */
101
    public $table_element = 'propal';
102
    /**
103
     * @var string    Name of subtable line
104
     */
105
    public $table_element_line = 'propaldet';
106
    /**
107
     * @var string Fieldname with ID of parent key if this field has a parent
108
     */
109
    public $fk_element = 'fk_propal';
110
    /**
111
     * @var string String with name of icon for myobject. Must be the part after the 'object_' into object_myobject.png
112
     */
113
    public $picto = 'propal';
114
    /**
115
     * 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
116
     * @var integer
117
     */
118
    public $restrictiononfksoc = 1;
119
    /**
120
     * ID of the client
121
     * @var int
122
     */
123
    public $socid;
124
    /**
125
     * ID of the contact
126
     * @var int
127
     */
128
    public $contactid;
129
    public $author;
130
    /**
131
     * Ref from thirdparty
132
     * @var string
133
     * @deprecated
134
     * @see $ref_customer
135
     */
136
    public $ref_client;
137
    /**
138
     * Ref from thirdparty
139
     * @var string
140
     */
141
    public $ref_customer;
142
    /**
143
     * @var static oldcopy with propal properties
144
     */
145
    public $oldcopy;
146
    /**
147
     * Status of the quote
148
     * @var int
149
     * @deprecated Try to use $status now
150
     * @see Propal::STATUS_DRAFT, Propal::STATUS_VALIDATED, Propal::STATUS_SIGNED, Propal::STATUS_NOTSIGNED, Propal::STATUS_BILLED, Propal::STATUS_CANCELED
151
     */
152
    public $statut;
153
    /**
154
     * Status of the quote
155
     * @var int
156
     * @see Propal::STATUS_DRAFT, Propal::STATUS_VALIDATED, Propal::STATUS_SIGNED, Propal::STATUS_NOTSIGNED, Propal::STATUS_BILLED, Propal::STATUS_CANCELED
157
     */
158
    public $status;
159
    /**
160
     * @deprecated
161
     * @see $date_creation
162
     */
163
    public $datec;
164
    /**
165
     * @var integer|string $date_creation ;
166
     */
167
    public $date_creation;
168
    /**
169
     * @deprecated
170
     * @see $date_validation
171
     */
172
    public $datev;
173
    /**
174
     * @var integer|string $date_validation ;
175
     */
176
    public $date_validation; // Date expected of shipment (date starting shipment, not the reception that occurs some days after)
177
    /**
178
     * @var integer|string $date_signature ;
179
     */
180
    public $date_signature;
181
    /**
182
     * @var User $user_signature
183
     */
184
    public $user_signature;
185
    /**
186
     * @var integer|string date of the quote;
187
     */
188
    public $date;
189
    /**
190
     * @deprecated
191
     * @see $date
192
     */
193
    public $datep;
194
/**
195
     * @var integer|string $delivery_date ;
196
     */
197
    public $delivery_date;
198
    public $fin_validite;    // code
199
        public $user_author_id;         // label
200
        /**
201
     * @deprecated
202
     * @see $total_ht
203
     */
204
    public $price;     // label doc
205
    /**
206
     * @deprecated
207
     * @see $total_tva
208
     */
209
    public $tva;    // code
210
        /**
211
     * @deprecated
212
     * @see $total_ttc
213
     */
214
    public $total;         // label
215
public $cond_reglement_code;
216
public $cond_reglement;
217
public $cond_reglement_doc;
218
public $mode_reglement_code;
219
public $mode_reglement;
220
    public $deposit_percent;
221
    /**
222
     * @var int ID
223
     * @deprecated
224
     */
225
    public $fk_address;
226
    public $address_type;
227
    public $address;
228
    /**
229
     * @var int availability ID
230
     */
231
    public $availability_id;       // id
232
        /**
233
     * @var int availability ID
234
     * @deprecated
235
     * @see $availability_id
236
     */
237
    public $fk_availability;     // code
238
        /**
239
     * @var string availability code
240
     */
241
    public $availability_code;          // label
242
    /**
243
     * @var string availability label
244
     */
245
    public $availability;
246
    public $duree_validite;
247
public $demand_reason_id;
248
public $demand_reason_code;
249
public $demand_reason;
250
    public $warehouse_id;
251
252
253
    /**
254
     *  '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')
255
     *         Note: Filter can be a string like "(t.ref:like:'SO-%') or (t.date_creation:<:'20160101') or (t.nature:is:NULL)"
256
     *  'label' the translation key.
257
     *  'enabled' is a condition when the field must be managed.
258
     *  'position' is the sort order of field.
259
     *  'notnull' is set to 1 if not null in database. Set to -1 if we must set data to null if empty ('' or 0).
260
     *  '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)
261
     *  'noteditable' says if field is not editable (1 or 0)
262
     *  '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.
263
     *  'index' if we want an index in database.
264
     *  'foreignkey'=>'tablename.field' if the field is a foreign key (it is recommended to name the field fk_...).
265
     *  'searchall' is 1 if we want to search in this field when making a search from the quick search button.
266
     *  '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).
267
     *  'css' is the CSS style to use on field. For example: 'maxwidth200'
268
     *  'help' is a string visible as a tooltip on field
269
     *  'showoncombobox' if value of the field must be visible into the label of the combobox that list record
270
     *  '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.
271
     *  'arrayofkeyval' to set list of value if type is a list of predefined values. For example: array("0"=>"Draft","1"=>"Active","-1"=>"Cancel")
272
     *  'comment' is not used. You can store here any text of your choice. It is not used by application.
273
     *
274
     *  Note: To have value dynamic, you can set value to 0 in definition and edit the value on the fly into the constructor.
275
     */
276
277
    // BEGIN MODULEBUILDER PROPERTIES
278
    public $extraparams = array();
279
    // END MODULEBUILDER PROPERTIES
280
    /**
281
     * @var PropaleLigne[]
282
     */
283
    public $lines = array();
284
    /**
285
     * @var PropaleLigne
286
     */
287
    public $line;
288
    public $labelStatus = array();
289
    public $labelStatusShort = array();
290
    /**
291
     * @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.
292
     */
293
    public $fields = array(
294
        'rowid' => array('type' => 'integer', 'label' => 'TechnicalID', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 10),
295
        'entity' => array('type' => 'integer', 'label' => 'Entity', 'default' => '1', 'enabled' => 1, 'visible' => -2, 'notnull' => 1, 'position' => 15, 'index' => 1),
296
        'ref' => array('type' => 'varchar(30)', 'label' => 'Ref', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'showoncombobox' => 1, 'position' => 20),
297
        'ref_client' => array('type' => 'varchar(255)', 'label' => 'RefCustomer', 'enabled' => 1, 'visible' => -1, 'position' => 22),
298
        'ref_ext' => array('type' => 'varchar(255)', 'label' => 'RefExt', 'enabled' => 1, 'visible' => 0, 'position' => 40),
299
        'fk_soc' => array('type' => 'integer:Societe:societe/class/societe.class.php', 'label' => 'ThirdParty', 'enabled' => 'isModEnabled("societe")', 'visible' => -1, 'position' => 23),
300
        '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),
301
        'tms' => array('type' => 'timestamp', 'label' => 'DateModification', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 25),
302
        'datec' => array('type' => 'datetime', 'label' => 'DateCreation', 'enabled' => 1, 'visible' => -1, 'position' => 55),
303
        'datep' => array('type' => 'date', 'label' => 'Date', 'enabled' => 1, 'visible' => -1, 'position' => 60),
304
        'fin_validite' => array('type' => 'datetime', 'label' => 'DateEnd', 'enabled' => 1, 'visible' => -1, 'position' => 65),
305
        'date_valid' => array('type' => 'datetime', 'label' => 'DateValidation', 'enabled' => 1, 'visible' => -1, 'position' => 70),
306
        'date_cloture' => array('type' => 'datetime', 'label' => 'DateClosing', 'enabled' => 1, 'visible' => -1, 'position' => 75),
307
        'fk_user_author' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'Fk user author', 'enabled' => 1, 'visible' => -1, 'position' => 80),
308
        'fk_user_modif' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserModif', 'enabled' => 1, 'visible' => -2, 'notnull' => -1, 'position' => 85),
309
        'fk_user_valid' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserValidation', 'enabled' => 1, 'visible' => -1, 'position' => 90),
310
        'fk_user_cloture' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'Fk user cloture', 'enabled' => 1, 'visible' => -1, 'position' => 95),
311
        'price' => array('type' => 'double', 'label' => 'Price', 'enabled' => 1, 'visible' => -1, 'position' => 105),
312
        'total_ht' => array('type' => 'double(24,8)', 'label' => 'TotalHT', 'enabled' => 1, 'visible' => -1, 'position' => 125, 'isameasure' => 1),
313
        'total_tva' => array('type' => 'double(24,8)', 'label' => 'VAT', 'enabled' => 1, 'visible' => -1, 'position' => 130, 'isameasure' => 1),
314
        'localtax1' => array('type' => 'double(24,8)', 'label' => 'LocalTax1', 'enabled' => 1, 'visible' => -1, 'position' => 135, 'isameasure' => 1),
315
        'localtax2' => array('type' => 'double(24,8)', 'label' => 'LocalTax2', 'enabled' => 1, 'visible' => -1, 'position' => 140, 'isameasure' => 1),
316
        'total_ttc' => array('type' => 'double(24,8)', 'label' => 'TotalTTC', 'enabled' => 1, 'visible' => -1, 'position' => 145, 'isameasure' => 1),
317
        'fk_account' => array('type' => 'integer', 'label' => 'BankAccount', 'enabled' => 'isModEnabled("bank")', 'visible' => -1, 'position' => 150),
318
        'fk_currency' => array('type' => 'varchar(3)', 'label' => 'Currency', 'enabled' => 1, 'visible' => -1, 'position' => 155),
319
        'fk_cond_reglement' => array('type' => 'integer', 'label' => 'PaymentTerm', 'enabled' => 1, 'visible' => -1, 'position' => 160),
320
        'deposit_percent' => array('type' => 'varchar(63)', 'label' => 'DepositPercent', 'enabled' => 1, 'visible' => -1, 'position' => 161),
321
        'fk_mode_reglement' => array('type' => 'integer', 'label' => 'PaymentMode', 'enabled' => 1, 'visible' => -1, 'position' => 165),
322
        'note_private' => array('type' => 'html', 'label' => 'NotePrivate', 'enabled' => 1, 'visible' => 0, 'position' => 170),
323
        'note_public' => array('type' => 'html', 'label' => 'NotePublic', 'enabled' => 1, 'visible' => 0, 'position' => 175),
324
        'model_pdf' => array('type' => 'varchar(255)', 'label' => 'PDFTemplate', 'enabled' => 1, 'visible' => 0, 'position' => 180),
325
        'date_livraison' => array('type' => 'date', 'label' => 'DateDeliveryPlanned', 'enabled' => 1, 'visible' => -1, 'position' => 185),
326
        'fk_shipping_method' => array('type' => 'integer', 'label' => 'ShippingMethod', 'enabled' => 1, 'visible' => -1, 'position' => 190),
327
        'fk_warehouse' => array('type' => 'integer:Entrepot:product/stock/class/entrepot.class.php', 'label' => 'Fk warehouse', 'enabled' => 'isModEnabled("stock")', 'visible' => -1, 'position' => 191),
328
        'fk_availability' => array('type' => 'integer', 'label' => 'Availability', 'enabled' => 1, 'visible' => -1, 'position' => 195),
329
        'fk_delivery_address' => array('type' => 'integer', 'label' => 'DeliveryAddress', 'enabled' => 1, 'visible' => 0, 'position' => 200), // deprecated
330
        'fk_input_reason' => array('type' => 'integer', 'label' => 'InputReason', 'enabled' => 1, 'visible' => -1, 'position' => 205),
331
        'extraparams' => array('type' => 'varchar(255)', 'label' => 'Extraparams', 'enabled' => 1, 'visible' => -1, 'position' => 215),
332
        'fk_incoterms' => array('type' => 'integer', 'label' => 'IncotermCode', 'enabled' => '$conf->incoterm->enabled', 'visible' => -1, 'position' => 220),
333
        'location_incoterms' => array('type' => 'varchar(255)', 'label' => 'IncotermLabel', 'enabled' => '$conf->incoterm->enabled', 'visible' => -1, 'position' => 225),
334
        'fk_multicurrency' => array('type' => 'integer', 'label' => 'MulticurrencyID', 'enabled' => 1, 'visible' => -1, 'position' => 230),
335
        'multicurrency_code' => array('type' => 'varchar(255)', 'label' => 'MulticurrencyCurrency', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 235),
336
        'multicurrency_tx' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyRate', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 240, 'isameasure' => 1),
337
        'multicurrency_total_ht' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyAmountHT', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 245, 'isameasure' => 1),
338
        'multicurrency_total_tva' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyAmountVAT', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 250, 'isameasure' => 1),
339
        'multicurrency_total_ttc' => array('type' => 'double(24,8)', 'label' => 'MulticurrencyAmountTTC', 'enabled' => 'isModEnabled("multicurrency")', 'visible' => -1, 'position' => 255, 'isameasure' => 1),
340
        'last_main_doc' => array('type' => 'varchar(255)', 'label' => 'LastMainDoc', 'enabled' => 1, 'visible' => -1, 'position' => 260),
341
        'fk_statut' => array('type' => 'smallint(6)', 'label' => 'Status', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 500),
342
        'import_key' => array('type' => 'varchar(14)', 'label' => 'ImportId', 'enabled' => 1, 'visible' => -2, 'position' => 900),
343
    );
344
        /**
345
     * {@inheritdoc}
346
     */
347
    protected $table_ref_field = 'ref'; // Todo rename into STATUS_CLOSE ?
348
349
    /**
350
     *  Constructor
351
     *
352
     * @param DoliDB $db Database handler
353
     * @param int $socid Id third party
354
     * @param int $propalid Id proposal
355
     */
356
    public function __construct($db, $socid = 0, $propalid = 0)
357
    {
358
        $this->db = $db;
359
360
        $this->ismultientitymanaged = 1;
361
        $this->socid = $socid;
362
        $this->id = $propalid;
363
364
        $this->duree_validite = getDolGlobalInt('PROPALE_VALIDITY_DURATION', 0);
365
    }
366
367
368
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
369
370
    /**
371
     * Function used to replace a thirdparty id with another one.
372
     *
373
     * @param DoliDB $dbs Database handler, because function is static we name it $dbs not $db to avoid breaking coding test
374
     * @param int $origin_id Old thirdparty id
375
     * @param int $dest_id New thirdparty id
376
     * @return  bool
377
     */
378
    public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
379
    {
380
        $tables = array(
381
            'propal'
382
        );
383
384
        return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
385
    }
386
387
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
388
389
    /**
390
     * Function used to replace a product id with another one.
391
     *
392
     * @param DoliDB $db Database handler
393
     * @param int $origin_id Old product id
394
     * @param int $dest_id New product id
395
     * @return bool
396
     */
397
    public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
398
    {
399
        $tables = array(
400
            'propaldet'
401
        );
402
403
        return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
404
    }
405
406
    /**
407
     *  Add line into array ->lines
408
     *  $this->thirdparty should be loaded
409
     *
410
     * @param int $idproduct Product Id to add
411
     * @param float $qty Quantity
412
     * @param float $remise_percent Discount effected on Product
413
     * @return int                         Return integer <0 if KO, >0 if OK
414
     *
415
     *  TODO    Replace calls to this function by generation object Ligne
416
     */
417
    public function add_product($idproduct, $qty, $remise_percent = 0)
418
    {
419
        // phpcs:enable
420
        global $conf, $mysoc;
421
422
        if (!$qty) {
423
            $qty = 1;
424
        }
425
426
        dol_syslog(get_only_class($this) . "::add_product $idproduct, $qty, $remise_percent");
427
        if ($idproduct > 0) {
428
            $prod = new Product($this->db);
429
            $prod->fetch($idproduct);
430
431
            $productdesc = $prod->description;
432
433
            $tva_tx = get_default_tva($mysoc, $this->thirdparty, $prod->id);
434
            $tva_npr = get_default_npr($mysoc, $this->thirdparty, $prod->id);
435
            if (empty($tva_tx)) {
436
                $tva_npr = 0;
437
            }
438
            $vat_src_code = ''; // May be defined into tva_tx
439
440
            $localtax1_tx = get_localtax($tva_tx, 1, $mysoc, $this->thirdparty, $tva_npr);
441
            $localtax2_tx = get_localtax($tva_tx, 2, $mysoc, $this->thirdparty, $tva_npr);
442
443
            // multiprices
444
            if ($conf->global->PRODUIT_MULTIPRICES && $this->thirdparty->price_level) {
445
                $price = $prod->multiprices[$this->thirdparty->price_level];
446
            } else {
447
                $price = $prod->price;
448
            }
449
450
            $line = new PropaleLigne($this->db);
451
452
            $line->fk_product = $idproduct;
453
            $line->desc = $productdesc;
454
            $line->qty = $qty;
455
            $line->subprice = $price;
456
            $line->remise_percent = $remise_percent;
457
            $line->vat_src_code = $vat_src_code;
458
            $line->tva_tx = $tva_tx;
459
            $line->fk_unit = $prod->fk_unit;
460
            if ($tva_npr) {
461
                $line->info_bits = 1;
462
            }
463
464
            $this->lines[] = $line;
465
        }
466
467
        return 1;
468
    }
469
470
    /**
471
     *  Load a proposal from database. Get also lines.
472
     *
473
     * @param int $rowid Id of object to load
474
     * @param string $ref Ref of proposal
475
     * @param string $ref_ext Ref ext of proposal
476
     * @param int $forceentity Entity id to force when searching on ref or ref_ext
477
     * @return     int                         >0 if OK, <0 if KO
478
     */
479
    public function fetch($rowid, $ref = '', $ref_ext = '', $forceentity = 0)
480
    {
481
        $sql = "SELECT p.rowid, p.ref, p.entity, p.fk_soc";
482
        $sql .= ", p.total_ttc, p.total_tva, p.localtax1, p.localtax2, p.total_ht";
483
        $sql .= ", p.datec";
484
        $sql .= ", p.date_signature as dates";
485
        $sql .= ", p.date_valid as datev";
486
        $sql .= ", p.datep as dp";
487
        $sql .= ", p.fin_validite as dfv";
488
        $sql .= ", p.date_livraison as delivery_date";
489
        $sql .= ", p.model_pdf, p.last_main_doc, p.ref_client, ref_ext, p.extraparams";
490
        $sql .= ", p.note_private, p.note_public";
491
        $sql .= ", p.fk_projet as fk_project, p.fk_statut";
492
        $sql .= ", p.fk_user_author, p.fk_user_valid, p.fk_user_cloture";
493
        $sql .= ", p.fk_delivery_address";
494
        $sql .= ", p.fk_availability";
495
        $sql .= ", p.fk_input_reason";
496
        $sql .= ", p.fk_cond_reglement";
497
        $sql .= ", p.fk_mode_reglement";
498
        $sql .= ', p.fk_account';
499
        $sql .= ", p.fk_shipping_method";
500
        $sql .= ", p.fk_warehouse";
501
        $sql .= ", p.fk_incoterms, p.location_incoterms";
502
        $sql .= ", p.fk_multicurrency, p.multicurrency_code, p.multicurrency_tx, p.multicurrency_total_ht, p.multicurrency_total_tva, p.multicurrency_total_ttc";
503
        $sql .= ", p.tms as date_modification";
504
        $sql .= ", i.libelle as label_incoterms";
505
        $sql .= ", c.label as statut_label";
506
        $sql .= ", ca.code as availability_code, ca.label as availability";
507
        $sql .= ", dr.code as demand_reason_code, dr.label as demand_reason";
508
        $sql .= ", cr.code as cond_reglement_code, cr.libelle as cond_reglement, cr.libelle_facture as cond_reglement_libelle_doc, p.deposit_percent";
509
        $sql .= ", cp.code as mode_reglement_code, cp.libelle as mode_reglement";
510
        $sql .= " FROM " . MAIN_DB_PREFIX . "propal as p";
511
        $sql .= ' LEFT JOIN ' . MAIN_DB_PREFIX . 'c_propalst as c ON p.fk_statut = c.id';
512
        $sql .= ' LEFT JOIN ' . MAIN_DB_PREFIX . 'c_paiement as cp ON p.fk_mode_reglement = cp.id AND cp.entity IN (' . getEntity('c_paiement') . ')';
513
        $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') . ')';
514
        $sql .= ' LEFT JOIN ' . MAIN_DB_PREFIX . 'c_availability as ca ON p.fk_availability = ca.rowid';
515
        $sql .= ' LEFT JOIN ' . MAIN_DB_PREFIX . 'c_input_reason as dr ON p.fk_input_reason = dr.rowid';
516
        $sql .= ' LEFT JOIN ' . MAIN_DB_PREFIX . 'c_incoterms as i ON p.fk_incoterms = i.rowid';
517
518
        if (!empty($ref)) {
519
            if (!empty($forceentity)) {
520
                $sql .= " WHERE p.entity = " . (int)$forceentity; // Check only the current entity because we may have the same reference in several entities
521
            } else {
522
                $sql .= " WHERE p.entity IN (" . getEntity('propal') . ")";
523
            }
524
            $sql .= " AND p.ref='" . $this->db->escape($ref) . "'";
525
        } else {
526
            // Don't use entity if you use rowid
527
            $sql .= " WHERE p.rowid = " . ((int)$rowid);
528
        }
529
530
        dol_syslog(get_only_class($this) . "::fetch", LOG_DEBUG);
531
        $resql = $this->db->query($sql);
532
        if ($resql) {
533
            if ($this->db->num_rows($resql)) {
534
                $obj = $this->db->fetch_object($resql);
535
536
                $this->id = $obj->rowid;
537
                $this->entity = $obj->entity;
538
539
                $this->ref = $obj->ref;
540
                $this->ref_client = $obj->ref_client;
541
                $this->ref_customer = $obj->ref_client;
542
                $this->ref_ext = $obj->ref_ext;
543
544
                $this->total = $obj->total_ttc;          // TODO deprecated
545
                $this->total_ttc = $obj->total_ttc;
546
                $this->total_ht = $obj->total_ht;
547
                $this->total_tva = $obj->total_tva;
548
                $this->total_localtax1 = $obj->localtax1;
549
                $this->total_localtax2 = $obj->localtax2;
550
551
                $this->socid = $obj->fk_soc;
552
                $this->thirdparty = null; // Clear if another value was already set by fetch_thirdparty
553
554
                $this->fk_project = $obj->fk_project;
555
                $this->project = null; // Clear if another value was already set by fetch_projet
556
557
                $this->model_pdf = $obj->model_pdf;
558
                $this->last_main_doc = $obj->last_main_doc;
559
                $this->note = $obj->note_private; // TODO deprecated
560
                $this->note_private = $obj->note_private;
561
                $this->note_public = $obj->note_public;
562
563
                $this->status = (int)$obj->fk_statut;
564
                $this->statut = $this->status; // deprecated
565
566
                $this->datec = $this->db->jdate($obj->datec); // TODO deprecated
567
                $this->datev = $this->db->jdate($obj->datev); // TODO deprecated
568
                $this->date_creation = $this->db->jdate($obj->datec); //Creation date
569
                $this->date_validation = $this->db->jdate($obj->datev); //Validation date
570
                $this->date_modification = $this->db->jdate($obj->date_modification); // tms
571
                $this->date_signature = $this->db->jdate($obj->dates); // Signature date
572
                $this->date = $this->db->jdate($obj->dp); // Proposal date
573
                $this->datep = $this->db->jdate($obj->dp); // deprecated
574
                $this->fin_validite = $this->db->jdate($obj->dfv);
575
                $this->delivery_date = $this->db->jdate($obj->delivery_date);
576
                $this->shipping_method_id = ($obj->fk_shipping_method > 0) ? $obj->fk_shipping_method : null;
577
                $this->warehouse_id = ($obj->fk_warehouse > 0) ? $obj->fk_warehouse : null;
578
                $this->availability_id = $obj->fk_availability;
579
                $this->availability_code = $obj->availability_code;
580
                $this->availability = $obj->availability;
581
                $this->demand_reason_id = $obj->fk_input_reason;
582
                $this->demand_reason_code = $obj->demand_reason_code;
583
                $this->demand_reason = $obj->demand_reason;
584
                $this->fk_address = $obj->fk_delivery_address;
585
586
                $this->mode_reglement_id = $obj->fk_mode_reglement;
587
                $this->mode_reglement_code = $obj->mode_reglement_code;
588
                $this->mode_reglement = $obj->mode_reglement;
589
                $this->fk_account = ($obj->fk_account > 0) ? $obj->fk_account : null;
590
                $this->cond_reglement_id = $obj->fk_cond_reglement;
591
                $this->cond_reglement_code = $obj->cond_reglement_code;
592
                $this->cond_reglement = $obj->cond_reglement;
593
                $this->cond_reglement_doc = $obj->cond_reglement_libelle_doc;
594
                $this->deposit_percent = $obj->deposit_percent;
595
596
                $this->extraparams = !empty($obj->extraparams) ? (array)json_decode($obj->extraparams, true) : array();
597
598
                $this->user_author_id = $obj->fk_user_author;
599
                $this->user_validation_id = $obj->fk_user_valid;
600
                $this->user_closing_id = $obj->fk_user_cloture;
601
602
                //Incoterms
603
                $this->fk_incoterms = $obj->fk_incoterms;
604
                $this->location_incoterms = $obj->location_incoterms;
605
                $this->label_incoterms = $obj->label_incoterms;
606
607
                // Multicurrency
608
                $this->fk_multicurrency = $obj->fk_multicurrency;
609
                $this->multicurrency_code = $obj->multicurrency_code;
610
                $this->multicurrency_tx = $obj->multicurrency_tx;
611
                $this->multicurrency_total_ht = $obj->multicurrency_total_ht;
612
                $this->multicurrency_total_tva = $obj->multicurrency_total_tva;
613
                $this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
614
615
                // Retrieve all extrafield
616
                // fetch optionals attributes and labels
617
                $this->fetch_optionals();
618
619
                $this->db->free($resql);
620
621
                $this->lines = array();
622
623
                // Lines
624
                $result = $this->fetch_lines();
625
                if ($result < 0) {
626
                    return -3;
627
                }
628
629
                return 1;
630
            }
631
632
            $this->error = "Record Not Found";
633
            return 0;
634
        } else {
635
            $this->error = $this->db->lasterror();
636
            return -1;
637
        }
638
    }
639
640
    /**
641
     * Load array lines
642
     *
643
     * @param int $only_product Return only physical products
644
     * @param int $loadalsotranslation Return translation for products
645
     * @param string $sqlforgedfilters Filter on other fields
646
     * @return     int                                 Return integer <0 if KO, >0 if OK
647
     */
648
    public function fetch_lines($only_product = 0, $loadalsotranslation = 0, $sqlforgedfilters = '')
649
    {
650
        // phpcs:enable
651
        $this->lines = array();
652
653
        $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,';
654
        $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,';
655
        $sql .= ' d.fk_unit,';
656
        $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,';
657
        $sql .= ' p.weight, p.weight_units, p.volume, p.volume_units,';
658
        $sql .= ' d.date_start, d.date_end,';
659
        $sql .= ' d.fk_multicurrency, d.multicurrency_code, d.multicurrency_subprice, d.multicurrency_total_ht, d.multicurrency_total_tva, d.multicurrency_total_ttc';
660
        $sql .= ' FROM ' . MAIN_DB_PREFIX . 'propaldet as d';
661
        $sql .= ' LEFT JOIN ' . MAIN_DB_PREFIX . 'product as p ON (d.fk_product = p.rowid)';
662
        $sql .= ' WHERE d.fk_propal = ' . ((int)$this->id);
663
        if ($only_product) {
664
            $sql .= ' AND p.fk_product_type = 0';
665
        }
666
        if ($sqlforgedfilters) {
667
            $sql .= $sqlforgedfilters;
668
        }
669
        $sql .= ' ORDER by d.rang';
670
671
        dol_syslog(get_only_class($this) . "::fetch_lines", LOG_DEBUG);
672
        $result = $this->db->query($sql);
673
        if ($result) {
674
            $num = $this->db->num_rows($result);
675
676
            $i = 0;
677
            while ($i < $num) {
678
                $objp = $this->db->fetch_object($result);
679
680
                $line = new PropaleLigne($this->db);
681
682
                $line->rowid = $objp->rowid; //Deprecated
0 ignored issues
show
Deprecated Code introduced by
The property Dolibarr\Core\Base\CommonObjectLine::$rowid has been deprecated: Try to use id property as possible (even if field into database is still rowid) ( Ignorable by Annotation )

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

682
                /** @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...
683
                $line->id = $objp->rowid;
684
                $line->fk_propal = $objp->fk_propal;
685
                $line->fk_parent_line = $objp->fk_parent_line;
686
                $line->product_type = $objp->product_type;
687
                $line->label = $objp->custom_label;
688
                $line->desc = $objp->description; // Description ligne
689
                $line->description = $objp->description; // Description ligne
690
                $line->qty = $objp->qty;
691
                $line->vat_src_code = $objp->vat_src_code;
692
                $line->tva_tx = $objp->tva_tx;
693
                $line->localtax1_tx = $objp->localtax1_tx;
694
                $line->localtax2_tx = $objp->localtax2_tx;
695
                $line->localtax1_type = $objp->localtax1_type;
696
                $line->localtax2_type = $objp->localtax2_type;
697
                $line->subprice = $objp->subprice;
698
                $line->fk_remise_except = $objp->fk_remise_except;
699
                $line->remise_percent = $objp->remise_percent;
700
                $line->price = $objp->price; // TODO deprecated
701
702
                $line->info_bits = $objp->info_bits;
703
                $line->total_ht = $objp->total_ht;
704
                $line->total_tva = $objp->total_tva;
705
                $line->total_localtax1 = $objp->total_localtax1;
706
                $line->total_localtax2 = $objp->total_localtax2;
707
                $line->total_ttc = $objp->total_ttc;
708
                $line->fk_fournprice = $objp->fk_fournprice;
709
                $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $line->fk_fournprice, $objp->pa_ht);
710
                $line->pa_ht = $marginInfos[0];
711
                $line->marge_tx = $marginInfos[1];
712
                $line->marque_tx = $marginInfos[2];
713
                $line->special_code = $objp->special_code;
714
                $line->rang = $objp->rang;
715
716
                $line->fk_product = $objp->fk_product;
717
718
                $line->ref = $objp->product_ref; // deprecated
719
                $line->libelle = $objp->product_label; // deprecated
720
721
                $line->product_ref = $objp->product_ref;
722
                $line->product_label = $objp->product_label;
723
                $line->product_desc = $objp->product_desc; // Description produit
724
                $line->product_tobatch = $objp->product_tobatch;
725
                $line->product_barcode = $objp->product_barcode;
726
727
                $line->fk_product_type = $objp->fk_product_type; // deprecated
728
                $line->fk_unit = $objp->fk_unit;
729
                $line->weight = $objp->weight;
730
                $line->weight_units = $objp->weight_units;
731
                $line->volume = $objp->volume;
732
                $line->volume_units = $objp->volume_units;
733
734
                $line->date_start = $this->db->jdate($objp->date_start);
735
                $line->date_end = $this->db->jdate($objp->date_end);
736
737
                // Multicurrency
738
                $line->fk_multicurrency = $objp->fk_multicurrency;
739
                $line->multicurrency_code = $objp->multicurrency_code;
740
                $line->multicurrency_subprice = $objp->multicurrency_subprice;
741
                $line->multicurrency_total_ht = $objp->multicurrency_total_ht;
742
                $line->multicurrency_total_tva = $objp->multicurrency_total_tva;
743
                $line->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
744
745
                $line->fetch_optionals();
746
747
                // multilangs
748
                if (getDolGlobalInt('MAIN_MULTILANGS') && !empty($objp->fk_product) && !empty($loadalsotranslation)) {
749
                    $tmpproduct = new Product($this->db);
750
                    $tmpproduct->fetch($objp->fk_product);
751
                    $tmpproduct->getMultiLangs();
752
753
                    $line->multilangs = $tmpproduct->multilangs;
754
                }
755
756
                $this->lines[$i] = $line;
757
758
                $i++;
759
            }
760
761
            $this->db->free($result);
762
763
            return $num;
764
        } else {
765
            $this->error = $this->db->lasterror();
766
            return -3;
767
        }
768
    }
769
770
    /**
771
     *  Adding line of fixed discount in the proposal in DB
772
     *
773
     * @param int $idremise Id of fixed discount
774
     * @return    int                          >0 if OK, <0 if KO
775
     */
776
    public function insert_discount($idremise)
777
    {
778
        // phpcs:enable
779
        global $langs;
780
781
        include_once DOL_DOCUMENT_ROOT . '/core/lib/price.lib.php';
782
        include_once DOL_DOCUMENT_ROOT . '/core/class/discount.class.php';
783
784
        $this->db->begin();
785
786
        $remise = new DiscountAbsolute($this->db);
787
        $result = $remise->fetch($idremise);
788
789
        if ($result > 0) {
790
            if ($remise->fk_facture) {  // Protection against multiple submission
791
                $this->error = $langs->trans("ErrorDiscountAlreadyUsed");
792
                $this->db->rollback();
793
                return -5;
794
            }
795
796
            $line = new PropaleLigne($this->db);
797
798
            $line->context = $this->context;
799
800
            $line->fk_propal = $this->id;
801
            $line->fk_remise_except = $remise->id;
802
            $line->desc = $remise->description; // Description ligne
803
            $line->vat_src_code = $remise->vat_src_code;
804
            $line->tva_tx = $remise->tva_tx;
805
            $line->subprice = -$remise->amount_ht;
806
            $line->fk_product = 0; // Id produit predefined
807
            $line->qty = 1;
808
            $line->remise_percent = 0;
809
            $line->rang = -1;
810
            $line->info_bits = 2;
811
812
            // TODO deprecated
813
            $line->price = -$remise->amount_ht;
814
815
            $line->total_ht = -$remise->amount_ht;
816
            $line->total_tva = -$remise->amount_tva;
817
            $line->total_ttc = -$remise->amount_ttc;
818
819
            $result = $line->insert();
820
            if ($result > 0) {
821
                $result = $this->update_price(1);
822
                if ($result > 0) {
823
                    $this->db->commit();
824
                    return 1;
825
                } else {
826
                    $this->db->rollback();
827
                    return -1;
828
                }
829
            } else {
830
                $this->error = $line->error;
831
                $this->errors = $line->errors;
832
                $this->db->rollback();
833
                return -2;
834
            }
835
        } else {
836
            $this->db->rollback();
837
            return -2;
838
        }
839
    }
840
841
    /**
842
     *  Update a proposal line
843
     *
844
     * @param int $rowid Id of line
845
     * @param float $pu Unit price (HT or TTC depending on price_base_type)
846
     * @param float $qty Quantity
847
     * @param float $remise_percent Discount on line
848
     * @param float|string $txtva VAT Rate (Can be '1.23' or '1.23 (ABC)')
849
     * @param float $txlocaltax1 Local tax 1 rate
850
     * @param float $txlocaltax2 Local tax 2 rate
851
     * @param string $desc Description
852
     * @param string $price_base_type HT or TTC
853
     * @param int $info_bits Miscellaneous information
854
     * @param int $special_code Special code (also used by externals modules!)
855
     * @param int $fk_parent_line Id of parent line (0 in most cases, used by modules adding sublevels into lines).
856
     * @param int $skip_update_total Keep fields total_xxx to 0 (used for special lines by some modules)
857
     * @param int $fk_fournprice Id of origin supplier price
858
     * @param int $pa_ht Price (without tax) of product when it was bought
859
     * @param string $label ???
860
     * @param int $type 0/1=Product/service
861
     * @param int|string $date_start Start date of the line
862
     * @param int|string $date_end End date of the line
863
     * @param array $array_options extrafields array
864
     * @param int|null $fk_unit Code of the unit to use. Null to use the default one
865
     * @param double $pu_ht_devise Unit price in currency
866
     * @param int $notrigger disable line update trigger
867
     * @param integer $rang line rank
868
     * @return     int                             0 if OK, <0 if KO
869
     */
870
    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)
871
    {
872
        global $mysoc, $langs;
873
874
        dol_syslog(get_only_class($this) . "::updateLine rowid=$rowid, pu=$pu, qty=$qty, remise_percent=$remise_percent,
875
        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");
876
        include_once DOL_DOCUMENT_ROOT . '/core/lib/price.lib.php';
877
878
        // Clean parameters
879
        $remise_percent = price2num($remise_percent);
880
        $qty = (float)price2num($qty);
881
        $pu = price2num($pu);
882
        $pu_ht_devise = price2num($pu_ht_devise);
883
        if (!preg_match('/\((.*)\)/', (string)$txtva)) {
884
            $txtva = price2num($txtva); // $txtva can have format '5.0(XXX)' or '5'
885
        }
886
        $txlocaltax1 = price2num($txlocaltax1);
887
        $txlocaltax2 = price2num($txlocaltax2);
888
        $pa_ht = price2num($pa_ht);
889
        if (empty($qty) && empty($special_code)) {
890
            $special_code = 3; // Set option tag
891
        }
892
        if (!empty($qty) && $special_code == 3) {
893
            $special_code = 0; // Remove option tag
894
        }
895
        if (empty($type)) {
896
            $type = 0;
897
        }
898
899
        if ($date_start && $date_end && $date_start > $date_end) {
900
            $langs->load("errors");
901
            $this->error = $langs->trans('ErrorStartDateGreaterEnd');
902
            return -1;
903
        }
904
905
        if ($this->status == self::STATUS_DRAFT) {
906
            $this->db->begin();
907
908
            // Calcul du total TTC et de la TVA pour la ligne a partir de
909
            // qty, pu, remise_percent et txtva
910
            // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
911
            // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
912
913
            $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc);
914
915
            // Clean vat code
916
            $reg = array();
917
            $vat_src_code = '';
918
            if (preg_match('/\((.*)\)/', $txtva, $reg)) {
919
                $vat_src_code = $reg[1];
920
                $txtva = preg_replace('/\s*\(.*\)/', '', $txtva); // Remove code into vatrate.
921
            }
922
923
            // TODO Implement  if (getDolGlobalInt('MAIN_UNIT_PRICE_WITH_TAX_IS_FOR_ALL_TAXES')) ?
924
925
            $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);
926
            $total_ht = $tabprice[0];
927
            $total_tva = $tabprice[1];
928
            $total_ttc = $tabprice[2];
929
            $total_localtax1 = $tabprice[9];
930
            $total_localtax2 = $tabprice[10];
931
            $pu_ht = $tabprice[3];
932
            $pu_tva = $tabprice[4];
933
            $pu_ttc = $tabprice[5];
934
935
            // MultiCurrency
936
            $multicurrency_total_ht = $tabprice[16];
937
            $multicurrency_total_tva = $tabprice[17];
938
            $multicurrency_total_ttc = $tabprice[18];
939
            $pu_ht_devise = $tabprice[19];
940
941
            // Anciens indicateurs: $price, $remise (a ne plus utiliser)
942
            $price = $pu;
943
            $remise = 0;
944
            if ((float)$remise_percent > 0) {
945
                $remise = round(((float)$pu * (float)$remise_percent / 100), 2);
946
                $price = (float)$pu - $remise;
947
            }
948
949
            //Fetch current line from the database and then clone the object and set it in $oldline property
950
            $line = new PropaleLigne($this->db);
951
            $line->fetch($rowid);
952
953
            $staticline = clone $line;
954
955
            $line->oldline = $staticline;
956
            $this->line = $line;
957
            $this->line->context = $this->context;
958
            $this->line->rang = $rang;
959
960
            // Reorder if fk_parent_line change
961
            if (!empty($fk_parent_line) && !empty($staticline->fk_parent_line) && $fk_parent_line != $staticline->fk_parent_line) {
962
                $rangmax = $this->line_max($fk_parent_line);
963
                $this->line->rang = $rangmax + 1;
964
            }
965
966
            $this->line->id = $rowid;
967
            $this->line->label = $label;
968
            $this->line->desc = $desc;
969
            $this->line->qty = $qty;
970
            $this->line->product_type = $type;
971
            $this->line->vat_src_code = $vat_src_code;
972
            $this->line->tva_tx = $txtva;
973
            $this->line->localtax1_tx = $txlocaltax1;
974
            $this->line->localtax2_tx = $txlocaltax2;
975
            $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
976
            $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
977
            $this->line->remise_percent = $remise_percent;
978
            $this->line->subprice = $pu_ht;
979
            $this->line->info_bits = $info_bits;
980
981
            $this->line->total_ht = $total_ht;
982
            $this->line->total_tva = $total_tva;
983
            $this->line->total_localtax1 = $total_localtax1;
984
            $this->line->total_localtax2 = $total_localtax2;
985
            $this->line->total_ttc = $total_ttc;
986
            $this->line->special_code = $special_code;
987
            $this->line->fk_parent_line = $fk_parent_line;
988
            $this->line->skip_update_total = $skip_update_total;
989
            $this->line->fk_unit = $fk_unit;
990
991
            $this->line->fk_fournprice = $fk_fournprice;
992
            $this->line->pa_ht = $pa_ht;
993
994
            $this->line->date_start = $date_start;
995
            $this->line->date_end = $date_end;
996
997
            if (is_array($array_options) && count($array_options) > 0) {
998
                // We replace values in this->line->array_options only for entries defined into $array_options
999
                foreach ($array_options as $key => $value) {
1000
                    $this->line->array_options[$key] = $array_options[$key];
1001
                }
1002
            }
1003
1004
            // Multicurrency
1005
            $this->line->multicurrency_subprice = $pu_ht_devise;
1006
            $this->line->multicurrency_total_ht = $multicurrency_total_ht;
1007
            $this->line->multicurrency_total_tva = $multicurrency_total_tva;
1008
            $this->line->multicurrency_total_ttc = $multicurrency_total_ttc;
1009
1010
            $result = $this->line->update($notrigger);
1011
            if ($result > 0) {
1012
                // Reorder if child line
1013
                if (!empty($fk_parent_line)) {
1014
                    $this->line_order(true, 'DESC');
1015
                }
1016
1017
                $this->update_price(1, 'auto');
1018
1019
                // $this is Propal
1020
                // $this->fk_propal = $this->id;
1021
                // $this->rowid = $rowid;
1022
1023
                $this->db->commit();
1024
                return $result;
1025
            } else {
1026
                $this->error = $this->line->error;
1027
                $this->errors = $this->line->errors;
1028
                $this->db->rollback();
1029
                return -1;
1030
            }
1031
        } else {
1032
            dol_syslog(get_only_class($this) . "::updateline Erreur -2 Propal en mode incompatible pour cette action");
1033
            return -2;
1034
        }
1035
    }
1036
1037
    /**
1038
     *      Update database
1039
     *
1040
     * @param User $user User that modify
1041
     * @param int $notrigger 0=launch triggers after, 1=disable triggers
1042
     * @return     int                     Return integer <0 if KO, >0 if OK
1043
     */
1044
    public function update(User $user, $notrigger = 0)
1045
    {
1046
        global $conf;
1047
1048
        $error = 0;
1049
1050
        // Clean parameters
1051
        if (isset($this->ref)) {
1052
            $this->ref = trim($this->ref);
1053
        }
1054
        if (isset($this->ref_client)) {
1055
            $this->ref_client = trim($this->ref_client);
1056
        }
1057
        if (isset($this->note) || isset($this->note_private)) {
1058
            $this->note_private = (isset($this->note_private) ? trim($this->note_private) : trim($this->note));
1059
        }
1060
        if (isset($this->note_public)) {
1061
            $this->note_public = trim($this->note_public);
1062
        }
1063
        if (isset($this->model_pdf)) {
1064
            $this->model_pdf = trim($this->model_pdf);
1065
        }
1066
        if (isset($this->import_key)) {
1067
            $this->import_key = trim($this->import_key);
1068
        }
1069
        if (!empty($this->duree_validite) && is_numeric($this->duree_validite)) {
1070
            $this->fin_validite = $this->date + ($this->duree_validite * 24 * 3600);
1071
        }
1072
1073
        // Check parameters
1074
        // Put here code to add control on parameters values
1075
1076
        // Update request
1077
        $sql = "UPDATE " . MAIN_DB_PREFIX . "propal SET";
1078
        $sql .= " ref=" . (isset($this->ref) ? "'" . $this->db->escape($this->ref) . "'" : "null") . ",";
1079
        $sql .= " ref_client=" . (isset($this->ref_client) ? "'" . $this->db->escape($this->ref_client) . "'" : "null") . ",";
1080
        $sql .= " ref_ext=" . (isset($this->ref_ext) ? "'" . $this->db->escape($this->ref_ext) . "'" : "null") . ",";
1081
        $sql .= " fk_soc=" . (isset($this->socid) ? $this->socid : "null") . ",";
1082
        $sql .= " datep=" . (strval($this->date) != '' ? "'" . $this->db->idate($this->date) . "'" : 'null') . ",";
1083
        if (!empty($this->fin_validite)) {
1084
            $sql .= " fin_validite=" . (strval($this->fin_validite) != '' ? "'" . $this->db->idate($this->fin_validite) . "'" : 'null') . ",";
1085
        }
1086
        $sql .= " date_valid=" . (strval($this->date_validation) != '' ? "'" . $this->db->idate($this->date_validation) . "'" : 'null') . ",";
1087
        $sql .= " total_tva=" . (isset($this->total_tva) ? $this->total_tva : "null") . ",";
1088
        $sql .= " localtax1=" . (isset($this->total_localtax1) ? $this->total_localtax1 : "null") . ",";
1089
        $sql .= " localtax2=" . (isset($this->total_localtax2) ? $this->total_localtax2 : "null") . ",";
1090
        $sql .= " total_ht=" . (isset($this->total_ht) ? $this->total_ht : "null") . ",";
1091
        $sql .= " total_ttc=" . (isset($this->total_ttc) ? $this->total_ttc : "null") . ",";
1092
        $sql .= " fk_statut=" . (isset($this->statut) ? $this->statut : "null") . ",";
1093
        $sql .= " fk_user_author=" . (isset($this->user_author_id) ? $this->user_author_id : "null") . ",";
1094
        $sql .= " fk_user_valid=" . (isset($this->user_validation_id) ? $this->user_validation_id : "null") . ",";
1095
        $sql .= " fk_projet=" . (isset($this->fk_project) ? $this->fk_project : "null") . ",";
1096
        $sql .= " fk_cond_reglement=" . (isset($this->cond_reglement_id) ? $this->cond_reglement_id : "null") . ",";
1097
        $sql .= " deposit_percent=" . (!empty($this->deposit_percent) ? "'" . $this->db->escape($this->deposit_percent) . "'" : "null") . ",";
1098
        $sql .= " fk_mode_reglement=" . (isset($this->mode_reglement_id) ? $this->mode_reglement_id : "null") . ",";
1099
        $sql .= " fk_input_reason=" . (isset($this->demand_reason_id) ? $this->demand_reason_id : "null") . ",";
1100
        $sql .= " note_private=" . (isset($this->note_private) ? "'" . $this->db->escape($this->note_private) . "'" : "null") . ",";
1101
        $sql .= " note_public=" . (isset($this->note_public) ? "'" . $this->db->escape($this->note_public) . "'" : "null") . ",";
1102
        $sql .= " model_pdf=" . (isset($this->model_pdf) ? "'" . $this->db->escape($this->model_pdf) . "'" : "null") . ",";
1103
        $sql .= " import_key=" . (isset($this->import_key) ? "'" . $this->db->escape($this->import_key) . "'" : "null");
1104
        $sql .= " WHERE rowid=" . ((int)$this->id);
1105
1106
        $this->db->begin();
1107
1108
        dol_syslog(get_only_class($this) . "::update", LOG_DEBUG);
1109
        $resql = $this->db->query($sql);
1110
        if (!$resql) {
1111
            $error++;
1112
            $this->errors[] = "Error " . $this->db->lasterror();
1113
        }
1114
1115
        if (!$error) {
1116
            $result = $this->insertExtraFields();
1117
            if ($result < 0) {
1118
                $error++;
1119
            }
1120
        }
1121
1122
        if (!$error && !$notrigger) {
1123
            // Call trigger
1124
            $result = $this->call_trigger('PROPAL_MODIFY', $user);
1125
            if ($result < 0) {
1126
                $error++;
1127
            }
1128
            // End call triggers
1129
        }
1130
1131
        // Commit or rollback
1132
        if ($error) {
1133
            foreach ($this->errors as $errmsg) {
1134
                dol_syslog(get_only_class($this) . "::update " . $errmsg, LOG_ERR);
1135
                $this->error .= ($this->error ? ', ' . $errmsg : $errmsg);
1136
            }
1137
            $this->db->rollback();
1138
            return -1 * $error;
1139
        } else {
1140
            $this->db->commit();
1141
            return 1;
1142
        }
1143
    }
1144
1145
    /**
1146
     *  Delete detail line
1147
     *
1148
     * @param int $lineid Id of line to delete
1149
     * @param int $id Id of object (for a check)
1150
     * @return     int                     >0 if OK, <0 if KO
1151
     */
1152
    public function deleteLine($lineid, $id = 0)
1153
    {
1154
        global $user;
1155
1156
        if ($this->statut == self::STATUS_DRAFT) {
1157
            $this->db->begin();
1158
1159
            $line = new PropaleLigne($this->db);
1160
1161
            $line->context = $this->context;
1162
1163
            // Load data
1164
            $line->fetch($lineid);
1165
1166
            if ($id > 0 && $line->fk_propal != $id) {
1167
                $this->error = 'ErrorLineIDDoesNotMatchWithObjectID';
1168
                return -1;
1169
            }
1170
1171
            // Memorize previous line for triggers
1172
            $staticline = clone $line;
1173
            $line->oldline = $staticline;
1174
1175
            if ($line->delete($user) > 0) {
1176
                $this->update_price(1);
1177
1178
                $this->db->commit();
1179
                return 1;
1180
            } else {
1181
                $this->error = $line->error;
1182
                $this->errors = $line->errors;
1183
                $this->db->rollback();
1184
                return -1;
1185
            }
1186
        } else {
1187
            $this->error = 'ErrorDeleteLineNotAllowedByObjectStatus';
1188
            return -2;
1189
        }
1190
    }
1191
1192
1193
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1194
1195
    /**
1196
     *  Delete proposal
1197
     *
1198
     * @param User $user Object user that delete
1199
     * @param int $notrigger 1=Does not execute triggers, 0= execute triggers
1200
     * @return int                     >0 if OK, <=0 if KO
1201
     */
1202
    public function delete($user, $notrigger = 0)
1203
    {
1204
        global $conf;
1205
        require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/files.lib.php';
1206
1207
        $error = 0;
1208
1209
        $this->db->begin();
1210
1211
        if (!$notrigger) {
1212
            // Call trigger
1213
            $result = $this->call_trigger('PROPAL_DELETE', $user);
1214
            if ($result < 0) {
1215
                $error++;
1216
            }
1217
            // End call triggers
1218
        }
1219
1220
        // Delete extrafields of lines and lines
1221
        if (!$error && !empty($this->table_element_line)) {
1222
            $tabletodelete = $this->table_element_line;
1223
            $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) . ")";
1224
            $sql = "DELETE FROM " . MAIN_DB_PREFIX . $tabletodelete . " WHERE " . $this->fk_element . " = " . ((int)$this->id);
1225
            if (!$this->db->query($sqlef) || !$this->db->query($sql)) {
1226
                $error++;
1227
                $this->error = $this->db->lasterror();
1228
                $this->errors[] = $this->error;
1229
                dol_syslog(get_only_class($this) . "::delete error " . $this->error, LOG_ERR);
1230
            }
1231
        }
1232
1233
        if (!$error) {
1234
            // Delete linked object
1235
            $res = $this->deleteObjectLinked();
1236
            if ($res < 0) {
1237
                $error++;
1238
            }
1239
        }
1240
1241
        if (!$error) {
1242
            // Delete linked contacts
1243
            $res = $this->delete_linked_contact();
1244
            if ($res < 0) {
1245
                $error++;
1246
            }
1247
        }
1248
1249
        // Removed extrafields of object
1250
        if (!$error) {
1251
            $result = $this->deleteExtraFields();
1252
            if ($result < 0) {
1253
                $error++;
1254
                dol_syslog(get_only_class($this) . "::delete error " . $this->error, LOG_ERR);
1255
            }
1256
        }
1257
1258
        // Delete main record
1259
        if (!$error) {
1260
            $sql = "DELETE FROM " . MAIN_DB_PREFIX . $this->table_element . " WHERE rowid = " . ((int)$this->id);
1261
            $res = $this->db->query($sql);
1262
            if (!$res) {
1263
                $error++;
1264
                $this->error = $this->db->lasterror();
1265
                $this->errors[] = $this->error;
1266
                dol_syslog(get_only_class($this) . "::delete error " . $this->error, LOG_ERR);
1267
            }
1268
        }
1269
1270
        // Delete record into ECM index and physically
1271
        if (!$error) {
1272
            $res = $this->deleteEcmFiles(0); // Deleting files physically is done later with the dol_delete_dir_recursive
1273
            $res = $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
1274
            if (!$res) {
1275
                $error++;
1276
            }
1277
        }
1278
1279
        if (!$error) {
1280
            // We remove directory
1281
            $ref = dol_sanitizeFileName($this->ref);
1282
            if ($conf->propal->multidir_output[$this->entity] && !empty($this->ref)) {
1283
                $dir = $conf->propal->multidir_output[$this->entity] . "/" . $ref;
1284
                $file = $dir . "/" . $ref . ".pdf";
1285
                if (file_exists($file)) {
1286
                    dol_delete_preview($this);
1287
1288
                    if (!dol_delete_file($file, 0, 0, 0, $this)) {
1289
                        $this->error = 'ErrorFailToDeleteFile';
1290
                        $this->errors[] = $this->error;
1291
                        $this->db->rollback();
1292
                        return 0;
1293
                    }
1294
                }
1295
                if (file_exists($dir)) {
1296
                    $res = @dol_delete_dir_recursive($dir);     // delete files physically + into ecm tables
1297
                    if (!$res) {
1298
                        $this->error = 'ErrorFailToDeleteDir';
1299
                        $this->errors[] = $this->error;
1300
                        $this->db->rollback();
1301
                        return 0;
1302
                    }
1303
                }
1304
            }
1305
        }
1306
1307
        if (!$error) {
1308
            dol_syslog(get_only_class($this) . "::delete " . $this->id . " by " . $user->id, LOG_DEBUG);
1309
            $this->db->commit();
1310
            return 1;
1311
        } else {
1312
            $this->db->rollback();
1313
            return -1;
1314
        }
1315
    }
1316
1317
    /**
1318
     *      Load an object from its id and create a new one in database
1319
     *
1320
     * @param User $user User making the clone
1321
     * @param int $socid Id of thirdparty
1322
     * @param int $forceentity Entity id to force
1323
     * @param bool $update_prices [=false] Update prices if true
1324
     * @param bool $update_desc [=false] Update description if true
1325
     * @return     int                     New id of clone
1326
     */
1327
    public function createFromClone(User $user, $socid = 0, $forceentity = null, $update_prices = false, $update_desc = false)
1328
    {
1329
        global $conf, $hookmanager, $mysoc;
1330
1331
        dol_include_once('/projet/class/project.class.php');
1332
1333
        $error = 0;
1334
        $now = dol_now();
1335
1336
        dol_syslog(__METHOD__, LOG_DEBUG);
1337
1338
        $object = new self($this->db);
1339
1340
        $this->db->begin();
1341
1342
        // Load source object
1343
        $object->fetch($this->id);
1344
1345
        $objsoc = new Societe($this->db);
1346
1347
        // Change socid if needed
1348
        if (!empty($socid) && $socid != $object->socid) {
1349
            if ($objsoc->fetch($socid) > 0) {
1350
                $object->socid = $objsoc->id;
1351
                $object->cond_reglement_id = (!empty($objsoc->cond_reglement_id) ? $objsoc->cond_reglement_id : 0);
1352
                $object->deposit_percent = (!empty($objsoc->deposit_percent) ? $objsoc->deposit_percent : null);
1353
                $object->mode_reglement_id = (!empty($objsoc->mode_reglement_id) ? $objsoc->mode_reglement_id : 0);
1354
                $object->fk_delivery_address = 0;
1355
1356
                /*if (isModEnabled('project'))
1357
                {
1358
                    $project = new Project($db);
1359
                    if ($this->fk_project > 0 && $project->fetch($this->fk_project)) {
1360
                        if ($project->socid <= 0) $clonedObj->fk_project = $this->fk_project;
1361
                        else $clonedObj->fk_project = '';
1362
                    } else {
1363
                        $clonedObj->fk_project = '';
1364
                    }
1365
                }*/
1366
                $object->fk_project = 0; // A cloned proposal is set by default to no project.
1367
            }
1368
1369
            // reset ref_client
1370
            $object->ref_client = '';
1371
1372
            // TODO Change product price if multi-prices
1373
        } else {
1374
            $objsoc->fetch($object->socid);
1375
        }
1376
1377
        // update prices
1378
        if ($update_prices === true || $update_desc === true) {
1379
            if ($objsoc->id > 0 && !empty($object->lines)) {
1380
                if ($update_prices === true && getDolGlobalString('PRODUIT_CUSTOMER_PRICES')) {
1381
                    // If price per customer
1382
                }
1383
1384
                foreach ($object->lines as $line) {
1385
                    $line->id = 0;
1386
1387
                    if ($line->fk_product > 0) {
1388
                        $prod = new Product($this->db);
1389
                        $res = $prod->fetch($line->fk_product);
1390
                        if ($res > 0) {
1391
                            if ($update_prices === true) {
1392
                                $pu_ht = $prod->price;
1393
                                $tva_tx = get_default_tva($mysoc, $objsoc, $prod->id);
1394
                                $remise_percent = $objsoc->remise_percent;
1395
1396
                                if (getDolGlobalString('PRODUIT_MULTIPRICES') && $objsoc->price_level > 0) {
1397
                                    $pu_ht = $prod->multiprices[$objsoc->price_level];
1398
                                    if (getDolGlobalString('PRODUIT_MULTIPRICES_USE_VAT_PER_LEVEL')) {  // using this option is a bug. kept for backward compatibility
1399
                                        if (isset($prod->multiprices_tva_tx[$objsoc->price_level])) {
1400
                                            $tva_tx = $prod->multiprices_tva_tx[$objsoc->price_level];
1401
                                        }
1402
                                    }
1403
                                } elseif (getDolGlobalString('PRODUIT_CUSTOMER_PRICES')) {
1404
                                    $prodcustprice = new ProductCustomerPrice($this->db);
1405
                                    $filter = array('t.fk_product' => $prod->id, 't.fk_soc' => $objsoc->id);
1406
                                    $result = $prodcustprice->fetchAll('', '', 0, 0, $filter);
1407
                                    if ($result) {
1408
                                        // If there is some prices specific to the customer
1409
                                        if (count($prodcustprice->lines) > 0) {
1410
                                            $pu_ht = price($prodcustprice->lines[0]->price);
1411
                                            $tva_tx = ($prodcustprice->lines[0]->default_vat_code ? $prodcustprice->lines[0]->tva_tx . ' (' . $prodcustprice->lines[0]->default_vat_code . ' )' : $prodcustprice->lines[0]->tva_tx);
1412
                                            if ($prodcustprice->lines[0]->default_vat_code && !preg_match('/\(.*\)/', $tva_tx)) {
1413
                                                $tva_tx .= ' (' . $prodcustprice->lines[0]->default_vat_code . ')';
1414
                                            }
1415
                                        }
1416
                                    }
1417
                                }
1418
1419
                                $line->subprice = $pu_ht;
1420
                                $line->tva_tx = $tva_tx;
1421
                                $line->remise_percent = $remise_percent;
1422
                            }
1423
                            if ($update_desc === true) {
1424
                                $line->desc = $prod->description;
1425
                            }
1426
                        }
1427
                    }
1428
                }
1429
            }
1430
        }
1431
1432
        $object->id = 0;
1433
        $object->ref = '';
1434
        $object->entity = (!empty($forceentity) ? $forceentity : $object->entity);
1435
        $object->statut = self::STATUS_DRAFT;
1436
1437
        // Clear fields
1438
        $object->user_creation_id = $user->id;
1439
        $object->user_validation_id = 0;
1440
        $object->date = $now;
1441
        $object->datep = $now; // deprecated
1442
        $object->fin_validite = $object->date + ($object->duree_validite * 24 * 3600);
1443
        if (!getDolGlobalString('MAIN_KEEP_REF_CUSTOMER_ON_CLONING')) {
1444
            $object->ref_client = '';
1445
        }
1446
        if (getDolGlobalInt('MAIN_DONT_KEEP_NOTE_ON_CLONING') == 1) {
1447
            $object->note_private = '';
1448
            $object->note_public = '';
1449
        }
1450
        // Create clone
1451
        $object->context['createfromclone'] = 'createfromclone';
1452
        $result = $object->create($user);
1453
        if ($result < 0) {
1454
            $this->error = $object->error;
1455
            $this->errors = array_merge($this->errors, $object->errors);
1456
            $error++;
1457
        }
1458
1459
        if (!$error && !getDolGlobalInt('MAIN_IGNORE_CONTACTS_ON_CLONING')) {
1460
            // copy internal contacts
1461
            if ($object->copy_linked_contact($this, 'internal') < 0) {
1462
                $error++;
1463
            }
1464
        }
1465
1466
        if (!$error) {
1467
            // copy external contacts if same company
1468
            if ($this->socid == $object->socid) {
1469
                if ($object->copy_linked_contact($this, 'external') < 0) {
1470
                    $error++;
1471
                }
1472
            }
1473
        }
1474
1475
        if (!$error) {
1476
            // Hook of thirdparty module
1477
            if (is_object($hookmanager)) {
1478
                $parameters = array('objFrom' => $this, 'clonedObj' => $object);
1479
                $action = '';
1480
                $reshook = $hookmanager->executeHooks('createFrom', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
1481
                if ($reshook < 0) {
1482
                    $this->setErrorsFromObject($hookmanager);
1483
                    $error++;
1484
                }
1485
            }
1486
        }
1487
1488
        unset($object->context['createfromclone']);
1489
1490
        // End
1491
        if (!$error) {
1492
            $this->db->commit();
1493
            return $object->id;
1494
        } else {
1495
            $this->db->rollback();
1496
            return -1;
1497
        }
1498
    }
1499
1500
1501
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1502
1503
    /**
1504
     *  Create commercial proposal into database
1505
     *  this->ref can be set or empty. If empty, we will use "(PROVid)"
1506
     *
1507
     * @param User $user User that create
1508
     * @param int $notrigger 1=Does not execute triggers, 0= execute triggers
1509
     * @return     int                 Return integer <0 if KO, >=0 if OK
1510
     */
1511
    public function create($user, $notrigger = 0)
1512
    {
1513
        global $conf, $hookmanager, $mysoc;
1514
        $error = 0;
1515
1516
        $now = dol_now();
1517
1518
        // Clean parameters
1519
        if (empty($this->date)) {
1520
            $this->date = $this->datep;
1521
        }
1522
        $this->fin_validite = $this->date + ($this->duree_validite * 24 * 3600);
1523
        if (empty($this->availability_id)) {
1524
            $this->availability_id = 0;
1525
        }
1526
        if (empty($this->demand_reason_id)) {
1527
            $this->demand_reason_id = 0;
1528
        }
1529
1530
        // Multicurrency (test on $this->multicurrency_tx because we should take the default rate only if not using origin rate)
1531
        if (!empty($this->multicurrency_code) && empty($this->multicurrency_tx)) {
1532
            list($this->fk_multicurrency, $this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $this->date);
1533
        } else {
1534
            $this->fk_multicurrency = MultiCurrency::getIdFromCode($this->db, $this->multicurrency_code);
1535
        }
1536
        if (empty($this->fk_multicurrency)) {
1537
            $this->multicurrency_code = $conf->currency;
1538
            $this->fk_multicurrency = 0;
1539
            $this->multicurrency_tx = 1;
1540
        }
1541
1542
        // Set tmp vars
1543
        $delivery_date = $this->delivery_date;
1544
1545
        dol_syslog(get_only_class($this) . "::create");
1546
1547
        // Check parameters
1548
        $result = $this->fetch_thirdparty();
1549
        if ($result < 0) {
1550
            $this->error = "Failed to fetch company";
1551
            dol_syslog(get_only_class($this) . "::create " . $this->error, LOG_ERR);
1552
            return -3;
1553
        }
1554
1555
        // Check parameters
1556
        if (!empty($this->ref)) {   // We check that ref is not already used
1557
            $result = self::isExistingObject($this->element, 0, $this->ref); // Check ref is not yet used
1558
            if ($result > 0) {
1559
                $this->error = 'ErrorRefAlreadyExists';
1560
                dol_syslog(get_only_class($this) . "::create " . $this->error, LOG_WARNING);
1561
                $this->db->rollback();
1562
                return -1;
1563
            }
1564
        }
1565
1566
        if (empty($this->date)) {
1567
            $this->error = "Date of proposal is required";
1568
            dol_syslog(get_only_class($this) . "::create " . $this->error, LOG_ERR);
1569
            return -4;
1570
        }
1571
1572
1573
        $this->db->begin();
1574
1575
        // Insert into database
1576
        $sql = "INSERT INTO " . MAIN_DB_PREFIX . "propal (";
1577
        $sql .= "fk_soc";
1578
        $sql .= ", price";
1579
        $sql .= ", total_tva";
1580
        $sql .= ", total_ttc";
1581
        $sql .= ", datep";
1582
        $sql .= ", datec";
1583
        $sql .= ", ref";
1584
        $sql .= ", fk_user_author";
1585
        $sql .= ", note_private";
1586
        $sql .= ", note_public";
1587
        $sql .= ", model_pdf";
1588
        $sql .= ", fin_validite";
1589
        $sql .= ", fk_cond_reglement";
1590
        $sql .= ", deposit_percent";
1591
        $sql .= ", fk_mode_reglement";
1592
        $sql .= ", fk_account";
1593
        $sql .= ", ref_client";
1594
        $sql .= ", ref_ext";
1595
        $sql .= ", date_livraison";
1596
        $sql .= ", fk_shipping_method";
1597
        $sql .= ", fk_warehouse";
1598
        $sql .= ", fk_availability";
1599
        $sql .= ", fk_input_reason";
1600
        $sql .= ", fk_projet";
1601
        $sql .= ", fk_incoterms";
1602
        $sql .= ", location_incoterms";
1603
        $sql .= ", entity";
1604
        $sql .= ", fk_multicurrency";
1605
        $sql .= ", multicurrency_code";
1606
        $sql .= ", multicurrency_tx";
1607
        $sql .= ") ";
1608
        $sql .= " VALUES (";
1609
        $sql .= $this->socid;
1610
        $sql .= ", 0";
1611
        $sql .= ", 0";
1612
        $sql .= ", 0";
1613
        $sql .= ", '" . $this->db->idate($this->date) . "'";
1614
        $sql .= ", '" . $this->db->idate($now) . "'";
1615
        $sql .= ", '(PROV)'";
1616
        $sql .= ", " . ($user->id > 0 ? ((int)$user->id) : "NULL");
1617
        $sql .= ", '" . $this->db->escape($this->note_private) . "'";
1618
        $sql .= ", '" . $this->db->escape($this->note_public) . "'";
1619
        $sql .= ", '" . $this->db->escape($this->model_pdf) . "'";
1620
        $sql .= ", " . ($this->fin_validite != '' ? "'" . $this->db->idate($this->fin_validite) . "'" : "NULL");
1621
        $sql .= ", " . ($this->cond_reglement_id > 0 ? ((int)$this->cond_reglement_id) : 'NULL');
1622
        $sql .= ", " . (!empty($this->deposit_percent) ? "'" . $this->db->escape($this->deposit_percent) . "'" : 'NULL');
1623
        $sql .= ", " . ($this->mode_reglement_id > 0 ? ((int)$this->mode_reglement_id) : 'NULL');
1624
        $sql .= ", " . ($this->fk_account > 0 ? ((int)$this->fk_account) : 'NULL');
1625
        $sql .= ", '" . $this->db->escape($this->ref_client) . "'";
1626
        $sql .= ", '" . $this->db->escape($this->ref_ext) . "'";
1627
        $sql .= ", " . (empty($delivery_date) ? "NULL" : "'" . $this->db->idate($delivery_date) . "'");
1628
        $sql .= ", " . ($this->shipping_method_id > 0 ? $this->shipping_method_id : 'NULL');
1629
        $sql .= ", " . ($this->warehouse_id > 0 ? $this->warehouse_id : 'NULL');
1630
        $sql .= ", " . $this->availability_id;
1631
        $sql .= ", " . $this->demand_reason_id;
1632
        $sql .= ", " . ($this->fk_project ? $this->fk_project : "null");
1633
        $sql .= ", " . (int)$this->fk_incoterms;
1634
        $sql .= ", '" . $this->db->escape($this->location_incoterms) . "'";
1635
        $sql .= ", " . setEntity($this);
1636
        $sql .= ", " . (int)$this->fk_multicurrency;
1637
        $sql .= ", '" . $this->db->escape($this->multicurrency_code) . "'";
1638
        $sql .= ", " . (float)$this->multicurrency_tx;
1639
        $sql .= ")";
1640
1641
        dol_syslog(get_only_class($this) . "::create", LOG_DEBUG);
1642
        $resql = $this->db->query($sql);
1643
        if ($resql) {
1644
            $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX . "propal");
1645
1646
            if ($this->id) {
1647
                $this->ref = '(PROV' . $this->id . ')';
1648
                $sql = 'UPDATE ' . MAIN_DB_PREFIX . "propal SET ref='" . $this->db->escape($this->ref) . "' WHERE rowid=" . ((int)$this->id);
1649
1650
                dol_syslog(get_only_class($this) . "::create", LOG_DEBUG);
1651
                $resql = $this->db->query($sql);
1652
                if (!$resql) {
1653
                    $error++;
1654
                }
1655
1656
                if (!empty($this->linkedObjectsIds) && empty($this->linked_objects)) {  // To use new linkedObjectsIds instead of old linked_objects
1657
                    $this->linked_objects = $this->linkedObjectsIds; // TODO Replace linked_objects with linkedObjectsIds
1658
                }
1659
1660
                // Add object linked
1661
                if (!$error && $this->id && !empty($this->linked_objects) && is_array($this->linked_objects)) {
1662
                    foreach ($this->linked_objects as $origin => $tmp_origin_id) {
1663
                        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, ...))
1664
                            foreach ($tmp_origin_id as $origin_id) {
1665
                                $ret = $this->add_object_linked($origin, $origin_id);
1666
                                if (!$ret) {
1667
                                    $this->error = $this->db->lasterror();
1668
                                    $error++;
1669
                                }
1670
                            }
1671
                        } else { // Old behaviour, if linked_object has only one link per type, so is something like array('contract'=>id1))
1672
                            $origin_id = $tmp_origin_id;
1673
                            $ret = $this->add_object_linked($origin, $origin_id);
1674
                            if (!$ret) {
1675
                                $this->error = $this->db->lasterror();
1676
                                $error++;
1677
                            }
1678
                        }
1679
                    }
1680
                }
1681
1682
                /*
1683
                 *  Insertion du detail des produits dans la base
1684
                 *  Insert products detail in database
1685
                 */
1686
                if (!$error) {
1687
                    $fk_parent_line = 0;
1688
                    $num = count($this->lines);
1689
1690
                    for ($i = 0; $i < $num; $i++) {
1691
                        if (!is_object($this->lines[$i])) { // If this->lines is not array of objects, coming from REST API
1692
                            // Convert into object this->lines[$i].
1693
                            $line = (object)$this->lines[$i];
1694
                        } else {
1695
                            $line = $this->lines[$i];
1696
                        }
1697
                        // Reset fk_parent_line for line that are not child lines or special product
1698
                        if (($line->product_type != 9 && empty($line->fk_parent_line)) || $line->product_type == 9) {
1699
                            $fk_parent_line = 0;
1700
                        }
1701
                        // Complete vat rate with code
1702
                        $vatrate = $line->tva_tx;
1703
                        if ($line->vat_src_code && !preg_match('/\(.*\)/', $vatrate)) {
1704
                            $vatrate .= ' (' . $line->vat_src_code . ')';
1705
                        }
1706
1707
                        if (getDolGlobalString('MAIN_CREATEFROM_KEEP_LINE_ORIGIN_INFORMATION')) {
1708
                            $originid = $line->origin_id;
1709
                            $origintype = $line->origin;
1710
                        } else {
1711
                            $originid = $line->id;
1712
                            $origintype = $this->element;
1713
                        }
1714
1715
                        $result = $this->addline(
1716
                            $line->desc,
1717
                            $line->subprice,
1718
                            $line->qty,
1719
                            $vatrate,
1720
                            $line->localtax1_tx,
1721
                            $line->localtax2_tx,
1722
                            $line->fk_product,
1723
                            $line->remise_percent,
1724
                            'HT',
1725
                            0,
1726
                            $line->info_bits,
1727
                            $line->product_type,
1728
                            $line->rang,
1729
                            $line->special_code,
1730
                            $fk_parent_line,
1731
                            $line->fk_fournprice,
1732
                            $line->pa_ht,
1733
                            $line->label,
1734
                            $line->date_start,
1735
                            $line->date_end,
1736
                            $line->array_options,
1737
                            $line->fk_unit,
1738
                            $origintype,
1739
                            $originid,
1740
                            0,
1741
                            0,
1742
                            1
1743
                        );
1744
1745
                        if ($result < 0) {
1746
                            $error++;
1747
                            $this->error = $this->db->error;
1748
                            dol_print_error($this->db);
1749
                            break;
1750
                        }
1751
1752
                        // Set the id on created row
1753
                        $line->id = $result;
1754
1755
                        // Defined the new fk_parent_line
1756
                        if ($result > 0 && $line->product_type == 9) {
1757
                            $fk_parent_line = $result;
1758
                        }
1759
                    }
1760
                }
1761
1762
                // Set delivery address
1763
                /*if (! $error && $this->fk_delivery_address)
1764
                {
1765
                    $sql = "UPDATE ".MAIN_DB_PREFIX."propal";
1766
                    $sql.= " SET fk_delivery_address = ".((int) $this->fk_delivery_address);
1767
                    $sql.= " WHERE ref = '".$this->db->escape($this->ref)."'";
1768
                    $sql.= " AND entity = ".setEntity($this);
1769
1770
                    $result=$this->db->query($sql);
1771
                }*/
1772
1773
                if (!$error) {
1774
                    // Mise a jour infos denormalisees
1775
                    $resql = $this->update_price(1, 'auto', 0, $mysoc);
1776
                    if ($resql) {
1777
                        $action = 'update';
1778
1779
                        // Actions on extra fields
1780
                        if (!$error) {
1781
                            $result = $this->insertExtraFields();
1782
                            if ($result < 0) {
1783
                                $error++;
1784
                            }
1785
                        }
1786
1787
                        if (!$error && !$notrigger) {
1788
                            // Call trigger
1789
                            $result = $this->call_trigger('PROPAL_CREATE', $user);
1790
                            if ($result < 0) {
1791
                                $error++;
1792
                            }
1793
                            // End call triggers
1794
                        }
1795
                    } else {
1796
                        $this->error = $this->db->lasterror();
1797
                        $error++;
1798
                    }
1799
                }
1800
            } else {
1801
                $this->error = $this->db->lasterror();
1802
                $error++;
1803
            }
1804
1805
            if (!$error) {
1806
                $this->db->commit();
1807
                dol_syslog(get_only_class($this) . "::create done id=" . $this->id);
1808
                return $this->id;
1809
            } else {
1810
                $this->db->rollback();
1811
                return -2;
1812
            }
1813
        } else {
1814
            $this->error = $this->db->lasterror();
1815
            $this->db->rollback();
1816
            return -1;
1817
        }
1818
    }
1819
1820
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1821
1822
    /**
1823
     *      Add a proposal line into database (linked to product/service or not)
1824
     *      The parameters are already supposed to be appropriate and with final values to the call
1825
     *      of this method. Also, for the VAT rate, it must have already been defined
1826
     *      by whose calling the method get_default_tva (societe_vendeuse, societe_acheteuse, '' product)
1827
     *      and desc must already have the right value (it's up to the caller to manage multilanguage)
1828
     *
1829
     * @param string $desc Description of line
1830
     * @param float $pu_ht Unit price
1831
     * @param float $qty Quantity
1832
     * @param float|string $txtva Force Vat rate, -1 for auto (Can contain the vat_src_code too with syntax '9.9 (CODE)')
1833
     * @param float $txlocaltax1 Local tax 1 rate (deprecated, use instead txtva with code inside)
1834
     * @param float $txlocaltax2 Local tax 2 rate (deprecated, use instead txtva with code inside)
1835
     * @param int $fk_product Product/Service ID predefined
1836
     * @param float $remise_percent Pourcentage de remise de la ligne
1837
     * @param string $price_base_type HT or TTC
1838
     * @param float $pu_ttc Prix unitaire TTC
1839
     * @param int $info_bits Bits for type of lines
1840
     * @param int $type Type of line (0=product, 1=service). Not used if fk_product is defined, the type of product is used.
1841
     * @param int $rang Position of line
1842
     * @param int $special_code Special code (also used by externals modules!)
1843
     * @param int $fk_parent_line Id of parent line
1844
     * @param int $fk_fournprice Id supplier price
1845
     * @param int $pa_ht Buying price without tax
1846
     * @param string $label ???
1847
     * @param int|string $date_start Start date of the line
1848
     * @param int|string $date_end End date of the line
1849
     * @param array $array_options extrafields array
1850
     * @param int|null $fk_unit Code of the unit to use. Null to use the default one
1851
     * @param string $origin Depend on global conf MAIN_CREATEFROM_KEEP_LINE_ORIGIN_INFORMATION can be 'orderdet', 'propaldet'..., else 'order','propal,'....
1852
     * @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
1853
     * @param double $pu_ht_devise Unit price in currency
1854
     * @param int $fk_remise_except Id discount if line is from a discount
1855
     * @param int $noupdateafterinsertline No update after insert of line
1856
     * @return     int                             >0 if OK, <0 if KO
1857
     * @see        add_product()
1858
     */
1859
    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)
1860
    {
1861
        global $mysoc, $conf, $langs;
1862
1863
        dol_syslog(get_only_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);
1864
1865
        if ($this->statut == self::STATUS_DRAFT) {
1866
            include_once DOL_DOCUMENT_ROOT . '/core/lib/price.lib.php';
1867
1868
            // Clean parameters
1869
            if (empty($remise_percent)) {
1870
                $remise_percent = 0;
1871
            }
1872
            if (empty($qty)) {
1873
                $qty = 0;
1874
            }
1875
            if (empty($info_bits)) {
1876
                $info_bits = 0;
1877
            }
1878
            if (empty($rang)) {
1879
                $rang = 0;
1880
            }
1881
            if (empty($fk_parent_line) || $fk_parent_line < 0) {
1882
                $fk_parent_line = 0;
1883
            }
1884
1885
            $remise_percent = price2num($remise_percent);
1886
            $qty = (float)price2num($qty);
1887
            $pu_ht = price2num($pu_ht);
1888
            $pu_ht_devise = price2num($pu_ht_devise);
1889
            $pu_ttc = price2num($pu_ttc);
1890
            if (!preg_match('/\((.*)\)/', (string)$txtva)) {
1891
                $txtva = price2num($txtva); // $txtva can have format '5,1' or '5.1' or '5.1(XXX)', we must clean only if '5,1'
1892
            }
1893
            $txlocaltax1 = price2num($txlocaltax1);
1894
            $txlocaltax2 = price2num($txlocaltax2);
1895
            $pa_ht = price2num($pa_ht);
1896
            if ($price_base_type == 'HT') {
1897
                $pu = $pu_ht;
1898
            } else {
1899
                $pu = $pu_ttc;
1900
            }
1901
1902
            // Check parameters
1903
            if ($type < 0) {
1904
                return -1;
1905
            }
1906
1907
            if ($date_start && $date_end && $date_start > $date_end) {
1908
                $langs->load("errors");
1909
                $this->error = $langs->trans('ErrorStartDateGreaterEnd');
1910
                return -1;
1911
            }
1912
1913
            $this->db->begin();
1914
1915
            $product_type = $type;
1916
            if (!empty($fk_product) && $fk_product > 0) {
1917
                $product = new Product($this->db);
1918
                $result = $product->fetch($fk_product);
1919
                $product_type = $product->type;
1920
1921
                if (getDolGlobalString('STOCK_MUST_BE_ENOUGH_FOR_PROPOSAL') && $product_type == 0 && $product->stock_reel < $qty) {
1922
                    $langs->load("errors");
1923
                    $this->error = $langs->trans('ErrorStockIsNotEnoughToAddProductOnProposal', $product->ref);
1924
                    $this->db->rollback();
1925
                    return -3;
1926
                }
1927
            }
1928
1929
            // Calcul du total TTC et de la TVA pour la ligne a partir de
1930
            // qty, pu, remise_percent et txtva
1931
            // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
1932
            // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
1933
1934
            $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc);
1935
1936
            // Clean vat code
1937
            $reg = array();
1938
            $vat_src_code = '';
1939
            if (preg_match('/\((.*)\)/', $txtva, $reg)) {
1940
                $vat_src_code = $reg[1];
1941
                $txtva = preg_replace('/\s*\(.*\)/', '', $txtva); // Remove code into vatrate.
1942
            }
1943
1944
            $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);
1945
1946
            $total_ht = $tabprice[0];
1947
            $total_tva = $tabprice[1];
1948
            $total_ttc = $tabprice[2];
1949
            $total_localtax1 = $tabprice[9];
1950
            $total_localtax2 = $tabprice[10];
1951
            $pu_ht = $tabprice[3];
1952
            $pu_tva = $tabprice[4];
1953
            $pu_ttc = $tabprice[5];
1954
1955
            // MultiCurrency
1956
            $multicurrency_total_ht = $tabprice[16];
1957
            $multicurrency_total_tva = $tabprice[17];
1958
            $multicurrency_total_ttc = $tabprice[18];
1959
            $pu_ht_devise = $tabprice[19];
1960
1961
            // Rang to use
1962
            $ranktouse = $rang;
1963
            if ($ranktouse == -1) {
1964
                $rangmax = $this->line_max($fk_parent_line);
1965
                $ranktouse = $rangmax + 1;
1966
            }
1967
1968
            // TODO A virer
1969
            // Anciens indicateurs: $price, $remise (a ne plus utiliser)
1970
            $price = $pu;
1971
            $remise = 0;
1972
            if ((float)$remise_percent > 0) {
1973
                $remise = round(((float)$pu * (float)$remise_percent / 100), 2);
1974
                $price = (float)$pu - $remise;
1975
            }
1976
1977
            // Insert line
1978
            $this->line = new PropaleLigne($this->db);
1979
1980
            $this->line->context = $this->context;
1981
1982
            $this->line->fk_propal = $this->id;
1983
            $this->line->label = $label;
1984
            $this->line->desc = $desc;
1985
            $this->line->qty = $qty;
1986
1987
            $this->line->vat_src_code = $vat_src_code;
1988
            $this->line->tva_tx = $txtva;
1989
            $this->line->localtax1_tx = ($total_localtax1 ? $localtaxes_type[1] : 0);
1990
            $this->line->localtax2_tx = ($total_localtax2 ? $localtaxes_type[3] : 0);
1991
            $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
1992
            $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
1993
            $this->line->fk_product = $fk_product;
1994
            $this->line->product_type = $type;
1995
            $this->line->fk_remise_except = $fk_remise_except;
1996
            $this->line->remise_percent = $remise_percent;
1997
            $this->line->subprice = $pu_ht;
1998
            $this->line->rang = $ranktouse;
1999
            $this->line->info_bits = $info_bits;
2000
            $this->line->total_ht = $total_ht;
2001
            $this->line->total_tva = $total_tva;
2002
            $this->line->total_localtax1 = $total_localtax1;
2003
            $this->line->total_localtax2 = $total_localtax2;
2004
            $this->line->total_ttc = $total_ttc;
2005
            $this->line->special_code = $special_code;
2006
            $this->line->fk_parent_line = $fk_parent_line;
2007
            $this->line->fk_unit = $fk_unit;
2008
2009
            $this->line->date_start = $date_start;
2010
            $this->line->date_end = $date_end;
2011
2012
            $this->line->fk_fournprice = $fk_fournprice;
2013
            $this->line->pa_ht = $pa_ht;
2014
2015
            $this->line->origin_id = $origin_id;
2016
            $this->line->origin = $origin;
0 ignored issues
show
Deprecated Code introduced by
The property Dolibarr\Core\Base\CommonObject::$origin has been deprecated: Use $origin_type and $origin_id instead. ( Ignorable by Annotation )

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

2016
            /** @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...
2017
2018
            // Multicurrency
2019
            $this->line->fk_multicurrency = $this->fk_multicurrency;
2020
            $this->line->multicurrency_code = $this->multicurrency_code;
2021
            $this->line->multicurrency_subprice = $pu_ht_devise;
2022
            $this->line->multicurrency_total_ht = $multicurrency_total_ht;
2023
            $this->line->multicurrency_total_tva = $multicurrency_total_tva;
2024
            $this->line->multicurrency_total_ttc = $multicurrency_total_ttc;
2025
2026
            // Mise en option de la ligne
2027
            if (empty($qty) && empty($special_code)) {
2028
                $this->line->special_code = 3;
2029
            }
2030
2031
            // TODO deprecated
2032
            $this->line->price = $price;
2033
2034
            if (is_array($array_options) && count($array_options) > 0) {
2035
                $this->line->array_options = $array_options;
2036
            }
2037
2038
            $result = $this->line->insert();
2039
            if ($result > 0) {
2040
                // Reorder if child line
2041
                if (!empty($fk_parent_line)) {
2042
                    $this->line_order(true, 'DESC');
2043
                } elseif ($ranktouse > 0 && $ranktouse <= count($this->lines)) { // Update all rank of all other lines
2044
                    $linecount = count($this->lines);
2045
                    for ($ii = $ranktouse; $ii <= $linecount; $ii++) {
2046
                        $this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1);
2047
                    }
2048
                }
2049
2050
                // Mise a jour information denormalisees au niveau de la propale meme
2051
                if (empty($noupdateafterinsertline)) {
2052
                    $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.
2053
                }
2054
2055
                if ($result > 0) {
2056
                    $this->db->commit();
2057
                    return $this->line->id;
2058
                } else {
2059
                    $this->error = $this->db->error();
2060
                    $this->db->rollback();
2061
                    return -1;
2062
                }
2063
            } else {
2064
                $this->error = $this->line->error;
2065
                $this->errors = $this->line->errors;
2066
                $this->db->rollback();
2067
                return -2;
2068
            }
2069
        } else {
2070
            dol_syslog(get_only_class($this) . "::addline status of proposal must be Draft to allow use of ->addline()", LOG_ERR);
2071
            return -3;
2072
        }
2073
    }
2074
2075
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2076
2077
    /**
2078
     *  Set status to validated
2079
     *
2080
     * @param User $user Object user that validate
2081
     * @param int $notrigger 1=Does not execute triggers, 0=execute triggers
2082
     * @return int                 Return integer <0 if KO, 0=Nothing done, >=0 if OK
2083
     */
2084
    public function valid($user, $notrigger = 0)
2085
    {
2086
        global $conf;
2087
2088
        require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/files.lib.php';
2089
2090
        $error = 0;
2091
2092
        // Protection
2093
        if ($this->statut == self::STATUS_VALIDATED) {
2094
            dol_syslog(get_only_class($this) . "::valid action abandoned: already validated", LOG_WARNING);
2095
            return 0;
2096
        }
2097
2098
        if (
2099
            !((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('propal', 'creer'))
2100
                || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('propal', 'propal_advance', 'validate')))
2101
        ) {
2102
            $this->error = 'ErrorPermissionDenied';
2103
            dol_syslog(get_only_class($this) . "::valid " . $this->error, LOG_ERR);
2104
            return -1;
2105
        }
2106
2107
        $now = dol_now();
2108
2109
        $this->db->begin();
2110
2111
        // Numbering module definition
2112
        $soc = new Societe($this->db);
2113
        $soc->fetch($this->socid);
2114
2115
        // Define new ref
2116
        if (!$error && (preg_match('/^[\(]?PROV/i', $this->ref) || empty($this->ref))) { // empty should not happened, but when it occurs, the test save life
2117
            $num = $this->getNextNumRef($soc);
2118
        } else {
2119
            $num = $this->ref;
2120
        }
2121
        $this->newref = dol_sanitizeFileName($num);
2122
2123
        $sql = "UPDATE " . MAIN_DB_PREFIX . "propal";
2124
        $sql .= " SET ref = '" . $this->db->escape($num) . "',";
2125
        $sql .= " fk_statut = " . self::STATUS_VALIDATED . ", date_valid='" . $this->db->idate($now) . "', fk_user_valid=" . ((int)$user->id);
2126
        $sql .= " WHERE rowid = " . ((int)$this->id) . " AND fk_statut = " . self::STATUS_DRAFT;
2127
2128
        dol_syslog(get_only_class($this) . "::valid", LOG_DEBUG);
2129
        $resql = $this->db->query($sql);
2130
        if (!$resql) {
2131
            dol_print_error($this->db);
2132
            $error++;
2133
        }
2134
2135
        // Trigger calls
2136
        if (!$error && !$notrigger) {
2137
            // Call trigger
2138
            $result = $this->call_trigger('PROPAL_VALIDATE', $user);
2139
            if ($result < 0) {
2140
                $error++;
2141
            }
2142
            // End call triggers
2143
        }
2144
2145
        if (!$error) {
2146
            $this->oldref = $this->ref;
2147
2148
            // Rename directory if dir was a temporary ref
2149
            if (preg_match('/^[\(]?PROV/i', $this->ref)) {
2150
                // Now we rename also files into index
2151
                $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) . "'";
2152
                $sql .= " WHERE filename LIKE '" . $this->db->escape($this->ref) . "%' AND filepath = 'propale/" . $this->db->escape($this->ref) . "' and entity = " . ((int)$conf->entity);
2153
                $resql = $this->db->query($sql);
2154
                if (!$resql) {
2155
                    $error++;
2156
                    $this->error = $this->db->lasterror();
2157
                }
2158
                $sql = 'UPDATE ' . MAIN_DB_PREFIX . "ecm_files set filepath = 'propale/" . $this->db->escape($this->newref) . "'";
2159
                $sql .= " WHERE filepath = 'propale/" . $this->db->escape($this->ref) . "' and entity = " . $conf->entity;
2160
                $resql = $this->db->query($sql);
2161
                if (!$resql) {
2162
                    $error++;
2163
                    $this->error = $this->db->lasterror();
2164
                }
2165
2166
                // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
2167
                $oldref = dol_sanitizeFileName($this->ref);
2168
                $newref = dol_sanitizeFileName($num);
2169
                $dirsource = $conf->propal->multidir_output[$this->entity] . '/' . $oldref;
2170
                $dirdest = $conf->propal->multidir_output[$this->entity] . '/' . $newref;
2171
                if (!$error && file_exists($dirsource)) {
2172
                    dol_syslog(get_only_class($this) . "::validate rename dir " . $dirsource . " into " . $dirdest);
2173
                    if (@rename($dirsource, $dirdest)) {
2174
                        dol_syslog("Rename ok");
2175
                        // Rename docs starting with $oldref with $newref
2176
                        $listoffiles = dol_dir_list($dirdest, 'files', 1, '^' . preg_quote($oldref, '/'));
2177
                        foreach ($listoffiles as $fileentry) {
2178
                            $dirsource = $fileentry['name'];
2179
                            $dirdest = preg_replace('/^' . preg_quote($oldref, '/') . '/', $newref, $dirsource);
2180
                            $dirsource = $fileentry['path'] . '/' . $dirsource;
2181
                            $dirdest = $fileentry['path'] . '/' . $dirdest;
2182
                            @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

2182
                            /** @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...
2183
                        }
2184
                    }
2185
                }
2186
            }
2187
2188
            $this->ref = $num;
2189
            $this->statut = self::STATUS_VALIDATED;
2190
            $this->status = self::STATUS_VALIDATED;
2191
            $this->user_validation_id = $user->id;
2192
            $this->datev = $now;
2193
            $this->date_validation = $now;
2194
2195
            $this->db->commit();
2196
            return 1;
2197
        } else {
2198
            $this->db->rollback();
2199
            return -1;
2200
        }
2201
    }
2202
2203
    /**
2204
     *  Returns the reference to the following non used Proposal used depending on the active numbering module
2205
     *  defined into PROPALE_ADDON
2206
     *
2207
     * @param Societe $soc Object thirdparty
2208
     * @return string              Reference libre pour la propale
2209
     */
2210
    public function getNextNumRef($soc)
2211
    {
2212
        global $conf, $langs;
2213
        $langs->load("propal");
2214
2215
        $classname = getDolGlobalString('PROPALE_ADDON');
2216
2217
        if (!empty($classname)) {
2218
            $mybool = false;
2219
2220
            $file = $classname . ".php";
2221
2222
            // Include file with class
2223
            $dirmodels = array_merge(array('/'), (array)$conf->modules_parts['models']);
2224
            foreach ($dirmodels as $reldir) {
2225
                $dir = dol_buildpath($reldir . "core/modules/propale/");
2226
2227
                // Load file with numbering class (if found)
2228
                $mybool = ((bool)@include_once $dir . $file) || $mybool;
2229
            }
2230
2231
            if (!$mybool) {
2232
                dol_print_error(null, "Failed to include file " . $file);
2233
                return '';
2234
            }
2235
2236
            $obj = new $classname();
2237
            $numref = "";
2238
            $numref = $obj->getNextValue($soc, $this);
2239
2240
            if ($numref != "") {
2241
                return $numref;
2242
            } else {
2243
                $this->error = $obj->error;
2244
                //dol_print_error($db,"Propale::getNextNumRef ".$obj->error);
2245
                return "";
2246
            }
2247
        } else {
2248
            $langs->load("errors");
2249
            print $langs->trans("Error") . " " . $langs->trans("ErrorModuleSetupNotComplete", $langs->transnoentitiesnoconv("Proposal"));
2250
            return "";
2251
        }
2252
    }
2253
2254
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2255
2256
    /**
2257
     *  Define proposal date
2258
     *
2259
     * @param User $user Object user that modify
2260
     * @param int $date Date
2261
     * @param int $notrigger 1=Does not execute triggers, 0= execute triggers
2262
     * @return int                     Return integer <0 if KO, >0 if OK
2263
     */
2264
    public function set_date($user, $date, $notrigger = 0)
2265
    {
2266
        // phpcs:enable
2267
        if (empty($date)) {
2268
            $this->error = 'ErrorBadParameter';
2269
            dol_syslog(get_only_class($this) . "::set_date " . $this->error, LOG_ERR);
2270
            return -1;
2271
        }
2272
2273
        if ($user->hasRight('propal', 'creer')) {
2274
            $error = 0;
2275
2276
            $this->db->begin();
2277
2278
            $sql = "UPDATE " . MAIN_DB_PREFIX . "propal SET datep = '" . $this->db->idate($date) . "'";
2279
            $sql .= " WHERE rowid = " . ((int)$this->id);
2280
2281
            dol_syslog(__METHOD__, LOG_DEBUG);
2282
            $resql = $this->db->query($sql);
2283
            if (!$resql) {
2284
                $this->errors[] = $this->db->error();
2285
                $error++;
2286
            }
2287
2288
            if (!$error) {
2289
                $this->oldcopy = clone $this;
2290
                $this->date = $date;
2291
                $this->datep = $date; // deprecated
2292
            }
2293
2294
            if (!$notrigger && empty($error)) {
2295
                // Call trigger
2296
                $result = $this->call_trigger('PROPAL_MODIFY', $user);
2297
                if ($result < 0) {
2298
                    $error++;
2299
                }
2300
                // End call triggers
2301
            }
2302
2303
            if (!$error) {
2304
                $this->db->commit();
2305
                return 1;
2306
            } else {
2307
                foreach ($this->errors as $errmsg) {
2308
                    dol_syslog(__METHOD__ . ' Error: ' . $errmsg, LOG_ERR);
2309
                    $this->error .= ($this->error ? ', ' . $errmsg : $errmsg);
2310
                }
2311
                $this->db->rollback();
2312
                return -1 * $error;
2313
            }
2314
        }
2315
2316
        return -1;
2317
    }
2318
2319
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2320
2321
    /**
2322
     *  Define end validity date
2323
     *
2324
     * @param User $user Object user that modify
2325
     * @param int $date_end_validity End of validity date
2326
     * @param int $notrigger 1=Does not execute triggers, 0= execute triggers
2327
     * @return     int                         Return integer <0 if KO, >0 if OK
2328
     */
2329
    public function set_echeance($user, $date_end_validity, $notrigger = 0)
2330
    {
2331
        // phpcs:enable
2332
        if ($user->hasRight('propal', 'creer')) {
2333
            $error = 0;
2334
2335
            $this->db->begin();
2336
2337
            $sql = "UPDATE " . MAIN_DB_PREFIX . "propal SET fin_validite = " . ($date_end_validity != '' ? "'" . $this->db->idate($date_end_validity) . "'" : 'null');
2338
            $sql .= " WHERE rowid = " . ((int)$this->id);
2339
2340
            dol_syslog(__METHOD__, LOG_DEBUG);
2341
2342
            $resql = $this->db->query($sql);
2343
            if (!$resql) {
2344
                $this->errors[] = $this->db->error();
2345
                $error++;
2346
            }
2347
2348
2349
            if (!$error) {
2350
                $this->oldcopy = clone $this;
2351
                $this->fin_validite = $date_end_validity;
2352
            }
2353
2354
            if (!$notrigger && empty($error)) {
2355
                // Call trigger
2356
                $result = $this->call_trigger('PROPAL_MODIFY', $user);
2357
                if ($result < 0) {
2358
                    $error++;
2359
                }
2360
                // End call triggers
2361
            }
2362
2363
            if (!$error) {
2364
                $this->db->commit();
2365
                return 1;
2366
            } else {
2367
                foreach ($this->errors as $errmsg) {
2368
                    dol_syslog(__METHOD__ . ' Error: ' . $errmsg, LOG_ERR);
2369
                    $this->error .= ($this->error ? ', ' . $errmsg : $errmsg);
2370
                }
2371
                $this->db->rollback();
2372
                return -1 * $error;
2373
            }
2374
        }
2375
2376
        return -1;
2377
    }
2378
2379
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2380
2381
    /**
2382
     *  Set delivery date
2383
     *
2384
     * @param User $user Object user that modify
2385
     * @param int $delivery_date Delivery date
2386
     * @param int $notrigger 1=Does not execute triggers, 0= execute triggers
2387
     * @return     int                         Return integer <0 if ko, >0 if ok
2388
     * @deprecated Use  setDeliveryDate
2389
     */
2390
    public function set_date_livraison($user, $delivery_date, $notrigger = 0)
2391
    {
2392
        // phpcs:enable
2393
        return $this->setDeliveryDate($user, $delivery_date, $notrigger);
2394
    }
2395
2396
    /**
2397
     *  Set delivery date
2398
     *
2399
     * @param User $user Object user that modify
2400
     * @param int $delivery_date Delivery date
2401
     * @param int $notrigger 1=Does not execute triggers, 0= execute triggers
2402
     * @return     int                         Return integer <0 if ko, >0 if ok
2403
     */
2404
    public function setDeliveryDate($user, $delivery_date, $notrigger = 0)
2405
    {
2406
        if ($user->hasRight('propal', 'creer')) {
2407
            $error = 0;
2408
2409
            $this->db->begin();
2410
2411
            $sql = "UPDATE " . MAIN_DB_PREFIX . "propal ";
2412
            $sql .= " SET date_livraison = " . ($delivery_date != '' ? "'" . $this->db->idate($delivery_date) . "'" : 'null');
2413
            $sql .= " WHERE rowid = " . ((int)$this->id);
2414
2415
            dol_syslog(__METHOD__, LOG_DEBUG);
2416
            $resql = $this->db->query($sql);
2417
            if (!$resql) {
2418
                $this->errors[] = $this->db->error();
2419
                $error++;
2420
            }
2421
2422
            if (!$error) {
2423
                $this->oldcopy = clone $this;
2424
                $this->delivery_date = $delivery_date;
2425
            }
2426
2427
            if (!$notrigger && empty($error)) {
2428
                // Call trigger
2429
                $result = $this->call_trigger('PROPAL_MODIFY', $user);
2430
                if ($result < 0) {
2431
                    $error++;
2432
                }
2433
                // End call triggers
2434
            }
2435
2436
            if (!$error) {
2437
                $this->db->commit();
2438
                return 1;
2439
            } else {
2440
                foreach ($this->errors as $errmsg) {
2441
                    dol_syslog(__METHOD__ . ' Error: ' . $errmsg, LOG_ERR);
2442
                    $this->error .= ($this->error ? ', ' . $errmsg : $errmsg);
2443
                }
2444
                $this->db->rollback();
2445
                return -1 * $error;
2446
            }
2447
        }
2448
2449
        return -1;
2450
    }
2451
2452
    /**
2453
     *  Set delivery
2454
     *
2455
     * @param User $user Object user that modify
2456
     * @param int $id Availability id
2457
     * @param int $notrigger 1=Does not execute triggers, 0= execute triggers
2458
     * @return     int                     Return integer <0 if KO, >0 if OK
2459
     */
2460
    public function set_availability($user, $id, $notrigger = 0)
2461
    {
2462
        // phpcs:enable
2463
        if ($user->hasRight('propal', 'creer') && $this->statut >= self::STATUS_DRAFT) {
2464
            $error = 0;
2465
2466
            $this->db->begin();
2467
2468
            $sql = "UPDATE " . MAIN_DB_PREFIX . "propal";
2469
            $sql .= " SET fk_availability = " . ((int)$id);
2470
            $sql .= " WHERE rowid = " . ((int)$this->id);
2471
2472
            dol_syslog(__METHOD__ . ' availability(' . $id . ')', LOG_DEBUG);
2473
            $resql = $this->db->query($sql);
2474
            if (!$resql) {
2475
                $this->errors[] = $this->db->error();
2476
                $error++;
2477
            }
2478
2479
            if (!$error) {
2480
                $this->oldcopy = clone $this;
2481
                $this->fk_availability = $id;
2482
                $this->availability_id = $id;
2483
            }
2484
2485
            if (!$notrigger && empty($error)) {
2486
                // Call trigger
2487
                $result = $this->call_trigger('PROPAL_MODIFY', $user);
2488
                if ($result < 0) {
2489
                    $error++;
2490
                }
2491
                // End call triggers
2492
            }
2493
2494
            if (!$error) {
2495
                $this->db->commit();
2496
                return 1;
2497
            } else {
2498
                foreach ($this->errors as $errmsg) {
2499
                    dol_syslog(__METHOD__ . ' Error: ' . $errmsg, LOG_ERR);
2500
                    $this->error .= ($this->error ? ', ' . $errmsg : $errmsg);
2501
                }
2502
                $this->db->rollback();
2503
                return -1 * $error;
2504
            }
2505
        } else {
2506
            $error_str = 'Propal status do not meet requirement ' . $this->statut;
2507
            dol_syslog(__METHOD__ . $error_str, LOG_ERR);
2508
            $this->error = $error_str;
2509
            $this->errors[] = $this->error;
2510
            return -2;
2511
        }
2512
    }
2513
2514
    /**
2515
     *  Set source of demand
2516
     *
2517
     * @param User $user Object user that modify
2518
     * @param int $id Input reason id
2519
     * @param int $notrigger 1=Does not execute triggers, 0= execute triggers
2520
     * @return     int                 Return integer <0 if KO, >0 if OK
2521
     */
2522
    public function set_demand_reason($user, $id, $notrigger = 0)
2523
    {
2524
        // phpcs:enable
2525
        if ($user->hasRight('propal', 'creer') && $this->statut >= self::STATUS_DRAFT) {
2526
            $error = 0;
2527
2528
            $this->db->begin();
2529
2530
            $sql = "UPDATE " . MAIN_DB_PREFIX . "propal ";
2531
            $sql .= " SET fk_input_reason = " . ((int)$id);
2532
            $sql .= " WHERE rowid = " . ((int)$this->id);
2533
2534
            dol_syslog(__METHOD__, LOG_DEBUG);
2535
            $resql = $this->db->query($sql);
2536
            if (!$resql) {
2537
                $this->errors[] = $this->db->error();
2538
                $error++;
2539
            }
2540
2541
2542
            if (!$error) {
2543
                $this->oldcopy = clone $this;
2544
2545
                $this->demand_reason_id = $id;
2546
            }
2547
2548
2549
            if (!$notrigger && empty($error)) {
2550
                // Call trigger
2551
                $result = $this->call_trigger('PROPAL_MODIFY', $user);
2552
                if ($result < 0) {
2553
                    $error++;
2554
                }
2555
                // End call triggers
2556
            }
2557
2558
            if (!$error) {
2559
                $this->db->commit();
2560
                return 1;
2561
            } else {
2562
                foreach ($this->errors as $errmsg) {
2563
                    dol_syslog(__METHOD__ . ' Error: ' . $errmsg, LOG_ERR);
2564
                    $this->error .= ($this->error ? ', ' . $errmsg : $errmsg);
2565
                }
2566
                $this->db->rollback();
2567
                return -1 * $error;
2568
            }
2569
        } else {
2570
            $error_str = 'Propal status do not meet requirement ' . $this->statut;
2571
            dol_syslog(__METHOD__ . $error_str, LOG_ERR);
2572
            $this->error = $error_str;
2573
            $this->errors[] = $this->error;
2574
            return -2;
2575
        }
2576
    }
2577
2578
    /**
2579
     * Set customer reference number
2580
     *
2581
     * @param User $user Object user that modify
2582
     * @param string $ref_client Customer reference
2583
     * @param int $notrigger 1=Does not execute triggers, 0= execute triggers
2584
     * @return     int                     Return integer <0 if ko, >0 if ok
2585
     */
2586
    public function set_ref_client($user, $ref_client, $notrigger = 0)
2587
    {
2588
        // phpcs:enable
2589
        if ($user->hasRight('propal', 'creer')) {
2590
            $error = 0;
2591
2592
            $this->db->begin();
2593
2594
            $sql = "UPDATE " . MAIN_DB_PREFIX . "propal SET ref_client = " . (empty($ref_client) ? 'NULL' : "'" . $this->db->escape($ref_client) . "'");
2595
            $sql .= " WHERE rowid = " . ((int)$this->id);
2596
2597
            dol_syslog(__METHOD__ . ' $this->id=' . $this->id . ', ref_client=' . $ref_client, LOG_DEBUG);
2598
            $resql = $this->db->query($sql);
2599
            if (!$resql) {
2600
                $this->errors[] = $this->db->error();
2601
                $error++;
2602
            }
2603
2604
            if (!$error) {
2605
                $this->oldcopy = clone $this;
2606
                $this->ref_client = $ref_client;
2607
            }
2608
2609
            if (!$notrigger && empty($error)) {
2610
                // Call trigger
2611
                $result = $this->call_trigger('PROPAL_MODIFY', $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
                foreach ($this->errors as $errmsg) {
2623
                    dol_syslog(__METHOD__ . ' Error: ' . $errmsg, LOG_ERR);
2624
                    $this->error .= ($this->error ? ', ' . $errmsg : $errmsg);
2625
                }
2626
                $this->db->rollback();
2627
                return -1 * $error;
2628
            }
2629
        }
2630
2631
        return -1;
2632
    }
2633
2634
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2635
2636
    /**
2637
     *  Reopen the commercial proposal
2638
     *
2639
     * @param User $user Object user that close
2640
     * @param int $status Status
2641
     * @param string $note Comment
2642
     * @param int $notrigger 1=Does not execute triggers, 0= execute triggers
2643
     * @return     int                 Return integer <0 if KO, >0 if OK
2644
     */
2645
    public function reopen($user, $status, $note = '', $notrigger = 0)
2646
    {
2647
        $error = 0;
2648
2649
        $sql = "UPDATE " . MAIN_DB_PREFIX . "propal";
2650
        $sql .= " SET fk_statut = " . ((int)$status) . ",";
2651
        if (!empty($note)) {
2652
            $sql .= " note_private = '" . $this->db->escape($note) . "',";
2653
        }
2654
        $sql .= " date_cloture=NULL, fk_user_cloture=NULL";
2655
        $sql .= " WHERE rowid = " . ((int)$this->id);
2656
2657
        $this->db->begin();
2658
2659
        dol_syslog(get_only_class($this) . "::reopen", LOG_DEBUG);
2660
        $resql = $this->db->query($sql);
2661
        if (!$resql) {
2662
            $error++;
2663
            $this->errors[] = "Error " . $this->db->lasterror();
2664
        }
2665
        if (!$error) {
2666
            if (!$notrigger) {
2667
                // Call trigger
2668
                $result = $this->call_trigger('PROPAL_REOPEN', $user);
2669
                if ($result < 0) {
2670
                    $error++;
2671
                }
2672
                // End call triggers
2673
            }
2674
        }
2675
2676
        // Commit or rollback
2677
        if ($error) {
2678
            if (!empty($this->errors)) {
2679
                foreach ($this->errors as $errmsg) {
2680
                    dol_syslog(get_only_class($this) . "::update " . $errmsg, LOG_ERR);
2681
                    $this->error .= ($this->error ? ', ' . $errmsg : $errmsg);
2682
                }
2683
            }
2684
            $this->db->rollback();
2685
            return -1 * $error;
2686
        } else {
2687
            $this->statut = $status;
2688
            $this->status = $status;
2689
2690
            $this->db->commit();
2691
            return 1;
2692
        }
2693
    }
2694
2695
2696
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2697
2698
    /**
2699
     *  Close/set the commercial proposal to status signed or refused (fill also date signature)
2700
     *
2701
     * @param User $user Object user that close
2702
     * @param int $status Status (self::STATUS_BILLED or self::STATUS_REFUSED)
2703
     * @param string $note Complete private note with this note
2704
     * @param int $notrigger 1=Does not execute triggers, 0=Execute triggers
2705
     * @return     int                 Return integer <0 if KO, >0 if OK
2706
     */
2707
    public function closeProposal($user, $status, $note = '', $notrigger = 0)
2708
    {
2709
        global $langs, $conf;
2710
2711
        $error = 0;
2712
        $now = dol_now();
2713
2714
        $this->db->begin();
2715
2716
        $newprivatenote = dol_concatdesc($this->note_private, $note);
2717
2718
        if (!getDolGlobalString('PROPALE_KEEP_OLD_SIGNATURE_INFO')) {
2719
            $date_signature = $now;
2720
            $fk_user_signature = $user->id;
2721
        } else {
2722
            $this->info($this->id);
2723
            if (!isset($this->date_signature) || $this->date_signature == '') {
2724
                $date_signature = $now;
2725
                $fk_user_signature = $user->id;
2726
            } else {
2727
                $date_signature = $this->date_signature;
2728
                $fk_user_signature = $this->user_signature->id;
2729
            }
2730
        }
2731
2732
        $sql = "UPDATE " . MAIN_DB_PREFIX . "propal";
2733
        $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;
2734
        $sql .= " WHERE rowid = " . ((int)$this->id);
2735
2736
        $resql = $this->db->query($sql);
2737
        if ($resql) {
2738
            // Status self::STATUS_REFUSED by default
2739
            $modelpdf = getDolGlobalString('PROPALE_ADDON_PDF_ODT_CLOSED', $this->model_pdf);
2740
            $trigger_name = 'PROPAL_CLOSE_REFUSED';     // used later in call_trigger()
2741
2742
            if ($status == self::STATUS_SIGNED) {   // Status self::STATUS_SIGNED
2743
                $trigger_name = 'PROPAL_CLOSE_SIGNED';  // used later in call_trigger()
2744
                $modelpdf = getDolGlobalString('PROPALE_ADDON_PDF_ODT_TOBILL') ? $conf->global->PROPALE_ADDON_PDF_ODT_TOBILL : $this->model_pdf;
2745
2746
                // The connected company is classified as a client
2747
                $soc = new Societe($this->db);
2748
                $soc->id = $this->socid;
2749
                $result = $soc->setAsCustomer();
2750
2751
                if ($result < 0) {
2752
                    $this->error = $this->db->lasterror();
2753
                    $this->db->rollback();
2754
                    return -2;
2755
                }
2756
            }
2757
2758
            if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE') && !getDolGlobalInt('PROPAL_DISABLE_AUTOUPDATE_ON_CLOSE')) {
2759
                // Define output language
2760
                $outputlangs = $langs;
2761
                if (getDolGlobalInt('MAIN_MULTILANGS')) {
2762
                    $outputlangs = new Translate("", $conf);
2763
                    $newlang = (GETPOST('lang_id', 'aZ09') ? GETPOST('lang_id', 'aZ09') : $this->thirdparty->default_lang);
2764
                    $outputlangs->setDefaultLang($newlang);
2765
                }
2766
2767
                // PDF
2768
                $hidedetails = (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_DETAILS') ? 1 : 0);
2769
                $hidedesc = (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_DESC') ? 1 : 0);
2770
                $hideref = (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_REF') ? 1 : 0);
2771
2772
                //$ret=$object->fetch($id);    // Reload to get new records
2773
                $this->generateDocument($modelpdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
2774
            }
2775
2776
            if (!$error) {
2777
                $this->oldcopy = clone $this;
2778
                $this->statut = $status;
2779
                $this->status = $status;
2780
                $this->date_signature = $date_signature;
2781
                $this->note_private = $newprivatenote;
2782
            }
2783
2784
            if (!$notrigger && empty($error)) {
2785
                // Call trigger
2786
                $result = $this->call_trigger($trigger_name, $user);
2787
                if ($result < 0) {
2788
                    $error++;
2789
                }
2790
                // End call triggers
2791
            }
2792
2793
            if (!$error) {
2794
                $this->db->commit();
2795
                return 1;
2796
            } else {
2797
                $this->statut = $this->oldcopy->statut;
2798
                $this->status = $this->oldcopy->statut;
2799
                $this->date_signature = $this->oldcopy->date_signature;
2800
                $this->note_private = $this->oldcopy->note_private;
2801
2802
                $this->db->rollback();
2803
                return -1;
2804
            }
2805
        } else {
2806
            $this->error = $this->db->lasterror();
2807
            $this->db->rollback();
2808
            return -1;
2809
        }
2810
    }
2811
2812
    /**
2813
     *  Object Proposal Information
2814
     *
2815
     * @param int $id Proposal id
2816
     * @return void
2817
     */
2818
    public function info($id)
2819
    {
2820
        $sql = "SELECT c.rowid, ";
2821
        $sql .= " c.datec, c.date_valid as datev, c.date_signature, c.date_cloture,";
2822
        $sql .= " c.fk_user_author, c.fk_user_valid, c.fk_user_signature, c.fk_user_cloture";
2823
        $sql .= " FROM " . MAIN_DB_PREFIX . "propal as c";
2824
        $sql .= " WHERE c.rowid = " . ((int)$id);
2825
2826
        $result = $this->db->query($sql);
2827
2828
        if ($result) {
2829
            if ($this->db->num_rows($result)) {
2830
                $obj = $this->db->fetch_object($result);
2831
2832
                $this->id = $obj->rowid;
2833
2834
                $this->date_creation = $this->db->jdate($obj->datec);
2835
                $this->date_validation = $this->db->jdate($obj->datev);
2836
                $this->date_signature = $this->db->jdate($obj->date_signature);
2837
                $this->date_cloture = $this->db->jdate($obj->date_cloture);
2838
2839
                $this->user_creation_id = $obj->fk_user_author;
2840
                $this->user_validation_id = $obj->fk_user_valid;
2841
2842
                if ($obj->fk_user_signature) {
2843
                    $user_signature = new User($this->db);
2844
                    $user_signature->fetch($obj->fk_user_signature);
2845
                    $this->user_signature = $user_signature;
2846
                }
2847
2848
                $this->user_closing_id = $obj->fk_user_cloture;
2849
            }
2850
            $this->db->free($result);
2851
        } else {
2852
            dol_print_error($this->db);
2853
        }
2854
    }
2855
2856
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2857
2858
    /**
2859
     *  Create a document onto disk according to template module.
2860
     *
2861
     * @param string $modele Force model to use ('' to not force)
2862
     * @param Translate $outputlangs Object langs to use for output
2863
     * @param int $hidedetails Hide details of lines
2864
     * @param int $hidedesc Hide description
2865
     * @param int $hideref Hide ref
2866
     * @param null|array $moreparams Array to provide more information
2867
     * @return     int                         0 if KO, 1 if OK
2868
     */
2869
    public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
2870
    {
2871
        global $conf, $langs;
2872
2873
        $langs->load("propale");
2874
        $outputlangs->load("products");
2875
2876
        if (!dol_strlen($modele)) {
2877
            $modele = 'azur';
2878
2879
            if ($this->model_pdf) {
2880
                $modele = $this->model_pdf;
2881
            } elseif (getDolGlobalString('PROPALE_ADDON_PDF')) {
2882
                $modele = getDolGlobalString('PROPALE_ADDON_PDF');
2883
            }
2884
        }
2885
2886
        $modelpath = "core/modules/propale/doc/";
2887
2888
        return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
2889
    }
2890
2891
    /**
2892
     *  Classify the proposal to status Billed
2893
     *
2894
     * @param User $user Object user
2895
     * @param int $notrigger 1=Does not execute triggers, 0= execute triggers
2896
     * @param string $note Complete private note with this note
2897
     * @return     int                 Return integer <0 if KO, 0 = nothing done, >0 if OK
2898
     */
2899
    public function classifyBilled(User $user, $notrigger = 0, $note = '')
2900
    {
2901
        global $conf, $langs;
2902
2903
        $error = 0;
2904
2905
        $now = dol_now();
2906
        $num = 0;
2907
2908
        $triggerName = 'PROPAL_CLASSIFY_BILLED';
2909
2910
        $this->db->begin();
2911
2912
        $newprivatenote = dol_concatdesc($this->note_private, $note);
2913
2914
        $sql = 'UPDATE ' . MAIN_DB_PREFIX . 'propal SET fk_statut = ' . self::STATUS_BILLED . ", ";
2915
        $sql .= " note_private = '" . $this->db->escape($newprivatenote) . "', date_cloture='" . $this->db->idate($now) . "', fk_user_cloture=" . ((int)$user->id);
2916
        $sql .= ' WHERE rowid = ' . ((int)$this->id) . ' AND fk_statut = ' . ((int)self::STATUS_SIGNED);
2917
2918
        dol_syslog(__METHOD__, LOG_DEBUG);
2919
        $resql = $this->db->query($sql);
2920
        if (!$resql) {
2921
            $this->errors[] = $this->db->error();
2922
            $error++;
2923
        } else {
2924
            $num = $this->db->affected_rows($resql);
2925
        }
2926
2927
        if (!$error) {
2928
            $modelpdf = getDolGlobalString('PROPALE_ADDON_PDF_ODT_CLOSED', $this->model_pdf);
2929
2930
            if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
2931
                // Define output language
2932
                $outputlangs = $langs;
2933
                if (getDolGlobalInt('MAIN_MULTILANGS')) {
2934
                    $outputlangs = new Translate("", $conf);
2935
                    $newlang = (GETPOST('lang_id', 'aZ09') ? GETPOST('lang_id', 'aZ09') : $this->thirdparty->default_lang);
2936
                    $outputlangs->setDefaultLang($newlang);
2937
                }
2938
2939
                // PDF
2940
                $hidedetails = (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_DETAILS') ? 1 : 0);
2941
                $hidedesc = (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_DESC') ? 1 : 0);
2942
                $hideref = (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_REF') ? 1 : 0);
2943
2944
                //$ret=$object->fetch($id);    // Reload to get new records
2945
                $this->generateDocument($modelpdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
2946
            }
2947
2948
            $this->oldcopy = clone $this;
2949
            $this->statut = self::STATUS_BILLED;
2950
            $this->date_cloture = $now;
2951
            $this->note_private = $newprivatenote;
2952
        }
2953
2954
        if (!$notrigger && empty($error)) {
2955
            // Call trigger
2956
            $result = $this->call_trigger($triggerName, $user);
2957
            if ($result < 0) {
2958
                $error++;
2959
            }
2960
            // End call triggers
2961
        }
2962
2963
        if (!$error) {
2964
            $this->db->commit();
2965
            return $num;
2966
        } else {
2967
            foreach ($this->errors as $errmsg) {
2968
                dol_syslog(__METHOD__ . ' Error: ' . $errmsg, LOG_ERR);
2969
                $this->error .= ($this->error ? ', ' . $errmsg : $errmsg);
2970
            }
2971
            $this->db->rollback();
2972
            return -1 * $error;
2973
        }
2974
    }
2975
2976
    /**
2977
     *  Cancel the proposal
2978
     *
2979
     * @param User $user Object user
2980
     * @return     int             Return integer if KO <0 , if OK >0
2981
     */
2982
    public function setCancel(User $user)
2983
    {
2984
        $error = 0;
2985
2986
        $this->db->begin();
2987
2988
        $sql = "UPDATE " . MAIN_DB_PREFIX . "propal";
2989
        $sql .= " SET fk_statut = " . self::STATUS_CANCELED . ",";
2990
        $sql .= " fk_user_modif = " . ((int)$user->id);
2991
        $sql .= " WHERE rowid = " . ((int)$this->id);
2992
2993
        dol_syslog(get_only_class($this) . "::cancel", LOG_DEBUG);
2994
        if ($this->db->query($sql)) {
2995
            if (!$error) {
2996
                // Call trigger
2997
                $result = $this->call_trigger('PROPAL_CANCEL', $user);
2998
                if ($result < 0) {
2999
                    $error++;
3000
                }
3001
                // End call triggers
3002
            }
3003
3004
            if (!$error) {
3005
                $this->statut = self::STATUS_CANCELED;
3006
                $this->db->commit();
3007
                return 1;
3008
            } else {
3009
                foreach ($this->errors as $errmsg) {
3010
                    dol_syslog(get_only_class($this) . "::cancel " . $errmsg, LOG_ERR);
3011
                    $this->error .= ($this->error ? ', ' . $errmsg : $errmsg);
3012
                }
3013
                $this->db->rollback();
3014
                return -1;
3015
            }
3016
        } else {
3017
            $this->error = $this->db->error();
3018
            $this->db->rollback();
3019
            return -1;
3020
        }
3021
    }
3022
3023
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3024
3025
    /**
3026
     *  Set draft status
3027
     *
3028
     * @param User $user Object user that modify
3029
     * @param int $notrigger 1=Does not execute triggers, 0= execute triggers
3030
     * @return     int                 Return integer <0 if KO, >0 if OK
3031
     */
3032
    public function setDraft($user, $notrigger = 0)
3033
    {
3034
        // phpcs:enable
3035
        $error = 0;
3036
3037
        // Protection
3038
        if ($this->statut <= self::STATUS_DRAFT) {
3039
            return 0;
3040
        }
3041
3042
        dol_syslog(get_only_class($this) . "::setDraft", LOG_DEBUG);
3043
3044
        $this->db->begin();
3045
3046
        $sql = "UPDATE " . MAIN_DB_PREFIX . "propal";
3047
        $sql .= " SET fk_statut = " . self::STATUS_DRAFT;
3048
        $sql .= ",  online_sign_ip = NULL , online_sign_name = NULL";
3049
        $sql .= " WHERE rowid = " . ((int)$this->id);
3050
3051
        $resql = $this->db->query($sql);
3052
        if (!$resql) {
3053
            $this->errors[] = $this->db->error();
3054
            $error++;
3055
        }
3056
3057
        if (!$error) {
3058
            $this->oldcopy = clone $this;
3059
        }
3060
3061
        if (!$notrigger && empty($error)) {
3062
            // Call trigger
3063
            $result = $this->call_trigger('PROPAL_MODIFY', $user);
3064
            if ($result < 0) {
3065
                $error++;
3066
            }
3067
            // End call triggers
3068
        }
3069
3070
        if (!$error) {
3071
            $this->statut = self::STATUS_DRAFT;
3072
            $this->status = self::STATUS_DRAFT;
3073
3074
            $this->db->commit();
3075
            return 1;
3076
        } else {
3077
            foreach ($this->errors as $errmsg) {
3078
                dol_syslog(__METHOD__ . ' Error: ' . $errmsg, LOG_ERR);
3079
                $this->error .= ($this->error ? ', ' . $errmsg : $errmsg);
3080
            }
3081
            $this->db->rollback();
3082
            return -1 * $error;
3083
        }
3084
    }
3085
3086
    /**
3087
     *    Return list of proposal (eventually filtered on user) into an array
3088
     *
3089
     * @param int $shortlist 0=Return array[id]=ref, 1=Return array[](id=>id,ref=>ref,name=>name)
3090
     * @param int $draft 0=not draft, 1=draft
3091
     * @param int $notcurrentuser 0=all user, 1=not current user
3092
     * @param int $socid Id third party
3093
     * @param int $limit For pagination
3094
     * @param int $offset For pagination
3095
     * @param string $sortfield Sort criteria
3096
     * @param string $sortorder Sort order
3097
     * @return   array|int                   -1 if KO, array with result if OK
3098
     */
3099
    public function liste_array($shortlist = 0, $draft = 0, $notcurrentuser = 0, $socid = 0, $limit = 0, $offset = 0, $sortfield = 'p.datep', $sortorder = 'DESC')
3100
    {
3101
        // phpcs:enable
3102
        global $user;
3103
3104
        $ga = array();
3105
3106
        $sql = "SELECT s.rowid, s.nom as name, s.client,";
3107
        $sql .= " p.rowid as propalid, p.fk_statut, p.total_ht, p.ref, p.remise, ";
3108
        $sql .= " p.datep as dp, p.fin_validite as datelimite";
3109
        $sql .= " FROM " . MAIN_DB_PREFIX . "societe as s, " . MAIN_DB_PREFIX . "propal as p, " . MAIN_DB_PREFIX . "c_propalst as c";
3110
        $sql .= " WHERE p.entity IN (" . getEntity('propal') . ")";
3111
        $sql .= " AND p.fk_soc = s.rowid";
3112
        $sql .= " AND p.fk_statut = c.id";
3113
3114
        // If the internal user must only see his customers, force searching by him
3115
        $search_sale = 0;
3116
        if (!$user->hasRight('societe', 'client', 'voir')) {
3117
            $search_sale = $user->id;
3118
        }
3119
        // Search on sale representative
3120
        if ($search_sale && $search_sale != '-1') {
3121
            if ($search_sale == -2) {
3122
                $sql .= " AND NOT EXISTS (SELECT sc.fk_soc FROM " . MAIN_DB_PREFIX . "societe_commerciaux as sc WHERE sc.fk_soc = p.fk_soc)";
3123
            } elseif ($search_sale > 0) {
3124
                $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) . ")";
3125
            }
3126
        }
3127
        // Search on socid
3128
        if ($socid) {
3129
            $sql .= " AND p.fk_soc = " . ((int)$socid);
3130
        }
3131
        if ($draft) {
3132
            $sql .= " AND p.fk_statut = " . ((int)self::STATUS_DRAFT);
3133
        }
3134
        if ($notcurrentuser > 0) {
3135
            $sql .= " AND p.fk_user_author <> " . ((int)$user->id);
3136
        }
3137
        $sql .= $this->db->order($sortfield, $sortorder);
3138
        $sql .= $this->db->plimit($limit, $offset);
3139
3140
        $result = $this->db->query($sql);
3141
        if ($result) {
3142
            $num = $this->db->num_rows($result);
3143
            if ($num) {
3144
                $i = 0;
3145
                while ($i < $num) {
3146
                    $obj = $this->db->fetch_object($result);
3147
3148
                    if ($shortlist == 1) {
3149
                        $ga[$obj->propalid] = $obj->ref;
3150
                    } elseif ($shortlist == 2) {
3151
                        $ga[$obj->propalid] = $obj->ref . ' (' . $obj->name . ')';
3152
                    } else {
3153
                        $ga[$i]['id'] = $obj->propalid;
3154
                        $ga[$i]['ref'] = $obj->ref;
3155
                        $ga[$i]['name'] = $obj->name;
3156
                    }
3157
3158
                    $i++;
3159
                }
3160
            }
3161
            return $ga;
3162
        } else {
3163
            dol_print_error($this->db);
3164
            return -1;
3165
        }
3166
    }
3167
3168
    /**
3169
     *  Returns an array with the numbers of related invoices
3170
     *
3171
     * @return array       Array of invoices
3172
     */
3173
    public function getInvoiceArrayList()
3174
    {
3175
        return $this->InvoiceArrayList($this->id);
3176
    }
3177
3178
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3179
3180
    /**
3181
     *  Returns an array with id and ref of related invoices
3182
     *
3183
     * @param int $id Id propal
3184
     * @return     array|int               Array of invoices id
3185
     */
3186
    public function InvoiceArrayList($id)
3187
    {
3188
        // phpcs:enable
3189
        $ga = array();
3190
        $linkedInvoices = array();
3191
3192
        $this->fetchObjectLinked($id, $this->element);
3193
        foreach ($this->linkedObjectsIds as $objecttype => $objectid) {
3194
            // Nouveau système du common object renvoi des rowid et non un id linéaire de 1 à n
3195
            // On parcourt donc une liste d'objets en tant qu'objet unique
3196
            foreach ($objectid as $key => $object) {
3197
                // Cas des factures liees directement
3198
                if ($objecttype == 'facture') {
3199
                    $linkedInvoices[] = $object;
3200
                } else {
3201
                    // Cas des factures liees par un autre object (ex: commande)
3202
                    $this->fetchObjectLinked($object, $objecttype);
3203
                    foreach ($this->linkedObjectsIds as $subobjecttype => $subobjectid) {
3204
                        foreach ($subobjectid as $subkey => $subobject) {
3205
                            if ($subobjecttype == 'facture') {
3206
                                $linkedInvoices[] = $subobject;
3207
                            }
3208
                        }
3209
                    }
3210
                }
3211
            }
3212
        }
3213
3214
        if (count($linkedInvoices) > 0) {
3215
            $sql = "SELECT rowid as facid, ref, total_ht as total, datef as df, fk_user_author, fk_statut, paye";
3216
            $sql .= " FROM " . MAIN_DB_PREFIX . "facture";
3217
            $sql .= " WHERE rowid IN (" . $this->db->sanitize(implode(',', $linkedInvoices)) . ")";
3218
3219
            dol_syslog(get_only_class($this) . "::InvoiceArrayList", LOG_DEBUG);
3220
            $resql = $this->db->query($sql);
3221
3222
            if ($resql) {
3223
                $tab_sqlobj = array();
3224
                $nump = $this->db->num_rows($resql);
3225
                for ($i = 0; $i < $nump; $i++) {
3226
                    $sqlobj = $this->db->fetch_object($resql);
3227
                    $tab_sqlobj[] = $sqlobj;
3228
                }
3229
                $this->db->free($resql);
3230
3231
                $nump = count($tab_sqlobj);
3232
3233
                if ($nump) {
3234
                    $i = 0;
3235
                    while ($i < $nump) {
3236
                        $obj = array_shift($tab_sqlobj);
3237
3238
                        $ga[$i] = $obj;
3239
3240
                        $i++;
3241
                    }
3242
                }
3243
                return $ga;
3244
            } else {
3245
                return -1;
3246
            }
3247
        } else {
3248
            return $ga;
3249
        }
3250
    }
3251
3252
3253
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3254
3255
    /**
3256
     *  Change the delivery time
3257
     *
3258
     * @param int $availability_id Id of new delivery time
3259
     * @param int $notrigger 1=Does not execute triggers, 0= execute triggers
3260
     * @return int                     >0 if OK, <0 if KO
3261
     * @deprecated  use set_availability
3262
     */
3263
    public function availability($availability_id, $notrigger = 0)
3264
    {
3265
        global $user;
3266
3267
        if ($this->statut >= self::STATUS_DRAFT) {
3268
            $error = 0;
3269
3270
            $this->db->begin();
3271
3272
            $sql = 'UPDATE ' . MAIN_DB_PREFIX . 'propal';
3273
            $sql .= ' SET fk_availability = ' . ((int)$availability_id);
3274
            $sql .= ' WHERE rowid=' . ((int)$this->id);
3275
3276
            dol_syslog(__METHOD__ . ' availability(' . $availability_id . ')', LOG_DEBUG);
3277
            $resql = $this->db->query($sql);
3278
            if (!$resql) {
3279
                $this->errors[] = $this->db->error();
3280
                $error++;
3281
            }
3282
3283
            if (!$error) {
3284
                $this->oldcopy = clone $this;
3285
                $this->availability_id = $availability_id;
3286
            }
3287
3288
            if (!$notrigger && empty($error)) {
3289
                // Call trigger
3290
                $result = $this->call_trigger('PROPAL_MODIFY', $user);
3291
                if ($result < 0) {
3292
                    $error++;
3293
                }
3294
                // End call triggers
3295
            }
3296
3297
            if (!$error) {
3298
                $this->db->commit();
3299
                return 1;
3300
            } else {
3301
                foreach ($this->errors as $errmsg) {
3302
                    dol_syslog(__METHOD__ . ' Error: ' . $errmsg, LOG_ERR);
3303
                    $this->error .= ($this->error ? ', ' . $errmsg : $errmsg);
3304
                }
3305
                $this->db->rollback();
3306
                return -1 * $error;
3307
            }
3308
        } else {
3309
            $error_str = 'Propal status do not meet requirement ' . $this->statut;
3310
            dol_syslog(__METHOD__ . $error_str, LOG_ERR);
3311
            $this->error = $error_str;
3312
            $this->errors[] = $this->error;
3313
            return -2;
3314
        }
3315
    }
3316
3317
    /**
3318
     *  Change source demand
3319
     *
3320
     * @param int $demand_reason_id Id of new source demand
3321
     * @param int $notrigger 1=Does not execute triggers, 0= execute triggers
3322
     * @return int                     >0 si ok, <0 si ko
3323
     * @deprecated use set_demand_reason
3324
     */
3325
    public function demand_reason($demand_reason_id, $notrigger = 0)
3326
    {
3327
        // phpcs:enable
3328
        global $user;
3329
3330
        if ($this->status >= self::STATUS_DRAFT) {
3331
            $error = 0;
3332
3333
            $this->db->begin();
3334
3335
            $sql = 'UPDATE ' . MAIN_DB_PREFIX . 'propal';
3336
            $sql .= ' SET fk_input_reason = ' . ((int)$demand_reason_id);
3337
            $sql .= ' WHERE rowid=' . ((int)$this->id);
3338
3339
            dol_syslog(__METHOD__ . ' demand_reason(' . $demand_reason_id . ')', LOG_DEBUG);
3340
            $resql = $this->db->query($sql);
3341
            if (!$resql) {
3342
                $this->errors[] = $this->db->error();
3343
                $error++;
3344
            }
3345
3346
            if (!$error) {
3347
                $this->oldcopy = clone $this;
3348
                $this->demand_reason_id = $demand_reason_id;
3349
            }
3350
3351
            if (!$notrigger && empty($error)) {
3352
                // Call trigger
3353
                $result = $this->call_trigger('PROPAL_MODIFY', $user);
3354
                if ($result < 0) {
3355
                    $error++;
3356
                }
3357
                // End call triggers
3358
            }
3359
3360
            if (!$error) {
3361
                $this->db->commit();
3362
                return 1;
3363
            } else {
3364
                foreach ($this->errors as $errmsg) {
3365
                    dol_syslog(__METHOD__ . ' Error: ' . $errmsg, LOG_ERR);
3366
                    $this->error .= ($this->error ? ', ' . $errmsg : $errmsg);
3367
                }
3368
                $this->db->rollback();
3369
                return -1 * $error;
3370
            }
3371
        } else {
3372
            $error_str = 'Propal status do not meet requirement ' . $this->statut;
3373
            dol_syslog(__METHOD__ . $error_str, LOG_ERR);
3374
            $this->error = $error_str;
3375
            $this->errors[] = $this->error;
3376
            return -2;
3377
        }
3378
    }
3379
3380
    /**
3381
     *      Load indicators for dashboard (this->nbtodo and this->nbtodolate)
3382
     *
3383
     * @param User $user Object user
3384
     * @param string $mode "opened" for proposal to close, "signed" for proposal to invoice
3385
     * @return WorkboardResponse|int Return integer <0 if KO, WorkboardResponse if OK
3386
     */
3387
    public function load_board($user, $mode)
3388
    {
3389
        // phpcs:enable
3390
        global $conf, $langs;
3391
3392
        $clause = " WHERE";
3393
3394
        $sql = "SELECT p.rowid, p.ref, p.datec as datec, p.fin_validite as datefin, p.total_ht";
3395
        $sql .= " FROM " . MAIN_DB_PREFIX . "propal as p";
3396
        $sql .= $clause . " p.entity IN (" . getEntity('propal') . ")";
3397
        if ($mode == 'opened') {
3398
            $sql .= " AND p.fk_statut = " . self::STATUS_VALIDATED;
3399
        }
3400
        if ($mode == 'signed') {
3401
            $sql .= " AND p.fk_statut = " . self::STATUS_SIGNED;
3402
        }
3403
        // If the internal user must only see his customers, force searching by him
3404
        $search_sale = 0;
3405
        if (!$user->hasRight('societe', 'client', 'voir')) {
3406
            $search_sale = $user->id;
3407
        }
3408
        // Search on sale representative
3409
        if ($search_sale && $search_sale != '-1') {
3410
            if ($search_sale == -2) {
3411
                $sql .= " AND NOT EXISTS (SELECT sc.fk_soc FROM " . MAIN_DB_PREFIX . "societe_commerciaux as sc WHERE sc.fk_soc = p.fk_soc)";
3412
            } elseif ($search_sale > 0) {
3413
                $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) . ")";
3414
            }
3415
        }
3416
3417
        $resql = $this->db->query($sql);
3418
        if ($resql) {
3419
            $langs->load("propal");
3420
            $now = dol_now();
3421
3422
            $delay_warning = 0;
3423
            $status = 0;
3424
            $label = $labelShort = '';
3425
            if ($mode == 'opened') {
3426
                $delay_warning = $conf->propal->cloture->warning_delay;
3427
                $status = self::STATUS_VALIDATED;
3428
                $label = $langs->transnoentitiesnoconv("PropalsToClose");
3429
                $labelShort = $langs->transnoentitiesnoconv("ToAcceptRefuse");
3430
            }
3431
            if ($mode == 'signed') {
3432
                $delay_warning = $conf->propal->facturation->warning_delay;
3433
                $status = self::STATUS_SIGNED;
3434
                $label = $langs->trans("PropalsToBill"); // We set here bill but may be billed or ordered
3435
                $labelShort = $langs->trans("ToBill");
3436
            }
3437
3438
            $response = new WorkboardResponse();
3439
            $response->warning_delay = $delay_warning / 60 / 60 / 24;
3440
            $response->label = $label;
3441
            $response->labelShort = $labelShort;
3442
            $response->url = constant('BASE_URL') . '/comm/propal/list.php?search_status=' . $status . '&mainmenu=commercial&leftmenu=propals';
3443
            $response->url_late = constant('BASE_URL') . '/comm/propal/list.php?search_status=' . $status . '&mainmenu=commercial&leftmenu=propals&sortfield=p.datep&sortorder=asc';
3444
            $response->img = img_object('', "propal");
3445
3446
            // This assignment in condition is not a bug. It allows walking the results.
3447
            while ($obj = $this->db->fetch_object($resql)) {
3448
                $response->nbtodo++;
3449
                $response->total += $obj->total_ht;
3450
3451
                if ($mode == 'opened') {
3452
                    $datelimit = $this->db->jdate($obj->datefin);
3453
                    if ($datelimit < ($now - $delay_warning)) {
3454
                        $response->nbtodolate++;
3455
                    }
3456
                }
3457
                // TODO Definir regle des propales a facturer en retard
3458
                // if ($mode == 'signed' && ! count($this->FactureListeArray($obj->rowid))) $this->nbtodolate++;
3459
            }
3460
3461
            return $response;
3462
        } else {
3463
            $this->error = $this->db->error();
3464
            return -1;
3465
        }
3466
    }
3467
3468
    /**
3469
     *  Initialise an instance with random values.
3470
     *  Used to build previews or test instances.
3471
     *  id must be 0 if object instance is a specimen.
3472
     *
3473
     * @return int
3474
     */
3475
    public function initAsSpecimen()
3476
    {
3477
        global $conf, $langs;
3478
3479
        // Load array of products prodids
3480
        $num_prods = 0;
3481
        $prodids = array();
3482
        $sql = "SELECT rowid";
3483
        $sql .= " FROM " . MAIN_DB_PREFIX . "product";
3484
        $sql .= " WHERE entity IN (" . getEntity('product') . ")";
3485
        $sql .= $this->db->plimit(100);
3486
3487
        $resql = $this->db->query($sql);
3488
        if ($resql) {
3489
            $num_prods = $this->db->num_rows($resql);
3490
            $i = 0;
3491
            while ($i < $num_prods) {
3492
                $i++;
3493
                $row = $this->db->fetch_row($resql);
3494
                $prodids[$i] = $row[0];
3495
            }
3496
        }
3497
3498
        // Initialise parameters
3499
        $this->id = 0;
3500
        $this->ref = 'SPECIMEN';
3501
        $this->ref_client = 'NEMICEPS';
3502
        $this->specimen = 1;
3503
        $this->socid = 1;
3504
        $this->date = time();
3505
        $this->fin_validite = $this->date + 3600 * 24 * 30;
3506
        $this->cond_reglement_id = 1;
3507
        $this->cond_reglement_code = 'RECEP';
3508
        $this->mode_reglement_id = 7;
3509
        $this->mode_reglement_code = 'CHQ';
3510
        $this->availability_id = 1;
3511
        $this->availability_code = 'AV_NOW';
3512
        $this->demand_reason_id = 1;
3513
        $this->demand_reason_code = 'SRC_00';
3514
        $this->note_public = 'This is a comment (public)';
3515
        $this->note_private = 'This is a comment (private)';
3516
3517
        $this->multicurrency_tx = 1;
3518
        $this->multicurrency_code = $conf->currency;
3519
3520
        // Lines
3521
        $nbp = 5;
3522
        $xnbp = 0;
3523
        while ($xnbp < $nbp) {
3524
            $line = new PropaleLigne($this->db);
3525
            $line->desc = $langs->trans("Description") . " " . $xnbp;
3526
            $line->qty = 1;
3527
            $line->subprice = 100;
3528
            $line->price = 100;
3529
            $line->tva_tx = 20;
3530
            $line->localtax1_tx = 0;
3531
            $line->localtax2_tx = 0;
3532
            if ($xnbp == 2) {
3533
                $line->total_ht = 50;
3534
                $line->total_ttc = 60;
3535
                $line->total_tva = 10;
3536
                $line->remise_percent = 50;
3537
            } else {
3538
                $line->total_ht = 100;
3539
                $line->total_ttc = 120;
3540
                $line->total_tva = 20;
3541
                $line->remise_percent = 0;
3542
            }
3543
3544
            if ($num_prods > 0) {
3545
                $prodid = mt_rand(1, $num_prods);
3546
                $line->fk_product = $prodids[$prodid];
3547
                $line->product_ref = 'SPECIMEN';
3548
            }
3549
3550
            $this->lines[$xnbp] = $line;
3551
3552
            $this->total_ht += $line->total_ht;
3553
            $this->total_tva += $line->total_tva;
3554
            $this->total_ttc += $line->total_ttc;
3555
3556
            $xnbp++;
3557
        }
3558
3559
        return 1;
3560
    }
3561
3562
    /**
3563
     *      Load the indicators this->nb for the state board
3564
     *
3565
     * @return     int         Return integer <0 if ko, >0 if ok
3566
     */
3567
    public function loadStateBoard()
3568
    {
3569
        global $user;
3570
3571
        $this->nb = array();
3572
        $clause = "WHERE";
3573
3574
        $sql = "SELECT count(p.rowid) as nb";
3575
        $sql .= " FROM " . MAIN_DB_PREFIX . "propal as p";
3576
        $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "societe as s ON p.fk_soc = s.rowid";
3577
        $sql .= " " . $clause . " p.entity IN (" . getEntity('propal') . ")";
3578
3579
        // If the internal user must only see his customers, force searching by him
3580
        $search_sale = 0;
3581
        if (!$user->hasRight('societe', 'client', 'voir')) {
3582
            $search_sale = $user->id;
3583
        }
3584
        // Search on sale representative
3585
        if ($search_sale && $search_sale != '-1') {
3586
            if ($search_sale == -2) {
3587
                $sql .= " AND NOT EXISTS (SELECT sc.fk_soc FROM " . MAIN_DB_PREFIX . "societe_commerciaux as sc WHERE sc.fk_soc = p.fk_soc)";
3588
            } elseif ($search_sale > 0) {
3589
                $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) . ")";
3590
            }
3591
        }
3592
3593
        $resql = $this->db->query($sql);
3594
        if ($resql) {
3595
            // This assignment in condition is not a bug. It allows walking the results.
3596
            while ($obj = $this->db->fetch_object($resql)) {
3597
                $this->nb["proposals"] = $obj->nb;
3598
            }
3599
            $this->db->free($resql);
3600
            return 1;
3601
        } else {
3602
            dol_print_error($this->db);
3603
            $this->error = $this->db->error();
3604
            return -1;
3605
        }
3606
    }
3607
3608
    /**
3609
     * getTooltipContentArray
3610
     * @param array $params params to construct tooltip data
3611
     * @return array
3612
     * @since v18
3613
     */
3614
    public function getTooltipContentArray($params)
3615
    {
3616
        global $conf, $langs, $user;
3617
3618
        $langs->load('propal');
3619
        $datas = [];
3620
        $nofetch = !empty($params['nofetch']);
3621
3622
        if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
3623
            return ['optimize' => $langs->trans("Proposal")];
3624
        }
3625
        if ($user->hasRight('propal', 'lire')) {
3626
            $datas['picto'] = img_picto('', $this->picto) . ' <u class="paddingrightonly">' . $langs->trans("Proposal") . '</u>';
3627
            if (isset($this->status)) {
3628
                $datas['status'] = ' ' . $this->getLibStatut(5);
3629
            }
3630
            if (!empty($this->ref)) {
3631
                $datas['ref'] = '<br><b>' . $langs->trans('Ref') . ':</b> ' . $this->ref;
3632
            }
3633
            if (!$nofetch) {
3634
                $langs->load('companies');
3635
                if (empty($this->thirdparty)) {
3636
                    $this->fetch_thirdparty();
3637
                }
3638
                $datas['customer'] = '<br><b>' . $langs->trans('Customer') . ':</b> ' . $this->thirdparty->getNomUrl(1, '', 0, 1);
3639
            }
3640
            if (!empty($this->ref_customer)) {
3641
                $datas['refcustomer'] = '<br><b>' . $langs->trans('RefCustomer') . ':</b> ' . $this->ref_customer;
3642
            }
3643
            if (!$nofetch) {
3644
                $langs->load('project');
3645
                if (is_null($this->project) || (is_object($this->project) && $this->project->isEmpty())) {
3646
                    $res = $this->fetch_project();
3647
                    if ($res > 0 && $this->project instanceof Project) {
3648
                        $datas['project'] = '<br><b>' . $langs->trans('Project') . ':</b> ' . $this->project->getNomUrl(1, '', 0, 1);
3649
                    }
3650
                }
3651
            }
3652
            if (!empty($this->total_ht)) {
3653
                $datas['amountht'] = '<br><b>' . $langs->trans('AmountHT') . ':</b> ' . price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
3654
            }
3655
            if (!empty($this->total_tva)) {
3656
                $datas['vat'] = '<br><b>' . $langs->trans('VAT') . ':</b> ' . price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
3657
            }
3658
            if (!empty($this->total_ttc)) {
3659
                $datas['amountttc'] = '<br><b>' . $langs->trans('AmountTTC') . ':</b> ' . price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
3660
            }
3661
            if (!empty($this->date)) {
3662
                $datas['date'] = '<br><b>' . $langs->trans('Date') . ':</b> ' . dol_print_date($this->date, 'day');
3663
            }
3664
            if (!empty($this->delivery_date)) {
3665
                $datas['deliverydate'] = '<br><b>' . $langs->trans('DeliveryDate') . ':</b> ' . dol_print_date($this->delivery_date, 'dayhour');
3666
            }
3667
        }
3668
3669
        return $datas;
3670
    }
3671
3672
    /**
3673
     *      Return label of status of proposal (draft, validated, ...)
3674
     *
3675
     * @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
3676
     * @return     string      Label
3677
     */
3678
    public function getLibStatut($mode = 0)
3679
    {
3680
        return $this->LibStatut($this->statut, $mode);
3681
    }
3682
3683
    /**
3684
     *      Return label of a status (draft, validated, ...)
3685
     *
3686
     * @param int $status Id status
3687
     * @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
3688
     * @return     string      Label
3689
     */
3690
    public function LibStatut($status, $mode = 1)
3691
    {
3692
        // phpcs:enable
3693
        global $hookmanager;
3694
3695
        // Init/load array of translation of status
3696
        if (empty($this->labelStatus) || empty($this->labelStatusShort)) {
3697
            global $langs;
3698
            $langs->load("propal");
3699
            $this->labelStatus[-1] = $langs->transnoentitiesnoconv("PropalStatusCanceled");
3700
            $this->labelStatus[0] = $langs->transnoentitiesnoconv("PropalStatusDraft");
3701
            $this->labelStatus[1] = $langs->transnoentitiesnoconv("PropalStatusValidated");
3702
            $this->labelStatus[2] = $langs->transnoentitiesnoconv("PropalStatusSigned");
3703
            $this->labelStatus[3] = $langs->transnoentitiesnoconv("PropalStatusNotSigned");
3704
            $this->labelStatus[4] = $langs->transnoentitiesnoconv("PropalStatusBilled");
3705
            $this->labelStatusShort[-1] = $langs->transnoentitiesnoconv("PropalStatusCanceledShort");
3706
            $this->labelStatusShort[0] = $langs->transnoentitiesnoconv("PropalStatusDraftShort");
3707
            $this->labelStatusShort[1] = $langs->transnoentitiesnoconv("PropalStatusValidatedShort");
3708
            $this->labelStatusShort[2] = $langs->transnoentitiesnoconv("PropalStatusSignedShort");
3709
            $this->labelStatusShort[3] = $langs->transnoentitiesnoconv("PropalStatusNotSignedShort");
3710
            $this->labelStatusShort[4] = $langs->transnoentitiesnoconv("PropalStatusBilledShort");
3711
        }
3712
3713
        $statusType = '';
3714
        if ($status == self::STATUS_CANCELED) {
3715
            $statusType = 'status9';
3716
        } elseif ($status == self::STATUS_DRAFT) {
3717
            $statusType = 'status0';
3718
        } elseif ($status == self::STATUS_VALIDATED) {
3719
            $statusType = 'status1';
3720
        } elseif ($status == self::STATUS_SIGNED) {
3721
            $statusType = 'status4';
3722
        } elseif ($status == self::STATUS_NOTSIGNED) {
3723
            $statusType = 'status9';
3724
        } elseif ($status == self::STATUS_BILLED) {
3725
            $statusType = 'status6';
3726
        }
3727
3728
        $parameters = array('status' => $status, 'mode' => $mode);
3729
        $reshook = $hookmanager->executeHooks('LibStatut', $parameters, $this); // Note that $action and $object may have been modified by hook
3730
3731
        if ($reshook > 0) {
3732
            return $hookmanager->resPrint;
3733
        }
3734
3735
        return dolGetStatus($this->labelStatus[$status], $this->labelStatusShort[$status], '', $statusType, $mode);
3736
    }
3737
3738
    /**
3739
     *  Return clicable link of object (with eventually picto)
3740
     *
3741
     * @param int $withpicto Add picto into link
3742
     * @param string $option Where point the link ('expedition', 'document', ...)
3743
     * @param string $get_params Parameters added to url
3744
     * @param int $notooltip 1=Disable tooltip
3745
     * @param int $save_lastsearch_value -1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking
3746
     * @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)
3747
     * @return     string                            String with URL
3748
     */
3749
    public function getNomUrl($withpicto = 0, $option = '', $get_params = '', $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = -1)
3750
    {
3751
        global $langs, $conf, $user, $hookmanager;
3752
3753
        if (!empty($conf->dol_no_mouse_hover)) {
3754
            $notooltip = 1; // Force disable tooltips
3755
        }
3756
3757
        $result = '';
3758
        $params = [
3759
            'id' => $this->id,
3760
            'objecttype' => $this->element,
3761
            'option' => $option,
3762
            'nofetch' => 1,
3763
        ];
3764
        $classfortooltip = 'classfortooltip';
3765
        $dataparams = '';
3766
        if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
3767
            $classfortooltip = 'classforajaxtooltip';
3768
            $dataparams = ' data-params="' . dol_escape_htmltag(json_encode($params)) . '"';
3769
            $label = '';
3770
        } else {
3771
            $label = implode($this->getTooltipContentArray($params));
3772
        }
3773
3774
        $url = '';
3775
        if ($user->hasRight('propal', 'lire')) {
3776
            if ($option == '') {
3777
                $url = constant('BASE_URL') . '/comm/propal/card.php?id=' . $this->id . $get_params;
3778
            } elseif ($option == 'compta') {  // deprecated
3779
                $url = constant('BASE_URL') . '/comm/propal/card.php?id=' . $this->id . $get_params;
3780
            } elseif ($option == 'expedition') {
3781
                $url = constant('BASE_URL') . '/expedition/propal.php?id=' . $this->id . $get_params;
3782
            } elseif ($option == 'document') {
3783
                $url = constant('BASE_URL') . '/comm/propal/document.php?id=' . $this->id . $get_params;
3784
            }
3785
3786
            if ($option != 'nolink') {
3787
                // Add param to save lastsearch_values or not
3788
                $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
3789
                if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
3790
                    $add_save_lastsearch_values = 1;
3791
                }
3792
                if ($add_save_lastsearch_values) {
3793
                    $url .= '&save_lastsearch_values=1';
3794
                }
3795
            }
3796
        }
3797
3798
        $linkclose = '';
3799
        if (empty($notooltip) && $user->hasRight('propal', 'lire')) {
3800
            if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
3801
                $label = $langs->trans("Proposal");
3802
                $linkclose .= ' alt="' . dol_escape_htmltag($label, 1) . '"';
3803
            }
3804
            $linkclose .= ($label ? ' title="' . dol_escape_htmltag($label, 1) . '"' : ' title="tocomplete"');
3805
            $linkclose .= $dataparams . ' class="' . $classfortooltip . '"';
3806
        }
3807
3808
        $linkstart = '<a href="' . $url . '"';
3809
        $linkstart .= $linkclose . '>';
3810
        $linkend = '</a>';
3811
3812
        $result .= $linkstart;
3813
        if ($withpicto) {
3814
            $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), (($withpicto != 2) ? 'class="paddingright"' : ''), 0, 0, $notooltip ? 0 : 1);
3815
        }
3816
        if ($withpicto != 2) {
3817
            $result .= $this->ref;
3818
        }
3819
        $result .= $linkend;
3820
3821
        if ($addlinktonotes >= 0) {
3822
            $txttoshow = '';
3823
3824
            if ($addlinktonotes == 0) {
3825
                if (!empty($this->note_private) || !empty($this->note_public)) {
3826
                    $txttoshow = $langs->trans('ViewPrivateNote');
3827
                }
3828
            } elseif ($addlinktonotes == 1) {
3829
                if (!empty($this->note_private)) {
3830
                    $txttoshow .= ($user->socid > 0 ? '' : dol_string_nohtmltag($this->note_private, 1));
3831
                }
3832
            } elseif ($addlinktonotes == 2) {
3833
                if (!empty($this->note_public)) {
3834
                    $txttoshow .= dol_string_nohtmltag($this->note_public, 1);
3835
                }
3836
            } elseif ($addlinktonotes == 3) {
3837
                if ($user->socid > 0) {
3838
                    if (!empty($this->note_public)) {
3839
                        $txttoshow .= dol_string_nohtmltag($this->note_public, 1);
3840
                    }
3841
                } else {
3842
                    if (!empty($this->note_public)) {
3843
                        $txttoshow .= dol_string_nohtmltag($this->note_public, 1);
3844
                    }
3845
                    if (!empty($this->note_private)) {
3846
                        if (!empty($txttoshow)) {
3847
                            $txttoshow .= '<br><br>';
3848
                        }
3849
                        $txttoshow .= dol_string_nohtmltag($this->note_private, 1);
3850
                    }
3851
                }
3852
            }
3853
3854
            if ($txttoshow) {
3855
                $result .= ' <span class="note inline-block">';
3856
                $result .= '<a href="' . constant('BASE_URL') . '/comm/propal/note.php?id=' . $this->id . '" class="classfortooltip" title="' . dol_escape_htmltag($txttoshow) . '">';
3857
                $result .= img_picto('', 'note');
3858
                $result .= '</a>';
3859
                $result .= '</span>';
3860
            }
3861
        }
3862
3863
        global $action;
3864
        $hookmanager->initHooks(array($this->element . 'dao'));
3865
        $parameters = array('id' => $this->id, 'getnomurl' => &$result);
3866
        $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
3867
        if ($reshook > 0) {
3868
            $result = $hookmanager->resPrint;
3869
        } else {
3870
            $result .= $hookmanager->resPrint;
3871
        }
3872
        return $result;
3873
    }
3874
3875
    /**
3876
     *  Retrieve an array of proposal lines
3877
     *
3878
     * @param string $sqlforgedfilters Filter on other fields
3879
     * @return int                             >0 if OK, <0 if KO
3880
     */
3881
    public function getLinesArray($sqlforgedfilters = '')
3882
    {
3883
        return $this->fetch_lines(0, 0, $sqlforgedfilters);
3884
    }
3885
3886
    /**
3887
     *  Return clicable link of object (with eventually picto)
3888
     *
3889
     * @param string $option Where point the link (0=> main card, 1,2 => shipment, 'nolink'=>No link)
3890
     * @param array $arraydata Array of data
3891
     * @return     string                              HTML Code for Kanban thumb.
3892
     */
3893
    public function getKanbanView($option = '', $arraydata = null)
3894
    {
3895
        global $langs;
3896
3897
        $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
3898
3899
        $return = '<div class="box-flex-item box-flex-grow-zero">';
3900
        $return .= '<div class="info-box info-box-sm">';
3901
        $return .= '<div class="info-box-icon bg-infobox-action">';
3902
        $return .= img_picto('', $this->picto);
3903
        $return .= '</div>';
3904
        $return .= '<div class="info-box-content">';
3905
        $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">' . (method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref) . '</span>';
3906
        if ($selected >= 0) {
3907
            $return .= '<input id="cb' . $this->id . '" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="' . $this->id . '"' . ($selected ? ' checked="checked"' : '') . '>';
3908
        }
3909
        if (!empty($arraydata['projectlink'])) {
3910
            $return .= '<span class="info-box-ref"> | ' . $arraydata['projectlink'] . '</span>';
3911
        }
3912
        $return .= '<br>';
3913
        if (property_exists($this, 'thirdparty') && is_object($this->thirdparty)) {
3914
            $return .= '<div class="info-box-ref tdoverflowmax150">' . $this->thirdparty->getNomUrl(1) . '</div>';
3915
        }
3916
        if (property_exists($this, 'total_ht')) {
3917
            $return .= '<span class="info-box-label amount" title="' . $langs->trans("AmountHT") . '">' . price($this->total_ht) . '</span>';
3918
        }
3919
        if (!empty($arraydata['authorlink'])) {
3920
            $return .= ' &nbsp; <span class="info-box-label">' . $arraydata['authorlink'] . '</span>';
3921
        }
3922
        if (method_exists($this, 'getLibStatut')) {
3923
            $return .= '<br><div class="info-box-status">' . $this->getLibStatut(3) . '</div>';
3924
        }
3925
        $return .= '</div>';
3926
        $return .= '</div>';
3927
        $return .= '</div>';
3928
        return $return;
3929
    }
3930
}
3931