Passed
Branch develop (a7390e)
by
unknown
25:38
created

Propal::set_remise_absolue()   B

Complexity

Conditions 11
Paths 98

Size

Total Lines 54
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 11
eloc 29
c 0
b 0
f 0
nc 98
nop 3
dl 0
loc 54
rs 7.3166

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
/* Copyright (C) 2002-2004 Rodolphe Quiedeville		<[email protected]>
3
 * Copyright (C) 2004      Eric Seigne				<[email protected]>
4
 * Copyright (C) 2004-2011 Laurent Destailleur		<[email protected]>
5
 * Copyright (C) 2005      Marc Barilley			<[email protected]>
6
 * Copyright (C) 2005-2013 Regis Houssin			<[email protected]>
7
 * Copyright (C) 2006      Andre Cianfarani			<[email protected]>
8
 * Copyright (C) 2008      Raphael Bertrand			<[email protected]>
9
 * Copyright (C) 2010-2014 Juanjo Menent			<[email protected]>
10
 * Copyright (C) 2010-2017 Philippe Grand			<[email protected]>
11
 * Copyright (C) 2012-2014 Christophe Battarel  	<[email protected]>
12
 * Copyright (C) 2012      Cedric Salvador          <[email protected]>
13
 * Copyright (C) 2013      Florian Henry		  	<[email protected]>
14
 * Copyright (C) 2014-2015 Marcos García            <[email protected]>
15
 * Copyright (C) 2018      Nicolas ZABOURI			<[email protected]>
16
 * Copyright (C) 2018      Frédéric France          <[email protected]>
17
 * Copyright (C) 2018      Ferran Marcet         	<[email protected]>
18
 *
19
 * This program is free software; you can redistribute it and/or modify
20
 * it under the terms of the GNU General Public License as published by
21
 * the Free Software Foundation; either version 3 of the License, or
22
 * (at your option) any later version.
23
 *
24
 * This program is distributed in the hope that it will be useful,
25
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
27
 * GNU General Public License for more details.
28
 *
29
 * You should have received a copy of the GNU General Public License
30
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
31
 */
32
33
/**
34
 *	\file       htdocs/comm/propal/class/propal.class.php
35
 *	\brief      File of class to manage proposals
36
 */
37
38
require_once DOL_DOCUMENT_ROOT .'/core/class/commonobject.class.php';
39
require_once DOL_DOCUMENT_ROOT ."/core/class/commonobjectline.class.php";
40
require_once DOL_DOCUMENT_ROOT .'/product/class/product.class.php';
41
require_once DOL_DOCUMENT_ROOT .'/contact/class/contact.class.php';
42
require_once DOL_DOCUMENT_ROOT .'/margin/lib/margins.lib.php';
43
require_once DOL_DOCUMENT_ROOT .'/multicurrency/class/multicurrency.class.php';
44
45
/**
46
 *	Class to manage proposals
47
 */
48
class Propal extends CommonObject
49
{
50
	/**
51
	 * @var string ID to identify managed object
52
	 */
53
	public $element='propal';
54
55
	/**
56
	 * @var string Name of table without prefix where object is stored
57
	 */
58
	public $table_element='propal';
59
60
	/**
61
	 * @var int    Name of subtable line
62
	 */
63
	public $table_element_line='propaldet';
64
65
	/**
66
	 * @var int Field with ID of parent key if this field has a parent
67
	 */
68
	public $fk_element ='fk_propal';
69
70
	/**
71
	 * @var string String with name of icon for myobject. Must be the part after the 'object_' into object_myobject.png
72
	 */
73
	public $picto='propal';
74
75
	/**
76
	 * 0=No test on entity, 1=Test with field entity, 2=Test with link by societe
77
	 * @var int
78
	 */
79
	public $ismultientitymanaged = 1;
80
81
	/**
82
	 * 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
83
	 * @var integer
84
	 */
85
	public $restrictiononfksoc = 1;
86
87
	/**
88
	 * {@inheritdoc}
89
	 */
90
	protected $table_ref_field = 'ref';
91
92
	/**
93
	 * ID of the client
94
	 * @var int
95
	 */
96
	public $socid;
97
98
	public $contactid;
99
	public $author;
100
	public $ref_client;
101
102
	/**
103
	 * Status of the quote
104
	 * @var int
105
	 * @see Propal::STATUS_DRAFT, Propal::STATUS_VALIDATED, Propal::STATUS_SIGNED, Propal::STATUS_NOTSIGNED, Propal::STATUS_BILLED
106
	 */
107
	public $statut;
108
109
	/**
110
	 * @deprecated
111
	 * @see $date_creation
112
	 */
113
	public $datec;
114
115
	/**
116
	 * Creation date
117
	 * @var int
118
	 */
119
	public $date_creation;
120
121
	/**
122
	 * @deprecated
123
	 * @see $date_validation
124
	 */
125
	public $datev;
126
127
	/**
128
	 * Validation date
129
	 * @var int
130
	 */
131
	public $date_validation;
132
133
	/**
134
	 * Date of the quote
135
	 * @var
136
	 */
137
	public $date;
138
139
	/**
140
	 * @deprecated
141
	 * @see $date
142
	 */
143
	public $datep;
144
	public $date_livraison;
145
	public $fin_validite;
146
147
	public $user_author_id;
148
	public $user_valid_id;
149
	public $user_close_id;
150
151
	/**
152
	 * @deprecated
153
	 * @see $total_ht
154
	 */
155
	public $price;
156
	/**
157
	 * @deprecated
158
	 * @see $total_tva
159
	 */
160
	public $tva;
161
	/**
162
	 * @deprecated
163
	 * @see $total_ttc
164
	 */
165
	public $total;
166
167
	public $cond_reglement_code;
168
	public $mode_reglement_code;
169
	public $remise = 0;
170
	public $remise_percent = 0;
171
	public $remise_absolue = 0;
172
173
	/**
174
     * @var int ID
175
     */
176
	public $fk_address;
177
178
	public $address_type;
179
	public $address;
180
	public $availability_id;
181
	public $availability_code;
182
	public $demand_reason_id;
183
	public $demand_reason_code;
184
185
	public $products=array();
186
	public $extraparams=array();
187
188
	/**
189
	 * @var PropaleLigne[]
190
	 */
191
	public $lines = array();
192
	public $line;
193
194
	public $labelstatut=array();
195
	public $labelstatut_short=array();
196
197
	public $specimen;
198
199
	// Multicurrency
200
	/**
201
     * @var int ID
202
     */
203
	public $fk_multicurrency;
204
205
	public $multicurrency_code;
206
	public $multicurrency_tx;
207
	public $multicurrency_total_ht;
208
	public $multicurrency_total_tva;
209
	public $multicurrency_total_ttc;
210
211
	public $oldcopy;
212
213
	/**
214
	 * Draft status
215
	 */
216
	const STATUS_DRAFT = 0;
217
	/**
218
	 * Validated status
219
	 */
220
	const STATUS_VALIDATED = 1;
221
	/**
222
	 * Signed quote
223
	 */
224
	const STATUS_SIGNED = 2;
225
	/**
226
	 * Not signed quote
227
	 */
228
	const STATUS_NOTSIGNED = 3;
229
	/**
230
	 * Billed or processed quote
231
	 */
232
	const STATUS_BILLED = 4;   // Todo rename into STATUS_CLOSE ?
233
234
235
	/**
236
	 *	Constructor
237
	 *
238
	 *	@param      DoliDB	$db         Database handler
239
	 *	@param      int		$socid		Id third party
240
	 *	@param      int		$propalid   Id proposal
241
	 */
242
    public function __construct($db, $socid = "", $propalid = 0)
243
	{
244
		global $conf,$langs;
245
246
		$this->db = $db;
247
248
		$this->socid = $socid;
1 ignored issue
show
Documentation Bug introduced by
It seems like $socid can also be of type string. However, the property $socid is declared as type integer. Maybe add an additional type check?

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

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

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

class Id
{
    public $id;

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

}

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

$account_id = false;

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

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
249
		$this->id = $propalid;
250
251
		$this->products = array();
252
253
		$this->duree_validite=$conf->global->PROPALE_VALIDITY_DURATION;
254
	}
255
256
257
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
258
	/**
259
	 *  Add line into array products
260
	 *  $this->thirdparty should be loaded
261
	 *
262
	 * 	@param  int		$idproduct       	Product Id to add
263
	 * 	@param  int		$qty             	Quantity
264
	 * 	@param  int		$remise_percent  	Discount effected on Product
265
	 *  @return	int							<0 if KO, >0 if OK
266
	 *
267
	 *	TODO	Replace calls to this function by generation objet Ligne
268
	 *			inserted into table $this->products
269
	 */
270
    public function add_product($idproduct, $qty, $remise_percent = 0)
271
	{
272
        // phpcs:enable
273
		global $conf, $mysoc;
274
275
		if (! $qty) $qty = 1;
276
277
		dol_syslog(get_class($this)."::add_product $idproduct, $qty, $remise_percent");
278
		if ($idproduct > 0)
279
		{
280
			$prod=new Product($this->db);
281
			$prod->fetch($idproduct);
282
283
			$productdesc = $prod->description;
284
285
			$tva_tx = get_default_tva($mysoc, $this->thirdparty, $prod->id);
286
			$tva_npr = get_default_npr($mysoc, $this->thirdparty, $prod->id);
287
			if (empty($tva_tx)) $tva_npr=0;
288
			$vat_src_code = '';     // May be defined into tva_tx
289
290
			$localtax1_tx = get_localtax($tva_tx, 1, $mysoc, $this->thirdparty, $tva_npr);
291
			$localtax2_tx = get_localtax($tva_tx, 2, $mysoc, $this->thirdparty, $tva_npr);
292
293
			// multiprices
294
			if($conf->global->PRODUIT_MULTIPRICES && $this->thirdparty->price_level)
295
			{
296
				$price = $prod->multiprices[$this->thirdparty->price_level];
297
			}
298
			else
299
			{
300
				$price = $prod->price;
301
			}
302
303
			$line = new PropaleLigne($this->db);
304
305
			$line->fk_product=$idproduct;
306
			$line->desc=$productdesc;
307
			$line->qty=$qty;
308
			$line->subprice=$price;
309
			$line->remise_percent=$remise_percent;
310
			$line->vat_src_code=$vat_src_code;
311
			$line->tva_tx=$tva_tx;
312
			$line->fk_unit=$prod->fk_unit;
313
			if ($tva_npr) $line->info_bits = 1;
314
315
			$this->lines[]=$line;
316
		}
317
	}
318
319
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
320
	/**
321
	 *	Adding line of fixed discount in the proposal in DB
322
	 *
323
	 *	@param     int		$idremise			Id of fixed discount
324
	 *  @return    int          				>0 if OK, <0 if KO
325
	 */
326
    public function insert_discount($idremise)
327
	{
328
        // phpcs:enable
329
		global $langs;
330
331
		include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
332
		include_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
333
334
		$this->db->begin();
335
336
		$remise=new DiscountAbsolute($this->db);
337
		$result=$remise->fetch($idremise);
338
339
		if ($result > 0)
340
		{
341
			if ($remise->fk_facture)	// Protection against multiple submission
342
			{
343
				$this->error=$langs->trans("ErrorDiscountAlreadyUsed");
344
				$this->db->rollback();
345
				return -5;
346
			}
347
348
			$line=new PropaleLigne($this->db);
349
350
			$this->line->context = $this->context;
351
352
			$line->fk_propal=$this->id;
353
			$line->fk_remise_except=$remise->id;
354
			$line->desc=$remise->description;   	// Description ligne
355
			$line->vat_src_code=$remise->vat_src_code;
0 ignored issues
show
Bug introduced by
The property vat_src_code does not seem to exist on DiscountAbsolute.
Loading history...
356
			$line->tva_tx=$remise->tva_tx;
357
			$line->subprice=-$remise->amount_ht;
358
			$line->fk_product=0;					// Id produit predefined
359
			$line->qty=1;
360
			$line->remise=0;
1 ignored issue
show
Deprecated Code introduced by
The property PropaleLigne::$remise has been deprecated. ( Ignorable by Annotation )

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

360
			/** @scrutinizer ignore-deprecated */ $line->remise=0;
Loading history...
361
			$line->remise_percent=0;
362
			$line->rang=-1;
363
			$line->info_bits=2;
364
365
			// TODO deprecated
366
			$line->price=-$remise->amount_ht;
1 ignored issue
show
Deprecated Code introduced by
The property PropaleLigne::$price has been deprecated. ( Ignorable by Annotation )

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

366
			/** @scrutinizer ignore-deprecated */ $line->price=-$remise->amount_ht;
Loading history...
367
368
			$line->total_ht  = -$remise->amount_ht;
369
			$line->total_tva = -$remise->amount_tva;
370
			$line->total_ttc = -$remise->amount_ttc;
371
372
			$result=$line->insert();
373
			if ($result > 0)
374
			{
375
				$result=$this->update_price(1);
376
				if ($result > 0)
377
				{
378
					$this->db->commit();
379
					return 1;
380
				}
381
				else
382
				{
383
					$this->db->rollback();
384
					return -1;
385
				}
386
			}
387
			else
388
			{
389
				$this->error=$line->error;
390
				$this->db->rollback();
391
				return -2;
392
			}
393
		}
394
		else
395
		{
396
			$this->db->rollback();
397
			return -2;
398
		}
399
	}
400
401
    /**
402
     *    	Add a proposal line into database (linked to product/service or not)
403
     *      The parameters are already supposed to be appropriate and with final values to the call
404
     *      of this method. Also, for the VAT rate, it must have already been defined
405
     *      by whose calling the method get_default_tva (societe_vendeuse, societe_acheteuse, '' product)
406
     *      and desc must already have the right value (it's up to the caller to manage multilanguage)
407
     *
408
     * 		@param    	string		$desc				Description of line
409
     * 		@param    	float		$pu_ht				Unit price
410
     * 		@param    	float		$qty             	Quantity
411
     * 		@param    	float		$txtva           	Force Vat rate, -1 for auto (Can contain the vat_src_code too with syntax '9.9 (CODE)')
412
     * 		@param		float		$txlocaltax1		Local tax 1 rate (deprecated, use instead txtva with code inside)
413
     *  	@param		float		$txlocaltax2		Local tax 2 rate (deprecated, use instead txtva with code inside)
414
     *		@param    	int			$fk_product      	Product/Service ID predefined
415
     * 		@param    	float		$remise_percent  	Pourcentage de remise de la ligne
416
     * 		@param    	string		$price_base_type	HT or TTC
417
     * 		@param    	float		$pu_ttc             Prix unitaire TTC
418
     * 		@param    	int			$info_bits			Bits for type of lines
419
     *      @param      int			$type               Type of line (0=product, 1=service). Not used if fk_product is defined, the type of product is used.
420
     *      @param      int			$rang               Position of line
421
     *      @param		int			$special_code		Special code (also used by externals modules!)
422
     *      @param		int			$fk_parent_line		Id of parent line
423
     *      @param		int			$fk_fournprice		Id supplier price
424
     *      @param		int			$pa_ht				Buying price without tax
425
     *      @param		string		$label				???
426
     *		@param      int			$date_start       	Start date of the line
427
     *		@param      int			$date_end         	End date of the line
428
     *      @param		array		$array_options		extrafields array
429
     * 		@param 		string		$fk_unit 			Code of the unit to use. Null to use the default one
430
     *      @param		string		$origin				'order', ...
431
     *      @param		int			$origin_id			Id of origin object
432
     * 		@param		double		$pu_ht_devise		Unit price in currency
433
     * 		@param		int    		$fk_remise_except	Id discount if line is from a discount
434
     *    	@return    	int         	    			>0 if OK, <0 if KO
435
     *    	@see       	add_product()
436
     */
437
    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 = 0, $fk_unit = null, $origin = '', $origin_id = 0, $pu_ht_devise = 0, $fk_remise_except = 0)
438
	{
439
		global $mysoc, $conf, $langs;
440
441
		dol_syslog(get_class($this)."::addline propalid=$this->id, desc=$desc, pu_ht=$pu_ht, qty=$qty, txtva=$txtva, fk_product=$fk_product, remise_except=$remise_percent, price_base_type=$price_base_type, pu_ttc=$pu_ttc, info_bits=$info_bits, type=$type, fk_remise_except=".$fk_remise_except);
442
443
		if ($this->statut == self::STATUS_DRAFT)
444
		{
445
			include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
446
447
			// Clean parameters
448
			if (empty($remise_percent)) $remise_percent=0;
449
			if (empty($qty)) $qty=0;
450
			if (empty($info_bits)) $info_bits=0;
451
			if (empty($rang)) $rang=0;
452
			if (empty($fk_parent_line) || $fk_parent_line < 0) $fk_parent_line=0;
453
454
			$remise_percent=price2num($remise_percent);
455
			$qty=price2num($qty);
456
			$pu_ht=price2num($pu_ht);
457
			$pu_ht_devise=price2num($pu_ht_devise);
458
			$pu_ttc=price2num($pu_ttc);
459
			if (!preg_match('/\((.*)\)/', $txtva)) {
460
				$txtva = price2num($txtva);               // $txtva can have format '5,1' or '5.1' or '5.1(XXX)', we must clean only if '5,1'
461
			}
462
			$txlocaltax1=price2num($txlocaltax1);
463
			$txlocaltax2=price2num($txlocaltax2);
464
			$pa_ht=price2num($pa_ht);
465
			if ($price_base_type=='HT')
466
			{
467
				$pu=$pu_ht;
468
			}
469
			else
470
			{
471
				$pu=$pu_ttc;
472
			}
473
474
			// Check parameters
475
			if ($type < 0) return -1;
476
477
			if ($date_start && $date_end && $date_start > $date_end) {
478
				$langs->load("errors");
479
				$this->error=$langs->trans('ErrorStartDateGreaterEnd');
480
				return -1;
481
			}
482
483
			$this->db->begin();
484
485
			$product_type=$type;
486
			if (!empty($fk_product))
487
			{
488
				$product=new Product($this->db);
489
				$result=$product->fetch($fk_product);
490
				$product_type=$product->type;
491
492
				if (! empty($conf->global->STOCK_MUST_BE_ENOUGH_FOR_PROPOSAL) && $product_type == 0 && $product->stock_reel < $qty) {
493
					$langs->load("errors");
494
					$this->error=$langs->trans('ErrorStockIsNotEnoughToAddProductOnProposal', $product->ref);
495
					$this->db->rollback();
496
					return -3;
497
				}
498
			}
499
500
			// Calcul du total TTC et de la TVA pour la ligne a partir de
501
			// qty, pu, remise_percent et txtva
502
			// TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
503
			// la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
504
505
			$localtaxes_type=getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc);
506
507
			// Clean vat code
508
			$vat_src_code='';
509
			if (preg_match('/\((.*)\)/', $txtva, $reg))
510
			{
511
				$vat_src_code = $reg[1];
512
				$txtva = preg_replace('/\s*\(.*\)/', '', $txtva);    // Remove code into vatrate.
513
			}
514
515
			$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);
516
517
			$total_ht  = $tabprice[0];
518
			$total_tva = $tabprice[1];
519
			$total_ttc = $tabprice[2];
520
			$total_localtax1 = $tabprice[9];
521
			$total_localtax2 = $tabprice[10];
522
			$pu_ht  = $tabprice[3];
523
			$pu_tva = $tabprice[4];
524
			$pu_ttc = $tabprice[5];
525
526
			// MultiCurrency
527
			$multicurrency_total_ht  = $tabprice[16];
528
			$multicurrency_total_tva = $tabprice[17];
529
			$multicurrency_total_ttc = $tabprice[18];
530
			$pu_ht_devise = $tabprice[19];
531
532
			// Rang to use
533
			$rangtouse = $rang;
534
			if ($rangtouse == -1)
535
			{
536
				$rangmax = $this->line_max($fk_parent_line);
537
				$rangtouse = $rangmax + 1;
538
			}
539
540
			// TODO A virer
541
			// Anciens indicateurs: $price, $remise (a ne plus utiliser)
542
			$price = $pu;
543
			$remise = 0;
544
			if ($remise_percent > 0)
545
			{
546
				$remise = round(($pu * $remise_percent / 100), 2);
547
				$price = $pu - $remise;
548
			}
549
550
			// Insert line
551
			$this->line=new PropaleLigne($this->db);
552
553
			$this->line->context = $this->context;
554
555
			$this->line->fk_propal=$this->id;
556
			$this->line->label=$label;
557
			$this->line->desc=$desc;
558
			$this->line->qty=$qty;
559
560
			$this->line->vat_src_code=$vat_src_code;
561
			$this->line->tva_tx=$txtva;
562
			$this->line->localtax1_tx=($total_localtax1?$localtaxes_type[1]:0);
563
			$this->line->localtax2_tx=($total_localtax2?$localtaxes_type[3]:0);
564
			$this->line->localtax1_type = $localtaxes_type[0];
565
			$this->line->localtax2_type = $localtaxes_type[2];
566
			$this->line->fk_product=$fk_product;
567
			$this->line->product_type=$type;
568
			$this->line->fk_remise_except=$fk_remise_except;
569
			$this->line->remise_percent=$remise_percent;
570
			$this->line->subprice=$pu_ht;
571
			$this->line->rang=$rangtouse;
572
			$this->line->info_bits=$info_bits;
573
			$this->line->total_ht=$total_ht;
574
			$this->line->total_tva=$total_tva;
575
			$this->line->total_localtax1=$total_localtax1;
576
			$this->line->total_localtax2=$total_localtax2;
577
			$this->line->total_ttc=$total_ttc;
578
			$this->line->special_code=$special_code;
579
			$this->line->fk_parent_line=$fk_parent_line;
580
			$this->line->fk_unit=$fk_unit;
581
582
			$this->line->date_start=$date_start;
583
			$this->line->date_end=$date_end;
584
585
			$this->line->fk_fournprice = $fk_fournprice;
586
			$this->line->pa_ht = $pa_ht;
587
588
			$this->line->origin_id = $origin_id;
589
			$this->line->origin = $origin;
590
591
			// Multicurrency
592
			$this->line->fk_multicurrency			= $this->fk_multicurrency;
593
			$this->line->multicurrency_code			= $this->multicurrency_code;
594
			$this->line->multicurrency_subprice		= $pu_ht_devise;
595
			$this->line->multicurrency_total_ht 	= $multicurrency_total_ht;
596
			$this->line->multicurrency_total_tva 	= $multicurrency_total_tva;
597
			$this->line->multicurrency_total_ttc 	= $multicurrency_total_ttc;
598
599
			// Mise en option de la ligne
600
			if (empty($qty) && empty($special_code)) $this->line->special_code=3;
601
602
			// TODO deprecated
603
			$this->line->price=$price;
1 ignored issue
show
Deprecated Code introduced by
The property PropaleLigne::$price has been deprecated. ( Ignorable by Annotation )

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

603
			/** @scrutinizer ignore-deprecated */ $this->line->price=$price;
Loading history...
604
			$this->line->remise=$remise;
1 ignored issue
show
Deprecated Code introduced by
The property PropaleLigne::$remise has been deprecated. ( Ignorable by Annotation )

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

604
			/** @scrutinizer ignore-deprecated */ $this->line->remise=$remise;
Loading history...
605
606
			if (is_array($array_options) && count($array_options)>0) {
607
				$this->line->array_options=$array_options;
608
			}
609
610
			$result=$this->line->insert();
611
			if ($result > 0)
612
			{
613
				// Reorder if child line
614
				if (! empty($fk_parent_line)) $this->line_order(true, 'DESC');
615
616
                // Mise a jour informations denormalisees au niveau de la propale meme
617
                $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.
618
                if ($result > 0)
619
                {
620
                    $this->db->commit();
621
                    return $this->line->rowid;
622
                }
623
                else
624
                {
625
                    $this->error=$this->db->error();
626
                    $this->db->rollback();
627
                    return -1;
628
                }
629
            }
630
            else
631
            {
632
                $this->error=$this->line->error;
633
                $this->db->rollback();
634
                return -2;
635
            }
636
        }
637
		else
638
		{
639
			dol_syslog(get_class($this)."::addline status of proposal must be Draft to allow use of ->addline()", LOG_ERR);
640
			return -3;
641
		}
642
    }
643
644
645
	/**
646
	 *  Update a proposal line
647
	 *
648
	 *  @param      int			$rowid           	Id de la ligne
649
	 *  @param      float		$pu		     	  	Prix unitaire (HT ou TTC selon price_base_type)
650
	 *  @param      float		$qty            	Quantity
651
	 *  @param      float		$remise_percent  	Remise effectuee sur le produit
652
	 *  @param      float		$txtva	          	Taux de TVA
653
	 * 	@param	  	float		$txlocaltax1		Local tax 1 rate
654
	 *  @param	  	float		$txlocaltax2		Local tax 2 rate
655
	 *  @param      string		$desc            	Description
656
	 *	@param	  	string		$price_base_type	HT ou TTC
657
	 *	@param      int			$info_bits        	Miscellaneous informations
658
	 *	@param		int			$special_code		Special code (also used by externals modules!)
659
	 * 	@param		int			$fk_parent_line		Id of parent line (0 in most cases, used by modules adding sublevels into lines).
660
	 * 	@param		int			$skip_update_total	Keep fields total_xxx to 0 (used for special lines by some modules)
661
	 *  @param		int			$fk_fournprice		Id of origin supplier price
662
	 *  @param		int			$pa_ht				Price (without tax) of product when it was bought
663
	 *  @param		string		$label				???
664
	 *  @param		int			$type				0/1=Product/service
665
	 *	@param      int			$date_start       	Start date of the line
666
	 *	@param      int			$date_end         	End date of the line
667
	 *  @param		array		$array_options		extrafields array
668
	 * 	@param 		string		$fk_unit 			Code of the unit to use. Null to use the default one
669
	 * 	@param		double		$pu_ht_devise		Unit price in currency
670
	 * 	@param		int			$notrigger			disable line update trigger
671
	 *  @return     int     		        		0 if OK, <0 if KO
672
	 */
673
    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 = 0, $fk_unit = null, $pu_ht_devise = 0, $notrigger = 0)
674
	{
675
		global $mysoc, $langs;
676
677
        dol_syslog(get_class($this)."::updateLine rowid=$rowid, pu=$pu, qty=$qty, remise_percent=$remise_percent,
678
        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");
679
		include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
680
681
		// Clean parameters
682
		$remise_percent=price2num($remise_percent);
683
		$qty=price2num($qty);
684
		$pu = price2num($pu);
685
		$pu_ht_devise=price2num($pu_ht_devise);
686
		$txtva = price2num($txtva);
687
		$txlocaltax1=price2num($txlocaltax1);
688
		$txlocaltax2=price2num($txlocaltax2);
689
		$pa_ht=price2num($pa_ht);
690
		if (empty($qty) && empty($special_code)) $special_code=3;    // Set option tag
691
		if (! empty($qty) && $special_code == 3) $special_code=0;    // Remove option tag
692
		if (empty($type)) $type=0;
693
694
        if ($date_start && $date_end && $date_start > $date_end) {
695
            $langs->load("errors");
696
            $this->error=$langs->trans('ErrorStartDateGreaterEnd');
697
            return -1;
698
        }
699
700
		if ($this->statut == self::STATUS_DRAFT)
701
		{
702
			$this->db->begin();
703
704
			// Calcul du total TTC et de la TVA pour la ligne a partir de
705
			// qty, pu, remise_percent et txtva
706
			// TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
707
			// la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
708
709
			$localtaxes_type=getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc);
710
711
			// Clean vat code
712
			$vat_src_code='';
713
			if (preg_match('/\((.*)\)/', $txtva, $reg))
714
			{
715
				$vat_src_code = $reg[1];
716
				$txtva = preg_replace('/\s*\(.*\)/', '', $txtva);    // Remove code into vatrate.
717
			}
718
719
			$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);
720
			$total_ht  = $tabprice[0];
721
			$total_tva = $tabprice[1];
722
			$total_ttc = $tabprice[2];
723
			$total_localtax1 = $tabprice[9];
724
			$total_localtax2 = $tabprice[10];
725
			$pu_ht  = $tabprice[3];
726
			$pu_tva = $tabprice[4];
727
			$pu_ttc = $tabprice[5];
728
729
			// MultiCurrency
730
			$multicurrency_total_ht  = $tabprice[16];
731
			$multicurrency_total_tva = $tabprice[17];
732
			$multicurrency_total_ttc = $tabprice[18];
733
			$pu_ht_devise = $tabprice[19];
734
735
			// Anciens indicateurs: $price, $remise (a ne plus utiliser)
736
			$price = $pu;
737
			$remise = 0;
738
			if ($remise_percent > 0)
739
			{
740
				$remise = round(($pu * $remise_percent / 100), 2);
741
				$price = $pu - $remise;
742
			}
743
744
			//Fetch current line from the database and then clone the object and set it in $oldline property
745
			$line = new PropaleLigne($this->db);
746
			$line->fetch($rowid);
747
			$line->fetch_optionals(); // Fetch extrafields for oldcopy
748
749
			$staticline = clone $line;
750
751
			$line->oldline = $staticline;
752
			$this->line = $line;
753
			$this->line->context = $this->context;
754
755
			// Reorder if fk_parent_line change
756
			if (! empty($fk_parent_line) && ! empty($staticline->fk_parent_line) && $fk_parent_line != $staticline->fk_parent_line)
757
			{
758
				$rangmax = $this->line_max($fk_parent_line);
759
				$this->line->rang = $rangmax + 1;
760
			}
761
762
			$this->line->rowid				= $rowid;
1 ignored issue
show
Deprecated Code introduced by
The property CommonObjectLine::$rowid has been deprecated: Try to use id property as possible (even if field into database is still rowid) ( Ignorable by Annotation )

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

762
			/** @scrutinizer ignore-deprecated */ $this->line->rowid				= $rowid;

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

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

Loading history...
763
			$this->line->label				= $label;
764
			$this->line->desc				= $desc;
765
			$this->line->qty				= $qty;
766
			$this->line->product_type		= $type;
767
			$this->line->vat_src_code		= $vat_src_code;
768
			$this->line->tva_tx				= $txtva;
769
			$this->line->localtax1_tx		= $txlocaltax1;
770
			$this->line->localtax2_tx		= $txlocaltax2;
771
			$this->line->localtax1_type		= $localtaxes_type[0];
772
			$this->line->localtax2_type		= $localtaxes_type[2];
773
			$this->line->remise_percent		= $remise_percent;
774
			$this->line->subprice			= $pu_ht;
775
			$this->line->info_bits			= $info_bits;
776
777
			$this->line->total_ht			= $total_ht;
778
			$this->line->total_tva			= $total_tva;
779
			$this->line->total_localtax1	= $total_localtax1;
780
			$this->line->total_localtax2	= $total_localtax2;
781
			$this->line->total_ttc			= $total_ttc;
782
			$this->line->special_code		= $special_code;
783
			$this->line->fk_parent_line		= $fk_parent_line;
784
			$this->line->skip_update_total	= $skip_update_total;
785
			$this->line->fk_unit	= $fk_unit;
786
787
			$this->line->fk_fournprice = $fk_fournprice;
788
			$this->line->pa_ht = $pa_ht;
789
790
			$this->line->date_start=$date_start;
791
			$this->line->date_end=$date_end;
792
793
			// TODO deprecated
794
			$this->line->price=$price;
1 ignored issue
show
Deprecated Code introduced by
The property PropaleLigne::$price has been deprecated. ( Ignorable by Annotation )

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

794
			/** @scrutinizer ignore-deprecated */ $this->line->price=$price;
Loading history...
795
			$this->line->remise=$remise;
1 ignored issue
show
Deprecated Code introduced by
The property PropaleLigne::$remise has been deprecated. ( Ignorable by Annotation )

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

795
			/** @scrutinizer ignore-deprecated */ $this->line->remise=$remise;
Loading history...
796
797
			if (is_array($array_options) && count($array_options)>0) {
798
				$this->line->array_options=$array_options;
799
			}
800
801
			// Multicurrency
802
			$this->line->multicurrency_subprice		= $pu_ht_devise;
803
			$this->line->multicurrency_total_ht 	= $multicurrency_total_ht;
804
			$this->line->multicurrency_total_tva 	= $multicurrency_total_tva;
805
			$this->line->multicurrency_total_ttc 	= $multicurrency_total_ttc;
806
807
			$result=$this->line->update($notrigger);
808
			if ($result > 0)
809
			{
810
				// Reorder if child line
811
				if (! empty($fk_parent_line)) $this->line_order(true, 'DESC');
812
813
				$this->update_price(1);
814
815
				$this->fk_propal = $this->id;
816
				$this->rowid = $rowid;
817
818
				$this->db->commit();
819
				return $result;
820
			}
821
			else
822
			{
823
				$this->error=$this->line->error;
824
825
				$this->db->rollback();
826
				return -1;
827
			}
828
		}
829
		else
830
		{
831
			dol_syslog(get_class($this)."::updateline Erreur -2 Propal en mode incompatible pour cette action");
832
			return -2;
833
		}
834
	}
835
836
837
	/**
838
	 *  Delete detail line
839
	 *
840
	 *  @param		int		$lineid			Id of line to delete
841
	 *  @return     int         			>0 if OK, <0 if KO
842
	 */
843
    public function deleteline($lineid)
844
	{
845
		global $user;
846
847
		if ($this->statut == self::STATUS_DRAFT)
848
		{
849
			$this->db->begin();
850
851
			$line=new PropaleLigne($this->db);
852
853
			// For triggers
854
			$line->fetch($lineid);
855
856
			if ($line->delete($user) > 0)
857
			{
858
				$this->update_price(1);
859
860
				$this->db->commit();
861
				return 1;
862
			}
863
			else
864
			{
865
				$this->db->rollback();
866
				return -1;
867
			}
868
		}
869
		else
870
		{
871
			$this->error='ErrorDeleteLineNotAllowedByObjectStatus';
872
			return -2;
873
		}
874
	}
875
876
877
	/**
878
	 *  Create commercial proposal into database
879
	 * 	this->ref can be set or empty. If empty, we will use "(PROVid)"
880
	 *
881
	 * 	@param		User	$user		User that create
882
	 * 	@param		int		$notrigger	1=Does not execute triggers, 0= execute triggers
883
	 *  @return     int     			<0 if KO, >=0 if OK
884
	 */
885
    public function create($user, $notrigger = 0)
886
	{
887
		global $conf,$hookmanager;
888
		$error=0;
889
890
		$now=dol_now();
891
892
		// Clean parameters
893
		if (empty($this->entity)) $this->entity = $conf->entity;
894
		if (empty($this->date)) $this->date=$this->datep;
1 ignored issue
show
Deprecated Code introduced by
The property Propal::$datep has been deprecated. ( Ignorable by Annotation )

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

894
		if (empty($this->date)) $this->date=/** @scrutinizer ignore-deprecated */ $this->datep;
Loading history...
895
		$this->fin_validite = $this->date + ($this->duree_validite * 24 * 3600);
896
		if (empty($this->availability_id)) $this->availability_id=0;
897
		if (empty($this->demand_reason_id)) $this->demand_reason_id=0;
898
899
		// Multicurrency (test on $this->multicurrency_tx because we should take the default rate only if not using origin rate)
900
		if (!empty($this->multicurrency_code) && empty($this->multicurrency_tx)) list($this->fk_multicurrency,$this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $this->date);
901
		else $this->fk_multicurrency = MultiCurrency::getIdFromCode($this->db, $this->multicurrency_code);
902
		if (empty($this->fk_multicurrency))
903
		{
904
			$this->multicurrency_code = $conf->currency;
905
			$this->fk_multicurrency = 0;
906
			$this->multicurrency_tx = 1;
907
		}
908
909
		dol_syslog(get_class($this)."::create");
910
911
		// Check parameters
912
		$result=$this->fetch_thirdparty();
913
		if ($result < 0)
914
		{
915
			$this->error="Failed to fetch company";
916
			dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR);
917
			return -3;
918
		}
919
920
		// Check parameters
921
		if (! empty($this->ref))	// We check that ref is not already used
922
		{
923
			$result=self::isExistingObject($this->element, 0, $this->ref);	// Check ref is not yet used
924
			if ($result > 0)
925
			{
926
				$this->error='ErrorRefAlreadyExists';
927
				dol_syslog(get_class($this)."::create ".$this->error, LOG_WARNING);
928
				$this->db->rollback();
929
				return -1;
930
			}
931
		}
932
933
		if (empty($this->date))
934
		{
935
			$this->error="Date of proposal is required";
936
			dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR);
937
			return -4;
938
		}
939
940
941
		$this->db->begin();
942
943
		// Insert into database
944
		$sql = "INSERT INTO ".MAIN_DB_PREFIX."propal (";
945
		$sql.= "fk_soc";
946
		$sql.= ", price";
947
		$sql.= ", remise";
948
		$sql.= ", remise_percent";
949
		$sql.= ", remise_absolue";
950
		$sql.= ", tva";
951
		$sql.= ", total";
952
		$sql.= ", datep";
953
		$sql.= ", datec";
954
		$sql.= ", ref";
955
		$sql.= ", fk_user_author";
956
		$sql.= ", note_private";
957
		$sql.= ", note_public";
958
		$sql.= ", model_pdf";
959
		$sql.= ", fin_validite";
960
		$sql.= ", fk_cond_reglement";
961
		$sql.= ", fk_mode_reglement";
962
		$sql.= ", fk_account";
963
		$sql.= ", ref_client";
964
		$sql.= ", date_livraison";
965
		$sql.= ", fk_shipping_method";
966
		$sql.= ", fk_availability";
967
		$sql.= ", fk_input_reason";
968
		$sql.= ", fk_projet";
969
		$sql.= ", fk_incoterms";
970
		$sql.= ", location_incoterms";
971
		$sql.= ", entity";
972
		$sql.= ", fk_multicurrency";
973
		$sql.= ", multicurrency_code";
974
		$sql.= ", multicurrency_tx";
975
		$sql.= ") ";
976
		$sql.= " VALUES (";
977
		$sql.= $this->socid;
978
		$sql.= ", 0";
979
		$sql.= ", ".$this->remise;
980
		$sql.= ", ".($this->remise_percent?$this->db->escape($this->remise_percent):'NULL');
981
		$sql.= ", ".($this->remise_absolue?$this->db->escape($this->remise_absolue):'NULL');
982
		$sql.= ", 0";
983
		$sql.= ", 0";
984
		$sql.= ", '".$this->db->idate($this->date)."'";
985
		$sql.= ", '".$this->db->idate($now)."'";
986
		$sql.= ", '(PROV)'";
987
		$sql.= ", ".($user->id > 0 ? "'".$user->id."'":"NULL");
988
		$sql.= ", '".$this->db->escape($this->note_private)."'";
989
		$sql.= ", '".$this->db->escape($this->note_public)."'";
990
		$sql.= ", '".$this->db->escape($this->modelpdf)."'";
991
		$sql.= ", ".($this->fin_validite!=''?"'".$this->db->idate($this->fin_validite)."'":"NULL");
992
		$sql.= ", ".($this->cond_reglement_id > 0 ? $this->cond_reglement_id : 'NULL');
993
		$sql.= ", ".($this->mode_reglement_id > 0 ? $this->mode_reglement_id : 'NULL');
994
		$sql.= ", ".($this->fk_account>0?$this->fk_account:'NULL');
995
		$sql.= ", '".$this->db->escape($this->ref_client)."'";
996
		$sql.= ", ".($this->date_livraison!=''?"'".$this->db->idate($this->date_livraison)."'":"NULL");
997
		$sql.= ", ".($this->shipping_method_id>0?$this->shipping_method_id:'NULL');
998
		$sql.= ", ".$this->availability_id;
999
		$sql.= ", ".$this->demand_reason_id;
1000
		$sql.= ", ".($this->fk_project?$this->fk_project:"null");
1001
		$sql.= ", ".(int) $this->fk_incoterms;
1002
		$sql.= ", '".$this->db->escape($this->location_incoterms)."'";
1003
		$sql.= ", ".$this->entity;
1004
		$sql.= ", ".(int) $this->fk_multicurrency;
1005
		$sql.= ", '".$this->db->escape($this->multicurrency_code)."'";
1006
		$sql.= ", ".(double) $this->multicurrency_tx;
1007
		$sql.= ")";
1008
1009
		dol_syslog(get_class($this)."::create", LOG_DEBUG);
1010
		$resql=$this->db->query($sql);
1011
		if ($resql)
1012
		{
1013
			$this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."propal");
1014
1015
			if ($this->id)
1016
			{
1017
				$this->ref='(PROV'.$this->id.')';
1018
				$sql = 'UPDATE '.MAIN_DB_PREFIX."propal SET ref='".$this->db->escape($this->ref)."' WHERE rowid=".$this->id;
1019
1020
				dol_syslog(get_class($this)."::create", LOG_DEBUG);
1021
				$resql=$this->db->query($sql);
1022
				if (! $resql) $error++;
1023
1024
                if (! empty($this->linkedObjectsIds) && empty($this->linked_objects))	// To use new linkedObjectsIds instead of old linked_objects
1025
                {
1026
                	$this->linked_objects = $this->linkedObjectsIds;	// TODO Replace linked_objects with linkedObjectsIds
1027
                }
1028
1029
                // Add object linked
1030
                if (! $error && $this->id && is_array($this->linked_objects) && ! empty($this->linked_objects))
1031
                {
1032
                	foreach($this->linked_objects as $origin => $tmp_origin_id)
1033
                	{
1034
                		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, ...))
1035
                		{
1036
                			foreach($tmp_origin_id as $origin_id)
1037
                			{
1038
                				$ret = $this->add_object_linked($origin, $origin_id);
1039
                				if (! $ret)
1040
                				{
1041
                					$this->error=$this->db->lasterror();
1042
                					$error++;
1043
                				}
1044
                			}
1045
                		}
1046
                		else                                // Old behaviour, if linked_object has only one link per type, so is something like array('contract'=>id1))
1047
                		{
1048
                			$origin_id = $tmp_origin_id;
1049
                			$ret = $this->add_object_linked($origin, $origin_id);
1050
                			if (! $ret)
1051
                			{
1052
                				$this->error=$this->db->lasterror();
1053
                				$error++;
1054
                			}
1055
                		}
1056
                	}
1057
                }
1058
1059
                // Add linked object (deprecated, use ->linkedObjectsIds instead)
1060
                if (! $error && $this->origin && $this->origin_id)
1061
                {
1062
                    dol_syslog('Deprecated use of linked object, use ->linkedObjectsIds instead', LOG_WARNING);
1063
                	$ret = $this->add_object_linked();
1064
                	if (! $ret)	dol_print_error($this->db);
1065
                }
1066
1067
                /*
1068
                 *  Insertion du detail des produits dans la base
1069
                 *  Insert products detail in database
1070
                 */
1071
                if (! $error)
1072
                {
1073
                    $fk_parent_line=0;
1074
                    $num=count($this->lines);
1075
1076
					for ($i=0;$i<$num;$i++)
1077
					{
1078
                        if (! is_object($this->lines[$i]))	// If this->lines is not array of objects, coming from REST API
1079
                        {   // Convert into object this->lines[$i].
1080
                            $line = (object) $this->lines[$i];
1081
                        }
1082
                        else
1083
                        {
1084
                            $line = $this->lines[$i];
1085
                        }
1086
						// Reset fk_parent_line for line that are not child lines or special product
1087
						if (($line->product_type != 9 && empty($line->fk_parent_line)) || $line->product_type == 9) {
1088
							$fk_parent_line = 0;
1089
						}
1090
                        // Complete vat rate with code
1091
						$vatrate = $line->tva_tx;
1092
						if ($line->vat_src_code && ! preg_match('/\(.*\)/', $vatrate)) $vatrate.=' ('.$line->vat_src_code.')';
1093
1094
    $result = $this->addline(
1095
							$line->desc,
1096
							$line->subprice,
1097
							$line->qty,
1098
							$vatrate,
1099
							$line->localtax1_tx,
1100
							$line->localtax2_tx,
1101
							$line->fk_product,
1102
							$line->remise_percent,
1103
							'HT',
1104
							0,
1105
							$line->info_bits,
1106
							$line->product_type,
1107
							$line->rang,
1108
							$line->special_code,
1109
							$fk_parent_line,
1110
							$line->fk_fournprice,
1111
							$line->pa_ht,
1112
							$line->label,
1113
							$line->date_start,
1114
							$line->date_end,
1115
							$line->array_options,
1116
							$line->fk_unit,
1117
							$this->element,
1118
							$line->id
1119
						);
1120
1121
						if ($result < 0)
1122
						{
1123
							$error++;
1124
							$this->error=$this->db->error;
1125
							dol_print_error($this->db);
1126
							break;
1127
						}
1128
						// Defined the new fk_parent_line
1129
						if ($result > 0 && $line->product_type == 9) {
1130
							$fk_parent_line = $result;
1131
						}
1132
					}
1133
				}
1134
1135
				// Set delivery address
1136
				if (! $error && $this->fk_delivery_address)
1 ignored issue
show
Deprecated Code introduced by
The property CommonObject::$fk_delivery_address has been deprecated. ( Ignorable by Annotation )

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

1136
				if (! $error && /** @scrutinizer ignore-deprecated */ $this->fk_delivery_address)
Loading history...
1137
				{
1138
					$sql = "UPDATE ".MAIN_DB_PREFIX."propal";
1139
					$sql.= " SET fk_delivery_address = ".$this->fk_delivery_address;
1 ignored issue
show
Deprecated Code introduced by
The property CommonObject::$fk_delivery_address has been deprecated. ( Ignorable by Annotation )

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

1139
					$sql.= " SET fk_delivery_address = "./** @scrutinizer ignore-deprecated */ $this->fk_delivery_address;
Loading history...
1140
					$sql.= " WHERE ref = '".$this->db->escape($this->ref)."'";
1141
					$sql.= " AND entity = ".$conf->entity;
1142
1143
					$result=$this->db->query($sql);
1144
				}
1145
1146
				if (! $error)
1147
				{
1148
					// Mise a jour infos denormalisees
1149
					$resql=$this->update_price(1);
1150
					if ($resql)
1151
					{
1152
						$action='update';
1153
1154
						// Actions on extra fields
1155
						if (! $error)
1156
						{
1157
							if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) // For avoid conflicts if trigger used
1158
							{
1159
								$result=$this->insertExtraFields();
1160
								if ($result < 0)
1161
								{
1162
									$error++;
1163
								}
1164
							}
1165
						}
1166
1167
						if (! $error && ! $notrigger)
1168
						{
1169
							// Call trigger
1170
							$result=$this->call_trigger('PROPAL_CREATE', $user);
1171
							if ($result < 0) { $error++; }
1172
							// End call triggers
1173
						}
1174
					}
1175
					else
1176
					{
1177
						$this->error=$this->db->lasterror();
1178
						$error++;
1179
					}
1180
				}
1181
			}
1182
			else
1183
			{
1184
				$this->error=$this->db->lasterror();
1185
				$error++;
1186
			}
1187
1188
			if (! $error)
1189
			{
1190
				$this->db->commit();
1191
				dol_syslog(get_class($this)."::create done id=".$this->id);
1192
				return $this->id;
1193
			}
1194
			else
1195
			{
1196
				$this->db->rollback();
1197
				return -2;
1198
			}
1199
		}
1200
		else
1201
		{
1202
			$this->error=$this->db->lasterror();
1203
			$this->db->rollback();
1204
			return -1;
1205
		}
1206
	}
1207
1208
1209
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1210
	/**
1211
	 *	Insert into DB a proposal object completely defined by its data members (ex, results from copy).
1212
	 *
1213
	 *	@param 		User	$user	User that create
1214
	 *	@return    	int				Id of the new object if ok, <0 if ko
1215
	 *	@see       	create()
1216
	 */
1217
    public function create_from($user)
1218
	{
1219
        // phpcs:enable
1220
		// i love this function because $this->products is not used in create function...
1221
		$this->products=$this->lines;
1222
1223
		return $this->create($user);
1224
	}
1225
1226
	/**
1227
	 *		Load an object from its id and create a new one in database
1228
	 *
1229
	 *      @param	    User	$user		    User making the clone
1230
	 *		@param		int		$socid			Id of thirdparty
1231
	 * 	 	@return		int						New id of clone
1232
	 */
1233
    public function createFromClone(User $user, $socid = 0)
1234
	{
1235
		global $conf,$hookmanager;
1236
1237
		dol_include_once('/projet/class/project.class.php');
1238
1239
		$error=0;
1240
		$now=dol_now();
1241
1242
		dol_syslog(__METHOD__, LOG_DEBUG);
1243
1244
		$object = new self($this->db);
1245
1246
		$this->db->begin();
1247
1248
		// Load source object
1249
		$object->fetch($this->id);
1250
		$object->fetch_lines();
1251
1252
		$objsoc=new Societe($this->db);
1253
1254
		// Change socid if needed
1255
		if (! empty($socid) && $socid != $object->socid)
1256
		{
1257
			if ($objsoc->fetch($socid) > 0)
1258
			{
1259
			    $object->socid 				= $objsoc->id;
1260
			    $object->cond_reglement_id	= (! empty($objsoc->cond_reglement_id) ? $objsoc->cond_reglement_id : 0);
1261
			    $object->mode_reglement_id	= (! empty($objsoc->mode_reglement_id) ? $objsoc->mode_reglement_id : 0);
1262
			    $object->fk_delivery_address	= '';
2 ignored issues
show
Deprecated Code introduced by
The property CommonObject::$fk_delivery_address has been deprecated. ( Ignorable by Annotation )

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

1262
			    /** @scrutinizer ignore-deprecated */ $object->fk_delivery_address	= '';
Loading history...
Documentation Bug introduced by
The property $fk_delivery_address was declared of type integer, but '' is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
1263
1264
				/*if (!empty($conf->projet->enabled))
1265
                {
1266
                    $project = new Project($db);
1267
    				if ($this->fk_project > 0 && $project->fetch($this->fk_project)) {
1268
    					if ($project->socid <= 0) $clonedObj->fk_project = $this->fk_project;
1269
    					else $clonedObj->fk_project = '';
1270
    				} else {
1271
    					$clonedObj->fk_project = '';
1272
    				}
1273
                }*/
1274
			    $object->fk_project = '';    // A cloned proposal is set by default to no project.
1 ignored issue
show
Documentation Bug introduced by
The property $fk_project was declared of type integer, but '' is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
1275
			}
1276
1277
			// reset ref_client
1278
			$object->ref_client  = '';
1279
1280
			// TODO Change product price if multi-prices
1281
		}
1282
		else
1283
		{
1284
		    $objsoc->fetch($object->socid);
1285
		}
1286
1287
		$object->id=0;
1288
		$object->ref='';
1289
		$object->statut=self::STATUS_DRAFT;
1290
1291
		// Clear fields
1292
		$object->user_author	= $user->id;
1293
		$object->user_valid	= '';
1294
		$object->date		= $now;
1295
		$object->datep		= $now;    // deprecated
1 ignored issue
show
Deprecated Code introduced by
The property Propal::$datep has been deprecated. ( Ignorable by Annotation )

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

1295
		/** @scrutinizer ignore-deprecated */ $object->datep		= $now;    // deprecated
Loading history...
1296
		$object->fin_validite	= $object->date + ($object->duree_validite * 24 * 3600);
1297
		if (empty($conf->global->MAIN_KEEP_REF_CUSTOMER_ON_CLONING)) $object->ref_client	= '';
1298
1299
		// Create clone
1300
		$object->context['createfromclone']='createfromclone';
1301
		$result=$object->create($user);
1302
		if ($result < 0)
1303
		{
1304
		    $this->error = $object->error;
1305
		    $this->errors = array_merge($this->errors, $object->errors);
1306
		    $error++;
1307
		}
1308
1309
		if (! $error)
1310
		{
1311
			// copy internal contacts
1312
		    if ($object->copy_linked_contact($this, 'internal') < 0)
1313
		    {
1314
				$error++;
1315
		    }
1316
		}
1317
1318
		if (! $error)
1319
		{
1320
			// copy external contacts if same company
1321
			if ($this->socid == $object->socid)
1322
			{
1323
			    if ($object->copy_linked_contact($this, 'external') < 0)
1324
					$error++;
1325
			}
1326
		}
1327
1328
		if (! $error)
1329
		{
1330
			// Hook of thirdparty module
1331
			if (is_object($hookmanager))
1332
			{
1333
				$parameters=array('objFrom'=>$this,'clonedObj'=>$object);
1334
				$action='';
1335
				$reshook=$hookmanager->executeHooks('createFrom', $parameters, $object, $action);    // Note that $action and $object may have been modified by some hooks
1336
				if ($reshook < 0) $error++;
1337
			}
1338
		}
1339
1340
		unset($object->context['createfromclone']);
1341
1342
		// End
1343
		if (! $error)
1344
		{
1345
			$this->db->commit();
1346
			return $object->id;
1347
		}
1348
		else
1349
		{
1350
			$this->db->rollback();
1351
			return -1;
1352
		}
1353
	}
1354
1355
	/**
1356
	 *	Load a proposal from database and its ligne array
1357
	 *
1358
	 *	@param      int			$rowid		id of object to load
1359
	 *	@param		string		$ref		Ref of proposal
1360
	 *	@return     int         			>0 if OK, <0 if KO
1361
	 */
1362
    public function fetch($rowid, $ref = '')
1363
	{
1364
1365
		$sql = "SELECT p.rowid, p.ref, p.entity, p.remise, p.remise_percent, p.remise_absolue, p.fk_soc";
1366
		$sql.= ", p.total, p.tva, p.localtax1, p.localtax2, p.total_ht";
1367
		$sql.= ", p.datec";
1368
		$sql.= ", p.date_valid as datev";
1369
		$sql.= ", p.datep as dp";
1370
		$sql.= ", p.fin_validite as dfv";
1371
		$sql.= ", p.date_livraison as date_livraison";
1372
		$sql.= ", p.model_pdf, p.last_main_doc, p.ref_client, p.extraparams";
1373
		$sql.= ", p.note_private, p.note_public";
1374
		$sql.= ", p.fk_projet as fk_project, p.fk_statut";
1375
		$sql.= ", p.fk_user_author, p.fk_user_valid, p.fk_user_cloture";
1376
		$sql.= ", p.fk_delivery_address";
1377
		$sql.= ", p.fk_availability";
1378
		$sql.= ", p.fk_input_reason";
1379
		$sql.= ", p.fk_cond_reglement";
1380
		$sql.= ", p.fk_mode_reglement";
1381
		$sql.= ', p.fk_account';
1382
		$sql.= ", p.fk_shipping_method";
1383
		$sql.= ", p.fk_incoterms, p.location_incoterms";
1384
		$sql.= ", p.fk_multicurrency, p.multicurrency_code, p.multicurrency_tx, p.multicurrency_total_ht, p.multicurrency_total_tva, p.multicurrency_total_ttc";
1385
		$sql.= ", p.tms as date_modification";
1386
		$sql.= ", i.libelle as label_incoterms";
1387
		$sql.= ", c.label as statut_label";
1388
		$sql.= ", ca.code as availability_code, ca.label as availability";
1389
		$sql.= ", dr.code as demand_reason_code, dr.label as demand_reason";
1390
		$sql.= ", cr.code as cond_reglement_code, cr.libelle as cond_reglement, cr.libelle_facture as cond_reglement_libelle_doc";
1391
		$sql.= ", cp.code as mode_reglement_code, cp.libelle as mode_reglement";
1392
		$sql.= " FROM ".MAIN_DB_PREFIX."propal as p";
1393
		$sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_propalst as c ON p.fk_statut = c.id';
1394
		$sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as cp ON p.fk_mode_reglement = cp.id AND cp.entity IN ('.getEntity('c_paiement').')';
1395
		$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').')';
1396
		$sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_availability as ca ON p.fk_availability = ca.rowid';
1397
		$sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_input_reason as dr ON p.fk_input_reason = dr.rowid';
1398
		$sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_incoterms as i ON p.fk_incoterms = i.rowid';
1399
1400
		if ($ref) {
1401
			$sql.= " WHERE p.entity IN (".getEntity('propal').")"; // Dont't use entity if you use rowid
1402
			$sql.= " AND p.ref='".$this->db->escape($ref)."'";
1403
		}
1404
		else $sql.= " WHERE p.rowid=".$rowid;
1405
1406
		dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
1407
		$resql=$this->db->query($sql);
1408
		if ($resql)
1409
		{
1410
			if ($this->db->num_rows($resql))
1411
			{
1412
				$obj = $this->db->fetch_object($resql);
1413
1414
				$this->id                   = $obj->rowid;
1415
				$this->entity               = $obj->entity;
1416
1417
				$this->ref                  = $obj->ref;
1418
				$this->ref_client           = $obj->ref_client;
1419
				$this->remise               = $obj->remise;
1420
				$this->remise_percent       = $obj->remise_percent;
1421
				$this->remise_absolue       = $obj->remise_absolue;
1422
				$this->total                = $obj->total; // TODO deprecated
1423
				$this->total_ht             = $obj->total_ht;
1424
				$this->total_tva            = $obj->tva;
1425
				$this->total_localtax1		= $obj->localtax1;
1426
				$this->total_localtax2		= $obj->localtax2;
1427
				$this->total_ttc            = $obj->total;
1428
				$this->socid                = $obj->fk_soc;
1429
				$this->fk_project           = $obj->fk_project;
1430
				$this->modelpdf             = $obj->model_pdf;
1431
				$this->last_main_doc		= $obj->last_main_doc;
1432
				$this->note                 = $obj->note_private; // TODO deprecated
1433
				$this->note_private         = $obj->note_private;
1434
				$this->note_public          = $obj->note_public;
1435
				$this->statut               = (int) $obj->fk_statut;
1436
				$this->statut_libelle       = $obj->statut_label;
1437
1438
				$this->datec                = $this->db->jdate($obj->datec); // TODO deprecated
1439
				$this->datev                = $this->db->jdate($obj->datev); // TODO deprecated
1440
				$this->date_creation		= $this->db->jdate($obj->datec); //Creation date
1441
				$this->date_validation		= $this->db->jdate($obj->datev); //Validation date
1442
				$this->date_modification	= $this->db->jdate($obj->date_modification); // tms
1443
				$this->date                 = $this->db->jdate($obj->dp);	// Proposal date
1444
				$this->datep                = $this->db->jdate($obj->dp);    // deprecated
1445
				$this->fin_validite         = $this->db->jdate($obj->dfv);
1446
				$this->date_livraison       = $this->db->jdate($obj->date_livraison);
1447
				$this->shipping_method_id   = ($obj->fk_shipping_method>0)?$obj->fk_shipping_method:null;
1448
				$this->availability_id      = $obj->fk_availability;
1449
				$this->availability_code    = $obj->availability_code;
1450
				$this->availability         = $obj->availability;
1451
				$this->demand_reason_id     = $obj->fk_input_reason;
1452
				$this->demand_reason_code   = $obj->demand_reason_code;
1453
				$this->demand_reason        = $obj->demand_reason;
1454
				$this->fk_address  			= $obj->fk_delivery_address;
1455
1456
				$this->mode_reglement_id    = $obj->fk_mode_reglement;
1457
				$this->mode_reglement_code  = $obj->mode_reglement_code;
1458
				$this->mode_reglement       = $obj->mode_reglement;
1459
				$this->fk_account           = ($obj->fk_account>0)?$obj->fk_account:null;
1460
				$this->cond_reglement_id    = $obj->fk_cond_reglement;
1461
				$this->cond_reglement_code  = $obj->cond_reglement_code;
1462
				$this->cond_reglement       = $obj->cond_reglement;
1463
				$this->cond_reglement_doc   = $obj->cond_reglement_libelle_doc;
1464
1465
				$this->extraparams			= (array) json_decode($obj->extraparams, true);
1466
1467
				$this->user_author_id = $obj->fk_user_author;
1468
				$this->user_valid_id  = $obj->fk_user_valid;
1469
				$this->user_close_id  = $obj->fk_user_cloture;
1470
1471
				//Incoterms
1472
				$this->fk_incoterms = $obj->fk_incoterms;
1473
				$this->location_incoterms = $obj->location_incoterms;
1474
				$this->label_incoterms = $obj->label_incoterms;
1475
1476
				// Multicurrency
1477
				$this->fk_multicurrency 		= $obj->fk_multicurrency;
1478
				$this->multicurrency_code 		= $obj->multicurrency_code;
1479
				$this->multicurrency_tx 		= $obj->multicurrency_tx;
1480
				$this->multicurrency_total_ht 	= $obj->multicurrency_total_ht;
1481
				$this->multicurrency_total_tva 	= $obj->multicurrency_total_tva;
1482
				$this->multicurrency_total_ttc 	= $obj->multicurrency_total_ttc;
1483
1484
				if ($obj->fk_statut == self::STATUS_DRAFT)
1485
				{
1486
					$this->brouillon = 1;
1487
				}
1488
1489
				// Retreive all extrafield
1490
				// fetch optionals attributes and labels
1491
				$this->fetch_optionals();
1492
1493
				$this->db->free($resql);
1494
1495
				$this->lines = array();
1496
1497
				/*
1498
                 * Lines
1499
                 */
1500
				$result=$this->fetch_lines();
1501
				if ($result < 0)
1502
				{
1503
					return -3;
1504
				}
1505
1506
				return 1;
1507
			}
1508
1509
			$this->error="Record Not Found";
1510
			return 0;
1511
		}
1512
		else
1513
		{
1514
			$this->error=$this->db->lasterror();
1515
			return -1;
1516
		}
1517
	}
1518
1519
	/**
1520
	 *      Update database
1521
	 *
1522
	 *      @param      User	$user        	User that modify
1523
	 *      @param      int		$notrigger	    0=launch triggers after, 1=disable triggers
1524
	 *      @return     int      			   	<0 if KO, >0 if OK
1525
	 */
1526
    public function update(User $user, $notrigger = 0)
1527
	{
1528
		global $conf;
1529
1530
		$error=0;
1531
1532
		// Clean parameters
1533
		if (isset($this->ref)) $this->ref=trim($this->ref);
1534
		if (isset($this->ref_client)) $this->ref_client=trim($this->ref_client);
1535
		if (isset($this->note) || isset($this->note_private)) $this->note_private=(isset($this->note_private) ? trim($this->note_private) : trim($this->note));
1536
		if (isset($this->note_public)) $this->note_public=trim($this->note_public);
1537
		if (isset($this->modelpdf)) $this->modelpdf=trim($this->modelpdf);
1538
		if (isset($this->import_key)) $this->import_key=trim($this->import_key);
1539
1540
		// Check parameters
1541
		// Put here code to add control on parameters values
1542
1543
		// Update request
1544
		$sql = "UPDATE ".MAIN_DB_PREFIX."propal SET";
1545
1546
		$sql.= " ref=".(isset($this->ref)?"'".$this->db->escape($this->ref)."'":"null").",";
1547
		$sql.= " ref_client=".(isset($this->ref_client)?"'".$this->db->escape($this->ref_client)."'":"null").",";
1548
		$sql.= " ref_ext=".(isset($this->ref_ext)?"'".$this->db->escape($this->ref_ext)."'":"null").",";
1549
		$sql.= " fk_soc=".(isset($this->socid)?$this->socid:"null").",";
1550
		$sql.= " datep=".(strval($this->datep)!='' ? "'".$this->db->idate($this->datep)."'" : 'null').",";
1551
		$sql.= " date_valid=".(strval($this->date_validation)!='' ? "'".$this->db->idate($this->date_validation)."'" : 'null').",";
1552
		$sql.= " tva=".(isset($this->total_tva)?$this->total_tva:"null").",";
1553
		$sql.= " localtax1=".(isset($this->total_localtax1)?$this->total_localtax1:"null").",";
1554
		$sql.= " localtax2=".(isset($this->total_localtax2)?$this->total_localtax2:"null").",";
1555
		$sql.= " total_ht=".(isset($this->total_ht)?$this->total_ht:"null").",";
1556
		$sql.= " total=".(isset($this->total_ttc)?$this->total_ttc:"null").",";
1557
		$sql.= " fk_statut=".(isset($this->statut)?$this->statut:"null").",";
1558
		$sql.= " fk_user_author=".(isset($this->user_author_id)?$this->user_author_id:"null").",";
1559
		$sql.= " fk_user_valid=".(isset($this->user_valid)?$this->user_valid:"null").",";
1560
		$sql.= " fk_projet=".(isset($this->fk_project)?$this->fk_project:"null").",";
1561
		$sql.= " fk_cond_reglement=".(isset($this->cond_reglement_id)?$this->cond_reglement_id:"null").",";
1562
		$sql.= " fk_mode_reglement=".(isset($this->mode_reglement_id)?$this->mode_reglement_id:"null").",";
1563
		$sql.= " note_private=".(isset($this->note_private)?"'".$this->db->escape($this->note_private)."'":"null").",";
1564
		$sql.= " note_public=".(isset($this->note_public)?"'".$this->db->escape($this->note_public)."'":"null").",";
1565
		$sql.= " model_pdf=".(isset($this->modelpdf)?"'".$this->db->escape($this->modelpdf)."'":"null").",";
1566
		$sql.= " import_key=".(isset($this->import_key)?"'".$this->db->escape($this->import_key)."'":"null")."";
1567
1568
		$sql.= " WHERE rowid=".$this->id;
1569
1570
		$this->db->begin();
1571
1572
		dol_syslog(get_class($this)."::update", LOG_DEBUG);
1573
		$resql = $this->db->query($sql);
1574
		if (! $resql) {
1575
			$error++; $this->errors[]="Error ".$this->db->lasterror();
1576
		}
1577
1578
		if (! $error && empty($conf->global->MAIN_EXTRAFIELDS_DISABLED) && is_array($this->array_options) && count($this->array_options)>0)
1579
		{
1580
			$result=$this->insertExtraFields();
1581
			if ($result < 0)
1582
			{
1583
				$error++;
1584
			}
1585
		}
1586
1587
		if (! $error && ! $notrigger)
1588
		{
1589
			// Call trigger
1590
			$result=$this->call_trigger('PROPAL_MODIFY', $user);
1591
			if ($result < 0) $error++;
1592
			// End call triggers
1593
		}
1594
1595
		// Commit or rollback
1596
		if ($error)
1597
		{
1598
			foreach($this->errors as $errmsg)
1599
			{
1600
				dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1601
				$this->error.=($this->error?', '.$errmsg:$errmsg);
1602
			}
1603
			$this->db->rollback();
1604
			return -1*$error;
1605
		}
1606
		else
1607
		{
1608
			$this->db->commit();
1609
			return 1;
1610
		}
1611
	}
1612
1613
1614
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1615
	/**
1616
	 * Load array lines
1617
	 *
1618
	 *	@param		int		$only_product	Return only physical products
1619
	 *	@param		int		$loadalsotranslation	Return translation for products
1620
	 *
1621
	 * @return		int						<0 if KO, >0 if OK
1622
	 */
1623
	public function fetch_lines($only_product = 0, $loadalsotranslation = 0)
1624
	{
1625
		global $langs, $conf;
1626
        // phpcs:enable
1627
		$this->lines=array();
1628
1629
		$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,';
1630
		$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,';
1631
		$sql.= ' d.fk_unit,';
1632
		$sql.= ' p.ref as product_ref, p.description as product_desc, p.fk_product_type, p.label as product_label, p.tobatch as product_batch,';
1633
		$sql.= ' p.weight, p.weight_units, p.volume, p.volume_units,';
1634
		$sql.= ' d.date_start, d.date_end,';
1635
		$sql.= ' d.fk_multicurrency, d.multicurrency_code, d.multicurrency_subprice, d.multicurrency_total_ht, d.multicurrency_total_tva, d.multicurrency_total_ttc';
1636
		$sql.= ' FROM '.MAIN_DB_PREFIX.'propaldet as d';
1637
		$sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON (d.fk_product = p.rowid)';
1638
		$sql.= ' WHERE d.fk_propal = '.$this->id;
1639
		if ($only_product) $sql .= ' AND p.fk_product_type = 0';
1640
		$sql.= ' ORDER by d.rang';
1641
1642
		dol_syslog(get_class($this)."::fetch_lines", LOG_DEBUG);
1643
		$result = $this->db->query($sql);
1644
		if ($result)
1645
		{
1646
			require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
1647
1648
			$num = $this->db->num_rows($result);
1649
1650
			$i = 0;
1651
			while ($i < $num)
1652
			{
1653
				$objp                   = $this->db->fetch_object($result);
1654
1655
				$line                   = new PropaleLigne($this->db);
1656
1657
				$line->rowid			= $objp->rowid; //Deprecated
1658
				$line->id				= $objp->rowid;
1659
				$line->fk_propal		= $objp->fk_propal;
1660
				$line->fk_parent_line	= $objp->fk_parent_line;
1661
				$line->product_type     = $objp->product_type;
1662
				$line->label            = $objp->custom_label;
1663
				$line->desc             = $objp->description;  // Description ligne
1664
				$line->description      = $objp->description;  // Description ligne
1665
				$line->qty              = $objp->qty;
1666
				$line->vat_src_code     = $objp->vat_src_code;
1667
				$line->tva_tx           = $objp->tva_tx;
1668
				$line->localtax1_tx		= $objp->localtax1_tx;
1669
				$line->localtax2_tx		= $objp->localtax2_tx;
1670
				$line->localtax1_type	= $objp->localtax1_type;
1671
				$line->localtax2_type	= $objp->localtax2_type;
1672
				$line->subprice         = $objp->subprice;
1673
				$line->fk_remise_except = $objp->fk_remise_except;
1674
				$line->remise_percent   = $objp->remise_percent;
1675
				$line->price            = $objp->price;		// TODO deprecated
1676
1677
				$line->info_bits        = $objp->info_bits;
1678
				$line->total_ht         = $objp->total_ht;
1679
				$line->total_tva        = $objp->total_tva;
1680
				$line->total_localtax1	= $objp->total_localtax1;
1681
				$line->total_localtax2	= $objp->total_localtax2;
1682
				$line->total_ttc        = $objp->total_ttc;
1683
				$line->fk_fournprice 	= $objp->fk_fournprice;
1684
				$marginInfos			= getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $line->fk_fournprice, $objp->pa_ht);
1685
				$line->pa_ht 			= $marginInfos[0];
1686
				$line->marge_tx			= $marginInfos[1];
1687
				$line->marque_tx		= $marginInfos[2];
1688
				$line->special_code     = $objp->special_code;
1689
				$line->rang             = $objp->rang;
1690
1691
				$line->fk_product       = $objp->fk_product;
1692
1693
				$line->ref				= $objp->product_ref;		// TODO deprecated
1694
				$line->product_ref		= $objp->product_ref;
1695
				$line->libelle			= $objp->product_label;		// TODO deprecated
1696
				$line->product_label	= $objp->product_label;
1697
				$line->product_desc     = $objp->product_desc; 		// Description produit
1698
				$line->product_tobatch  = $objp->product_tobatch;
1699
				$line->fk_product_type  = $objp->fk_product_type;	// TODO deprecated
1700
				$line->fk_unit          = $objp->fk_unit;
1701
				$line->weight = $objp->weight;
1702
				$line->weight_units = $objp->weight_units;
1703
				$line->volume = $objp->volume;
1704
				$line->volume_units = $objp->volume_units;
1705
1706
				$line->date_start  		= $this->db->jdate($objp->date_start);
1707
				$line->date_end  		= $this->db->jdate($objp->date_end);
1708
1709
				// Multicurrency
1710
				$line->fk_multicurrency 		= $objp->fk_multicurrency;
1711
				$line->multicurrency_code 		= $objp->multicurrency_code;
1712
				$line->multicurrency_subprice 	= $objp->multicurrency_subprice;
1713
				$line->multicurrency_total_ht 	= $objp->multicurrency_total_ht;
1714
				$line->multicurrency_total_tva 	= $objp->multicurrency_total_tva;
1715
				$line->multicurrency_total_ttc 	= $objp->multicurrency_total_ttc;
1716
1717
				$line->fetch_optionals();
1718
1719
				// multilangs
1720
        		if (! empty($conf->global->MAIN_MULTILANGS) && ! empty($objp->fk_product) && ! empty($loadalsotranslation)) {
1721
        		$line = new Product($this->db);
1722
        		$line->fetch($objp->fk_product);
1723
        		$line->getMultiLangs();
1724
        		}
1725
1726
				$this->lines[$i]        = $line;
1727
				//dol_syslog("1 ".$line->fk_product);
1728
				//print "xx $i ".$this->lines[$i]->fk_product;
1729
				$i++;
1730
			}
1731
1732
			$this->db->free($result);
1733
1734
			return $num;
1735
		}
1736
		else
1737
		{
1738
			$this->error=$this->db->lasterror();
1739
			return -3;
1740
		}
1741
	}
1742
1743
	/**
1744
	 *  Set status to validated
1745
	 *
1746
	 *  @param	User	$user       Object user that validate
1747
	 *  @param	int		$notrigger	1=Does not execute triggers, 0=execute triggers
1748
	 *  @return int         		<0 if KO, 0=Nothing done, >=0 if OK
1749
	 */
1750
    public function valid($user, $notrigger = 0)
1751
	{
1752
		global $conf;
1753
1754
		require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1755
1756
		$error=0;
1757
1758
		// Protection
1759
		if ($this->statut == self::STATUS_VALIDATED)
1760
		{
1761
			dol_syslog(get_class($this)."::valid action abandonned: already validated", LOG_WARNING);
1762
			return 0;
1763
		}
1764
1765
		if (! ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->propal->creer))
1766
	   	|| (! empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->propal->propal_advance->validate))))
1767
		{
1768
			$this->error='ErrorPermissionDenied';
1769
			dol_syslog(get_class($this)."::valid ".$this->error, LOG_ERR);
1770
			return -1;
1771
		}
1772
1773
		$now=dol_now();
1774
1775
		$this->db->begin();
1776
1777
		// Numbering module definition
1778
		$soc = new Societe($this->db);
1779
		$soc->fetch($this->socid);
1780
1781
		// Define new ref
1782
		if (! $error && (preg_match('/^[\(]?PROV/i', $this->ref) || empty($this->ref))) // empty should not happened, but when it occurs, the test save life
1783
		{
1784
			$num = $this->getNextNumRef($soc);
1785
		}
1786
		else
1787
		{
1788
			$num = $this->ref;
1789
		}
1790
		$this->newref = $num;
1791
1792
		$sql = "UPDATE ".MAIN_DB_PREFIX."propal";
1793
		$sql.= " SET ref = '".$num."',";
1794
		$sql.= " fk_statut = ".self::STATUS_VALIDATED.", date_valid='".$this->db->idate($now)."', fk_user_valid=".$user->id;
1795
		$sql.= " WHERE rowid = ".$this->id." AND fk_statut = ".self::STATUS_DRAFT;
1796
1797
		dol_syslog(get_class($this)."::valid", LOG_DEBUG);
1798
		$resql=$this->db->query($sql);
1799
		if (! $resql)
1800
		{
1801
			dol_print_error($this->db);
1802
			$error++;
1803
		}
1804
1805
		// Trigger calls
1806
		if (! $error && ! $notrigger)
1807
		{
1808
			// Call trigger
1809
			$result=$this->call_trigger('PROPAL_VALIDATE', $user);
1810
			if ($result < 0) { $error++; }
1811
			// End call triggers
1812
		}
1813
1814
		if (! $error)
1815
		{
1816
			$this->oldref = $this->ref;
1817
1818
			// Rename directory if dir was a temporary ref
1819
			if (preg_match('/^[\(]?PROV/i', $this->ref))
1820
			{
1821
				// Now we rename also files into index
1822
				$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)."'";
1823
				$sql.= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'propale/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
1824
				$resql = $this->db->query($sql);
1825
				if (! $resql) { $error++; $this->error = $this->db->lasterror(); }
1826
1827
				// We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
1828
				$oldref = dol_sanitizeFileName($this->ref);
1829
				$newref = dol_sanitizeFileName($num);
1830
				$dirsource = $conf->propal->multidir_output[$this->entity].'/'.$oldref;
1831
				$dirdest = $conf->propal->multidir_output[$this->entity].'/'.$newref;
1832
				if (! $error && file_exists($dirsource))
1833
				{
1834
					dol_syslog(get_class($this)."::validate rename dir ".$dirsource." into ".$dirdest);
1835
					if (@rename($dirsource, $dirdest))
1836
					{
1837
						dol_syslog("Rename ok");
1838
						// Rename docs starting with $oldref with $newref
1839
						$listoffiles=dol_dir_list($dirdest, 'files', 1, '^'.preg_quote($oldref, '/'));
1840
						foreach($listoffiles as $fileentry)
1841
						{
1842
							$dirsource=$fileentry['name'];
1843
							$dirdest=preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
1844
							$dirsource=$fileentry['path'].'/'.$dirsource;
1845
							$dirdest=$fileentry['path'].'/'.$dirdest;
1846
							@rename($dirsource, $dirdest);
1 ignored issue
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

1846
							/** @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...
1847
						}
1848
					}
1849
				}
1850
			}
1851
1852
			$this->ref=$num;
1853
			$this->brouillon=0;
1854
			$this->statut = self::STATUS_VALIDATED;
1855
			$this->user_valid_id=$user->id;
1856
			$this->datev=$now;
1 ignored issue
show
Deprecated Code introduced by
The property Propal::$datev has been deprecated. ( Ignorable by Annotation )

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

1856
			/** @scrutinizer ignore-deprecated */ $this->datev=$now;
Loading history...
1857
1858
			$this->db->commit();
1859
			return 1;
1860
		}
1861
		else
1862
		{
1863
			$this->db->rollback();
1864
			return -1;
1865
		}
1866
	}
1867
1868
1869
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1870
	/**
1871
	 *  Define proposal date
1872
	 *
1873
	 *  @param  User		$user      	Object user that modify
1874
	 *  @param  int			$date		Date
1875
	 *  @param  int			$notrigger	1=Does not execute triggers, 0= execute triggers
1876
	 *  @return	int         			<0 if KO, >0 if OK
1877
	 */
1878
    public function set_date($user, $date, $notrigger = 0)
1879
	{
1880
        // phpcs:enable
1881
		if (empty($date))
1882
		{
1883
			$this->error='ErrorBadParameter';
1884
			dol_syslog(get_class($this)."::set_date ".$this->error, LOG_ERR);
1885
			return -1;
1886
		}
1887
1888
		if (! empty($user->rights->propal->creer))
1889
		{
1890
			$error=0;
1891
1892
			$this->db->begin();
1893
1894
			$sql = "UPDATE ".MAIN_DB_PREFIX."propal SET datep = '".$this->db->idate($date)."'";
1895
			$sql.= " WHERE rowid = ".$this->id." AND fk_statut = ".self::STATUS_DRAFT;
1896
1897
			dol_syslog(__METHOD__, LOG_DEBUG);
1898
			$resql=$this->db->query($sql);
1899
			if (!$resql)
1900
			{
1901
				$this->errors[]=$this->db->error();
1902
				$error++;
1903
			}
1904
1905
			if (! $error)
1906
			{
1907
				$this->oldcopy= clone $this;
1908
				$this->date = $date;
1909
				$this->datep = $date;    // deprecated
1 ignored issue
show
Deprecated Code introduced by
The property Propal::$datep has been deprecated. ( Ignorable by Annotation )

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

1909
				/** @scrutinizer ignore-deprecated */ $this->datep = $date;    // deprecated
Loading history...
1910
			}
1911
1912
			if (! $notrigger && empty($error))
1913
			{
1914
				// Call trigger
1915
				$result=$this->call_trigger('PROPAL_MODIFY', $user);
1916
				if ($result < 0) $error++;
1917
				// End call triggers
1918
			}
1919
1920
			if (! $error)
1921
			{
1922
				$this->db->commit();
1923
				return 1;
1924
			}
1925
			else
1926
			{
1927
				foreach($this->errors as $errmsg)
1928
				{
1929
					dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
1930
					$this->error.=($this->error?', '.$errmsg:$errmsg);
1931
				}
1932
				$this->db->rollback();
1933
				return -1*$error;
1934
			}
1935
		}
1936
	}
1937
1938
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1939
	/**
1940
	 *	Define end validity date
1941
	 *
1942
	 *	@param		User	$user        		Object user that modify
1943
	 *	@param      int		$date_fin_validite	End of validity date
1944
	 *  @param  	int		$notrigger			1=Does not execute triggers, 0= execute triggers
1945
	 *	@return     int         				<0 if KO, >0 if OK
1946
	 */
1947
    public function set_echeance($user, $date_fin_validite, $notrigger = 0)
1948
	{
1949
        // phpcs:enable
1950
		if (! empty($user->rights->propal->creer))
1951
		{
1952
			$error=0;
1953
1954
			$this->db->begin();
1955
1956
			$sql = "UPDATE ".MAIN_DB_PREFIX."propal SET fin_validite = ".($date_fin_validite!=''?"'".$this->db->idate($date_fin_validite)."'":'null');
1957
			$sql.= " WHERE rowid = ".$this->id." AND fk_statut = ".self::STATUS_DRAFT;
1958
1959
			dol_syslog(__METHOD__, LOG_DEBUG);
1960
			$resql=$this->db->query($sql);
1961
			if (!$resql)
1962
			{
1963
				$this->errors[]=$this->db->error();
1964
				$error++;
1965
			}
1966
1967
1968
			if (! $error)
1969
			{
1970
				$this->oldcopy= clone $this;
1971
				$this->fin_validite = $date_fin_validite;
1972
			}
1973
1974
			if (! $notrigger && empty($error))
1975
			{
1976
				// Call trigger
1977
				$result=$this->call_trigger('PROPAL_MODIFY', $user);
1978
				if ($result < 0) $error++;
1979
				// End call triggers
1980
			}
1981
1982
			if (! $error)
1983
			{
1984
				$this->db->commit();
1985
				return 1;
1986
			}
1987
			else
1988
			{
1989
				foreach($this->errors as $errmsg)
1990
				{
1991
					dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
1992
					$this->error.=($this->error?', '.$errmsg:$errmsg);
1993
				}
1994
				$this->db->rollback();
1995
				return -1*$error;
1996
			}
1997
		}
1998
	}
1999
2000
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2001
	/**
2002
	 *	Set delivery date
2003
	 *
2004
	 *	@param      User 	$user        		Object user that modify
2005
	 *	@param      int		$date_livraison     Delivery date
2006
	 *  @param  	int		$notrigger			1=Does not execute triggers, 0= execute triggers
2007
	 *	@return     int         				<0 if ko, >0 if ok
2008
	 */
2009
    public function set_date_livraison($user, $date_livraison, $notrigger = 0)
2010
	{
2011
        // phpcs:enable
2012
		if (! empty($user->rights->propal->creer))
2013
		{
2014
			$error=0;
2015
2016
			$this->db->begin();
2017
2018
			$sql = "UPDATE ".MAIN_DB_PREFIX."propal ";
2019
			$sql.= " SET date_livraison = ".($date_livraison!=''?"'".$this->db->idate($date_livraison)."'":'null');
2020
			$sql.= " WHERE rowid = ".$this->id;
2021
2022
			dol_syslog(__METHOD__, LOG_DEBUG);
2023
			$resql=$this->db->query($sql);
2024
			if (!$resql)
2025
			{
2026
				$this->errors[]=$this->db->error();
2027
				$error++;
2028
			}
2029
2030
			if (! $error)
2031
			{
2032
				$this->oldcopy= clone $this;
2033
				$this->date_livraison = $date_livraison;
2034
			}
2035
2036
			if (! $notrigger && empty($error))
2037
			{
2038
				// Call trigger
2039
				$result=$this->call_trigger('PROPAL_MODIFY', $user);
2040
				if ($result < 0) $error++;
2041
				// End call triggers
2042
			}
2043
2044
			if (! $error)
2045
			{
2046
				$this->db->commit();
2047
				return 1;
2048
			}
2049
			else
2050
			{
2051
				foreach($this->errors as $errmsg)
2052
				{
2053
					dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2054
					$this->error.=($this->error?', '.$errmsg:$errmsg);
2055
				}
2056
				$this->db->rollback();
2057
				return -1*$error;
2058
			}
2059
		}
2060
	}
2061
2062
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2063
	/**
2064
	 *  Set delivery
2065
	 *
2066
	 *  @param		User	$user		  	Object user that modify
2067
	 *  @param      int		$id				Availability id
2068
	 *  @param  	int		$notrigger		1=Does not execute triggers, 0= execute triggers
2069
	 *  @return     int           			<0 if KO, >0 if OK
2070
	 */
2071
    public function set_availability($user, $id, $notrigger = 0)
2072
	{
2073
        // phpcs:enable
2074
		if (! empty($user->rights->propal->creer) && $this->statut >= self::STATUS_DRAFT)
2075
		{
2076
			$error=0;
2077
2078
			$this->db->begin();
2079
2080
			$sql = "UPDATE ".MAIN_DB_PREFIX."propal ";
2081
			$sql.= " SET fk_availability = '".$id."'";
2082
			$sql.= " WHERE rowid = ".$this->id;
2083
2084
			dol_syslog(__METHOD__.' availability('.$id.')', LOG_DEBUG);
2085
			$resql=$this->db->query($sql);
2086
			if (!$resql)
2087
			{
2088
				$this->errors[]=$this->db->error();
2089
				$error++;
2090
			}
2091
2092
			if (! $error)
2093
			{
2094
				$this->oldcopy= clone $this;
2095
				$this->fk_availability = $id;
2096
				$this->availability_id = $id;
2097
			}
2098
2099
			if (! $notrigger && empty($error))
2100
			{
2101
				// Call trigger
2102
				$result=$this->call_trigger('PROPAL_MODIFY', $user);
2103
				if ($result < 0) $error++;
2104
				// End call triggers
2105
			}
2106
2107
			if (! $error)
2108
			{
2109
				$this->db->commit();
2110
				return 1;
2111
			}
2112
			else
2113
			{
2114
				foreach($this->errors as $errmsg)
2115
				{
2116
					dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2117
					$this->error.=($this->error?', '.$errmsg:$errmsg);
2118
				}
2119
				$this->db->rollback();
2120
				return -1*$error;
2121
			}
2122
		}
2123
		else
2124
		{
2125
			$error_str='Propal status do not meet requirement '.$this->statut;
2126
			dol_syslog(__METHOD__.$error_str, LOG_ERR);
2127
			$this->error=$error_str;
2128
			$this->errors[]= $this->error;
2129
			return -2;
2130
		}
2131
	}
2132
2133
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2134
	/**
2135
	 *  Set source of demand
2136
	 *
2137
	 *  @param		User	$user		Object user that modify
2138
	 *  @param      int		$id			Input reason id
2139
	 *  @param  	int		$notrigger	1=Does not execute triggers, 0= execute triggers
2140
	 *  @return     int           		<0 if KO, >0 if OK
2141
	 */
2142
    public function set_demand_reason($user, $id, $notrigger = 0)
2143
	{
2144
        // phpcs:enable
2145
		if (! empty($user->rights->propal->creer) && $this->statut >= self::STATUS_DRAFT)
2146
		{
2147
			$error=0;
2148
2149
			$this->db->begin();
2150
2151
			$sql = "UPDATE ".MAIN_DB_PREFIX."propal ";
2152
			$sql.= " SET fk_input_reason = ".$id;
2153
			$sql.= " WHERE rowid = ".$this->id;
2154
2155
			dol_syslog(__METHOD__, LOG_DEBUG);
2156
			$resql=$this->db->query($sql);
2157
			if (!$resql)
2158
			{
2159
				$this->errors[]=$this->db->error();
2160
				$error++;
2161
			}
2162
2163
2164
			if (! $error)
2165
			{
2166
				$this->oldcopy= clone $this;
2167
				$this->fk_input_reason = $id;
2168
				$this->demand_reason_id = $id;
2169
			}
2170
2171
2172
			if (! $notrigger && empty($error))
2173
			{
2174
				// Call trigger
2175
				$result=$this->call_trigger('PROPAL_MODIFY', $user);
2176
				if ($result < 0) $error++;
2177
				// End call triggers
2178
			}
2179
2180
			if (! $error)
2181
			{
2182
				$this->db->commit();
2183
				return 1;
2184
			}
2185
			else
2186
			{
2187
				foreach($this->errors as $errmsg)
2188
				{
2189
					dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2190
					$this->error.=($this->error?', '.$errmsg:$errmsg);
2191
				}
2192
				$this->db->rollback();
2193
				return -1*$error;
2194
			}
2195
		}
2196
		else
2197
		{
2198
			$error_str='Propal status do not meet requirement '.$this->statut;
2199
			dol_syslog(__METHOD__.$error_str, LOG_ERR);
2200
			$this->error=$error_str;
2201
			$this->errors[]= $this->error;
2202
			return -2;
2203
		}
2204
	}
2205
2206
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2207
	/**
2208
	 * Set customer reference number
2209
	 *
2210
	 *  @param      User	$user			Object user that modify
2211
	 *  @param      string	$ref_client		Customer reference
2212
	 *  @param  	int		$notrigger		1=Does not execute triggers, 0= execute triggers
2213
	 *  @return     int						<0 if ko, >0 if ok
2214
	 */
2215
    public function set_ref_client($user, $ref_client, $notrigger = 0)
2216
	{
2217
        // phpcs:enable
2218
		if (! empty($user->rights->propal->creer))
2219
		{
2220
			$error=0;
2221
2222
			$this->db->begin();
2223
2224
			$sql = 'UPDATE '.MAIN_DB_PREFIX.'propal SET ref_client = '.(empty($ref_client) ? 'NULL' : '\''.$this->db->escape($ref_client).'\'');
2225
			$sql.= ' WHERE rowid = '.$this->id;
2226
2227
			dol_syslog(__METHOD__.' $this->id='.$this->id.', ref_client='.$ref_client, LOG_DEBUG);
2228
			$resql=$this->db->query($sql);
2229
			if (!$resql)
2230
			{
2231
				$this->errors[]=$this->db->error();
2232
				$error++;
2233
			}
2234
2235
			if (! $error)
2236
			{
2237
				$this->oldcopy= clone $this;
2238
				$this->ref_client = $ref_client;
2239
			}
2240
2241
			if (! $notrigger && empty($error))
2242
			{
2243
				// Call trigger
2244
				$result=$this->call_trigger('PROPAL_MODIFY', $user);
2245
				if ($result < 0) $error++;
2246
				// End call triggers
2247
			}
2248
2249
			if (! $error)
2250
			{
2251
				$this->db->commit();
2252
				return 1;
2253
			}
2254
			else
2255
			{
2256
				foreach($this->errors as $errmsg)
2257
				{
2258
					dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2259
					$this->error.=($this->error?', '.$errmsg:$errmsg);
2260
				}
2261
				$this->db->rollback();
2262
				return -1*$error;
2263
			}
2264
		}
2265
		else
2266
		{
2267
			return -1;
2268
		}
2269
	}
2270
2271
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2272
	/**
2273
	 *	Set an overall discount on the proposal
2274
	 *
2275
	 *	@param      User	$user       Object user that modify
2276
	 *	@param      double	$remise     Amount discount
2277
	 *  @param  	int		$notrigger	1=Does not execute triggers, 0= execute triggers
2278
	 *	@return     int         		<0 if ko, >0 if ok
2279
	 */
2280
    public function set_remise_percent($user, $remise, $notrigger = 0)
2281
	{
2282
        // phpcs:enable
2283
		$remise=trim($remise)?trim($remise):0;
2284
2285
		if (! empty($user->rights->propal->creer))
2286
		{
2287
			$remise = price2num($remise);
2288
2289
			$error=0;
2290
2291
			$this->db->begin();
2292
2293
			$sql = "UPDATE ".MAIN_DB_PREFIX."propal SET remise_percent = ".$remise;
2294
			$sql.= " WHERE rowid = ".$this->id." AND fk_statut = ".self::STATUS_DRAFT;
2295
2296
			dol_syslog(__METHOD__, LOG_DEBUG);
2297
			$resql=$this->db->query($sql);
2298
			if (!$resql)
2299
			{
2300
				$this->errors[]=$this->db->error();
2301
				$error++;
2302
			}
2303
2304
			if (! $error)
2305
			{
2306
				$this->oldcopy= clone $this;
2307
				$this->remise_percent = $remise;
2308
				$this->update_price(1);
2309
			}
2310
2311
			if (! $notrigger && empty($error))
2312
			{
2313
				// Call trigger
2314
				$result=$this->call_trigger('PROPAL_MODIFY', $user);
2315
				if ($result < 0) $error++;
2316
				// End call triggers
2317
			}
2318
2319
			if (! $error)
2320
			{
2321
				$this->db->commit();
2322
				return 1;
2323
			}
2324
			else
2325
			{
2326
				foreach($this->errors as $errmsg)
2327
				{
2328
					dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2329
					$this->error.=($this->error?', '.$errmsg:$errmsg);
2330
				}
2331
				$this->db->rollback();
2332
				return -1*$error;
2333
			}
2334
		}
2335
	}
2336
2337
2338
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2339
	/**
2340
	 *	Set an absolute overall discount on the proposal
2341
	 *
2342
	 *	@param      User	$user       Object user that modify
2343
	 *	@param      double	$remise     Amount discount
2344
	 *  @param  	int		$notrigger	1=Does not execute triggers, 0= execute triggers
2345
	 *	@return     int         		<0 if ko, >0 if ok
2346
	 */
2347
    public function set_remise_absolue($user, $remise, $notrigger = 0)
2348
	{
2349
        // phpcs:enable
2350
		$remise=trim($remise)?trim($remise):0;
2351
2352
		if (! empty($user->rights->propal->creer))
2353
		{
2354
			$remise = price2num($remise);
2355
2356
			$error=0;
2357
2358
			$this->db->begin();
2359
2360
			$sql = "UPDATE ".MAIN_DB_PREFIX."propal ";
2361
			$sql.= " SET remise_absolue = ".$remise;
2362
			$sql.= " WHERE rowid = ".$this->id." AND fk_statut = ".self::STATUS_DRAFT;
2363
2364
			dol_syslog(__METHOD__, LOG_DEBUG);
2365
			$resql=$this->db->query($sql);
2366
			if (!$resql)
2367
			{
2368
				$this->errors[]=$this->db->error();
2369
				$error++;
2370
			}
2371
2372
			if (! $error)
2373
			{
2374
				$this->oldcopy= clone $this;
2375
				$this->remise_absolue = $remise;
2376
				$this->update_price(1);
2377
			}
2378
2379
			if (! $notrigger && empty($error))
2380
			{
2381
				// Call trigger
2382
				$result=$this->call_trigger('PROPAL_MODIFY', $user);
2383
				if ($result < 0) $error++;
2384
				// End call triggers
2385
			}
2386
2387
			if (! $error)
2388
			{
2389
				$this->db->commit();
2390
				return 1;
2391
			}
2392
			else
2393
			{
2394
				foreach($this->errors as $errmsg)
2395
				{
2396
					dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2397
					$this->error.=($this->error?', '.$errmsg:$errmsg);
2398
				}
2399
				$this->db->rollback();
2400
				return -1*$error;
2401
			}
2402
		}
2403
	}
2404
2405
2406
2407
	/**
2408
	 *	Reopen the commercial proposal
2409
	 *
2410
	 *	@param      User	$user		Object user that close
2411
	 *	@param      int		$statut		Statut
2412
	 *	@param      string	$note		Comment
2413
	 *  @param		int		$notrigger	1=Does not execute triggers, 0= execute triggers
2414
	 *	@return     int         		<0 if KO, >0 if OK
2415
	 */
2416
    public function reopen($user, $statut, $note = '', $notrigger = 0)
2417
	{
2418
2419
		$this->statut = $statut;
2420
		$error=0;
2421
2422
		$sql = "UPDATE ".MAIN_DB_PREFIX."propal";
2423
		$sql.= " SET fk_statut = ".$this->statut.",";
2424
		if (! empty($note)) $sql.= " note_private = '".$this->db->escape($note)."',";
2425
		$sql.= " date_cloture=NULL, fk_user_cloture=NULL";
2426
		$sql.= " WHERE rowid = ".$this->id;
2427
2428
		$this->db->begin();
2429
2430
		dol_syslog(get_class($this)."::reopen", LOG_DEBUG);
2431
		$resql = $this->db->query($sql);
2432
		if (! $resql) {
2433
			$error++; $this->errors[]="Error ".$this->db->lasterror();
2434
		}
2435
		if (! $error)
2436
		{
2437
			if (! $notrigger)
2438
			{
2439
				// Call trigger
2440
				$result=$this->call_trigger('PROPAL_REOPEN', $user);
2441
				if ($result < 0) { $error++; }
2442
				// End call triggers
2443
			}
2444
		}
2445
2446
		// Commit or rollback
2447
		if ($error)
2448
		{
2449
			if (!empty($this->errors))
2450
			{
2451
				foreach($this->errors as $errmsg)
2452
				{
2453
					dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
2454
					$this->error.=($this->error?', '.$errmsg:$errmsg);
2455
				}
2456
			}
2457
			$this->db->rollback();
2458
			return -1*$error;
2459
		}
2460
		else
2461
		{
2462
			$this->db->commit();
2463
			return 1;
2464
		}
2465
	}
2466
2467
2468
	/**
2469
	 *	Close the commercial proposal
2470
	 *
2471
	 *	@param      User	$user		Object user that close
2472
	 *	@param      int		$statut		Statut
2473
	 *	@param      string	$note		Complete private note with this note
2474
	 *  @param		int		$notrigger	1=Does not execute triggers, 0=Execute triggers
2475
	 *	@return     int         		<0 if KO, >0 if OK
2476
	 */
2477
    public function cloture($user, $statut, $note = "", $notrigger = 0)
2478
	{
2479
		global $langs,$conf;
2480
2481
		$error=0;
2482
		$now=dol_now();
2483
2484
		$this->db->begin();
2485
2486
		$newprivatenote = dol_concatdesc($this->note_private, $note);
2487
2488
		$sql = "UPDATE ".MAIN_DB_PREFIX."propal";
2489
		$sql.= " SET fk_statut = ".$statut.", note_private = '".$this->db->escape($newprivatenote)."', date_cloture='".$this->db->idate($now)."', fk_user_cloture=".$user->id;
2490
		$sql.= " WHERE rowid = ".$this->id;
2491
2492
		$resql=$this->db->query($sql);
2493
		if ($resql)
2494
		{
2495
			$modelpdf=$conf->global->PROPALE_ADDON_PDF_ODT_CLOSED?$conf->global->PROPALE_ADDON_PDF_ODT_CLOSED:$this->modelpdf;
2496
			$trigger_name='PROPAL_CLOSE_REFUSED';
2497
2498
			if ($statut == self::STATUS_SIGNED)
2499
			{
2500
				$trigger_name='PROPAL_CLOSE_SIGNED';
2501
				$modelpdf=$conf->global->PROPALE_ADDON_PDF_ODT_TOBILL?$conf->global->PROPALE_ADDON_PDF_ODT_TOBILL:$this->modelpdf;
2502
2503
				// The connected company is classified as a client
2504
				$soc=new Societe($this->db);
2505
				$soc->id = $this->socid;
2506
				$result=$soc->set_as_client();
2507
2508
				if ($result < 0)
2509
				{
2510
					$this->error=$this->db->lasterror();
2511
					$this->db->rollback();
2512
					return -2;
2513
				}
2514
			}
2515
			if ($statut == self::STATUS_BILLED)	// Why this ?
2516
			{
2517
				$trigger_name='PROPAL_CLASSIFY_BILLED';
2518
			}
2519
2520
			if (empty($conf->global->MAIN_DISABLE_PDF_AUTOUPDATE))
2521
			{
2522
				// Define output language
2523
				$outputlangs = $langs;
2524
				if (! empty($conf->global->MAIN_MULTILANGS))
2525
				{
2526
					$outputlangs = new Translate("", $conf);
2527
					$newlang=(GETPOST('lang_id', 'aZ09') ? GETPOST('lang_id', 'aZ09') : $this->thirdparty->default_lang);
2528
					$outputlangs->setDefaultLang($newlang);
2529
				}
2530
				//$ret=$object->fetch($id);    // Reload to get new records
2531
				$this->generateDocument($modelpdf, $outputlangs);
2532
			}
2533
2534
			if (! $error)
2535
			{
2536
				$this->oldcopy= clone $this;
2537
				$this->statut = $statut;
2538
				$this->date_cloture = $now;
2539
				$this->note_private = $newprivatenote;
2540
			}
2541
2542
			if (! $notrigger && empty($error))
2543
			{
2544
				// Call trigger
2545
				$result=$this->call_trigger($trigger_name, $user);
2546
				if ($result < 0) { $error++; }
2547
				// End call triggers
2548
			}
2549
2550
			if (! $error)
2551
			{
2552
				$this->db->commit();
2553
				return 1;
2554
			}
2555
			else
2556
			{
2557
				$this->statut = $this->oldcopy->statut;
2558
				$this->date_cloture = $this->oldcopy->date_cloture;
2559
				$this->note_private = $this->oldcopy->note_private;
2560
2561
				$this->db->rollback();
2562
				return -1;
2563
			}
2564
		}
2565
		else
2566
		{
2567
			$this->error=$this->db->lasterror();
2568
			$this->db->rollback();
2569
			return -1;
2570
		}
2571
	}
2572
2573
	/**
2574
	 *	Class invoiced the Propal
2575
	 *
2576
	 *	@param  	User	$user    	Object user
2577
	 *  @param		int		$notrigger	1=Does not execute triggers, 0= execute triggers
2578
	 *	@return     int     			<0 si ko, >0 si ok
2579
	 */
2580
    public function classifyBilled(User $user, $notrigger = 0)
2581
	{
2582
		$error=0;
2583
2584
		$this->db->begin();
2585
2586
		$sql = 'UPDATE '.MAIN_DB_PREFIX.'propal SET fk_statut = '.self::STATUS_BILLED;
2587
		$sql .= ' WHERE rowid = '.$this->id.' AND fk_statut > '.self::STATUS_DRAFT;
2588
2589
		dol_syslog(__METHOD__, LOG_DEBUG);
2590
		$resql=$this->db->query($sql);
2591
		if (!$resql)
2592
		{
2593
			$this->errors[]=$this->db->error();
2594
			$error++;
2595
		}
2596
2597
		if (! $error)
2598
		{
2599
			$this->oldcopy= clone $this;
2600
			$this->statut=self::STATUS_BILLED;
2601
		}
2602
2603
		if (! $notrigger && empty($error))
2604
		{
2605
			// Call trigger
2606
			$result=$this->call_trigger('PROPAL_MODIFY', $user);
2607
			if ($result < 0) $error++;
2608
			// End call triggers
2609
		}
2610
2611
		if (! $error)
2612
		{
2613
			$this->db->commit();
2614
			return 1;
2615
		}
2616
		else
2617
		{
2618
			foreach($this->errors as $errmsg)
2619
			{
2620
				dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2621
				$this->error.=($this->error?', '.$errmsg:$errmsg);
2622
			}
2623
			$this->db->rollback();
2624
			return -1*$error;
2625
		}
2626
	}
2627
2628
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2629
	/**
2630
	 *	Set draft status
2631
	 *
2632
	 *	@param		User	$user		Object user that modify
2633
	 *  @param		int		$notrigger	1=Does not execute triggers, 0= execute triggers
2634
	 *	@return		int					<0 if KO, >0 if OK
2635
	 */
2636
    public function setDraft($user, $notrigger = 0)
2637
	{
2638
        // phpcs:enable
2639
		$error=0;
2640
2641
		// Protection
2642
		if ($this->statut <= self::STATUS_DRAFT)
2643
		{
2644
		    return 0;
2645
		}
2646
2647
		dol_syslog(get_class($this)."::setDraft", LOG_DEBUG);
2648
2649
		$this->db->begin();
2650
2651
		$sql = "UPDATE ".MAIN_DB_PREFIX."propal";
2652
		$sql.= " SET fk_statut = ".self::STATUS_DRAFT;
2653
		$sql.= " WHERE rowid = ".$this->id;
2654
2655
		$resql=$this->db->query($sql);
2656
		if (!$resql)
2657
		{
2658
			$this->errors[]=$this->db->error();
2659
			$error++;
2660
		}
2661
2662
		if (! $error)
2663
		{
2664
			$this->oldcopy= clone $this;
2665
		}
2666
2667
		if (! $notrigger && empty($error))
2668
		{
2669
			// Call trigger
2670
			$result=$this->call_trigger('PROPAL_MODIFY', $user);
2671
			if ($result < 0) $error++;
2672
			// End call triggers
2673
		}
2674
2675
		if (! $error)
2676
		{
2677
		    $this->statut = self::STATUS_DRAFT;
2678
		    $this->brouillon = 1;
2679
2680
		    $this->db->commit();
2681
			return 1;
2682
		}
2683
		else
2684
		{
2685
			foreach($this->errors as $errmsg)
2686
			{
2687
				dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2688
				$this->error.=($this->error?', '.$errmsg:$errmsg);
2689
			}
2690
			$this->db->rollback();
2691
			return -1*$error;
2692
		}
2693
	}
2694
2695
2696
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2697
	/**
2698
	 *    Return list of proposal (eventually filtered on user) into an array
2699
	 *
2700
	 *    @param	int		$shortlist			0=Return array[id]=ref, 1=Return array[](id=>id,ref=>ref,name=>name)
2701
	 *    @param	int		$draft				0=not draft, 1=draft
2702
	 *    @param	int		$notcurrentuser		0=all user, 1=not current user
2703
	 *    @param    int		$socid				Id third pary
2704
	 *    @param    int		$limit				For pagination
2705
	 *    @param    int		$offset				For pagination
2706
	 *    @param    string	$sortfield			Sort criteria
2707
	 *    @param    string	$sortorder			Sort order
2708
	 *    @return	int		       				-1 if KO, array with result if OK
2709
	 */
2710
    public function liste_array($shortlist = 0, $draft = 0, $notcurrentuser = 0, $socid = 0, $limit = 0, $offset = 0, $sortfield = 'p.datep', $sortorder = 'DESC')
2711
	{
2712
        // phpcs:enable
2713
		global $user;
2714
2715
		$ga = array();
2716
2717
		$sql = "SELECT s.rowid, s.nom as name, s.client,";
2718
		$sql.= " p.rowid as propalid, p.fk_statut, p.total_ht, p.ref, p.remise, ";
2719
		$sql.= " p.datep as dp, p.fin_validite as datelimite";
2720
		if (! $user->rights->societe->client->voir && ! $socid) $sql .= ", sc.fk_soc, sc.fk_user";
2721
		$sql.= " FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."propal as p, ".MAIN_DB_PREFIX."c_propalst as c";
2722
		if (! $user->rights->societe->client->voir && ! $socid) $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc";
2723
		$sql.= " WHERE p.entity IN (".getEntity('propal').")";
2724
		$sql.= " AND p.fk_soc = s.rowid";
2725
		$sql.= " AND p.fk_statut = c.id";
2726
		if (! $user->rights->societe->client->voir && ! $socid) //restriction
2727
		{
2728
			$sql.= " AND s.rowid = sc.fk_soc AND sc.fk_user = " .$user->id;
2729
		}
2730
		if ($socid) $sql.= " AND s.rowid = ".$socid;
2731
		if ($draft)	$sql.= " AND p.fk_statut = ".self::STATUS_DRAFT;
2732
		if ($notcurrentuser > 0) $sql.= " AND p.fk_user_author <> ".$user->id;
2733
		$sql.= $this->db->order($sortfield, $sortorder);
2734
		$sql.= $this->db->plimit($limit, $offset);
2735
2736
		$result=$this->db->query($sql);
2737
		if ($result)
2738
		{
2739
			$num = $this->db->num_rows($result);
2740
			if ($num)
2741
			{
2742
				$i = 0;
2743
				while ($i < $num)
2744
				{
2745
					$obj = $this->db->fetch_object($result);
2746
2747
					if ($shortlist == 1)
2748
					{
2749
						$ga[$obj->propalid] = $obj->ref;
2750
					}
2751
					elseif ($shortlist == 2)
2752
					{
2753
						$ga[$obj->propalid] = $obj->ref.' ('.$obj->name.')';
2754
					}
2755
					else
2756
					{
2757
						$ga[$i]['id']	= $obj->propalid;
2758
						$ga[$i]['ref'] 	= $obj->ref;
2759
						$ga[$i]['name'] = $obj->name;
2760
					}
2761
2762
					$i++;
2763
				}
2764
			}
2765
			return $ga;
2766
		}
2767
		else
2768
		{
2769
			dol_print_error($this->db);
2770
			return -1;
2771
		}
2772
	}
2773
2774
	/**
2775
	 *  Returns an array with the numbers of related invoices
2776
	 *
2777
	 *	@return	array		Array of invoices
2778
	 */
2779
    public function getInvoiceArrayList()
2780
	{
2781
		return $this->InvoiceArrayList($this->id);
2782
	}
2783
2784
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2785
	/**
2786
	 *  Returns an array with id and ref of related invoices
2787
	 *
2788
	 *	@param		int		$id			Id propal
2789
	 *	@return		array				Array of invoices id
2790
	 */
2791
    public function InvoiceArrayList($id)
2792
	{
2793
        // phpcs:enable
2794
		$ga = array();
2795
		$linkedInvoices = array();
2796
2797
		$this->fetchObjectLinked($id, $this->element);
2798
		foreach($this->linkedObjectsIds as $objecttype => $objectid)
2799
		{
2800
			// Nouveau système du comon object renvoi des rowid et non un id linéaire de 1 à n
2801
			// On parcourt donc une liste d'objets en tant qu'objet unique
2802
			foreach($objectid as $key => $object)
2803
			{
2804
				// Cas des factures liees directement
2805
				if ($objecttype == 'facture')
2806
				{
2807
					$linkedInvoices[] = $object;
2808
				}
2809
				// Cas des factures liees par un autre objet (ex: commande)
2810
				else
2811
				{
2812
					$this->fetchObjectLinked($object, $objecttype);
2813
					foreach($this->linkedObjectsIds as $subobjecttype => $subobjectid)
2814
					{
2815
						foreach($subobjectid as $subkey => $subobject)
2816
						{
2817
							if ($subobjecttype == 'facture')
2818
							{
2819
								$linkedInvoices[] = $subobject;
2820
							}
2821
						}
2822
					}
2823
				}
2824
			}
2825
		}
2826
2827
		if (count($linkedInvoices) > 0)
2828
		{
2829
			$sql= "SELECT rowid as facid, ref, total, datef as df, fk_user_author, fk_statut, paye";
2830
			$sql.= " FROM ".MAIN_DB_PREFIX."facture";
2831
			$sql.= " WHERE rowid IN (".implode(',', $linkedInvoices).")";
2832
2833
			dol_syslog(get_class($this)."::InvoiceArrayList", LOG_DEBUG);
2834
			$resql=$this->db->query($sql);
2835
2836
			if ($resql)
2837
			{
2838
				$tab_sqlobj=array();
2839
				$nump = $this->db->num_rows($resql);
2840
				for ($i = 0;$i < $nump;$i++)
2841
				{
2842
					$sqlobj = $this->db->fetch_object($resql);
2843
					$tab_sqlobj[] = $sqlobj;
2844
				}
2845
				$this->db->free($resql);
2846
2847
				$nump = count($tab_sqlobj);
2848
2849
				if ($nump)
2850
				{
2851
					$i = 0;
2852
					while ($i < $nump)
2853
					{
2854
						$obj = array_shift($tab_sqlobj);
2855
2856
						$ga[$i] = $obj;
2857
2858
						$i++;
2859
					}
2860
				}
2861
				return $ga;
2862
			}
2863
			else
2864
			{
2865
				return -1;
2866
			}
2867
		}
2868
		else return $ga;
2869
	}
2870
2871
	/**
2872
	 *	Delete proposal
2873
	 *
2874
	 *	@param	User	$user        	Object user that delete
2875
	 *	@param	int		$notrigger		1=Does not execute triggers, 0= execute triggers
2876
	 *	@return	int						1 if ok, otherwise if error
2877
	 */
2878
    public function delete($user, $notrigger = 0)
2879
	{
2880
		global $conf;
2881
		require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2882
2883
		$error=0;
2884
2885
		$this->db->begin();
2886
2887
		if (! $notrigger)
2888
		{
2889
			// Call trigger
2890
			$result=$this->call_trigger('PROPAL_DELETE', $user);
2891
			if ($result < 0) { $error++; }
2892
			// End call triggers
2893
		}
2894
2895
		if (! $error)
2896
		{
2897
			$sql = "DELETE FROM ".MAIN_DB_PREFIX."propaldet WHERE fk_propal = ".$this->id;
2898
			if ($this->db->query($sql))
2899
			{
2900
				$sql = "DELETE FROM ".MAIN_DB_PREFIX."propal WHERE rowid = ".$this->id;
2901
				if ($this->db->query($sql))
2902
				{
2903
					// Delete linked object
2904
					$res = $this->deleteObjectLinked();
2905
					if ($res < 0) $error++;
2906
2907
					// Delete linked contacts
2908
					$res = $this->delete_linked_contact();
2909
					if ($res < 0) $error++;
2910
2911
					if (! $error)
2912
					{
2913
						// We remove directory
2914
						$ref = dol_sanitizeFileName($this->ref);
2915
						if ($conf->propal->multidir_output[$this->entity] && !empty($this->ref))
2916
						{
2917
							$dir = $conf->propal->multidir_output[$this->entity] . "/" . $ref ;
2918
							$file = $dir . "/" . $ref . ".pdf";
2919
							if (file_exists($file))
2920
							{
2921
								dol_delete_preview($this);
2922
2923
								if (! dol_delete_file($file, 0, 0, 0, $this)) // For triggers
2924
								{
2925
									$this->error='ErrorFailToDeleteFile';
2926
									$this->errors=array('ErrorFailToDeleteFile');
2927
									$this->db->rollback();
2928
									return 0;
2929
								}
2930
							}
2931
							if (file_exists($dir))
2932
							{
2933
								$res=@dol_delete_dir_recursive($dir);
2934
								if (! $res)
2935
								{
2936
									$this->error='ErrorFailToDeleteDir';
2937
									$this->errors=array('ErrorFailToDeleteDir');
2938
									$this->db->rollback();
2939
									return 0;
2940
								}
2941
							}
2942
						}
2943
					}
2944
2945
					// Removed extrafields
2946
					if (! $error)
2947
					{
2948
						if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) // For avoid conflicts if trigger used
2949
						{
2950
							$result=$this->deleteExtraFields();
2951
							if ($result < 0)
2952
							{
2953
								$error++;
2954
								$errorflag=-4;
2955
								dol_syslog(get_class($this)."::delete erreur ".$errorflag." ".$this->error, LOG_ERR);
2956
							}
2957
						}
2958
					}
2959
2960
					if (! $error)
2961
					{
2962
						dol_syslog(get_class($this)."::delete ".$this->id." by ".$user->id, LOG_DEBUG);
2963
						$this->db->commit();
2964
						return 1;
2965
					}
2966
					else
2967
					{
2968
						$this->error=$this->db->lasterror();
2969
						$this->db->rollback();
2970
						return 0;
2971
					}
2972
				}
2973
				else
2974
				{
2975
					$this->error=$this->db->lasterror();
2976
					$this->db->rollback();
2977
					return -3;
2978
				}
2979
			}
2980
			else
2981
			{
2982
				$this->error=$this->db->lasterror();
2983
				$this->db->rollback();
2984
				return -2;
2985
			}
2986
		}
2987
		else
2988
		{
2989
			$this->db->rollback();
2990
			return -1;
2991
		}
2992
	}
2993
2994
	/**
2995
	 *  Change the delivery time
2996
	 *
2997
	 *  @param	int	$availability_id	Id of new delivery time
2998
	 * 	@param	int	$notrigger			1=Does not execute triggers, 0= execute triggers
2999
	 *  @return int                  	>0 if OK, <0 if KO
3000
	 *  @deprecated  use set_availability
3001
	 */
3002
    public function availability($availability_id, $notrigger = 0)
3003
	{
3004
		global $user;
3005
3006
		if ($this->statut >= self::STATUS_DRAFT)
3007
		{
3008
			$error=0;
3009
3010
			$this->db->begin();
3011
3012
			$sql = 'UPDATE '.MAIN_DB_PREFIX.'propal';
3013
			$sql .= ' SET fk_availability = '.$availability_id;
3014
			$sql .= ' WHERE rowid='.$this->id;
3015
3016
			dol_syslog(__METHOD__.' availability('.$availability_id.')', LOG_DEBUG);
3017
			$resql=$this->db->query($sql);
3018
			if (!$resql)
3019
			{
3020
				$this->errors[]=$this->db->error();
3021
				$error++;
3022
			}
3023
3024
			if (! $error)
3025
			{
3026
				$this->oldcopy= clone $this;
3027
				$this->availability_id = $availability_id;
3028
			}
3029
3030
			if (! $notrigger && empty($error))
3031
			{
3032
				// Call trigger
3033
				$result=$this->call_trigger('PROPAL_MODIFY', $user);
3034
				if ($result < 0) $error++;
3035
				// End call triggers
3036
			}
3037
3038
			if (! $error)
3039
			{
3040
				$this->db->commit();
3041
				return 1;
3042
			}
3043
			else
3044
			{
3045
				foreach($this->errors as $errmsg)
3046
				{
3047
					dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
3048
					$this->error.=($this->error?', '.$errmsg:$errmsg);
3049
				}
3050
				$this->db->rollback();
3051
				return -1*$error;
3052
			}
3053
		}
3054
		else
3055
		{
3056
			$error_str='Propal status do not meet requirement '.$this->statut;
3057
			dol_syslog(__METHOD__.$error_str, LOG_ERR);
3058
			$this->error=$error_str;
3059
			$this->errors[]= $this->error;
3060
			return -2;
3061
		}
3062
	}
3063
3064
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3065
	/**
3066
	 *	Change source demand
3067
	 *
3068
	 *	@param	int $demand_reason_id 	Id of new source demand
3069
	 * 	@param	int	$notrigger			1=Does not execute triggers, 0= execute triggers
3070
	 *	@return int						>0 si ok, <0 si ko
3071
	 *	@deprecated use set_demand_reason
3072
	 */
3073
    public function demand_reason($demand_reason_id, $notrigger = 0)
3074
	{
3075
        // phpcs:enable
3076
		global $user;
3077
3078
		if ($this->statut >= self::STATUS_DRAFT)
3079
		{
3080
			$error=0;
3081
3082
			$this->db->begin();
3083
3084
			$sql = 'UPDATE '.MAIN_DB_PREFIX.'propal';
3085
			$sql .= ' SET fk_input_reason = '.$demand_reason_id;
3086
			$sql .= ' WHERE rowid='.$this->id;
3087
3088
			dol_syslog(__METHOD__.' demand_reason('.$demand_reason_id.')', LOG_DEBUG);
3089
			$resql=$this->db->query($sql);
3090
			if (!$resql)
3091
			{
3092
				$this->errors[]=$this->db->error();
3093
				$error++;
3094
			}
3095
3096
			if (! $error)
3097
			{
3098
				$this->oldcopy= clone $this;
3099
				$this->demand_reason_id = $demand_reason_id;
3100
			}
3101
3102
			if (! $notrigger && empty($error))
3103
			{
3104
				// Call trigger
3105
				$result=$this->call_trigger('PROPAL_MODIFY', $user);
3106
				if ($result < 0) $error++;
3107
				// End call triggers
3108
			}
3109
3110
			if (! $error)
3111
			{
3112
				$this->db->commit();
3113
				return 1;
3114
			}
3115
			else
3116
			{
3117
				foreach($this->errors as $errmsg)
3118
				{
3119
					dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
3120
					$this->error.=($this->error?', '.$errmsg:$errmsg);
3121
				}
3122
				$this->db->rollback();
3123
				return -1*$error;
3124
			}
3125
		}
3126
		else
3127
		{
3128
			$error_str='Propal status do not meet requirement '.$this->statut;
3129
			dol_syslog(__METHOD__.$error_str, LOG_ERR);
3130
			$this->error=$error_str;
3131
			$this->errors[]= $this->error;
3132
			return -2;
3133
		}
3134
	}
3135
3136
3137
	/**
3138
	 *	Object Proposal Information
3139
	 *
3140
	 * 	@param	int		$id		Proposal id
3141
	 *  @return	void
3142
	 */
3143
    public function info($id)
3144
	{
3145
		$sql = "SELECT c.rowid, ";
3146
		$sql.= " c.datec, c.date_valid as datev, c.date_cloture as dateo,";
3147
		$sql.= " c.fk_user_author, c.fk_user_valid, c.fk_user_cloture";
3148
		$sql.= " FROM ".MAIN_DB_PREFIX."propal as c";
3149
		$sql.= " WHERE c.rowid = ".$id;
3150
3151
		$result = $this->db->query($sql);
3152
3153
		if ($result)
3154
		{
3155
			if ($this->db->num_rows($result))
3156
			{
3157
				$obj = $this->db->fetch_object($result);
3158
3159
				$this->id                = $obj->rowid;
3160
3161
				$this->date_creation     = $this->db->jdate($obj->datec);
3162
				$this->date_validation   = $this->db->jdate($obj->datev);
3163
				$this->date_cloture      = $this->db->jdate($obj->dateo);
3164
3165
				$cuser = new User($this->db);
3166
				$cuser->fetch($obj->fk_user_author);
3167
				$this->user_creation     = $cuser;
3168
3169
				if ($obj->fk_user_valid)
3170
				{
3171
					$vuser = new User($this->db);
3172
					$vuser->fetch($obj->fk_user_valid);
3173
					$this->user_validation     = $vuser;
3174
				}
3175
3176
				if ($obj->fk_user_cloture)
3177
				{
3178
					$cluser = new User($this->db);
3179
					$cluser->fetch($obj->fk_user_cloture);
3180
					$this->user_cloture     = $cluser;
3181
				}
3182
			}
3183
			$this->db->free($result);
3184
		}
3185
		else
3186
		{
3187
			dol_print_error($this->db);
3188
		}
3189
	}
3190
3191
3192
	/**
3193
	 *    	Return label of status of proposal (draft, validated, ...)
3194
	 *
3195
	 *    	@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
3196
	 *    	@return     string		Label
3197
	 */
3198
    public function getLibStatut($mode = 0)
3199
	{
3200
		return $this->LibStatut($this->statut, $mode);
3201
	}
3202
3203
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3204
	/**
3205
	 *    	Return label of a status (draft, validated, ...)
3206
	 *
3207
	 *    	@param      int			$statut		id statut
3208
	 *    	@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
3209
	 *    	@return     string		Label
3210
	 */
3211
    public function LibStatut($statut, $mode = 1)
3212
	{
3213
        // phpcs:enable
3214
		global $conf;
3215
3216
		// Init/load array of translation of status
3217
		if (empty($this->labelstatut) || empty($this->labelstatut_short))
3218
		{
3219
			global $langs;
3220
			$langs->load("propal");
3221
			$this->labelstatut[0]=$langs->trans("PropalStatusDraft");
3222
			$this->labelstatut[1]=$langs->trans("PropalStatusValidated");
3223
			$this->labelstatut[2]=$langs->trans("PropalStatusSigned");
3224
			$this->labelstatut[3]=$langs->trans("PropalStatusNotSigned");
3225
			$this->labelstatut[4]=$langs->trans("PropalStatusBilled");
3226
			$this->labelstatut_short[0]=$langs->trans("PropalStatusDraftShort");
3227
			$this->labelstatut_short[1]=$langs->trans("PropalStatusValidatedShort");
3228
			$this->labelstatut_short[2]=$langs->trans("PropalStatusSignedShort");
3229
			$this->labelstatut_short[3]=$langs->trans("PropalStatusNotSignedShort");
3230
			$this->labelstatut_short[4]=$langs->trans("PropalStatusBilledShort");
3231
		}
3232
3233
		$statusType='';
3234
		if ($statut==self::STATUS_DRAFT) $statusType='status0';
3235
		elseif ($statut==self::STATUS_VALIDATED) $statusType='status1';
3236
		elseif ($statut==self::STATUS_SIGNED) $statusType='status3';
3237
		elseif ($statut==self::STATUS_NOTSIGNED) $statusType='status5';
3238
		elseif ($statut==self::STATUS_BILLED) $statusType='status6';
3239
3240
		return dolGetStatus($this->labelstatut[$statut], $this->labelstatut_short[$statut], '', $statusType, $mode);
3241
	}
3242
3243
3244
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3245
	/**
3246
	 *      Load indicators for dashboard (this->nbtodo and this->nbtodolate)
3247
	 *
3248
	 *      @param          User	$user   Object user
3249
	 *      @param          int		$mode   "opened" for proposal to close, "signed" for proposal to invoice
3250
	 *      @return WorkboardResponse|int <0 if KO, WorkboardResponse if OK
3251
	 */
3252
    public function load_board($user, $mode)
3253
	{
3254
        // phpcs:enable
3255
		global $conf, $langs;
3256
3257
		$clause = " WHERE";
3258
3259
		$sql = "SELECT p.rowid, p.ref, p.datec as datec, p.fin_validite as datefin, p.total_ht";
3260
		$sql.= " FROM ".MAIN_DB_PREFIX."propal as p";
3261
		if (!$user->rights->societe->client->voir && !$user->societe_id)
1 ignored issue
show
Deprecated Code introduced by
The property User::$societe_id has been deprecated. ( Ignorable by Annotation )

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

3261
		if (!$user->rights->societe->client->voir && !/** @scrutinizer ignore-deprecated */ $user->societe_id)
Loading history...
3262
		{
3263
			$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON p.fk_soc = sc.fk_soc";
3264
			$sql.= " WHERE sc.fk_user = " .$user->id;
3265
			$clause = " AND";
3266
		}
3267
		$sql.= $clause." p.entity IN (".getEntity('propal').")";
3268
		if ($mode == 'opened') $sql.= " AND p.fk_statut = ".self::STATUS_VALIDATED;
3269
		if ($mode == 'signed') $sql.= " AND p.fk_statut = ".self::STATUS_SIGNED;
3270
		if ($user->societe_id) $sql.= " AND p.fk_soc = ".$user->societe_id;
1 ignored issue
show
Deprecated Code introduced by
The property User::$societe_id has been deprecated. ( Ignorable by Annotation )

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

3270
		if (/** @scrutinizer ignore-deprecated */ $user->societe_id) $sql.= " AND p.fk_soc = ".$user->societe_id;
Loading history...
3271
3272
		$resql=$this->db->query($sql);
3273
		if ($resql)
3274
		{
3275
			$langs->load("propal");
3276
			$now=dol_now();
3277
3278
			$delay_warning = 0;
3279
			$statut = 0;
3280
			$label = '';
3281
			if ($mode == 'opened') {
3282
				$delay_warning=$conf->propal->cloture->warning_delay;
3283
				$statut = self::STATUS_VALIDATED;
3284
				$label = $langs->trans("PropalsToClose");
3285
			}
3286
			if ($mode == 'signed') {
3287
				$delay_warning=$conf->propal->facturation->warning_delay;
3288
				$statut = self::STATUS_SIGNED;
3289
				$label = $langs->trans("PropalsToBill");         // We set here bill but may be billed or ordered
3290
			}
3291
3292
			$response = new WorkboardResponse();
3293
			$response->warning_delay = $delay_warning/60/60/24;
3294
			$response->label = $label;
3295
			$response->url = DOL_URL_ROOT.'/comm/propal/list.php?viewstatut='.$statut.'&mainmenu=commercial&leftmenu=propals';
3296
			$response->url_late = DOL_URL_ROOT.'/comm/propal/list.php?viewstatut='.$statut.'&mainmenu=commercial&leftmenu=propals&sortfield=p.datep&sortorder=asc';
3297
			$response->img = img_object('', "propal");
3298
3299
			// This assignment in condition is not a bug. It allows walking the results.
3300
			while ($obj=$this->db->fetch_object($resql))
3301
			{
3302
				$response->nbtodo++;
3303
				$response->total+=$obj->total_ht;
3304
3305
				if ($mode == 'opened')
3306
				{
3307
					$datelimit = $this->db->jdate($obj->datefin);
3308
					if ($datelimit < ($now - $delay_warning))
3309
					{
3310
						$response->nbtodolate++;
3311
					}
3312
				}
3313
				// TODO Definir regle des propales a facturer en retard
3314
				// if ($mode == 'signed' && ! count($this->FactureListeArray($obj->rowid))) $this->nbtodolate++;
3315
			}
3316
3317
			return $response;
3318
		}
3319
		else
3320
		{
3321
			$this->error=$this->db->error();
3322
			return -1;
3323
		}
3324
	}
3325
3326
3327
	/**
3328
	 *  Initialise an instance with random values.
3329
	 *  Used to build previews or test instances.
3330
	 *	id must be 0 if object instance is a specimen.
3331
	 *
3332
	 *  @return	void
3333
	 */
3334
    public function initAsSpecimen()
3335
	{
3336
		global $langs;
3337
3338
		// Load array of products prodids
3339
		$num_prods = 0;
3340
		$prodids = array();
3341
		$sql = "SELECT rowid";
3342
		$sql.= " FROM ".MAIN_DB_PREFIX."product";
3343
		$sql.= " WHERE entity IN (".getEntity('product').")";
3344
		$resql = $this->db->query($sql);
3345
		if ($resql)
3346
		{
3347
			$num_prods = $this->db->num_rows($resql);
3348
			$i = 0;
3349
			while ($i < $num_prods)
3350
			{
3351
				$i++;
3352
				$row = $this->db->fetch_row($resql);
3353
				$prodids[$i] = $row[0];
3354
			}
3355
		}
3356
3357
		// Initialise parametres
3358
		$this->id=0;
3359
		$this->ref = 'SPECIMEN';
3360
		$this->ref_client='NEMICEPS';
3361
		$this->specimen=1;
3362
		$this->socid = 1;
3363
		$this->date = time();
3364
		$this->fin_validite = $this->date+3600*24*30;
3365
		$this->cond_reglement_id   = 1;
3366
		$this->cond_reglement_code = 'RECEP';
3367
		$this->mode_reglement_id   = 7;
3368
		$this->mode_reglement_code = 'CHQ';
3369
		$this->availability_id     = 1;
3370
		$this->availability_code   = 'AV_NOW';
3371
		$this->demand_reason_id    = 1;
3372
		$this->demand_reason_code  = 'SRC_00';
3373
		$this->note_public='This is a comment (public)';
3374
		$this->note_private='This is a comment (private)';
3375
		// Lines
3376
		$nbp = 5;
3377
		$xnbp = 0;
3378
		while ($xnbp < $nbp)
3379
		{
3380
			$line=new PropaleLigne($this->db);
3381
			$line->desc=$langs->trans("Description")." ".$xnbp;
3382
			$line->qty=1;
3383
			$line->subprice=100;
3384
			$line->price=100;
1 ignored issue
show
Deprecated Code introduced by
The property PropaleLigne::$price has been deprecated. ( Ignorable by Annotation )

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

3384
			/** @scrutinizer ignore-deprecated */ $line->price=100;
Loading history...
3385
			$line->tva_tx=20;
3386
			$line->localtax1_tx=0;
3387
			$line->localtax2_tx=0;
3388
			if ($xnbp == 2)
3389
			{
3390
				$line->total_ht=50;
3391
				$line->total_ttc=60;
3392
				$line->total_tva=10;
3393
				$line->remise_percent=50;
3394
			}
3395
			else
3396
			{
3397
				$line->total_ht=100;
3398
				$line->total_ttc=120;
3399
				$line->total_tva=20;
3400
				$line->remise_percent=00;
3401
			}
3402
3403
			if ($num_prods > 0)
3404
			{
3405
				$prodid = mt_rand(1, $num_prods);
3406
				$line->fk_product=$prodids[$prodid];
3407
		$line->product_ref='SPECIMEN';
3408
			}
3409
3410
			$this->lines[$xnbp]=$line;
3411
3412
			$this->total_ht       += $line->total_ht;
3413
			$this->total_tva      += $line->total_tva;
3414
			$this->total_ttc      += $line->total_ttc;
3415
3416
			$xnbp++;
3417
		}
3418
	}
3419
3420
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3421
	/**
3422
	 *      Charge indicateurs this->nb de tableau de bord
3423
	 *
3424
	 *      @return     int         <0 if ko, >0 if ok
3425
	 */
3426
    public function load_state_board()
3427
	{
3428
        // phpcs:enable
3429
		global $user;
3430
3431
		$this->nb=array();
3432
		$clause = "WHERE";
3433
3434
		$sql = "SELECT count(p.rowid) as nb";
3435
		$sql.= " FROM ".MAIN_DB_PREFIX."propal as p";
3436
		$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid";
3437
		if (!$user->rights->societe->client->voir && !$user->societe_id)
3438
		{
3439
			$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
3440
			$sql.= " WHERE sc.fk_user = " .$user->id;
3441
			$clause = "AND";
3442
		}
3443
		$sql.= " ".$clause." p.entity IN (".getEntity('propal').")";
3444
3445
		$resql=$this->db->query($sql);
3446
		if ($resql)
3447
		{
3448
			// This assignment in condition is not a bug. It allows walking the results.
3449
			while ($obj=$this->db->fetch_object($resql))
3450
			{
3451
				$this->nb["proposals"]=$obj->nb;
3452
			}
3453
			$this->db->free($resql);
3454
			return 1;
3455
		}
3456
		else
3457
		{
3458
			dol_print_error($this->db);
3459
			$this->error=$this->db->error();
3460
			return -1;
3461
		}
3462
	}
3463
3464
3465
	/**
3466
	 *  Returns the reference to the following non used Proposal used depending on the active numbering module
3467
	 *  defined into PROPALE_ADDON
3468
	 *
3469
	 *  @param	Societe		$soc  	Object thirdparty
3470
	 *  @return string      		Reference libre pour la propale
3471
	 */
3472
    public function getNextNumRef($soc)
3473
	{
3474
		global $conf,$langs;
3475
		$langs->load("propal");
3476
3477
		$classname = $conf->global->PROPALE_ADDON;
3478
3479
		if (! empty($classname))
3480
		{
3481
			$mybool=false;
3482
3483
			$file = $classname.".php";
3484
3485
			// Include file with class
3486
			$dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
3487
			foreach ($dirmodels as $reldir) {
3488
3489
				$dir = dol_buildpath($reldir."core/modules/propale/");
3490
3491
				// Load file with numbering class (if found)
3492
				$mybool|=@include_once $dir.$file;
3493
			}
3494
3495
			if (! $mybool)
1 ignored issue
show
Bug Best Practice introduced by
The expression $mybool of type false|integer is loosely compared to false; this is ambiguous if the integer can be 0. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
3496
			{
3497
				dol_print_error('', "Failed to include file ".$file);
3498
				return '';
3499
			}
3500
3501
			$obj = new $classname();
3502
			$numref = "";
3503
			$numref = $obj->getNextValue($soc, $this);
3504
3505
			if ($numref != "")
3506
			{
3507
				return $numref;
3508
			}
3509
			else
3510
			{
3511
				$this->error=$obj->error;
3512
				//dol_print_error($db,"Propale::getNextNumRef ".$obj->error);
3513
				return "";
3514
			}
3515
		}
3516
		else
3517
		{
3518
			$langs->load("errors");
3519
			print $langs->trans("Error")." ".$langs->trans("ErrorModuleSetupNotComplete");
3520
			return "";
3521
		}
3522
	}
3523
3524
	/**
3525
	 *	Return clicable link of object (with eventually picto)
3526
	 *
3527
	 *	@param      int		$withpicto		          Add picto into link
3528
	 *	@param      string	$option			          Where point the link ('expedition', 'document', ...)
3529
	 *	@param      string	$get_params    	          Parametres added to url
3530
	 *  @param	    int   	$notooltip		          1=Disable tooltip
3531
	 *  @param      int     $save_lastsearch_value    -1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking
3532
	 *	@return     string          		          String with URL
3533
	 */
3534
    public function getNomUrl($withpicto = 0, $option = '', $get_params = '', $notooltip = 0, $save_lastsearch_value = -1)
3535
	{
3536
		global $langs, $conf, $user;
3537
3538
		if (! empty($conf->dol_no_mouse_hover)) $notooltip=1;   // Force disable tooltips
3539
3540
		$result='';
3541
		$label='';
3542
		$url='';
3543
3544
		if ($user->rights->propal->lire)
3545
		{
3546
			$label = '<u>' . $langs->trans("ShowPropal") . '</u>';
3547
			if (! empty($this->ref))
3548
				$label.= '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
3549
			if (! empty($this->ref_client))
3550
				$label.= '<br><b>'.$langs->trans('RefCustomer').':</b> '.$this->ref_client;
3551
			if (! empty($this->total_ht))
3552
				$label.= '<br><b>' . $langs->trans('AmountHT') . ':</b> ' . price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
3553
			if (! empty($this->total_tva))
3554
				$label.= '<br><b>' . $langs->trans('VAT') . ':</b> ' . price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
3555
			if (! empty($this->total_ttc))
3556
				$label.= '<br><b>' . $langs->trans('AmountTTC') . ':</b> ' . price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
3557
			if ($option == '') {
3558
				$url = DOL_URL_ROOT.'/comm/propal/card.php?id='.$this->id. $get_params;
3559
			}
3560
			elseif ($option == 'compta') {  // deprecated
3561
				$url = DOL_URL_ROOT.'/comm/propal/card.php?id='.$this->id. $get_params;
3562
			}
3563
			elseif ($option == 'expedition') {
3564
				$url = DOL_URL_ROOT.'/expedition/propal.php?id='.$this->id. $get_params;
3565
			}
3566
			elseif ($option == 'document') {
3567
				$url = DOL_URL_ROOT.'/comm/propal/document.php?id='.$this->id. $get_params;
3568
			}
3569
3570
			if ($option != 'nolink')
3571
			{
3572
				// Add param to save lastsearch_values or not
3573
				$add_save_lastsearch_values=($save_lastsearch_value == 1 ? 1 : 0);
3574
				if ($save_lastsearch_value == -1 && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) $add_save_lastsearch_values=1;
3575
				if ($add_save_lastsearch_values) $url.='&save_lastsearch_values=1';
3576
			}
3577
		}
3578
3579
		$linkclose='';
3580
		if (empty($notooltip) && $user->rights->propal->lire)
3581
		{
3582
			if (! empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER))
3583
			{
3584
				$label=$langs->trans("ShowPropal");
3585
				$linkclose.=' alt="'.dol_escape_htmltag($label, 1).'"';
3586
			}
3587
			$linkclose.= ' title="'.dol_escape_htmltag($label, 1).'"';
3588
			$linkclose.=' class="classfortooltip"';
3589
		}
3590
3591
		$linkstart = '<a href="'.$url.'"';
3592
		$linkstart.=$linkclose.'>';
3593
		$linkend='</a>';
3594
3595
		$result .= $linkstart;
3596
		if ($withpicto) $result.=img_object(($notooltip?'':$label), $this->picto, ($notooltip?(($withpicto != 2) ? 'class="paddingright"' : ''):'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip?0:1);
3597
		if ($withpicto != 2) $result.= $this->ref;
3598
		$result .= $linkend;
3599
3600
		return $result;
3601
	}
3602
3603
	/**
3604
	 * 	Retrieve an array of proposal lines
3605
	 *
3606
	 * 	@return int		>0 if OK, <0 if KO
3607
	 */
3608
    public function getLinesArray()
3609
	{
3610
		return $this->fetch_lines();
3611
	}
3612
3613
	/**
3614
	 *  Create a document onto disk according to template module.
3615
	 *
3616
	 * 	@param	    string		$modele			Force model to use ('' to not force)
3617
	 * 	@param		Translate	$outputlangs	Object langs to use for output
3618
	 *  @param      int			$hidedetails    Hide details of lines
3619
	 *  @param      int			$hidedesc       Hide description
3620
	 *  @param      int			$hideref        Hide ref
3621
         *  @param   null|array  $moreparams     Array to provide more information
3622
	 * 	@return     int         				0 if KO, 1 if OK
3623
	 */
3624
	public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
3625
	{
3626
		global $conf,$langs;
3627
3628
		$langs->load("propale");
3629
3630
		if (! dol_strlen($modele)) {
3631
3632
			$modele = 'azur';
3633
3634
			if ($this->modelpdf) {
3635
				$modele = $this->modelpdf;
3636
			} elseif (! empty($conf->global->PROPALE_ADDON_PDF)) {
3637
				$modele = $conf->global->PROPALE_ADDON_PDF;
3638
			}
3639
		}
3640
3641
		$modelpath = "core/modules/propale/doc/";
3642
3643
		return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
3644
	}
3645
3646
	/**
3647
	 * Function used to replace a thirdparty id with another one.
3648
	 *
3649
	 * @param DoliDB $db Database handler
3650
	 * @param int $origin_id Old thirdparty id
3651
	 * @param int $dest_id New thirdparty id
3652
	 * @return bool
3653
	 */
3654
	public static function replaceThirdparty(DoliDB $db, $origin_id, $dest_id)
3655
	{
3656
		$tables = array(
3657
			'propal'
3658
		);
3659
3660
		return CommonObject::commonReplaceThirdparty($db, $origin_id, $dest_id, $tables);
3661
	}
3662
}
3663
3664
3665
/**
3666
 *	Class to manage commercial proposal lines
3667
 */
3668
class PropaleLigne extends CommonObjectLine
3669
{
3670
	/**
3671
	 * @var string ID to identify managed object
3672
	 */
3673
	public $element='propaldet';
3674
3675
	/**
3676
	 * @var string Name of table without prefix where object is stored
3677
	 */
3678
	public $table_element='propaldet';
3679
3680
    public $oldline;
3681
3682
	// From llx_propaldet
3683
    public $fk_propal;
3684
    public $fk_parent_line;
3685
    public $desc;          	// Description ligne
3686
    public $fk_product;		// Id produit predefini
3687
	/**
3688
	 * @deprecated
3689
	 * @see $product_type
3690
	 */
3691
    public $fk_product_type;
3692
	/**
3693
	 * Product type.
3694
	 * @var int
3695
	 * @see Product::TYPE_PRODUCT, Product::TYPE_SERVICE
3696
	 */
3697
    public $product_type = Product::TYPE_PRODUCT;
3698
3699
    public $qty;
3700
    public $tva_tx;
3701
    public $subprice;
3702
    public $remise_percent;
3703
    public $fk_remise_except;
3704
3705
    public $rang = 0;
3706
3707
    public $fk_fournprice;
3708
    public $pa_ht;
3709
    public $marge_tx;
3710
    public $marque_tx;
3711
3712
    public $special_code;	// Tag for special lines (exlusive tags)
3713
	// 1: frais de port
3714
	// 2: ecotaxe
3715
	// 3: option line (when qty = 0)
3716
3717
    public $info_bits = 0;	// Some other info:
3718
	// Bit 0: 	0 si TVA normal - 1 si TVA NPR
3719
	// Bit 1:	0 ligne normale - 1 si ligne de remise fixe
3720
3721
    public $total_ht;			// Total HT  de la ligne toute quantite et incluant la remise ligne
3722
    public $total_tva;			// Total TVA  de la ligne toute quantite et incluant la remise ligne
3723
    public $total_ttc;			// Total TTC de la ligne toute quantite et incluant la remise ligne
3724
3725
	/**
3726
	 * @deprecated
3727
	 * @see $remise_percent, $fk_remise_except
3728
	 */
3729
    public $remise;
3730
	/**
3731
	 * @deprecated
3732
	 * @see $subprice
3733
	 */
3734
    public $price;
3735
3736
	// From llx_product
3737
	/**
3738
	 * @deprecated
3739
	 * @see $product_ref
3740
	 */
3741
    public $ref;
3742
	/**
3743
	 * Product reference
3744
	 * @var string
3745
	 */
3746
	public $product_ref;
3747
	/**
3748
	 * @deprecated
3749
	 * @see $product_label
3750
	 */
3751
    public $libelle;
3752
	/**
3753
	 *  Product label
3754
	 * @var string
3755
	 */
3756
	public $product_label;
3757
	/**
3758
	 * Product description
3759
	 * @var string
3760
	 */
3761
	public $product_desc;
3762
3763
    public $localtax1_tx;		// Local tax 1
3764
    public $localtax2_tx;		// Local tax 2
3765
    public $localtax1_type;	    // Local tax 1 type
3766
    public $localtax2_type;	    // Local tax 2 type
3767
    public $total_localtax1;  	// Line total local tax 1
3768
    public $total_localtax2;	// Line total local tax 2
3769
3770
    public $date_start;
3771
    public $date_end;
3772
3773
    public $skip_update_total; // Skip update price total for special lines
3774
3775
	// Multicurrency
3776
    public $fk_multicurrency;
3777
    public $multicurrency_code;
3778
    public $multicurrency_subprice;
3779
    public $multicurrency_total_ht;
3780
    public $multicurrency_total_tva;
3781
    public $multicurrency_total_ttc;
3782
3783
	/**
3784
	 * 	Class line Contructor
3785
	 *
3786
	 * 	@param	DoliDB	$db	Database handler
3787
	 */
3788
    public function __construct($db)
3789
	{
3790
		$this->db= $db;
3791
	}
3792
3793
	/**
3794
	 *	Retrieve the propal line object
3795
	 *
3796
	 *	@param	int		$rowid		Propal line id
3797
	 *	@return	int					<0 if KO, >0 if OK
3798
	 */
3799
    public function fetch($rowid)
3800
	{
3801
		$sql = 'SELECT pd.rowid, pd.fk_propal, pd.fk_parent_line, pd.fk_product, pd.label as custom_label, pd.description, pd.price, pd.qty, pd.vat_src_code, pd.tva_tx,';
3802
		$sql.= ' pd.remise, pd.remise_percent, pd.fk_remise_except, pd.subprice,';
3803
		$sql.= ' pd.info_bits, pd.total_ht, pd.total_tva, pd.total_ttc, pd.fk_product_fournisseur_price as fk_fournprice, pd.buy_price_ht as pa_ht, pd.special_code, pd.rang,';
3804
		$sql.= ' pd.fk_unit,';
3805
		$sql.= ' pd.localtax1_tx, pd.localtax2_tx, pd.total_localtax1, pd.total_localtax2,';
3806
		$sql.= ' pd.fk_multicurrency, pd.multicurrency_code, pd.multicurrency_subprice, pd.multicurrency_total_ht, pd.multicurrency_total_tva, pd.multicurrency_total_ttc,';
3807
		$sql.= ' p.ref as product_ref, p.label as product_label, p.description as product_desc,';
3808
		$sql.= ' pd.date_start, pd.date_end, pd.product_type';
3809
		$sql.= ' FROM '.MAIN_DB_PREFIX.'propaldet as pd';
3810
		$sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON pd.fk_product = p.rowid';
3811
		$sql.= ' WHERE pd.rowid = '.$rowid;
3812
3813
		$result = $this->db->query($sql);
3814
		if ($result)
3815
		{
3816
			$objp = $this->db->fetch_object($result);
3817
3818
			if ($objp)
3819
			{
3820
				$this->id               = $objp->rowid;
3821
				$this->rowid			= $objp->rowid;     // deprecated
3822
				$this->fk_propal		= $objp->fk_propal;
3823
				$this->fk_parent_line	= $objp->fk_parent_line;
3824
				$this->label			= $objp->custom_label;
3825
				$this->desc				= $objp->description;
3826
				$this->qty				= $objp->qty;
3827
				$this->price			= $objp->price;		// deprecated
3828
				$this->subprice			= $objp->subprice;
3829
				$this->vat_src_code		= $objp->vat_src_code;
3830
				$this->tva_tx			= $objp->tva_tx;
3831
				$this->remise			= $objp->remise;    // deprecated
3832
				$this->remise_percent	= $objp->remise_percent;
3833
				$this->fk_remise_except = $objp->fk_remise_except;
3834
				$this->fk_product		= $objp->fk_product;
3835
				$this->info_bits		= $objp->info_bits;
3836
3837
				$this->total_ht			= $objp->total_ht;
3838
				$this->total_tva		= $objp->total_tva;
3839
				$this->total_ttc		= $objp->total_ttc;
3840
3841
				$this->fk_fournprice	= $objp->fk_fournprice;
3842
3843
				$marginInfos			= getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $this->fk_fournprice, $objp->pa_ht);
3844
				$this->pa_ht			= $marginInfos[0];
3845
				$this->marge_tx			= $marginInfos[1];
3846
				$this->marque_tx		= $marginInfos[2];
3847
3848
				$this->special_code		= $objp->special_code;
3849
				$this->product_type		= $objp->product_type;
3850
				$this->rang				= $objp->rang;
3851
3852
				$this->ref				= $objp->product_ref;      // deprecated
3853
				$this->product_ref		= $objp->product_ref;
3854
				$this->libelle			= $objp->product_label;  // deprecated
3855
				$this->product_label	= $objp->product_label;
3856
				$this->product_desc		= $objp->product_desc;
3857
				$this->fk_unit          = $objp->fk_unit;
3858
3859
				$this->date_start       = $this->db->jdate($objp->date_start);
3860
				$this->date_end         = $this->db->jdate($objp->date_end);
3861
3862
				// Multicurrency
3863
				$this->fk_multicurrency 		= $objp->fk_multicurrency;
3864
				$this->multicurrency_code 		= $objp->multicurrency_code;
3865
				$this->multicurrency_subprice 	= $objp->multicurrency_subprice;
3866
				$this->multicurrency_total_ht 	= $objp->multicurrency_total_ht;
3867
				$this->multicurrency_total_tva 	= $objp->multicurrency_total_tva;
3868
				$this->multicurrency_total_ttc 	= $objp->multicurrency_total_ttc;
3869
3870
				$this->fetch_optionals();
3871
3872
				$this->db->free($result);
3873
3874
				return 1;
3875
			}
3876
			else
3877
			{
3878
				return 0;
3879
			}
3880
		}
3881
		else
3882
		{
3883
			return -1;
3884
		}
3885
	}
3886
3887
	/**
3888
	 *  Insert object line propal in database
3889
	 *
3890
	 *	@param		int		$notrigger		1=Does not execute triggers, 0= execute triggers
3891
	 *	@return		int						<0 if KO, >0 if OK
3892
	 */
3893
    public function insert($notrigger = 0)
3894
	{
3895
		global $conf,$user;
3896
3897
		$error=0;
3898
3899
		dol_syslog(get_class($this)."::insert rang=".$this->rang);
3900
3901
		$pa_ht_isemptystring = (empty($this->pa_ht) && $this->pa_ht == ''); // If true, we can use a default value. If this->pa_ht = '0', we must use '0'.
3902
3903
		// Clean parameters
3904
		if (empty($this->tva_tx)) $this->tva_tx=0;
3905
		if (empty($this->localtax1_tx)) $this->localtax1_tx=0;
3906
		if (empty($this->localtax2_tx)) $this->localtax2_tx=0;
3907
		if (empty($this->localtax1_type)) $this->localtax1_type=0;
3908
		if (empty($this->localtax2_type)) $this->localtax2_type=0;
3909
		if (empty($this->total_localtax1)) $this->total_localtax1=0;
3910
		if (empty($this->total_localtax2)) $this->total_localtax2=0;
3911
		if (empty($this->rang)) $this->rang=0;
3912
		if (empty($this->remise)) $this->remise=0;
1 ignored issue
show
Deprecated Code introduced by
The property PropaleLigne::$remise has been deprecated. ( Ignorable by Annotation )

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

3912
		if (empty(/** @scrutinizer ignore-deprecated */ $this->remise)) $this->remise=0;
Loading history...
3913
		if (empty($this->remise_percent) || ! is_numeric($this->remise_percent)) $this->remise_percent=0;
3914
		if (empty($this->info_bits)) $this->info_bits=0;
3915
		if (empty($this->special_code)) $this->special_code=0;
3916
		if (empty($this->fk_parent_line)) $this->fk_parent_line=0;
3917
		if (empty($this->fk_fournprice)) $this->fk_fournprice=0;
3918
		if (! is_numeric($this->qty)) $this->qty = 0;
3919
		if (empty($this->pa_ht)) $this->pa_ht=0;
3920
		if (empty($this->multicurrency_subprice))  $this->multicurrency_subprice=0;
3921
		if (empty($this->multicurrency_total_ht))  $this->multicurrency_total_ht=0;
3922
		if (empty($this->multicurrency_total_tva)) $this->multicurrency_total_tva=0;
3923
		if (empty($this->multicurrency_total_ttc)) $this->multicurrency_total_ttc=0;
3924
3925
	   // if buy price not defined, define buyprice as configured in margin admin
3926
		if ($this->pa_ht == 0 && $pa_ht_isemptystring)
3927
		{
3928
			if (($result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product)) < 0)
3929
			{
3930
				return $result;
3931
			}
3932
			else
3933
			{
3934
				$this->pa_ht = $result;
3935
			}
3936
		}
3937
3938
		// Check parameters
3939
		if ($this->product_type < 0) return -1;
3940
3941
		$this->db->begin();
3942
3943
		// Insert line into database
3944
		$sql = 'INSERT INTO '.MAIN_DB_PREFIX.'propaldet';
3945
		$sql.= ' (fk_propal, fk_parent_line, label, description, fk_product, product_type,';
3946
		$sql.= ' fk_remise_except, qty, vat_src_code, tva_tx, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type,';
3947
		$sql.= ' subprice, remise_percent, ';
3948
		$sql.= ' info_bits, ';
3949
		$sql.= ' total_ht, total_tva, total_localtax1, total_localtax2, total_ttc, fk_product_fournisseur_price, buy_price_ht, special_code, rang,';
3950
		$sql.= ' fk_unit,';
3951
		$sql.= ' date_start, date_end';
3952
		$sql.= ', fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc)';
3953
		$sql.= " VALUES (".$this->fk_propal.",";
3954
		$sql.= " ".($this->fk_parent_line>0?"'".$this->db->escape($this->fk_parent_line)."'":"null").",";
3955
		$sql.= " ".(! empty($this->label)?"'".$this->db->escape($this->label)."'":"null").",";
3956
		$sql.= " '".$this->db->escape($this->desc)."',";
3957
		$sql.= " ".($this->fk_product?"'".$this->db->escape($this->fk_product)."'":"null").",";
3958
		$sql.= " '".$this->db->escape($this->product_type)."',";
3959
		$sql.= " ".($this->fk_remise_except?"'".$this->db->escape($this->fk_remise_except)."'":"null").",";
3960
		$sql.= " ".price2num($this->qty).",";
3961
		$sql.= " ".(empty($this->vat_src_code)?"''":"'".$this->db->escape($this->vat_src_code)."'").",";
3962
		$sql.= " ".price2num($this->tva_tx).",";
3963
		$sql.= " ".price2num($this->localtax1_tx).",";
3964
		$sql.= " ".price2num($this->localtax2_tx).",";
3965
		$sql.= " '".$this->db->escape($this->localtax1_type)."',";
3966
		$sql.= " '".$this->db->escape($this->localtax2_type)."',";
3967
		$sql.= " ".(price2num($this->subprice)!==''?price2num($this->subprice):"null").",";
3968
		$sql.= " ".price2num($this->remise_percent).",";
3969
		$sql.= " ".(isset($this->info_bits)?"'".$this->db->escape($this->info_bits)."'":"null").",";
3970
		$sql.= " ".price2num($this->total_ht).",";
3971
		$sql.= " ".price2num($this->total_tva).",";
3972
		$sql.= " ".price2num($this->total_localtax1).",";
3973
		$sql.= " ".price2num($this->total_localtax2).",";
3974
		$sql.= " ".price2num($this->total_ttc).",";
3975
		$sql.= " ".(!empty($this->fk_fournprice)?"'".$this->db->escape($this->fk_fournprice)."'":"null").",";
3976
		$sql.= " ".(isset($this->pa_ht)?"'".price2num($this->pa_ht)."'":"null").",";
3977
		$sql.= ' '.$this->special_code.',';
3978
		$sql.= ' '.$this->rang.',';
3979
		$sql.= ' '.(!$this->fk_unit ? 'NULL' : $this->fk_unit).',';
3980
		$sql.= " ".(! empty($this->date_start)?"'".$this->db->idate($this->date_start)."'":"null").',';
3981
		$sql.= " ".(! empty($this->date_end)?"'".$this->db->idate($this->date_end)."'":"null");
3982
		$sql.= ", ".($this->fk_multicurrency > 0?$this->fk_multicurrency:'null');
3983
		$sql.= ", '".$this->db->escape($this->multicurrency_code)."'";
3984
		$sql.= ", ".$this->multicurrency_subprice;
3985
		$sql.= ", ".$this->multicurrency_total_ht;
3986
		$sql.= ", ".$this->multicurrency_total_tva;
3987
		$sql.= ", ".$this->multicurrency_total_ttc;
3988
		$sql.= ')';
3989
3990
		dol_syslog(get_class($this).'::insert', LOG_DEBUG);
3991
		$resql=$this->db->query($sql);
3992
		if ($resql)
3993
		{
3994
			$this->rowid=$this->db->last_insert_id(MAIN_DB_PREFIX.'propaldet');
3995
3996
			if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) // For avoid conflicts if trigger used
3997
			{
3998
				$this->id=$this->rowid;
3999
				$result=$this->insertExtraFields();
4000
				if ($result < 0)
4001
				{
4002
					$error++;
4003
				}
4004
			}
4005
4006
			if (! $error && ! $notrigger)
4007
			{
4008
				// Call trigger
4009
				$result=$this->call_trigger('LINEPROPAL_INSERT', $user);
4010
				if ($result < 0)
4011
				{
4012
					$this->db->rollback();
4013
					return -1;
4014
				}
4015
				// End call triggers
4016
			}
4017
4018
			$this->db->commit();
4019
			return 1;
4020
		}
4021
		else
4022
		{
4023
			$this->error=$this->db->error()." sql=".$sql;
4024
			$this->db->rollback();
4025
			return -1;
4026
		}
4027
	}
4028
4029
	/**
4030
	 * 	Delete line in database
4031
	 *
4032
	 *  @param	User	$user		Object user
4033
	 *	@param 	int		$notrigger	1=Does not execute triggers, 0= execute triggers
4034
	 *	@return	 int  				<0 if ko, >0 if ok
4035
	 */
4036
    public function delete(User $user, $notrigger = 0)
4037
	{
4038
		global $conf;
4039
4040
		$error=0;
4041
		$this->db->begin();
4042
4043
		$sql = "DELETE FROM ".MAIN_DB_PREFIX."propaldet WHERE rowid = ".$this->rowid;
1 ignored issue
show
Deprecated Code introduced by
The property CommonObjectLine::$rowid has been deprecated: Try to use id property as possible (even if field into database is still rowid) ( Ignorable by Annotation )

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

4043
		$sql = "DELETE FROM ".MAIN_DB_PREFIX."propaldet WHERE rowid = "./** @scrutinizer ignore-deprecated */ $this->rowid;

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

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

Loading history...
4044
		dol_syslog("PropaleLigne::delete", LOG_DEBUG);
4045
		if ($this->db->query($sql) )
4046
		{
4047
4048
			// Remove extrafields
4049
			if ((! $error) && (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED))) // For avoid conflicts if trigger used
4050
			{
4051
				$this->id=$this->rowid;
1 ignored issue
show
Deprecated Code introduced by
The property CommonObjectLine::$rowid has been deprecated: Try to use id property as possible (even if field into database is still rowid) ( Ignorable by Annotation )

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

4051
				$this->id=/** @scrutinizer ignore-deprecated */ $this->rowid;

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

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

Loading history...
4052
				$result=$this->deleteExtraFields();
4053
				if ($result < 0)
4054
				{
4055
					$error++;
4056
					dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
4057
				}
4058
			}
4059
4060
			if (! $error && ! $notrigger)
4061
			{
4062
				// Call trigger
4063
				$result=$this->call_trigger('LINEPROPAL_DELETE', $user);
4064
				if ($result < 0)
4065
				{
4066
					$this->db->rollback();
4067
					return -1;
4068
				}
4069
			}
4070
			// End call triggers
4071
4072
			$this->db->commit();
4073
4074
			return 1;
4075
		}
4076
		else
4077
		{
4078
			$this->error=$this->db->error()." sql=".$sql;
4079
			$this->db->rollback();
4080
			return -1;
4081
		}
4082
	}
4083
4084
	/**
4085
	 *	Update propal line object into DB
4086
	 *
4087
	 *	@param 	int		$notrigger	1=Does not execute triggers, 0= execute triggers
4088
	 *	@return	int					<0 if ko, >0 if ok
4089
	 */
4090
    public function update($notrigger = 0)
4091
	{
4092
		global $conf,$user;
4093
4094
		$error=0;
4095
4096
		$pa_ht_isemptystring = (empty($this->pa_ht) && $this->pa_ht == ''); // If true, we can use a default value. If this->pa_ht = '0', we must use '0'.
4097
4098
		// Clean parameters
4099
		if (empty($this->tva_tx)) $this->tva_tx=0;
4100
		if (empty($this->localtax1_tx)) $this->localtax1_tx=0;
4101
		if (empty($this->localtax2_tx)) $this->localtax2_tx=0;
4102
		if (empty($this->total_localtax1)) $this->total_localtax1=0;
4103
		if (empty($this->total_localtax2)) $this->total_localtax2=0;
4104
		if (empty($this->localtax1_type)) $this->localtax1_type=0;
4105
		if (empty($this->localtax2_type)) $this->localtax2_type=0;
4106
		if (empty($this->marque_tx)) $this->marque_tx=0;
4107
		if (empty($this->marge_tx)) $this->marge_tx=0;
4108
		if (empty($this->price)) $this->price=0;	// TODO A virer
1 ignored issue
show
Deprecated Code introduced by
The property PropaleLigne::$price has been deprecated. ( Ignorable by Annotation )

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

4108
		if (empty($this->price)) /** @scrutinizer ignore-deprecated */ $this->price=0;	// TODO A virer
Loading history...
4109
		if (empty($this->remise)) $this->remise=0;	// TODO A virer
1 ignored issue
show
Deprecated Code introduced by
The property PropaleLigne::$remise has been deprecated. ( Ignorable by Annotation )

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

4109
		if (empty(/** @scrutinizer ignore-deprecated */ $this->remise)) $this->remise=0;	// TODO A virer
Loading history...
4110
		if (empty($this->remise_percent)) $this->remise_percent=0;
4111
		if (empty($this->info_bits)) $this->info_bits=0;
4112
		if (empty($this->special_code)) $this->special_code=0;
4113
		if (empty($this->fk_parent_line)) $this->fk_parent_line=0;
4114
		if (empty($this->fk_fournprice)) $this->fk_fournprice=0;
4115
		if (empty($this->subprice)) $this->subprice=0;
4116
		if (empty($this->pa_ht)) $this->pa_ht=0;
4117
4118
		// if buy price not defined, define buyprice as configured in margin admin
4119
		if ($this->pa_ht == 0 && $pa_ht_isemptystring)
4120
		{
4121
			if (($result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product)) < 0)
4122
			{
4123
				return $result;
4124
			}
4125
			else
4126
			{
4127
				$this->pa_ht = $result;
4128
			}
4129
		}
4130
4131
		$this->db->begin();
4132
4133
		// Mise a jour ligne en base
4134
		$sql = "UPDATE ".MAIN_DB_PREFIX."propaldet SET";
4135
		$sql.= " description='".$this->db->escape($this->desc)."'";
4136
		$sql.= ", label=".(! empty($this->label)?"'".$this->db->escape($this->label)."'":"null");
4137
		$sql.= ", product_type=".$this->product_type;
4138
		$sql.= ", vat_src_code = '".(empty($this->vat_src_code)?'':$this->vat_src_code)."'";
4139
		$sql.= ", tva_tx='".price2num($this->tva_tx)."'";
4140
		$sql.= ", localtax1_tx=".price2num($this->localtax1_tx);
4141
		$sql.= ", localtax2_tx=".price2num($this->localtax2_tx);
4142
		$sql.= ", localtax1_type='".$this->db->escape($this->localtax1_type)."'";
4143
		$sql.= ", localtax2_type='".$this->db->escape($this->localtax2_type)."'";
4144
		$sql.= ", qty='".price2num($this->qty)."'";
4145
		$sql.= ", subprice=".price2num($this->subprice)."";
4146
		$sql.= ", remise_percent=".price2num($this->remise_percent)."";
4147
		$sql.= ", price=".price2num($this->price)."";					// TODO A virer
1 ignored issue
show
Deprecated Code introduced by
The property PropaleLigne::$price has been deprecated. ( Ignorable by Annotation )

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

4147
		$sql.= ", price=".price2num(/** @scrutinizer ignore-deprecated */ $this->price)."";					// TODO A virer
Loading history...
4148
		$sql.= ", remise=".price2num($this->remise)."";				// TODO A virer
1 ignored issue
show
Deprecated Code introduced by
The property PropaleLigne::$remise has been deprecated. ( Ignorable by Annotation )

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

4148
		$sql.= ", remise=".price2num(/** @scrutinizer ignore-deprecated */ $this->remise)."";				// TODO A virer
Loading history...
4149
		$sql.= ", info_bits='".$this->db->escape($this->info_bits)."'";
4150
		if (empty($this->skip_update_total))
4151
		{
4152
			$sql.= ", total_ht=".price2num($this->total_ht)."";
4153
			$sql.= ", total_tva=".price2num($this->total_tva)."";
4154
			$sql.= ", total_ttc=".price2num($this->total_ttc)."";
4155
			$sql.= ", total_localtax1=".price2num($this->total_localtax1)."";
4156
			$sql.= ", total_localtax2=".price2num($this->total_localtax2)."";
4157
		}
4158
		$sql.= ", fk_product_fournisseur_price=".(! empty($this->fk_fournprice)?"'".$this->db->escape($this->fk_fournprice)."'":"null");
4159
		$sql.= ", buy_price_ht=".price2num($this->pa_ht);
4160
		if (strlen($this->special_code)) $sql.= ", special_code=".$this->special_code;
4161
		$sql.= ", fk_parent_line=".($this->fk_parent_line>0?$this->fk_parent_line:"null");
4162
		if (! empty($this->rang)) $sql.= ", rang=".$this->rang;
4163
		$sql.= ", date_start=".(! empty($this->date_start)?"'".$this->db->idate($this->date_start)."'":"null");
4164
		$sql.= ", date_end=".(! empty($this->date_end)?"'".$this->db->idate($this->date_end)."'":"null");
4165
		$sql.= ", fk_unit=".(!$this->fk_unit ? 'NULL' : $this->fk_unit);
4166
4167
		// Multicurrency
4168
		$sql.= ", multicurrency_subprice=".price2num($this->multicurrency_subprice)."";
4169
		$sql.= ", multicurrency_total_ht=".price2num($this->multicurrency_total_ht)."";
4170
		$sql.= ", multicurrency_total_tva=".price2num($this->multicurrency_total_tva)."";
4171
		$sql.= ", multicurrency_total_ttc=".price2num($this->multicurrency_total_ttc)."";
4172
4173
		$sql.= " WHERE rowid = ".$this->rowid;
1 ignored issue
show
Deprecated Code introduced by
The property CommonObjectLine::$rowid has been deprecated: Try to use id property as possible (even if field into database is still rowid) ( Ignorable by Annotation )

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

4173
		$sql.= " WHERE rowid = "./** @scrutinizer ignore-deprecated */ $this->rowid;

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

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

Loading history...
4174
4175
		dol_syslog(get_class($this)."::update", LOG_DEBUG);
4176
		$resql=$this->db->query($sql);
4177
		if ($resql)
4178
		{
4179
			if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) // For avoid conflicts if trigger used
4180
			{
4181
				$this->id=$this->rowid;
4182
				$result=$this->insertExtraFields();
4183
				if ($result < 0)
4184
				{
4185
					$error++;
4186
				}
4187
			}
4188
4189
			if (! $error && ! $notrigger)
4190
			{
4191
				// Call trigger
4192
				$result=$this->call_trigger('LINEPROPAL_UPDATE', $user);
4193
				if ($result < 0)
4194
				{
4195
					$this->db->rollback();
4196
					return -1;
4197
				}
4198
				// End call triggers
4199
			}
4200
4201
			$this->db->commit();
4202
			return 1;
4203
		}
4204
		else
4205
		{
4206
			$this->error=$this->db->error();
4207
			$this->db->rollback();
4208
			return -2;
4209
		}
4210
	}
4211
4212
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4213
	/**
4214
	 *	Update DB line fields total_xxx
4215
	 *	Used by migration
4216
	 *
4217
	 *	@return		int		<0 if KO, >0 if OK
4218
	 */
4219
    public function update_total()
4220
	{
4221
        // phpcs:enable
4222
		$this->db->begin();
4223
4224
		// Mise a jour ligne en base
4225
		$sql = "UPDATE ".MAIN_DB_PREFIX."propaldet SET";
4226
		$sql.= " total_ht=".price2num($this->total_ht, 'MT')."";
4227
		$sql.= ",total_tva=".price2num($this->total_tva, 'MT')."";
4228
		$sql.= ",total_ttc=".price2num($this->total_ttc, 'MT')."";
4229
		$sql.= " WHERE rowid = ".$this->rowid;
1 ignored issue
show
Deprecated Code introduced by
The property CommonObjectLine::$rowid has been deprecated: Try to use id property as possible (even if field into database is still rowid) ( Ignorable by Annotation )

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

4229
		$sql.= " WHERE rowid = "./** @scrutinizer ignore-deprecated */ $this->rowid;

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

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

Loading history...
4230
4231
		dol_syslog("PropaleLigne::update_total", LOG_DEBUG);
4232
4233
		$resql=$this->db->query($sql);
4234
		if ($resql)
4235
		{
4236
			$this->db->commit();
4237
			return 1;
4238
		}
4239
		else
4240
		{
4241
			$this->error=$this->db->error();
4242
			$this->db->rollback();
4243
			return -2;
4244
		}
4245
	}
4246
}
4247