Completed
Branch develop (edb367)
by
unknown
28:40
created

Propal::updateline()   F

Complexity

Conditions 16
Paths 392

Size

Total Lines 156

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 16
nc 392
nop 23
dl 0
loc 156
rs 1.8666
c 0
b 0
f 0

How to fix   Long Method    Complexity    Many Parameters   

Long Method

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

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

Commonly applied refactorings include:

Many Parameters

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

There are several approaches to avoid long parameter lists:

1
<?php
2
/* 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
 *
17
 * This program is free software; you can redistribute it and/or modify
18
 * it under the terms of the GNU General Public License as published by
19
 * the Free Software Foundation; either version 3 of the License, or
20
 * (at your option) any later version.
21
 *
22
 * This program is distributed in the hope that it will be useful,
23
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25
 * GNU General Public License for more details.
26
 *
27
 * You should have received a copy of the GNU General Public License
28
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
29
 */
30
31
/**
32
 *	\file       htdocs/comm/propal/class/propal.class.php
33
 *	\brief      File of class to manage proposals
34
 */
35
36
require_once DOL_DOCUMENT_ROOT .'/core/class/commonobject.class.php';
37
require_once DOL_DOCUMENT_ROOT ."/core/class/commonobjectline.class.php";
38
require_once DOL_DOCUMENT_ROOT .'/product/class/product.class.php';
39
require_once DOL_DOCUMENT_ROOT .'/contact/class/contact.class.php';
40
require_once DOL_DOCUMENT_ROOT .'/margin/lib/margins.lib.php';
41
require_once DOL_DOCUMENT_ROOT .'/multicurrency/class/multicurrency.class.php';
42
43
/**
44
 *	Class to manage proposals
45
 */
46
class Propal extends CommonObject
47
{
48
	/**
49
	 * @var string ID to identify managed object
50
	 */
51
	public $element='propal';
52
53
	/**
54
	 * @var string Name of table without prefix where object is stored
55
	 */
56
	public $table_element='propal';
57
58
	/**
59
	 * @var int    Name of subtable line
60
	 */
61
	public $table_element_line='propaldet';
62
63
	/**
64
	 * @var int Field with ID of parent key if this field has a parent
65
	 */
66
	public $fk_element ='fk_propal';
67
68
	/**
69
	 * @var string String with name of icon for myobject. Must be the part after the 'object_' into object_myobject.png
70
	 */
71
	public $picto='propal';
72
73
	/**
74
	 * 0=No test on entity, 1=Test with field entity, 2=Test with link by societe
75
	 * @var int
76
	 */
77
	public $ismultientitymanaged = 1;
78
79
	/**
80
	 * 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
81
	 * @var integer
82
	 */
83
	public $restrictiononfksoc = 1;
84
85
	/**
86
	 * {@inheritdoc}
87
	 */
88
	protected $table_ref_field = 'ref';
89
90
	/**
91
	 * ID of the client
92
	 * @var int
93
	 */
94
	public $socid;
95
96
	public $contactid;
97
	public $author;
98
	public $ref_client;
99
100
	/**
101
	 * Status of the quote
102
	 * @var int
103
	 * @see Propal::STATUS_DRAFT, Propal::STATUS_VALIDATED, Propal::STATUS_SIGNED, Propal::STATUS_NOTSIGNED, Propal::STATUS_BILLED
104
	 */
105
	public $statut;
106
107
	/**
108
	 * @deprecated
109
	 * @see date_creation
110
	 */
111
	public $datec;
112
113
	/**
114
	 * Creation date
115
	 * @var int
116
	 */
117
	public $date_creation;
118
119
	/**
120
	 * @deprecated
121
	 * @see date_validation
122
	 */
123
	public $datev;
124
125
	/**
126
	 * Validation date
127
	 * @var int
128
	 */
129
	public $date_validation;
130
131
	/**
132
	 * Date of the quote
133
	 * @var
134
	 */
135
	public $date;
136
137
	/**
138
	 * @deprecated
139
	 * @see date
140
	 */
141
	public $datep;
142
	public $date_livraison;
143
	public $fin_validite;
144
145
	public $user_author_id;
146
	public $user_valid_id;
147
	public $user_close_id;
148
149
	/**
150
	 * @deprecated
151
	 * @see total_ht
152
	 */
153
	public $price;
154
	/**
155
	 * @deprecated
156
	 * @see total_tva
157
	 */
158
	public $tva;
159
	/**
160
	 * @deprecated
161
	 * @see total_ttc
162
	 */
163
	public $total;
164
165
	public $cond_reglement_code;
166
	public $mode_reglement_code;
167
	public $remise = 0;
168
	public $remise_percent = 0;
169
	public $remise_absolue = 0;
170
	public $fk_address;
171
	public $address_type;
172
	public $address;
173
	public $availability_id;
174
	public $availability_code;
175
	public $demand_reason_id;
176
	public $demand_reason_code;
177
178
	public $products=array();
179
	public $extraparams=array();
180
181
	/**
182
	 * @var PropaleLigne[]
183
	 */
184
	public $lines = array();
185
	public $line;
186
187
	public $labelstatut=array();
188
	public $labelstatut_short=array();
189
190
	public $specimen;
191
192
	// Multicurrency
193
	public $fk_multicurrency;
194
	public $multicurrency_code;
195
	public $multicurrency_tx;
196
	public $multicurrency_total_ht;
197
	public $multicurrency_total_tva;
198
	public $multicurrency_total_ttc;
199
200
	public $oldcopy;
201
202
	/**
203
	 * Draft status
204
	 */
205
	const STATUS_DRAFT = 0;
206
	/**
207
	 * Validated status
208
	 */
209
	const STATUS_VALIDATED = 1;
210
	/**
211
	 * Signed quote
212
	 */
213
	const STATUS_SIGNED = 2;
214
	/**
215
	 * Not signed quote
216
	 */
217
	const STATUS_NOTSIGNED = 3;
218
	/**
219
	 * Billed or processed quote
220
	 */
221
	const STATUS_BILLED = 4;   // Todo rename into STATUS_CLOSE ?
222
223
224
	/**
225
	 *	Constructor
226
	 *
227
	 *	@param      DoliDB	$db         Database handler
228
	 *	@param      int		$socid		Id third party
229
	 *	@param      int		$propalid   Id proposal
230
	 */
231
	function __construct($db, $socid="", $propalid=0)
232
	{
233
		global $conf,$langs;
234
235
		$this->db = $db;
236
237
		$this->socid = $socid;
238
		$this->id = $propalid;
239
240
		$this->products = array();
241
242
		$this->duree_validite=$conf->global->PROPALE_VALIDITY_DURATION;
243
	}
244
245
246
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
247
	/**
248
	 *  Add line into array products
249
	 *  $this->thirdparty should be loaded
250
	 *
251
	 * 	@param  int		$idproduct       	Product Id to add
252
	 * 	@param  int		$qty             	Quantity
253
	 * 	@param  int		$remise_percent  	Discount effected on Product
254
	 *  @return	int							<0 if KO, >0 if OK
255
	 *
256
	 *	TODO	Replace calls to this function by generation objet Ligne
257
	 *			inserted into table $this->products
258
	 */
259
	function add_product($idproduct, $qty, $remise_percent=0)
260
	{
261
        // phpcs:enable
262
		global $conf, $mysoc;
263
264
		if (! $qty) $qty = 1;
265
266
		dol_syslog(get_class($this)."::add_product $idproduct, $qty, $remise_percent");
267
		if ($idproduct > 0)
268
		{
269
			$prod=new Product($this->db);
270
			$prod->fetch($idproduct);
271
272
			$productdesc = $prod->description;
273
274
			$tva_tx = get_default_tva($mysoc,$this->thirdparty,$prod->id);
275
			$tva_npr = get_default_npr($mysoc,$this->thirdparty,$prod->id);
276
			if (empty($tva_tx)) $tva_npr=0;
277
			$vat_src_code = '';     // May be defined into tva_tx
278
279
			$localtax1_tx = get_localtax($tva_tx,1,$mysoc,$this->thirdparty,$tva_npr);
280
			$localtax2_tx = get_localtax($tva_tx,2,$mysoc,$this->thirdparty,$tva_npr);
281
282
			// multiprices
283
			if($conf->global->PRODUIT_MULTIPRICES && $this->thirdparty->price_level)
284
			{
285
				$price = $prod->multiprices[$this->thirdparty->price_level];
286
			}
287
			else
288
			{
289
				$price = $prod->price;
290
			}
291
292
			$line = new PropaleLigne($this->db);
293
294
			$line->fk_product=$idproduct;
295
			$line->desc=$productdesc;
296
			$line->qty=$qty;
297
			$line->subprice=$price;
298
			$line->remise_percent=$remise_percent;
299
			$line->vat_src_code=$vat_src_code;
300
			$line->tva_tx=$tva_tx;
301
			$line->fk_unit=$prod->fk_unit;
302
			if ($tva_npr) $line->info_bits = 1;
303
304
			$this->lines[]=$line;
305
		}
306
	}
307
308
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
309
	/**
310
	 *	Adding line of fixed discount in the proposal in DB
311
	 *
312
	 *	@param     int		$idremise			Id of fixed discount
313
	 *  @return    int          				>0 if OK, <0 if KO
314
	 */
315
	function insert_discount($idremise)
316
	{
317
        // phpcs:enable
318
		global $langs;
319
320
		include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
321
		include_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
322
323
		$this->db->begin();
324
325
		$remise=new DiscountAbsolute($this->db);
326
		$result=$remise->fetch($idremise);
327
328
		if ($result > 0)
329
		{
330
			if ($remise->fk_facture)	// Protection against multiple submission
331
			{
332
				$this->error=$langs->trans("ErrorDiscountAlreadyUsed");
333
				$this->db->rollback();
334
				return -5;
335
			}
336
337
			$line=new PropaleLigne($this->db);
338
339
			$this->line->context = $this->context;
340
341
			$line->fk_propal=$this->id;
342
			$line->fk_remise_except=$remise->id;
343
			$line->desc=$remise->description;   	// Description ligne
344
			$line->vat_src_code=$remise->vat_src_code;
345
			$line->tva_tx=$remise->tva_tx;
346
			$line->subprice=-$remise->amount_ht;
347
			$line->fk_product=0;					// Id produit predefined
348
			$line->qty=1;
349
			$line->remise=0;
350
			$line->remise_percent=0;
351
			$line->rang=-1;
352
			$line->info_bits=2;
353
354
			// TODO deprecated
355
			$line->price=-$remise->amount_ht;
356
357
			$line->total_ht  = -$remise->amount_ht;
358
			$line->total_tva = -$remise->amount_tva;
359
			$line->total_ttc = -$remise->amount_ttc;
360
361
			$result=$line->insert();
362
			if ($result > 0)
363
			{
364
				$result=$this->update_price(1);
365
				if ($result > 0)
366
				{
367
					$this->db->commit();
368
					return 1;
369
				}
370
				else
371
				{
372
					$this->db->rollback();
373
					return -1;
374
				}
375
			}
376
			else
377
			{
378
				$this->error=$line->error;
379
				$this->db->rollback();
380
				return -2;
381
			}
382
		}
383
		else
384
		{
385
			$this->db->rollback();
386
			return -2;
387
		}
388
	}
389
390
    /**
391
     *    	Add a proposal line into database (linked to product/service or not)
392
     *      The parameters are already supposed to be appropriate and with final values to the call
393
     *      of this method. Also, for the VAT rate, it must have already been defined
394
     *      by whose calling the method get_default_tva (societe_vendeuse, societe_acheteuse, '' product)
395
     *      and desc must already have the right value (it's up to the caller to manage multilanguage)
396
     *
397
     * 		@param    	string		$desc				Description of line
398
     * 		@param    	float		$pu_ht				Unit price
399
     * 		@param    	float		$qty             	Quantity
400
     * 		@param    	float		$txtva           	Force Vat rate, -1 for auto (Can contain the vat_src_code too with syntax '9.9 (CODE)')
401
     * 		@param		float		$txlocaltax1		Local tax 1 rate (deprecated, use instead txtva with code inside)
402
     *  	@param		float		$txlocaltax2		Local tax 2 rate (deprecated, use instead txtva with code inside)
403
     *		@param    	int			$fk_product      	Id du produit/service predefini
404
     * 		@param    	float		$remise_percent  	Pourcentage de remise de la ligne
405
     * 		@param    	string		$price_base_type	HT or TTC
406
     * 		@param    	float		$pu_ttc             Prix unitaire TTC
407
     * 		@param    	int			$info_bits			Bits de type de lignes
408
     *      @param      int			$type               Type of line (0=product, 1=service). Not used if fk_product is defined, the type of product is used.
409
     *      @param      int			$rang               Position of line
410
     *      @param		int			$special_code		Special code (also used by externals modules!)
411
     *      @param		int			$fk_parent_line		Id of parent line
412
     *      @param		int			$fk_fournprice		Id supplier price
413
     *      @param		int			$pa_ht				Buying price without tax
414
     *      @param		string		$label				???
415
     *		@param      int			$date_start       	Start date of the line
416
     *		@param      int			$date_end         	End date of the line
417
     *      @param		array		$array_options		extrafields array
418
     * 		@param 		string		$fk_unit 			Code of the unit to use. Null to use the default one
419
     *      @param		string		$origin				'order', ...
420
     *      @param		int			$origin_id			Id of origin object
421
     * 		@param		double		$pu_ht_devise		Unit price in currency
422
     * 		@param		int    		$fk_remise_except	Id discount if line is from a discount
423
     *    	@return    	int         	    			>0 if OK, <0 if KO
424
     *    	@see       	add_product
425
     */
426
	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)
427
	{
428
		global $mysoc, $conf, $langs;
429
430
		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);
431
		include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
432
433
		// Clean parameters
434
		if (empty($remise_percent)) $remise_percent=0;
435
		if (empty($qty)) $qty=0;
436
		if (empty($info_bits)) $info_bits=0;
437
		if (empty($rang)) $rang=0;
438
		if (empty($fk_parent_line) || $fk_parent_line < 0) $fk_parent_line=0;
439
440
		$remise_percent=price2num($remise_percent);
441
		$qty=price2num($qty);
442
		$pu_ht=price2num($pu_ht);
443
		$pu_ht_devise=price2num($pu_ht_devise);
444
		$pu_ttc=price2num($pu_ttc);
445
		$txtva=price2num($txtva);               // $txtva can have format '5.0(XXX)' or '5'
446
		$txlocaltax1=price2num($txlocaltax1);
447
		$txlocaltax2=price2num($txlocaltax2);
448
		$pa_ht=price2num($pa_ht);
449
		if ($price_base_type=='HT')
450
		{
451
			$pu=$pu_ht;
452
		}
453
		else
454
		{
455
			$pu=$pu_ttc;
456
		}
457
458
		// Check parameters
459
		if ($type < 0) return -1;
460
461
		if ($this->statut == self::STATUS_DRAFT)
462
		{
463
			$this->db->begin();
464
465
			$product_type=$type;
466
			if (!empty($fk_product))
467
			{
468
				$product=new Product($this->db);
469
				$result=$product->fetch($fk_product);
470
				$product_type=$product->type;
471
472
				if (! empty($conf->global->STOCK_MUST_BE_ENOUGH_FOR_PROPOSAL) && $product_type == 0 && $product->stock_reel < $qty) {
473
					$langs->load("errors");
474
					$this->error=$langs->trans('ErrorStockIsNotEnoughToAddProductOnProposal', $product->ref);
475
					$this->db->rollback();
476
					return -3;
477
				}
478
			}
479
480
			// Calcul du total TTC et de la TVA pour la ligne a partir de
481
			// qty, pu, remise_percent et txtva
482
			// TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
483
			// la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
484
485
			$localtaxes_type=getLocalTaxesFromRate($txtva,0,$this->thirdparty,$mysoc);
486
487
			// Clean vat code
488
			$vat_src_code='';
489
			if (preg_match('/\((.*)\)/', $txtva, $reg))
490
			{
491
				$vat_src_code = $reg[1];
492
				$txtva = preg_replace('/\s*\(.*\)/', '', $txtva);    // Remove code into vatrate.
493
			}
494
495
			$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);
496
497
			$total_ht  = $tabprice[0];
498
			$total_tva = $tabprice[1];
499
			$total_ttc = $tabprice[2];
500
			$total_localtax1 = $tabprice[9];
501
			$total_localtax2 = $tabprice[10];
502
			$pu_ht  = $tabprice[3];
503
			$pu_tva = $tabprice[4];
504
			$pu_ttc = $tabprice[5];
505
506
			// MultiCurrency
507
			$multicurrency_total_ht  = $tabprice[16];
508
			$multicurrency_total_tva = $tabprice[17];
509
			$multicurrency_total_ttc = $tabprice[18];
510
			$pu_ht_devise = $tabprice[19];
511
512
			// Rang to use
513
			$rangtouse = $rang;
514
			if ($rangtouse == -1)
515
			{
516
				$rangmax = $this->line_max($fk_parent_line);
517
				$rangtouse = $rangmax + 1;
518
			}
519
520
			// TODO A virer
521
			// Anciens indicateurs: $price, $remise (a ne plus utiliser)
522
			$price = $pu;
523
			$remise = 0;
524
			if ($remise_percent > 0)
525
			{
526
				$remise = round(($pu * $remise_percent / 100), 2);
527
				$price = $pu - $remise;
528
			}
529
530
			// Insert line
531
			$this->line=new PropaleLigne($this->db);
532
533
			$this->line->context = $this->context;
534
535
			$this->line->fk_propal=$this->id;
536
			$this->line->label=$label;
537
			$this->line->desc=$desc;
538
			$this->line->qty=$qty;
539
540
			$this->line->vat_src_code=$vat_src_code;
541
			$this->line->tva_tx=$txtva;
542
			$this->line->localtax1_tx=($total_localtax1?$localtaxes_type[1]:0);
543
			$this->line->localtax2_tx=($total_localtax2?$localtaxes_type[3]:0);
544
			$this->line->localtax1_type = $localtaxes_type[0];
545
			$this->line->localtax2_type = $localtaxes_type[2];
546
			$this->line->fk_product=$fk_product;
547
			$this->line->product_type=$type;
548
			$this->line->fk_remise_except=$fk_remise_except;
549
			$this->line->remise_percent=$remise_percent;
550
			$this->line->subprice=$pu_ht;
551
			$this->line->rang=$rangtouse;
552
			$this->line->info_bits=$info_bits;
553
			$this->line->total_ht=$total_ht;
554
			$this->line->total_tva=$total_tva;
555
			$this->line->total_localtax1=$total_localtax1;
556
			$this->line->total_localtax2=$total_localtax2;
557
			$this->line->total_ttc=$total_ttc;
558
			$this->line->special_code=$special_code;
559
			$this->line->fk_parent_line=$fk_parent_line;
560
			$this->line->fk_unit=$fk_unit;
561
562
			$this->line->date_start=$date_start;
563
			$this->line->date_end=$date_end;
564
565
			$this->line->fk_fournprice = $fk_fournprice;
566
			$this->line->pa_ht = $pa_ht;
567
568
			$this->line->origin_id = $origin_id;
569
			$this->line->origin = $origin;
570
571
			// Multicurrency
572
			$this->line->fk_multicurrency			= $this->fk_multicurrency;
573
			$this->line->multicurrency_code			= $this->multicurrency_code;
574
			$this->line->multicurrency_subprice		= $pu_ht_devise;
575
			$this->line->multicurrency_total_ht 	= $multicurrency_total_ht;
576
			$this->line->multicurrency_total_tva 	= $multicurrency_total_tva;
577
			$this->line->multicurrency_total_ttc 	= $multicurrency_total_ttc;
578
579
			// Mise en option de la ligne
580
			if (empty($qty) && empty($special_code)) $this->line->special_code=3;
581
582
			// TODO deprecated
583
			$this->line->price=$price;
584
			$this->line->remise=$remise;
585
586
			if (is_array($array_options) && count($array_options)>0) {
587
				$this->line->array_options=$array_options;
588
			}
589
590
			$result=$this->line->insert();
591
			if ($result > 0)
592
			{
593
				// Reorder if child line
594
				if (! empty($fk_parent_line)) $this->line_order(true,'DESC');
595
596
                // Mise a jour informations denormalisees au niveau de la propale meme
597
                $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.
598
                if ($result > 0)
599
                {
600
                    $this->db->commit();
601
                    return $this->line->rowid;
602
                }
603
                else
604
                {
605
                    $this->error=$this->db->error();
606
                    $this->db->rollback();
607
                    return -1;
608
                }
609
            }
610
            else
611
            {
612
                $this->error=$this->line->error;
613
                $this->db->rollback();
614
                return -2;
615
            }
616
        }
617
		else
618
		{
619
			dol_syslog(get_class($this)."::addline status of order must be Draft to allow use of ->addline()", LOG_ERR);
620
			return -3;
621
		}
622
    }
623
624
625
	/**
626
	 *  Update a proposal line
627
	 *
628
	 *  @param      int			$rowid           	Id de la ligne
629
	 *  @param      float		$pu		     	  	Prix unitaire (HT ou TTC selon price_base_type)
630
	 *  @param      float		$qty            	Quantity
631
	 *  @param      float		$remise_percent  	Remise effectuee sur le produit
632
	 *  @param      float		$txtva	          	Taux de TVA
633
	 * 	@param	  	float		$txlocaltax1		Local tax 1 rate
634
	 *  @param	  	float		$txlocaltax2		Local tax 2 rate
635
	 *  @param      string		$desc            	Description
636
	 *	@param	  	string		$price_base_type	HT ou TTC
637
	 *	@param      int			$info_bits        	Miscellaneous informations
638
	 *	@param		int			$special_code		Special code (also used by externals modules!)
639
	 * 	@param		int			$fk_parent_line		Id of parent line (0 in most cases, used by modules adding sublevels into lines).
640
	 * 	@param		int			$skip_update_total	Keep fields total_xxx to 0 (used for special lines by some modules)
641
	 *  @param		int			$fk_fournprice		Id of origin supplier price
642
	 *  @param		int			$pa_ht				Price (without tax) of product when it was bought
643
	 *  @param		string		$label				???
644
	 *  @param		int			$type				0/1=Product/service
645
	 *	@param      int			$date_start       	Start date of the line
646
	 *	@param      int			$date_end         	End date of the line
647
	 *  @param		array		$array_options		extrafields array
648
	 * 	@param 		string		$fk_unit 			Code of the unit to use. Null to use the default one
649
	 * 	@param		double		$pu_ht_devise		Unit price in currency
650
	 * 	@param		int			$notrigger			disable line update trigger
651
	 *  @return     int     		        		0 if OK, <0 if KO
652
	 */
653
	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)
654
	{
655
		global $mysoc;
656
657
		dol_syslog(get_class($this)."::updateLine rowid=$rowid, pu=$pu, qty=$qty, remise_percent=$remise_percent,
658
        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");
659
		include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
660
661
		// Clean parameters
662
		$remise_percent=price2num($remise_percent);
663
		$qty=price2num($qty);
664
		$pu = price2num($pu);
665
		$pu_ht_devise=price2num($pu_ht_devise);
666
		$txtva = price2num($txtva);
667
		$txlocaltax1=price2num($txlocaltax1);
668
		$txlocaltax2=price2num($txlocaltax2);
669
		$pa_ht=price2num($pa_ht);
670
		if (empty($qty) && empty($special_code)) $special_code=3;    // Set option tag
671
		if (! empty($qty) && $special_code == 3) $special_code=0;    // Remove option tag
672
		if (empty($type)) $type=0;
673
674
		if ($this->statut == self::STATUS_DRAFT)
675
		{
676
			$this->db->begin();
677
678
			// Calcul du total TTC et de la TVA pour la ligne a partir de
679
			// qty, pu, remise_percent et txtva
680
			// TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
681
			// la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
682
683
			$localtaxes_type=getLocalTaxesFromRate($txtva,0,$this->thirdparty,$mysoc);
684
685
			// Clean vat code
686
			$vat_src_code='';
687
			if (preg_match('/\((.*)\)/', $txtva, $reg))
688
			{
689
				$vat_src_code = $reg[1];
690
				$txtva = preg_replace('/\s*\(.*\)/', '', $txtva);    // Remove code into vatrate.
691
			}
692
693
			$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);
694
			$total_ht  = $tabprice[0];
695
			$total_tva = $tabprice[1];
696
			$total_ttc = $tabprice[2];
697
			$total_localtax1 = $tabprice[9];
698
			$total_localtax2 = $tabprice[10];
699
			$pu_ht  = $tabprice[3];
700
			$pu_tva = $tabprice[4];
701
			$pu_ttc = $tabprice[5];
702
703
			// MultiCurrency
704
			$multicurrency_total_ht  = $tabprice[16];
705
			$multicurrency_total_tva = $tabprice[17];
706
			$multicurrency_total_ttc = $tabprice[18];
707
			$pu_ht_devise = $tabprice[19];
708
709
			// Anciens indicateurs: $price, $remise (a ne plus utiliser)
710
			$price = $pu;
711
			$remise = 0;
712
			if ($remise_percent > 0)
713
			{
714
				$remise = round(($pu * $remise_percent / 100), 2);
715
				$price = $pu - $remise;
716
			}
717
718
			//Fetch current line from the database and then clone the object and set it in $oldline property
719
			$line = new PropaleLigne($this->db);
720
			$line->fetch($rowid);
721
			$line->fetch_optionals(); // Fetch extrafields for oldcopy
722
723
			$staticline = clone $line;
724
725
			$line->oldline = $staticline;
726
			$this->line = $line;
727
			$this->line->context = $this->context;
728
729
			// Reorder if fk_parent_line change
730
			if (! empty($fk_parent_line) && ! empty($staticline->fk_parent_line) && $fk_parent_line != $staticline->fk_parent_line)
731
			{
732
				$rangmax = $this->line_max($fk_parent_line);
733
				$this->line->rang = $rangmax + 1;
734
			}
735
736
			$this->line->rowid				= $rowid;
737
			$this->line->label				= $label;
738
			$this->line->desc				= $desc;
739
			$this->line->qty				= $qty;
740
			$this->line->product_type		= $type;
741
			$this->line->vat_src_code		= $vat_src_code;
742
			$this->line->tva_tx				= $txtva;
743
			$this->line->localtax1_tx		= $txlocaltax1;
744
			$this->line->localtax2_tx		= $txlocaltax2;
745
			$this->line->localtax1_type		= $localtaxes_type[0];
746
			$this->line->localtax2_type		= $localtaxes_type[2];
747
			$this->line->remise_percent		= $remise_percent;
748
			$this->line->subprice			= $pu_ht;
749
			$this->line->info_bits			= $info_bits;
750
751
			$this->line->total_ht			= $total_ht;
752
			$this->line->total_tva			= $total_tva;
753
			$this->line->total_localtax1	= $total_localtax1;
754
			$this->line->total_localtax2	= $total_localtax2;
755
			$this->line->total_ttc			= $total_ttc;
756
			$this->line->special_code		= $special_code;
757
			$this->line->fk_parent_line		= $fk_parent_line;
758
			$this->line->skip_update_total	= $skip_update_total;
759
			$this->line->fk_unit	= $fk_unit;
760
761
			$this->line->fk_fournprice = $fk_fournprice;
762
			$this->line->pa_ht = $pa_ht;
763
764
			$this->line->date_start=$date_start;
765
			$this->line->date_end=$date_end;
766
767
			// TODO deprecated
768
			$this->line->price=$price;
769
			$this->line->remise=$remise;
770
771
			if (is_array($array_options) && count($array_options)>0) {
772
				$this->line->array_options=$array_options;
773
			}
774
775
			// Multicurrency
776
			$this->line->multicurrency_subprice		= $pu_ht_devise;
777
			$this->line->multicurrency_total_ht 	= $multicurrency_total_ht;
778
			$this->line->multicurrency_total_tva 	= $multicurrency_total_tva;
779
			$this->line->multicurrency_total_ttc 	= $multicurrency_total_ttc;
780
781
			$result=$this->line->update($notrigger);
782
			if ($result > 0)
783
			{
784
				// Reorder if child line
785
				if (! empty($fk_parent_line)) $this->line_order(true,'DESC');
786
787
				$this->update_price(1);
788
789
				$this->fk_propal = $this->id;
790
				$this->rowid = $rowid;
791
792
				$this->db->commit();
793
				return $result;
794
			}
795
			else
796
			{
797
				$this->error=$this->line->error;
798
799
				$this->db->rollback();
800
				return -1;
801
			}
802
		}
803
		else
804
		{
805
			dol_syslog(get_class($this)."::updateline Erreur -2 Propal en mode incompatible pour cette action");
806
			return -2;
807
		}
808
	}
809
810
811
	/**
812
	 *  Delete detail line
813
	 *
814
	 *  @param		int		$lineid			Id of line to delete
815
	 *  @return     int         			>0 if OK, <0 if KO
816
	 */
817
	function deleteline($lineid)
818
	{
819
		global $user;
820
821
		if ($this->statut == self::STATUS_DRAFT)
822
		{
823
			$this->db->begin();
824
825
			$line=new PropaleLigne($this->db);
826
827
			// For triggers
828
			$line->fetch($lineid);
829
830
			if ($line->delete($user) > 0)
831
			{
832
				$this->update_price(1);
833
834
				$this->db->commit();
835
				return 1;
836
			}
837
			else
838
			{
839
				$this->db->rollback();
840
				return -1;
841
			}
842
		}
843
		else
844
		{
845
			$this->error='ErrorDeleteLineNotAllowedByObjectStatus';
846
			return -2;
847
		}
848
	}
849
850
851
	/**
852
	 *  Create commercial proposal into database
853
	 * 	this->ref can be set or empty. If empty, we will use "(PROVid)"
854
	 *
855
	 * 	@param		User	$user		User that create
856
	 * 	@param		int		$notrigger	1=Does not execute triggers, 0= execute triggers
857
	 *  @return     int     			<0 if KO, >=0 if OK
858
	 */
859
	function create($user, $notrigger=0)
860
	{
861
		global $conf,$hookmanager;
862
		$error=0;
863
864
		$now=dol_now();
865
866
		// Clean parameters
867
		if (empty($this->date)) $this->date=$this->datep;
868
		$this->fin_validite = $this->date + ($this->duree_validite * 24 * 3600);
869
		if (empty($this->availability_id)) $this->availability_id=0;
870
		if (empty($this->demand_reason_id)) $this->demand_reason_id=0;
871
872
		// Multicurrency (test on $this->multicurrency_tx because we should take the default rate only if not using origin rate)
873
		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);
874
		else $this->fk_multicurrency = MultiCurrency::getIdFromCode($this->db, $this->multicurrency_code);
875
		if (empty($this->fk_multicurrency))
876
		{
877
			$this->multicurrency_code = $conf->currency;
878
			$this->fk_multicurrency = 0;
879
			$this->multicurrency_tx = 1;
880
		}
881
882
		dol_syslog(get_class($this)."::create");
883
884
		// Check parameters
885
		$result=$this->fetch_thirdparty();
886
		if ($result < 0)
887
		{
888
			$this->error="Failed to fetch company";
889
			dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR);
890
			return -3;
891
		}
892
893
		// Check parameters
894
		if (! empty($this->ref))	// We check that ref is not already used
895
		{
896
			$result=self::isExistingObject($this->element, 0, $this->ref);	// Check ref is not yet used
897
			if ($result > 0)
898
			{
899
				$this->error='ErrorRefAlreadyExists';
900
				dol_syslog(get_class($this)."::create ".$this->error,LOG_WARNING);
901
				$this->db->rollback();
902
				return -1;
903
			}
904
		}
905
906
		if (empty($this->date))
907
		{
908
			$this->error="Date of proposal is required";
909
			dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR);
910
			return -4;
911
		}
912
913
914
		$this->db->begin();
915
916
		// Insert into database
917
		$sql = "INSERT INTO ".MAIN_DB_PREFIX."propal (";
918
		$sql.= "fk_soc";
919
		$sql.= ", price";
920
		$sql.= ", remise";
921
		$sql.= ", remise_percent";
922
		$sql.= ", remise_absolue";
923
		$sql.= ", tva";
924
		$sql.= ", total";
925
		$sql.= ", datep";
926
		$sql.= ", datec";
927
		$sql.= ", ref";
928
		$sql.= ", fk_user_author";
929
		$sql.= ", note_private";
930
		$sql.= ", note_public";
931
		$sql.= ", model_pdf";
932
		$sql.= ", fin_validite";
933
		$sql.= ", fk_cond_reglement";
934
		$sql.= ", fk_mode_reglement";
935
		$sql.= ", fk_account";
936
		$sql.= ", ref_client";
937
		$sql.= ", date_livraison";
938
		$sql.= ", fk_shipping_method";
939
		$sql.= ", fk_availability";
940
		$sql.= ", fk_input_reason";
941
		$sql.= ", fk_projet";
942
		$sql.= ", fk_incoterms";
943
		$sql.= ", location_incoterms";
944
		$sql.= ", entity";
945
		$sql.= ", fk_multicurrency";
946
		$sql.= ", multicurrency_code";
947
		$sql.= ", multicurrency_tx";
948
		$sql.= ") ";
949
		$sql.= " VALUES (";
950
		$sql.= $this->socid;
951
		$sql.= ", 0";
952
		$sql.= ", ".$this->remise;
953
		$sql.= ", ".($this->remise_percent?$this->db->escape($this->remise_percent):'NULL');
954
		$sql.= ", ".($this->remise_absolue?$this->db->escape($this->remise_absolue):'NULL');
955
		$sql.= ", 0";
956
		$sql.= ", 0";
957
		$sql.= ", '".$this->db->idate($this->date)."'";
958
		$sql.= ", '".$this->db->idate($now)."'";
959
		$sql.= ", '(PROV)'";
960
		$sql.= ", ".($user->id > 0 ? "'".$user->id."'":"NULL");
961
		$sql.= ", '".$this->db->escape($this->note_private)."'";
962
		$sql.= ", '".$this->db->escape($this->note_public)."'";
963
		$sql.= ", '".$this->db->escape($this->modelpdf)."'";
964
		$sql.= ", ".($this->fin_validite!=''?"'".$this->db->idate($this->fin_validite)."'":"NULL");
965
		$sql.= ", ".($this->cond_reglement_id > 0 ? $this->cond_reglement_id : 'NULL');
966
		$sql.= ", ".($this->mode_reglement_id > 0 ? $this->mode_reglement_id : 'NULL');
967
		$sql.= ", ".($this->fk_account>0?$this->fk_account:'NULL');
968
		$sql.= ", '".$this->db->escape($this->ref_client)."'";
969
		$sql.= ", ".($this->date_livraison!=''?"'".$this->db->idate($this->date_livraison)."'":"NULL");
970
		$sql.= ", ".($this->shipping_method_id>0?$this->shipping_method_id:'NULL');
971
		$sql.= ", ".$this->availability_id;
972
		$sql.= ", ".$this->demand_reason_id;
973
		$sql.= ", ".($this->fk_project?$this->fk_project:"null");
974
		$sql.= ", ".(int) $this->fk_incoterms;
975
		$sql.= ", '".$this->db->escape($this->location_incoterms)."'";
976
		$sql.= ", ".$conf->entity;
977
		$sql.= ", ".(int) $this->fk_multicurrency;
978
		$sql.= ", '".$this->db->escape($this->multicurrency_code)."'";
979
		$sql.= ", ".(double) $this->multicurrency_tx;
980
		$sql.= ")";
981
982
		dol_syslog(get_class($this)."::create", LOG_DEBUG);
983
		$resql=$this->db->query($sql);
984
		if ($resql)
985
		{
986
			$this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."propal");
987
988
			if ($this->id)
989
			{
990
				$this->ref='(PROV'.$this->id.')';
991
				$sql = 'UPDATE '.MAIN_DB_PREFIX."propal SET ref='".$this->db->escape($this->ref)."' WHERE rowid=".$this->id;
992
993
				dol_syslog(get_class($this)."::create", LOG_DEBUG);
994
				$resql=$this->db->query($sql);
995
				if (! $resql) $error++;
996
997
                if (! empty($this->linkedObjectsIds) && empty($this->linked_objects))	// To use new linkedObjectsIds instead of old linked_objects
998
                {
999
                	$this->linked_objects = $this->linkedObjectsIds;	// TODO Replace linked_objects with linkedObjectsIds
1000
                }
1001
1002
                // Add object linked
1003
                if (! $error && $this->id && is_array($this->linked_objects) && ! empty($this->linked_objects))
1004
                {
1005
                	foreach($this->linked_objects as $origin => $tmp_origin_id)
1006
                	{
1007
                		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, ...))
1008
                		{
1009
                			foreach($tmp_origin_id as $origin_id)
1010
                			{
1011
                				$ret = $this->add_object_linked($origin, $origin_id);
1012
                				if (! $ret)
1013
                				{
1014
                					$this->error=$this->db->lasterror();
1015
                					$error++;
1016
                				}
1017
                			}
1018
                		}
1019
                		else                                // Old behaviour, if linked_object has only one link per type, so is something like array('contract'=>id1))
1020
                		{
1021
                			$origin_id = $tmp_origin_id;
1022
                			$ret = $this->add_object_linked($origin, $origin_id);
1023
                			if (! $ret)
1024
                			{
1025
                				$this->error=$this->db->lasterror();
1026
                				$error++;
1027
                			}
1028
                		}
1029
                	}
1030
                }
1031
1032
                // Add linked object (deprecated, use ->linkedObjectsIds instead)
1033
                if (! $error && $this->origin && $this->origin_id)
1034
                {
1035
                	$ret = $this->add_object_linked();
1036
                	if (! $ret)	dol_print_error($this->db);
1037
                }
1038
1039
                /*
1040
                 *  Insertion du detail des produits dans la base
1041
                 *  Insert products detail in database
1042
                 */
1043
                if (! $error)
1044
                {
1045
                    $fk_parent_line=0;
1046
                    $num=count($this->lines);
1047
1048
					for ($i=0;$i<$num;$i++)
1049
					{
1050
                        if (! is_object($this->lines[$i]))	// If this->lines is not array of objects, coming from REST API
1051
                            {   // Convert into object this->lines[$i].
1052
                                $line = (object) $this->lines[$i];
1053
                            }
1054
                            else
1055
                            {
1056
                                    $line = $this->lines[$i];
1057
                            }
1058
						// Reset fk_parent_line for line that are not child lines or special product
1059
						if (($line->product_type != 9 && empty($line->fk_parent_line)) || $line->product_type == 9) {
1060
							$fk_parent_line = 0;
1061
						}
1062
                        // Complete vat rate with code
1063
						$vatrate = $line->tva_tx;
1064
						if ($line->vat_src_code && ! preg_match('/\(.*\)/', $vatrate)) $vatrate.=' ('.$line->vat_src_code.')';
1065
1066
						$result = $this->addline(
1067
							$line->desc,
1068
							$line->subprice,
1069
							$line->qty,
1070
							$vatrate,
1071
							$line->localtax1_tx,
1072
							$line->localtax2_tx,
1073
							$line->fk_product,
1074
							$line->remise_percent,
1075
							'HT',
1076
							0,
1077
							$line->info_bits,
1078
							$line->product_type,
1079
							$line->rang,
1080
							$line->special_code,
1081
							$fk_parent_line,
1082
							$line->fk_fournprice,
1083
							$line->pa_ht,
1084
							$line->label,
1085
							$line->date_start,
1086
							$line->date_end,
1087
							$line->array_options,
1088
							$line->fk_unit,
1089
							$this->element,
1090
							$line->id
1091
						);
1092
1093
						if ($result < 0)
1094
						{
1095
							$error++;
1096
							$this->error=$this->db->error;
1097
							dol_print_error($this->db);
1098
							break;
1099
						}
1100
						// Defined the new fk_parent_line
1101
						if ($result > 0 && $line->product_type == 9) {
1102
							$fk_parent_line = $result;
1103
						}
1104
					}
1105
				}
1106
1107
				// Add linked object
1108
				if (! $error && $this->origin && $this->origin_id)
1109
				{
1110
					$ret = $this->add_object_linked();
1111
					if (! $ret)	dol_print_error($this->db);
1112
				}
1113
1114
				// Set delivery address
1115
				if (! $error && $this->fk_delivery_address)
1116
				{
1117
					$sql = "UPDATE ".MAIN_DB_PREFIX."propal";
1118
					$sql.= " SET fk_delivery_address = ".$this->fk_delivery_address;
1119
					$sql.= " WHERE ref = '".$this->db->escape($this->ref)."'";
1120
					$sql.= " AND entity = ".$conf->entity;
1121
1122
					$result=$this->db->query($sql);
1123
				}
1124
1125
				if (! $error)
1126
				{
1127
					// Mise a jour infos denormalisees
1128
					$resql=$this->update_price(1);
1129
					if ($resql)
1130
					{
1131
						$action='update';
1132
1133
						// Actions on extra fields
1134
						if (! $error)
1135
						{
1136
							if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) // For avoid conflicts if trigger used
1137
							{
1138
								$result=$this->insertExtraFields();
1139
								if ($result < 0)
1140
								{
1141
									$error++;
1142
								}
1143
							}
1144
						}
1145
1146
						if (! $error && ! $notrigger)
1147
						{
1148
							// Call trigger
1149
							$result=$this->call_trigger('PROPAL_CREATE',$user);
1150
							if ($result < 0) { $error++; }
1151
							// End call triggers
1152
						}
1153
					}
1154
					else
1155
					{
1156
						$this->error=$this->db->lasterror();
1157
						$error++;
1158
					}
1159
				}
1160
			}
1161
			else
1162
			{
1163
				$this->error=$this->db->lasterror();
1164
				$error++;
1165
			}
1166
1167
			if (! $error)
1168
			{
1169
				$this->db->commit();
1170
				dol_syslog(get_class($this)."::create done id=".$this->id);
1171
				return $this->id;
1172
			}
1173
			else
1174
			{
1175
				$this->db->rollback();
1176
				return -2;
1177
			}
1178
		}
1179
		else
1180
		{
1181
			$this->error=$this->db->lasterror();
1182
			$this->db->rollback();
1183
			return -1;
1184
		}
1185
	}
1186
1187
1188
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1189
	/**
1190
	 *	Insert into DB a proposal object completely defined by its data members (ex, results from copy).
1191
	 *
1192
	 *	@param 		User	$user	User that create
1193
	 *	@return    	int				Id of the new object if ok, <0 if ko
1194
	 *	@see       	create
1195
	 */
1196
	function create_from($user)
1197
	{
1198
        // phpcs:enable
1199
		// i love this function because $this->products is not used in create function...
1200
		$this->products=$this->lines;
1201
1202
		return $this->create($user);
1203
	}
1204
1205
	/**
1206
	 *		Load an object from its id and create a new one in database
1207
	 *
1208
	 *		@param		int				$socid			Id of thirdparty
1209
	 * 	 	@return		int								New id of clone
1210
	 */
1211
	function createFromClone($socid=0)
1212
	{
1213
		global $user,$conf,$hookmanager;
1214
1215
		dol_include_once('/projet/class/project.class.php');
1216
1217
		$this->context['createfromclone']='createfromclone';
1218
1219
		$error=0;
1220
		$now=dol_now();
1221
1222
		$this->db->begin();
1223
1224
		// get extrafields so they will be clone
1225
		foreach($this->lines as $line)
1226
			$line->fetch_optionals();
1227
1228
		// Load dest object
1229
		$clonedObj = clone $this;
1230
1231
		$objsoc=new Societe($this->db);
1232
1233
		// Change socid if needed
1234
		if (! empty($socid) && $socid != $clonedObj->socid)
1235
		{
1236
			if ($objsoc->fetch($socid) > 0)
1237
			{
1238
				$clonedObj->socid 				= $objsoc->id;
1239
				$clonedObj->cond_reglement_id	= (! empty($objsoc->cond_reglement_id) ? $objsoc->cond_reglement_id : 0);
1240
				$clonedObj->mode_reglement_id	= (! empty($objsoc->mode_reglement_id) ? $objsoc->mode_reglement_id : 0);
1241
				$clonedObj->fk_delivery_address	= '';
1242
1243
				/*if (!empty($conf->projet->enabled))
1244
                {
1245
                    $project = new Project($db);
1246
    				if ($this->fk_project > 0 && $project->fetch($this->fk_project)) {
1247
    					if ($project->socid <= 0) $clonedObj->fk_project = $this->fk_project;
1248
    					else $clonedObj->fk_project = '';
1249
    				} else {
1250
    					$clonedObj->fk_project = '';
1251
    				}
1252
                }*/
1253
				$clonedObj->fk_project = '';    // A cloned proposal is set by default to no project.
1254
			}
1255
1256
			// reset ref_client
1257
			$clonedObj->ref_client  = '';
1258
1259
			// TODO Change product price if multi-prices
1260
		}
1261
		else
1262
		{
1263
			$objsoc->fetch($clonedObj->socid);
1264
		}
1265
1266
		$clonedObj->id=0;
1267
		$clonedObj->ref='';
1268
		$clonedObj->statut=self::STATUS_DRAFT;
1269
1270
		// Clear fields
1271
		$clonedObj->user_author	= $user->id;
1272
		$clonedObj->user_valid	= '';
1273
		$clonedObj->date		= $now;
1274
		$clonedObj->datep		= $now;    // deprecated
1275
		$clonedObj->fin_validite	= $clonedObj->date + ($clonedObj->duree_validite * 24 * 3600);
1276
		if (empty($conf->global->MAIN_KEEP_REF_CUSTOMER_ON_CLONING)) $clonedObj->ref_client	= '';
1277
1278
		// Create clone
1279
		$result=$clonedObj->create($user);
1280
		if ($result < 0) $error++;
1281
		else
1282
		{
1283
			// copy internal contacts
1284
			if ($clonedObj->copy_linked_contact($this, 'internal') < 0)
1285
				$error++;
1286
1287
			// copy external contacts if same company
1288
			elseif ($this->socid == $clonedObj->socid)
1289
			{
1290
				if ($clonedObj->copy_linked_contact($this, 'external') < 0)
1291
					$error++;
1292
			}
1293
		}
1294
1295
		if (! $error)
1296
		{
1297
			// Hook of thirdparty module
1298
			if (is_object($hookmanager))
1299
			{
1300
				$parameters=array('objFrom'=>$this,'clonedObj'=>$clonedObj);
1301
				$action='';
1302
				$reshook=$hookmanager->executeHooks('createFrom',$parameters,$clonedObj,$action);    // Note that $action and $object may have been modified by some hooks
1303
				if ($reshook < 0) $error++;
1304
			}
1305
		}
1306
1307
		unset($this->context['createfromclone']);
1308
1309
		// End
1310
		if (! $error)
1311
		{
1312
			$this->db->commit();
1313
			return $clonedObj->id;
1314
		}
1315
		else
1316
		{
1317
			$this->db->rollback();
1318
			return -1;
1319
		}
1320
	}
1321
1322
	/**
1323
	 *	Load a proposal from database and its ligne array
1324
	 *
1325
	 *	@param      int			$rowid		id of object to load
1326
	 *	@param		string		$ref		Ref of proposal
1327
	 *	@return     int         			>0 if OK, <0 if KO
1328
	 */
1329
	function fetch($rowid,$ref='')
1330
	{
1331
1332
		$sql = "SELECT p.rowid, p.ref, p.entity, p.remise, p.remise_percent, p.remise_absolue, p.fk_soc";
1333
		$sql.= ", p.total, p.tva, p.localtax1, p.localtax2, p.total_ht";
1334
		$sql.= ", p.datec";
1335
		$sql.= ", p.date_valid as datev";
1336
		$sql.= ", p.datep as dp";
1337
		$sql.= ", p.fin_validite as dfv";
1338
		$sql.= ", p.date_livraison as date_livraison";
1339
		$sql.= ", p.model_pdf, p.last_main_doc, p.ref_client, p.extraparams";
1340
		$sql.= ", p.note_private, p.note_public";
1341
		$sql.= ", p.fk_projet, p.fk_statut";
1342
		$sql.= ", p.fk_user_author, p.fk_user_valid, p.fk_user_cloture";
1343
		$sql.= ", p.fk_delivery_address";
1344
		$sql.= ", p.fk_availability";
1345
		$sql.= ", p.fk_input_reason";
1346
		$sql.= ", p.fk_cond_reglement";
1347
		$sql.= ", p.fk_mode_reglement";
1348
		$sql.= ', p.fk_account';
1349
		$sql.= ", p.fk_shipping_method";
1350
		$sql.= ", p.fk_incoterms, p.location_incoterms";
1351
		$sql.= ", p.fk_multicurrency, p.multicurrency_code, p.multicurrency_tx, p.multicurrency_total_ht, p.multicurrency_total_tva, p.multicurrency_total_ttc";
1352
		$sql.= ", p.tms as date_modification";
1353
		$sql.= ", i.libelle as libelle_incoterms";
1354
		$sql.= ", c.label as statut_label";
1355
		$sql.= ", ca.code as availability_code, ca.label as availability";
1356
		$sql.= ", dr.code as demand_reason_code, dr.label as demand_reason";
1357
		$sql.= ", cr.code as cond_reglement_code, cr.libelle as cond_reglement, cr.libelle_facture as cond_reglement_libelle_doc";
1358
		$sql.= ", cp.code as mode_reglement_code, cp.libelle as mode_reglement";
1359
		$sql.= " FROM ".MAIN_DB_PREFIX."propal as p";
1360
		$sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_propalst as c ON p.fk_statut = c.id';
1361
		$sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as cp ON p.fk_mode_reglement = cp.id AND cp.entity IN ('.getEntity('c_paiement').')';
1362
		$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').')';
1363
		$sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_availability as ca ON p.fk_availability = ca.rowid';
1364
		$sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_input_reason as dr ON p.fk_input_reason = dr.rowid';
1365
		$sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_incoterms as i ON p.fk_incoterms = i.rowid';
1366
1367
		if ($ref) {
1368
			$sql.= " WHERE p.entity IN (".getEntity('propal').")"; // Dont't use entity if you use rowid
1369
			$sql.= " AND p.ref='".$this->db->escape($ref)."'";
1370
		}
1371
		else $sql.= " WHERE p.rowid=".$rowid;
1372
1373
		dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
1374
		$resql=$this->db->query($sql);
1375
		if ($resql)
1376
		{
1377
			if ($this->db->num_rows($resql))
1378
			{
1379
				$obj = $this->db->fetch_object($resql);
1380
1381
				$this->id                   = $obj->rowid;
1382
				$this->entity               = $obj->entity;
1383
1384
				$this->ref                  = $obj->ref;
1385
				$this->ref_client           = $obj->ref_client;
1386
				$this->remise               = $obj->remise;
1387
				$this->remise_percent       = $obj->remise_percent;
1388
				$this->remise_absolue       = $obj->remise_absolue;
1389
				$this->total                = $obj->total; // TODO deprecated
1390
				$this->total_ht             = $obj->total_ht;
1391
				$this->total_tva            = $obj->tva;
1392
				$this->total_localtax1		= $obj->localtax1;
1393
				$this->total_localtax2		= $obj->localtax2;
1394
				$this->total_ttc            = $obj->total;
1395
				$this->socid                = $obj->fk_soc;
1396
				$this->fk_project           = $obj->fk_projet;
1397
				$this->modelpdf             = $obj->model_pdf;
1398
				$this->last_main_doc		= $obj->last_main_doc;
1399
				$this->note                 = $obj->note_private; // TODO deprecated
1400
				$this->note_private         = $obj->note_private;
1401
				$this->note_public          = $obj->note_public;
1402
				$this->statut               = (int) $obj->fk_statut;
1403
				$this->statut_libelle       = $obj->statut_label;
1404
1405
				$this->datec                = $this->db->jdate($obj->datec); // TODO deprecated
1406
				$this->datev                = $this->db->jdate($obj->datev); // TODO deprecated
1407
				$this->date_creation		= $this->db->jdate($obj->datec); //Creation date
1408
				$this->date_validation		= $this->db->jdate($obj->datev); //Validation date
1409
				$this->date_modification	= $this->db->jdate($obj->date_modification); // tms
1410
				$this->date                 = $this->db->jdate($obj->dp);	// Proposal date
1411
				$this->datep                = $this->db->jdate($obj->dp);    // deprecated
1412
				$this->fin_validite         = $this->db->jdate($obj->dfv);
1413
				$this->date_livraison       = $this->db->jdate($obj->date_livraison);
1414
				$this->shipping_method_id   = ($obj->fk_shipping_method>0)?$obj->fk_shipping_method:null;
1415
				$this->availability_id      = $obj->fk_availability;
1416
				$this->availability_code    = $obj->availability_code;
1417
				$this->availability         = $obj->availability;
1418
				$this->demand_reason_id     = $obj->fk_input_reason;
1419
				$this->demand_reason_code   = $obj->demand_reason_code;
1420
				$this->demand_reason        = $obj->demand_reason;
1421
				$this->fk_address  			= $obj->fk_delivery_address;
1422
1423
				$this->mode_reglement_id    = $obj->fk_mode_reglement;
1424
				$this->mode_reglement_code  = $obj->mode_reglement_code;
1425
				$this->mode_reglement       = $obj->mode_reglement;
1426
				$this->fk_account           = ($obj->fk_account>0)?$obj->fk_account:null;
1427
				$this->cond_reglement_id    = $obj->fk_cond_reglement;
1428
				$this->cond_reglement_code  = $obj->cond_reglement_code;
1429
				$this->cond_reglement       = $obj->cond_reglement;
1430
				$this->cond_reglement_doc   = $obj->cond_reglement_libelle_doc;
1431
1432
				$this->extraparams			= (array) json_decode($obj->extraparams, true);
1433
1434
				$this->user_author_id = $obj->fk_user_author;
1435
				$this->user_valid_id  = $obj->fk_user_valid;
1436
				$this->user_close_id  = $obj->fk_user_cloture;
1437
1438
				//Incoterms
1439
				$this->fk_incoterms = $obj->fk_incoterms;
1440
				$this->location_incoterms = $obj->location_incoterms;
1441
				$this->libelle_incoterms = $obj->libelle_incoterms;
1442
1443
				// Multicurrency
1444
				$this->fk_multicurrency 		= $obj->fk_multicurrency;
1445
				$this->multicurrency_code 		= $obj->multicurrency_code;
1446
				$this->multicurrency_tx 		= $obj->multicurrency_tx;
1447
				$this->multicurrency_total_ht 	= $obj->multicurrency_total_ht;
1448
				$this->multicurrency_total_tva 	= $obj->multicurrency_total_tva;
1449
				$this->multicurrency_total_ttc 	= $obj->multicurrency_total_ttc;
1450
1451
				if ($obj->fk_statut == self::STATUS_DRAFT)
1452
				{
1453
					$this->brouillon = 1;
1454
				}
1455
1456
				// Retreive all extrafield
1457
				// fetch optionals attributes and labels
1458
				$this->fetch_optionals();
1459
1460
				$this->db->free($resql);
1461
1462
				$this->lines = array();
1463
1464
				/*
1465
                 * Lines
1466
                 */
1467
				$result=$this->fetch_lines();
1468
				if ($result < 0)
1469
				{
1470
					return -3;
1471
				}
1472
1473
				return 1;
1474
			}
1475
1476
			$this->error="Record Not Found";
1477
			return 0;
1478
		}
1479
		else
1480
		{
1481
			$this->error=$this->db->lasterror();
1482
			return -1;
1483
		}
1484
	}
1485
1486
	/**
1487
	 *      Update database
1488
	 *
1489
	 *      @param      User	$user        	User that modify
1490
	 *      @param      int		$notrigger	    0=launch triggers after, 1=disable triggers
1491
	 *      @return     int      			   	<0 if KO, >0 if OK
1492
	 */
1493
	function update(User $user, $notrigger=0)
1494
	{
1495
		global $conf;
1496
1497
		$error=0;
1498
1499
		// Clean parameters
1500
		if (isset($this->ref)) $this->ref=trim($this->ref);
1501
		if (isset($this->ref_client)) $this->ref_client=trim($this->ref_client);
1502
		if (isset($this->note) || isset($this->note_private)) $this->note_private=(isset($this->note_private) ? trim($this->note_private) : trim($this->note));
1503
		if (isset($this->note_public)) $this->note_public=trim($this->note_public);
1504
		if (isset($this->modelpdf)) $this->modelpdf=trim($this->modelpdf);
1505
		if (isset($this->import_key)) $this->import_key=trim($this->import_key);
1506
1507
		// Check parameters
1508
		// Put here code to add control on parameters values
1509
1510
		// Update request
1511
		$sql = "UPDATE ".MAIN_DB_PREFIX."propal SET";
1512
1513
		$sql.= " ref=".(isset($this->ref)?"'".$this->db->escape($this->ref)."'":"null").",";
1514
		$sql.= " ref_client=".(isset($this->ref_client)?"'".$this->db->escape($this->ref_client)."'":"null").",";
1515
		$sql.= " ref_ext=".(isset($this->ref_ext)?"'".$this->db->escape($this->ref_ext)."'":"null").",";
1516
		$sql.= " fk_soc=".(isset($this->socid)?$this->socid:"null").",";
1517
		$sql.= " datep=".(strval($this->datep)!='' ? "'".$this->db->idate($this->datep)."'" : 'null').",";
1518
		$sql.= " date_valid=".(strval($this->date_validation)!='' ? "'".$this->db->idate($this->date_validation)."'" : 'null').",";
1519
		$sql.= " tva=".(isset($this->total_tva)?$this->total_tva:"null").",";
1520
		$sql.= " localtax1=".(isset($this->total_localtax1)?$this->total_localtax1:"null").",";
1521
		$sql.= " localtax2=".(isset($this->total_localtax2)?$this->total_localtax2:"null").",";
1522
		$sql.= " total_ht=".(isset($this->total_ht)?$this->total_ht:"null").",";
1523
		$sql.= " total=".(isset($this->total_ttc)?$this->total_ttc:"null").",";
1524
		$sql.= " fk_statut=".(isset($this->statut)?$this->statut:"null").",";
1525
		$sql.= " fk_user_author=".(isset($this->user_author_id)?$this->user_author_id:"null").",";
1526
		$sql.= " fk_user_valid=".(isset($this->user_valid)?$this->user_valid:"null").",";
1527
		$sql.= " fk_projet=".(isset($this->fk_project)?$this->fk_project:"null").",";
1528
		$sql.= " fk_cond_reglement=".(isset($this->cond_reglement_id)?$this->cond_reglement_id:"null").",";
1529
		$sql.= " fk_mode_reglement=".(isset($this->mode_reglement_id)?$this->mode_reglement_id:"null").",";
1530
		$sql.= " note_private=".(isset($this->note_private)?"'".$this->db->escape($this->note_private)."'":"null").",";
1531
		$sql.= " note_public=".(isset($this->note_public)?"'".$this->db->escape($this->note_public)."'":"null").",";
1532
		$sql.= " model_pdf=".(isset($this->modelpdf)?"'".$this->db->escape($this->modelpdf)."'":"null").",";
1533
		$sql.= " import_key=".(isset($this->import_key)?"'".$this->db->escape($this->import_key)."'":"null")."";
1534
1535
		$sql.= " WHERE rowid=".$this->id;
1536
1537
		$this->db->begin();
1538
1539
		dol_syslog(get_class($this)."::update", LOG_DEBUG);
1540
		$resql = $this->db->query($sql);
1541
		if (! $resql) {
1542
			$error++; $this->errors[]="Error ".$this->db->lasterror();
1543
		}
1544
1545
		if (! $error && empty($conf->global->MAIN_EXTRAFIELDS_DISABLED) && is_array($this->array_options) && count($this->array_options)>0)
1546
		{
1547
			$result=$this->insertExtraFields();
1548
			if ($result < 0)
1549
			{
1550
				$error++;
1551
			}
1552
		}
1553
1554
		if (! $error && ! $notrigger)
1555
		{
1556
			// Call trigger
1557
			$result=$this->call_trigger('PROPAL_MODIFY', $user);
1558
			if ($result < 0) $error++;
1559
			// End call triggers
1560
		}
1561
1562
		// Commit or rollback
1563
		if ($error)
1564
		{
1565
			foreach($this->errors as $errmsg)
1566
			{
1567
				dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1568
				$this->error.=($this->error?', '.$errmsg:$errmsg);
1569
			}
1570
			$this->db->rollback();
1571
			return -1*$error;
1572
		}
1573
		else
1574
		{
1575
			$this->db->commit();
1576
			return 1;
1577
		}
1578
	}
1579
1580
1581
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1582
	/**
1583
	 * Load array lines
1584
	 *
1585
	 * @param		int		$only_product	Return only physical products
1586
	 * @return		int						<0 if KO, >0 if OK
1587
	 */
1588
	function fetch_lines($only_product=0)
1589
	{
1590
        // phpcs:enable
1591
		$this->lines=array();
1592
1593
		$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,';
1594
		$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,';
1595
		$sql.= ' d.fk_unit,';
1596
		$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,';
1597
		$sql.= ' p.weight, p.weight_units, p.volume, p.volume_units,';
1598
		$sql.= ' d.date_start, d.date_end,';
1599
		$sql.= ' d.fk_multicurrency, d.multicurrency_code, d.multicurrency_subprice, d.multicurrency_total_ht, d.multicurrency_total_tva, d.multicurrency_total_ttc';
1600
		$sql.= ' FROM '.MAIN_DB_PREFIX.'propaldet as d';
1601
		$sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON (d.fk_product = p.rowid)';
1602
		$sql.= ' WHERE d.fk_propal = '.$this->id;
1603
		if ($only_product) $sql .= ' AND p.fk_product_type = 0';
1604
		$sql.= ' ORDER by d.rang';
1605
1606
		dol_syslog(get_class($this)."::fetch_lines", LOG_DEBUG);
1607
		$result = $this->db->query($sql);
1608
		if ($result)
1609
		{
1610
			require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
1611
1612
			$num = $this->db->num_rows($result);
1613
1614
			$i = 0;
1615
			while ($i < $num)
1616
			{
1617
				$objp                   = $this->db->fetch_object($result);
1618
1619
				$line                   = new PropaleLigne($this->db);
1620
1621
				$line->rowid			= $objp->rowid; //Deprecated
1622
				$line->id				= $objp->rowid;
1623
				$line->fk_propal		= $objp->fk_propal;
1624
				$line->fk_parent_line	= $objp->fk_parent_line;
1625
				$line->product_type     = $objp->product_type;
1626
				$line->label            = $objp->custom_label;
1627
				$line->desc             = $objp->description;  // Description ligne
1628
				$line->qty              = $objp->qty;
1629
				$line->vat_src_code     = $objp->vat_src_code;
1630
				$line->tva_tx           = $objp->tva_tx;
1631
				$line->localtax1_tx		= $objp->localtax1_tx;
1632
				$line->localtax2_tx		= $objp->localtax2_tx;
1633
				$line->localtax1_type	= $objp->localtax1_type;
1634
				$line->localtax2_type	= $objp->localtax2_type;
1635
				$line->subprice         = $objp->subprice;
1636
				$line->fk_remise_except = $objp->fk_remise_except;
1637
				$line->remise_percent   = $objp->remise_percent;
1638
				$line->price            = $objp->price;		// TODO deprecated
1639
1640
				$line->info_bits        = $objp->info_bits;
1641
				$line->total_ht         = $objp->total_ht;
1642
				$line->total_tva        = $objp->total_tva;
1643
				$line->total_localtax1	= $objp->total_localtax1;
1644
				$line->total_localtax2	= $objp->total_localtax2;
1645
				$line->total_ttc        = $objp->total_ttc;
1646
				$line->fk_fournprice 	= $objp->fk_fournprice;
1647
				$marginInfos			= getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $line->fk_fournprice, $objp->pa_ht);
1648
				$line->pa_ht 			= $marginInfos[0];
1649
				$line->marge_tx			= $marginInfos[1];
1650
				$line->marque_tx		= $marginInfos[2];
1651
				$line->special_code     = $objp->special_code;
1652
				$line->rang             = $objp->rang;
1653
1654
				$line->fk_product       = $objp->fk_product;
1655
1656
				$line->ref				= $objp->product_ref;		// TODO deprecated
1657
				$line->product_ref		= $objp->product_ref;
1658
				$line->libelle			= $objp->product_label;		// TODO deprecated
1659
				$line->product_label	= $objp->product_label;
1660
				$line->product_desc     = $objp->product_desc; 		// Description produit
1661
				$line->product_tobatch  = $objp->product_tobatch;
1662
				$line->fk_product_type  = $objp->fk_product_type;	// TODO deprecated
1663
				$line->fk_unit          = $objp->fk_unit;
1664
				$line->weight = $objp->weight;
1665
				$line->weight_units = $objp->weight_units;
1666
				$line->volume = $objp->volume;
1667
				$line->volume_units = $objp->volume_units;
1668
1669
				$line->date_start  		= $this->db->jdate($objp->date_start);
1670
				$line->date_end  		= $this->db->jdate($objp->date_end);
1671
1672
				// Multicurrency
1673
				$line->fk_multicurrency 		= $objp->fk_multicurrency;
1674
				$line->multicurrency_code 		= $objp->multicurrency_code;
1675
				$line->multicurrency_subprice 	= $objp->multicurrency_subprice;
1676
				$line->multicurrency_total_ht 	= $objp->multicurrency_total_ht;
1677
				$line->multicurrency_total_tva 	= $objp->multicurrency_total_tva;
1678
				$line->multicurrency_total_ttc 	= $objp->multicurrency_total_ttc;
1679
1680
				$line->fetch_optionals();
1681
1682
				$this->lines[$i]        = $line;
1683
				//dol_syslog("1 ".$line->fk_product);
1684
				//print "xx $i ".$this->lines[$i]->fk_product;
1685
				$i++;
1686
			}
1687
1688
			$this->db->free($result);
1689
1690
			return $num;
1691
		}
1692
		else
1693
		{
1694
			$this->error=$this->db->lasterror();
1695
			return -3;
1696
		}
1697
	}
1698
1699
	/**
1700
	 *  Set status to validated
1701
	 *
1702
	 *  @param	User	$user       Object user that validate
1703
	 *  @param	int		$notrigger	1=Does not execute triggers, 0=execute triggers
1704
	 *  @return int         		<0 if KO, 0=Nothing done, >=0 if OK
1705
	 */
1706
	function valid($user, $notrigger=0)
1707
	{
1708
		require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1709
1710
		global $conf;
1711
1712
		$error=0;
1713
1714
		// Protection
1715
		if ($this->statut == self::STATUS_VALIDATED)
1716
		{
1717
			dol_syslog(get_class($this)."::valid action abandonned: already validated", LOG_WARNING);
1718
			return 0;
1719
		}
1720
1721
		if (! ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->propal->creer))
1722
	   	|| (! empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->propal->propal_advance->validate))))
1723
		{
1724
			$this->error='ErrorPermissionDenied';
1725
			dol_syslog(get_class($this)."::valid ".$this->error, LOG_ERR);
1726
			return -1;
1727
		}
1728
1729
		$now=dol_now();
1730
1731
		$this->db->begin();
1732
1733
		// Numbering module definition
1734
		$soc = new Societe($this->db);
1735
		$soc->fetch($this->socid);
1736
1737
		// Define new ref
1738
		if (! $error && (preg_match('/^[\(]?PROV/i', $this->ref) || empty($this->ref))) // empty should not happened, but when it occurs, the test save life
1739
		{
1740
			$num = $this->getNextNumRef($soc);
1741
		}
1742
		else
1743
		{
1744
			$num = $this->ref;
1745
		}
1746
		$this->newref = $num;
1747
1748
		$sql = "UPDATE ".MAIN_DB_PREFIX."propal";
1749
		$sql.= " SET ref = '".$num."',";
1750
		$sql.= " fk_statut = ".self::STATUS_VALIDATED.", date_valid='".$this->db->idate($now)."', fk_user_valid=".$user->id;
1751
		$sql.= " WHERE rowid = ".$this->id." AND fk_statut = ".self::STATUS_DRAFT;
1752
1753
		dol_syslog(get_class($this)."::valid", LOG_DEBUG);
1754
		$resql=$this->db->query($sql);
1755
		if (! $resql)
1756
		{
1757
			dol_print_error($this->db);
1758
			$error++;
1759
		}
1760
1761
		// Trigger calls
1762
		if (! $error && ! $notrigger)
1763
		{
1764
			// Call trigger
1765
			$result=$this->call_trigger('PROPAL_VALIDATE',$user);
1766
			if ($result < 0) { $error++; }
1767
			// End call triggers
1768
		}
1769
1770
		if (! $error)
1771
		{
1772
			$this->oldref = $this->ref;
1773
1774
			// Rename directory if dir was a temporary ref
1775
			if (preg_match('/^[\(]?PROV/i', $this->ref))
1776
			{
1777
				// Rename of propal directory ($this->ref = old ref, $num = new ref)
1778
				// to  not lose the linked files
1779
				$oldref = dol_sanitizeFileName($this->ref);
1780
				$newref = dol_sanitizeFileName($num);
1781
				$dirsource = $conf->propal->multidir_output[$this->entity].'/'.$oldref;
1782
				$dirdest = $conf->propal->multidir_output[$this->entity].'/'.$newref;
1783
1784
				if (file_exists($dirsource))
1785
				{
1786
					dol_syslog(get_class($this)."::validate rename dir ".$dirsource." into ".$dirdest);
1787
					if (@rename($dirsource, $dirdest))
1788
					{
1789
						dol_syslog("Rename ok");
1790
						// Rename docs starting with $oldref with $newref
1791
						$listoffiles=dol_dir_list($dirdest, 'files', 1, '^'.preg_quote($oldref,'/'));
1792
						foreach($listoffiles as $fileentry)
1793
						{
1794
							$dirsource=$fileentry['name'];
1795
							$dirdest=preg_replace('/^'.preg_quote($oldref,'/').'/',$newref, $dirsource);
1796
							$dirsource=$fileentry['path'].'/'.$dirsource;
1797
							$dirdest=$fileentry['path'].'/'.$dirdest;
1798
							@rename($dirsource, $dirdest);
1799
						}
1800
					}
1801
				}
1802
			}
1803
1804
			$this->ref=$num;
1805
			$this->brouillon=0;
1806
			$this->statut = self::STATUS_VALIDATED;
1807
			$this->user_valid_id=$user->id;
1808
			$this->datev=$now;
1809
1810
			$this->db->commit();
1811
			return 1;
1812
		}
1813
		else
1814
		{
1815
			$this->db->rollback();
1816
			return -1;
1817
		}
1818
	}
1819
1820
1821
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1822
	/**
1823
	 *  Define proposal date
1824
	 *
1825
	 *  @param  User		$user      	Object user that modify
1826
	 *  @param  int			$date		Date
1827
	 *  @param  int			$notrigger	1=Does not execute triggers, 0= execute triggers
1828
	 *  @return	int         			<0 if KO, >0 if OK
1829
	 */
1830
	function set_date($user, $date, $notrigger=0)
1831
	{
1832
        // phpcs:enable
1833
		if (empty($date))
1834
		{
1835
			$this->error='ErrorBadParameter';
1836
			dol_syslog(get_class($this)."::set_date ".$this->error, LOG_ERR);
1837
			return -1;
1838
		}
1839
1840
		if (! empty($user->rights->propal->creer))
1841
		{
1842
			$error=0;
1843
1844
			$this->db->begin();
1845
1846
			$sql = "UPDATE ".MAIN_DB_PREFIX."propal SET datep = '".$this->db->idate($date)."'";
1847
			$sql.= " WHERE rowid = ".$this->id." AND fk_statut = ".self::STATUS_DRAFT;
1848
1849
			dol_syslog(__METHOD__, LOG_DEBUG);
1850
			$resql=$this->db->query($sql);
1851
			if (!$resql)
1852
			{
1853
				$this->errors[]=$this->db->error();
1854
				$error++;
1855
			}
1856
1857
			if (! $error)
1858
			{
1859
				$this->oldcopy= clone $this;
1860
				$this->date = $date;
1861
				$this->datep = $date;    // deprecated
1862
			}
1863
1864
			if (! $notrigger && empty($error))
1865
			{
1866
				// Call trigger
1867
				$result=$this->call_trigger('PROPAL_MODIFY',$user);
1868
				if ($result < 0) $error++;
1869
				// End call triggers
1870
			}
1871
1872
			if (! $error)
1873
			{
1874
				$this->db->commit();
1875
				return 1;
1876
			}
1877
			else
1878
			{
1879
				foreach($this->errors as $errmsg)
1880
				{
1881
					dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
1882
					$this->error.=($this->error?', '.$errmsg:$errmsg);
1883
				}
1884
				$this->db->rollback();
1885
				return -1*$error;
1886
			}
1887
		}
1888
	}
1889
1890
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1891
	/**
1892
	 *	Define end validity date
1893
	 *
1894
	 *	@param		User	$user        		Object user that modify
1895
	 *	@param      int		$date_fin_validite	End of validity date
1896
	 *  @param  	int		$notrigger			1=Does not execute triggers, 0= execute triggers
1897
	 *	@return     int         				<0 if KO, >0 if OK
1898
	 */
1899
	function set_echeance($user, $date_fin_validite, $notrigger=0)
1900
	{
1901
        // phpcs:enable
1902
		if (! empty($user->rights->propal->creer))
1903
		{
1904
			$error=0;
1905
1906
			$this->db->begin();
1907
1908
			$sql = "UPDATE ".MAIN_DB_PREFIX."propal SET fin_validite = ".($date_fin_validite!=''?"'".$this->db->idate($date_fin_validite)."'":'null');
1909
			$sql.= " WHERE rowid = ".$this->id." AND fk_statut = ".self::STATUS_DRAFT;
1910
1911
			dol_syslog(__METHOD__, LOG_DEBUG);
1912
			$resql=$this->db->query($sql);
1913
			if (!$resql)
1914
			{
1915
				$this->errors[]=$this->db->error();
1916
				$error++;
1917
			}
1918
1919
1920
			if (! $error)
1921
			{
1922
				$this->oldcopy= clone $this;
1923
				$this->fin_validite = $date_fin_validite;
1924
			}
1925
1926
			if (! $notrigger && empty($error))
1927
			{
1928
				// Call trigger
1929
				$result=$this->call_trigger('PROPAL_MODIFY',$user);
1930
				if ($result < 0) $error++;
1931
				// End call triggers
1932
			}
1933
1934
			if (! $error)
1935
			{
1936
				$this->db->commit();
1937
				return 1;
1938
			}
1939
			else
1940
			{
1941
				foreach($this->errors as $errmsg)
1942
				{
1943
					dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
1944
					$this->error.=($this->error?', '.$errmsg:$errmsg);
1945
				}
1946
				$this->db->rollback();
1947
				return -1*$error;
1948
			}
1949
		}
1950
	}
1951
1952
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1953
	/**
1954
	 *	Set delivery date
1955
	 *
1956
	 *	@param      User 	$user        		Object user that modify
1957
	 *	@param      int		$date_livraison     Delivery date
1958
	 *  @param  	int		$notrigger			1=Does not execute triggers, 0= execute triggers
1959
	 *	@return     int         				<0 if ko, >0 if ok
1960
	 */
1961
	function set_date_livraison($user, $date_livraison, $notrigger=0)
1962
	{
1963
        // phpcs:enable
1964
		if (! empty($user->rights->propal->creer))
1965
		{
1966
			$error=0;
1967
1968
			$this->db->begin();
1969
1970
			$sql = "UPDATE ".MAIN_DB_PREFIX."propal ";
1971
			$sql.= " SET date_livraison = ".($date_livraison!=''?"'".$this->db->idate($date_livraison)."'":'null');
1972
			$sql.= " WHERE rowid = ".$this->id;
1973
1974
			dol_syslog(__METHOD__, LOG_DEBUG);
1975
			$resql=$this->db->query($sql);
1976
			if (!$resql)
1977
			{
1978
				$this->errors[]=$this->db->error();
1979
				$error++;
1980
			}
1981
1982
			if (! $error)
1983
			{
1984
				$this->oldcopy= clone $this;
1985
				$this->date_livraison = $date_livraison;
1986
			}
1987
1988
			if (! $notrigger && empty($error))
1989
			{
1990
				// Call trigger
1991
				$result=$this->call_trigger('PROPAL_MODIFY',$user);
1992
				if ($result < 0) $error++;
1993
				// End call triggers
1994
			}
1995
1996
			if (! $error)
1997
			{
1998
				$this->db->commit();
1999
				return 1;
2000
			}
2001
			else
2002
			{
2003
				foreach($this->errors as $errmsg)
2004
				{
2005
					dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2006
					$this->error.=($this->error?', '.$errmsg:$errmsg);
2007
				}
2008
				$this->db->rollback();
2009
				return -1*$error;
2010
			}
2011
		}
2012
	}
2013
2014
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
2015
	/**
2016
	 *  Set delivery
2017
	 *
2018
	 *  @param		User	$user		  	Object user that modify
2019
	 *  @param      int		$id				Availability id
2020
	 *  @param  	int		$notrigger		1=Does not execute triggers, 0= execute triggers
2021
	 *  @return     int           			<0 if KO, >0 if OK
2022
	 */
2023
	function set_availability($user, $id, $notrigger=0)
2024
	{
2025
        // phpcs:enable
2026
		if (! empty($user->rights->propal->creer) && $this->statut >= self::STATUS_DRAFT)
2027
		{
2028
			$error=0;
2029
2030
			$this->db->begin();
2031
2032
			$sql = "UPDATE ".MAIN_DB_PREFIX."propal ";
2033
			$sql.= " SET fk_availability = '".$id."'";
2034
			$sql.= " WHERE rowid = ".$this->id;
2035
2036
			dol_syslog(__METHOD__.' availability('.$id.')', LOG_DEBUG);
2037
			$resql=$this->db->query($sql);
2038
			if (!$resql)
2039
			{
2040
				$this->errors[]=$this->db->error();
2041
				$error++;
2042
			}
2043
2044
			if (! $error)
2045
			{
2046
				$this->oldcopy= clone $this;
2047
				$this->fk_availability = $id;
2048
				$this->availability_id = $id;
2049
			}
2050
2051
			if (! $notrigger && empty($error))
2052
			{
2053
				// Call trigger
2054
				$result=$this->call_trigger('PROPAL_MODIFY',$user);
2055
				if ($result < 0) $error++;
2056
				// End call triggers
2057
			}
2058
2059
			if (! $error)
2060
			{
2061
				$this->db->commit();
2062
				return 1;
2063
			}
2064
			else
2065
			{
2066
				foreach($this->errors as $errmsg)
2067
				{
2068
					dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2069
					$this->error.=($this->error?', '.$errmsg:$errmsg);
2070
				}
2071
				$this->db->rollback();
2072
				return -1*$error;
2073
			}
2074
		}
2075
		else
2076
		{
2077
			$error_str='Propal status do not meet requirement '.$this->statut;
2078
			dol_syslog(__METHOD__.$error_str, LOG_ERR);
2079
			$this->error=$error_str;
2080
			$this->errors[]= $this->error;
2081
			return -2;
2082
		}
2083
	}
2084
2085
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
2086
	/**
2087
	 *  Set source of demand
2088
	 *
2089
	 *  @param		User	$user		Object user that modify
2090
	 *  @param      int		$id			Input reason id
2091
	 *  @param  	int		$notrigger	1=Does not execute triggers, 0= execute triggers
2092
	 *  @return     int           		<0 if KO, >0 if OK
2093
	 */
2094
	function set_demand_reason($user, $id, $notrigger=0)
2095
	{
2096
        // phpcs:enable
2097
		if (! empty($user->rights->propal->creer) && $this->statut >= self::STATUS_DRAFT)
2098
		{
2099
			$error=0;
2100
2101
			$this->db->begin();
2102
2103
			$sql = "UPDATE ".MAIN_DB_PREFIX."propal ";
2104
			$sql.= " SET fk_input_reason = ".$id;
2105
			$sql.= " WHERE rowid = ".$this->id;
2106
2107
			dol_syslog(__METHOD__, LOG_DEBUG);
2108
			$resql=$this->db->query($sql);
2109
			if (!$resql)
2110
			{
2111
				$this->errors[]=$this->db->error();
2112
				$error++;
2113
			}
2114
2115
2116
			if (! $error)
2117
			{
2118
				$this->oldcopy= clone $this;
2119
				$this->fk_input_reason = $id;
2120
				$this->demand_reason_id = $id;
2121
			}
2122
2123
2124
			if (! $notrigger && empty($error))
2125
			{
2126
				// Call trigger
2127
				$result=$this->call_trigger('PROPAL_MODIFY',$user);
2128
				if ($result < 0) $error++;
2129
				// End call triggers
2130
			}
2131
2132
			if (! $error)
2133
			{
2134
				$this->db->commit();
2135
				return 1;
2136
			}
2137
			else
2138
			{
2139
				foreach($this->errors as $errmsg)
2140
				{
2141
					dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2142
					$this->error.=($this->error?', '.$errmsg:$errmsg);
2143
				}
2144
				$this->db->rollback();
2145
				return -1*$error;
2146
			}
2147
		}
2148
		else
2149
		{
2150
			$error_str='Propal status do not meet requirement '.$this->statut;
2151
			dol_syslog(__METHOD__.$error_str, LOG_ERR);
2152
			$this->error=$error_str;
2153
			$this->errors[]= $this->error;
2154
			return -2;
2155
		}
2156
	}
2157
2158
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
2159
	/**
2160
	 * Set customer reference number
2161
	 *
2162
	 *  @param      User	$user			Object user that modify
2163
	 *  @param      string	$ref_client		Customer reference
2164
	 *  @param  	int		$notrigger		1=Does not execute triggers, 0= execute triggers
2165
	 *  @return     int						<0 if ko, >0 if ok
2166
	 */
2167
	function set_ref_client($user, $ref_client, $notrigger=0)
2168
	{
2169
        // phpcs:enable
2170
		if (! empty($user->rights->propal->creer))
2171
		{
2172
			$error=0;
2173
2174
			$this->db->begin();
2175
2176
			$sql = 'UPDATE '.MAIN_DB_PREFIX.'propal SET ref_client = '.(empty($ref_client) ? 'NULL' : '\''.$this->db->escape($ref_client).'\'');
2177
			$sql.= ' WHERE rowid = '.$this->id;
2178
2179
			dol_syslog(__METHOD__.' $this->id='.$this->id.', ref_client='.$ref_client, LOG_DEBUG);
2180
			$resql=$this->db->query($sql);
2181
			if (!$resql)
2182
			{
2183
				$this->errors[]=$this->db->error();
2184
				$error++;
2185
			}
2186
2187
			if (! $error)
2188
			{
2189
				$this->oldcopy= clone $this;
2190
				$this->ref_client = $ref_client;
2191
			}
2192
2193
			if (! $notrigger && empty($error))
2194
			{
2195
				// Call trigger
2196
				$result=$this->call_trigger('PROPAL_MODIFY',$user);
2197
				if ($result < 0) $error++;
2198
				// End call triggers
2199
			}
2200
2201
			if (! $error)
2202
			{
2203
				$this->db->commit();
2204
				return 1;
2205
			}
2206
			else
2207
			{
2208
				foreach($this->errors as $errmsg)
2209
				{
2210
					dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2211
					$this->error.=($this->error?', '.$errmsg:$errmsg);
2212
				}
2213
				$this->db->rollback();
2214
				return -1*$error;
2215
			}
2216
		}
2217
		else
2218
		{
2219
			return -1;
2220
		}
2221
	}
2222
2223
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
2224
	/**
2225
	 *	Set an overall discount on the proposal
2226
	 *
2227
	 *	@param      User	$user       Object user that modify
2228
	 *	@param      double	$remise     Amount discount
2229
	 *  @param  	int		$notrigger	1=Does not execute triggers, 0= execute triggers
2230
	 *	@return     int         		<0 if ko, >0 if ok
2231
	 */
2232
	function set_remise_percent($user, $remise, $notrigger=0)
2233
	{
2234
        // phpcs:enable
2235
		$remise=trim($remise)?trim($remise):0;
2236
2237
		if (! empty($user->rights->propal->creer))
2238
		{
2239
			$remise = price2num($remise);
2240
2241
			$error=0;
2242
2243
			$this->db->begin();
2244
2245
			$sql = "UPDATE ".MAIN_DB_PREFIX."propal SET remise_percent = ".$remise;
2246
			$sql.= " WHERE rowid = ".$this->id." AND fk_statut = ".self::STATUS_DRAFT;
2247
2248
			dol_syslog(__METHOD__, LOG_DEBUG);
2249
			$resql=$this->db->query($sql);
2250
			if (!$resql)
2251
			{
2252
				$this->errors[]=$this->db->error();
2253
				$error++;
2254
			}
2255
2256
			if (! $error)
2257
			{
2258
				$this->oldcopy= clone $this;
2259
				$this->remise_percent = $remise;
1 ignored issue
show
Documentation Bug introduced by
It seems like $remise of type string or double is incompatible with the declared type integer of property $remise_percent.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
2260
				$this->update_price(1);
2261
			}
2262
2263
			if (! $notrigger && empty($error))
2264
			{
2265
				// Call trigger
2266
				$result=$this->call_trigger('PROPAL_MODIFY',$user);
2267
				if ($result < 0) $error++;
2268
				// End call triggers
2269
			}
2270
2271
			if (! $error)
2272
			{
2273
				$this->db->commit();
2274
				return 1;
2275
			}
2276
			else
2277
			{
2278
				foreach($this->errors as $errmsg)
2279
				{
2280
					dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2281
					$this->error.=($this->error?', '.$errmsg:$errmsg);
2282
				}
2283
				$this->db->rollback();
2284
				return -1*$error;
2285
			}
2286
		}
2287
	}
2288
2289
2290
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
2291
	/**
2292
	 *	Set an absolute overall discount on the proposal
2293
	 *
2294
	 *	@param      User	$user       Object user that modify
2295
	 *	@param      double	$remise     Amount discount
2296
	 *  @param  	int		$notrigger	1=Does not execute triggers, 0= execute triggers
2297
	 *	@return     int         		<0 if ko, >0 if ok
2298
	 */
2299
	function set_remise_absolue($user, $remise, $notrigger=0)
2300
	{
2301
        // phpcs:enable
2302
		$remise=trim($remise)?trim($remise):0;
2303
2304
		if (! empty($user->rights->propal->creer))
2305
		{
2306
			$remise = price2num($remise);
2307
2308
			$error=0;
2309
2310
			$this->db->begin();
2311
2312
			$sql = "UPDATE ".MAIN_DB_PREFIX."propal ";
2313
			$sql.= " SET remise_absolue = ".$remise;
2314
			$sql.= " WHERE rowid = ".$this->id." AND fk_statut = ".self::STATUS_DRAFT;
2315
2316
			dol_syslog(__METHOD__, LOG_DEBUG);
2317
			$resql=$this->db->query($sql);
2318
			if (!$resql)
2319
			{
2320
				$this->errors[]=$this->db->error();
2321
				$error++;
2322
			}
2323
2324
			if (! $error)
2325
			{
2326
				$this->oldcopy= clone $this;
2327
				$this->remise_absolue = $remise;
0 ignored issues
show
Documentation Bug introduced by
It seems like $remise of type string or double is incompatible with the declared type integer of property $remise_absolue.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
2328
				$this->update_price(1);
2329
			}
2330
2331
			if (! $notrigger && empty($error))
2332
			{
2333
				// Call trigger
2334
				$result=$this->call_trigger('PROPAL_MODIFY',$user);
2335
				if ($result < 0) $error++;
2336
				// End call triggers
2337
			}
2338
2339
			if (! $error)
2340
			{
2341
				$this->db->commit();
2342
				return 1;
2343
			}
2344
			else
2345
			{
2346
				foreach($this->errors as $errmsg)
2347
				{
2348
					dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2349
					$this->error.=($this->error?', '.$errmsg:$errmsg);
2350
				}
2351
				$this->db->rollback();
2352
				return -1*$error;
2353
			}
2354
		}
2355
	}
2356
2357
2358
2359
	/**
2360
	 *	Reopen the commercial proposal
2361
	 *
2362
	 *	@param      User	$user		Object user that close
2363
	 *	@param      int		$statut		Statut
2364
	 *	@param      string	$note		Comment
2365
	 *  @param		int		$notrigger	1=Does not execute triggers, 0= execute triggers
2366
	 *	@return     int         		<0 if KO, >0 if OK
2367
	 */
2368
	function reopen($user, $statut, $note='', $notrigger=0)
2369
	{
2370
2371
		$this->statut = $statut;
2372
		$error=0;
2373
2374
		$sql = "UPDATE ".MAIN_DB_PREFIX."propal";
2375
		$sql.= " SET fk_statut = ".$this->statut.",";
2376
		if (! empty($note)) $sql.= " note_private = '".$this->db->escape($note)."',";
2377
		$sql.= " date_cloture=NULL, fk_user_cloture=NULL";
2378
		$sql.= " WHERE rowid = ".$this->id;
2379
2380
		$this->db->begin();
2381
2382
		dol_syslog(get_class($this)."::reopen", LOG_DEBUG);
2383
		$resql = $this->db->query($sql);
2384
		if (! $resql) {
2385
			$error++; $this->errors[]="Error ".$this->db->lasterror();
2386
		}
2387
		if (! $error)
2388
		{
2389
			if (! $notrigger)
2390
			{
2391
				// Call trigger
2392
				$result=$this->call_trigger('PROPAL_REOPEN',$user);
2393
				if ($result < 0) { $error++; }
2394
				// End call triggers
2395
			}
2396
		}
2397
2398
		// Commit or rollback
2399
		if ($error)
2400
		{
2401
			if (!empty($this->errors))
2402
			{
2403
				foreach($this->errors as $errmsg)
2404
				{
2405
					dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
2406
					$this->error.=($this->error?', '.$errmsg:$errmsg);
2407
				}
2408
			}
2409
			$this->db->rollback();
2410
			return -1*$error;
2411
		}
2412
		else
2413
		{
2414
			$this->db->commit();
2415
			return 1;
2416
		}
2417
	}
2418
2419
2420
	/**
2421
	 *	Close the commercial proposal
2422
	 *
2423
	 *	@param      User	$user		Object user that close
2424
	 *	@param      int		$statut		Statut
2425
	 *	@param      string	$note		Complete private note with this note
2426
	 *  @param		int		$notrigger	1=Does not execute triggers, 0=Execute triggers
2427
	 *	@return     int         		<0 if KO, >0 if OK
2428
	 */
2429
	function cloture($user, $statut, $note, $notrigger=0)
2430
	{
2431
		global $langs,$conf;
2432
2433
		$error=0;
2434
		$now=dol_now();
2435
2436
		$this->db->begin();
2437
2438
		$newprivatenote = dol_concatdesc($this->note_private, $note);
2439
2440
		$sql = "UPDATE ".MAIN_DB_PREFIX."propal";
2441
		$sql.= " SET fk_statut = ".$statut.", note_private = '".$this->db->escape($newprivatenote)."', date_cloture='".$this->db->idate($now)."', fk_user_cloture=".$user->id;
2442
		$sql.= " WHERE rowid = ".$this->id;
2443
2444
		$resql=$this->db->query($sql);
2445
		if ($resql)
2446
		{
2447
			$modelpdf=$conf->global->PROPALE_ADDON_PDF_ODT_CLOSED?$conf->global->PROPALE_ADDON_PDF_ODT_CLOSED:$this->modelpdf;
2448
			$trigger_name='PROPAL_CLOSE_REFUSED';
2449
2450
			if ($statut == self::STATUS_SIGNED)
2451
			{
2452
				$trigger_name='PROPAL_CLOSE_SIGNED';
2453
				$modelpdf=$conf->global->PROPALE_ADDON_PDF_ODT_TOBILL?$conf->global->PROPALE_ADDON_PDF_ODT_TOBILL:$this->modelpdf;
2454
2455
				// The connected company is classified as a client
2456
				$soc=new Societe($this->db);
2457
				$soc->id = $this->socid;
2458
				$result=$soc->set_as_client();
2459
2460
				if ($result < 0)
2461
				{
2462
					$this->error=$this->db->lasterror();
2463
					$this->db->rollback();
2464
					return -2;
2465
				}
2466
			}
2467
			if ($statut == self::STATUS_BILLED)	// Why this ?
2468
			{
2469
				$trigger_name='PROPAL_CLASSIFY_BILLED';
2470
			}
2471
2472
			if (empty($conf->global->MAIN_DISABLE_PDF_AUTOUPDATE))
2473
			{
2474
			 	// Define output language
2475
			  	$outputlangs = $langs;
2476
			   	if (! empty($conf->global->MAIN_MULTILANGS))
2477
			   	{
2478
			   		$outputlangs = new Translate("",$conf);
2479
			   		$newlang=(GETPOST('lang_id','aZ09') ? GETPOST('lang_id','aZ09') : $this->thirdparty->default_lang);
2480
			   		$outputlangs->setDefaultLang($newlang);
2481
			   	}
2482
			   	//$ret=$object->fetch($id);    // Reload to get new records
2483
				   $this->generateDocument($modelpdf, $outputlangs);
2484
			}
2485
2486
			if (! $error)
2487
			{
2488
				$this->oldcopy= clone $this;
2489
				$this->statut = $statut;
2490
				$this->date_cloture = $now;
2491
				$this->note_private = $note;
2492
			}
2493
2494
			if (! $notrigger && empty($error))
2495
			{
2496
				// Call trigger
2497
				$result=$this->call_trigger($trigger_name,$user);
2498
				if ($result < 0) { $error++; }
2499
				// End call triggers
2500
			}
2501
2502
			if ( ! $error )
2503
			{
2504
				$this->db->commit();
2505
				return 1;
2506
			}
2507
			else
2508
			{
2509
				$this->db->rollback();
2510
				return -1;
2511
			}
2512
		}
2513
		else
2514
		{
2515
			$this->error=$this->db->lasterror();
2516
			$this->db->rollback();
2517
			return -1;
2518
		}
2519
	}
2520
2521
	/**
2522
	 *	Class invoiced the Propal
2523
	 *
2524
	 *	@param  	User	$user    	Object user
2525
	 *  @param		int		$notrigger	1=Does not execute triggers, 0= execute triggers
2526
	 *	@return     int     			<0 si ko, >0 si ok
2527
	 */
2528
	function classifyBilled(User $user, $notrigger=0)
2529
	{
2530
		$error=0;
2531
2532
		$this->db->begin();
2533
2534
		$sql = 'UPDATE '.MAIN_DB_PREFIX.'propal SET fk_statut = '.self::STATUS_BILLED;
2535
		$sql .= ' WHERE rowid = '.$this->id.' AND fk_statut > '.self::STATUS_DRAFT;
2536
2537
		dol_syslog(__METHOD__, LOG_DEBUG);
2538
		$resql=$this->db->query($sql);
2539
		if (!$resql)
2540
		{
2541
			$this->errors[]=$this->db->error();
2542
			$error++;
2543
		}
2544
2545
		if (! $error)
2546
		{
2547
			$this->oldcopy= clone $this;
2548
			$this->statut=self::STATUS_BILLED;
2549
		}
2550
2551
		if (! $notrigger && empty($error))
2552
		{
2553
			// Call trigger
2554
			$result=$this->call_trigger('PROPAL_MODIFY',$user);
2555
			if ($result < 0) $error++;
2556
			// End call triggers
2557
		}
2558
2559
		if (! $error)
2560
		{
2561
			$this->db->commit();
2562
			return 1;
2563
		}
2564
		else
2565
		{
2566
			foreach($this->errors as $errmsg)
2567
			{
2568
				dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2569
				$this->error.=($this->error?', '.$errmsg:$errmsg);
2570
			}
2571
			$this->db->rollback();
2572
			return -1*$error;
2573
		}
2574
	}
2575
2576
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
2577
	/**
2578
	 *	Set draft status
2579
	 *
2580
	 *	@param		User	$user		Object user that modify
2581
	 *  @param		int		$notrigger	1=Does not execute triggers, 0= execute triggers
2582
	 *	@return		int					<0 if KO, >0 if OK
2583
	 */
2584
	function set_draft($user, $notrigger=0)
2585
	{
2586
        // phpcs:enable
2587
		$error=0;
2588
2589
		$this->db->begin();
2590
2591
		$sql = "UPDATE ".MAIN_DB_PREFIX."propal SET fk_statut = ".self::STATUS_DRAFT;
2592
		$sql.= " WHERE rowid = ".$this->id;
2593
2594
		dol_syslog(__METHOD__, LOG_DEBUG);
2595
		$resql=$this->db->query($sql);
2596
		if (!$resql)
2597
		{
2598
			$this->errors[]=$this->db->error();
2599
			$error++;
2600
		}
2601
2602
		if (! $error)
2603
		{
2604
			$this->oldcopy= clone $this;
2605
			$this->statut = self::STATUS_DRAFT;
2606
			$this->brouillon = 1;
2607
		}
2608
2609
		if (! $notrigger && empty($error))
2610
		{
2611
			// Call trigger
2612
			$result=$this->call_trigger('PROPAL_MODIFY',$user);
2613
			if ($result < 0) $error++;
2614
			// End call triggers
2615
		}
2616
2617
		if (! $error)
2618
		{
2619
			$this->db->commit();
2620
			return 1;
2621
		}
2622
		else
2623
		{
2624
			foreach($this->errors as $errmsg)
2625
			{
2626
				dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2627
				$this->error.=($this->error?', '.$errmsg:$errmsg);
2628
			}
2629
			$this->db->rollback();
2630
			return -1*$error;
2631
		}
2632
	}
2633
2634
2635
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
2636
	/**
2637
	 *    Return list of proposal (eventually filtered on user) into an array
2638
	 *
2639
	 *    @param	int		$shortlist			0=Return array[id]=ref, 1=Return array[](id=>id,ref=>ref,name=>name)
2640
	 *    @param	int		$draft				0=not draft, 1=draft
2641
	 *    @param	int		$notcurrentuser		0=all user, 1=not current user
2642
	 *    @param    int		$socid				Id third pary
2643
	 *    @param    int		$limit				For pagination
2644
	 *    @param    int		$offset				For pagination
2645
	 *    @param    string	$sortfield			Sort criteria
2646
	 *    @param    string	$sortorder			Sort order
2647
	 *    @return	int		       				-1 if KO, array with result if OK
2648
	 */
2649
	function liste_array($shortlist=0, $draft=0, $notcurrentuser=0, $socid=0, $limit=0, $offset=0, $sortfield='p.datep', $sortorder='DESC')
2650
	{
2651
        // phpcs:enable
2652
		global $user;
2653
2654
		$ga = array();
2655
2656
		$sql = "SELECT s.rowid, s.nom as name, s.client,";
2657
		$sql.= " p.rowid as propalid, p.fk_statut, p.total_ht, p.ref, p.remise, ";
2658
		$sql.= " p.datep as dp, p.fin_validite as datelimite";
2659
		if (! $user->rights->societe->client->voir && ! $socid) $sql .= ", sc.fk_soc, sc.fk_user";
2660
		$sql.= " FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."propal as p, ".MAIN_DB_PREFIX."c_propalst as c";
2661
		if (! $user->rights->societe->client->voir && ! $socid) $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc";
2662
		$sql.= " WHERE p.entity IN (".getEntity('propal').")";
2663
		$sql.= " AND p.fk_soc = s.rowid";
2664
		$sql.= " AND p.fk_statut = c.id";
2665
		if (! $user->rights->societe->client->voir && ! $socid) //restriction
2666
		{
2667
			$sql.= " AND s.rowid = sc.fk_soc AND sc.fk_user = " .$user->id;
2668
		}
2669
		if ($socid) $sql.= " AND s.rowid = ".$socid;
2670
		if ($draft)	$sql.= " AND p.fk_statut = ".self::STATUS_DRAFT;
2671
		if ($notcurrentuser > 0) $sql.= " AND p.fk_user_author <> ".$user->id;
2672
		$sql.= $this->db->order($sortfield,$sortorder);
2673
		$sql.= $this->db->plimit($limit,$offset);
2674
2675
		$result=$this->db->query($sql);
2676
		if ($result)
2677
		{
2678
			$num = $this->db->num_rows($result);
2679
			if ($num)
2680
			{
2681
				$i = 0;
2682
				while ($i < $num)
2683
				{
2684
					$obj = $this->db->fetch_object($result);
2685
2686
					if ($shortlist == 1)
2687
					{
2688
						$ga[$obj->propalid] = $obj->ref;
2689
					}
2690
					else if ($shortlist == 2)
2691
					{
2692
						$ga[$obj->propalid] = $obj->ref.' ('.$obj->name.')';
2693
					}
2694
					else
2695
					{
2696
						$ga[$i]['id']	= $obj->propalid;
2697
						$ga[$i]['ref'] 	= $obj->ref;
2698
						$ga[$i]['name'] = $obj->name;
2699
					}
2700
2701
					$i++;
2702
				}
2703
			}
2704
			return $ga;
2705
		}
2706
		else
2707
		{
2708
			dol_print_error($this->db);
2709
			return -1;
2710
		}
2711
	}
2712
2713
	/**
2714
	 *  Returns an array with the numbers of related invoices
2715
	 *
2716
	 *	@return	array		Array of invoices
2717
	 */
2718
	function getInvoiceArrayList()
2719
	{
2720
		return $this->InvoiceArrayList($this->id);
2721
	}
2722
2723
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
2724
	/**
2725
	 *  Returns an array with id and ref of related invoices
2726
	 *
2727
	 *	@param		int		$id			Id propal
2728
	 *	@return		array				Array of invoices id
2729
	 */
2730
	function InvoiceArrayList($id)
2731
	{
2732
        // phpcs:enable
2733
		$ga = array();
2734
		$linkedInvoices = array();
2735
2736
		$this->fetchObjectLinked($id,$this->element);
2737
		foreach($this->linkedObjectsIds as $objecttype => $objectid)
2738
		{
2739
			// Nouveau système du comon object renvoi des rowid et non un id linéaire de 1 à n
2740
			// On parcourt donc une liste d'objets en tant qu'objet unique
2741
			foreach($objectid as $key => $object)
2742
			{
2743
				// Cas des factures liees directement
2744
				if ($objecttype == 'facture')
2745
				{
2746
					$linkedInvoices[] = $object;
2747
				}
2748
				// Cas des factures liees par un autre objet (ex: commande)
2749
				else
2750
				{
2751
					$this->fetchObjectLinked($object,$objecttype);
2752
					foreach($this->linkedObjectsIds as $subobjecttype => $subobjectid)
2753
					{
2754
						foreach($subobjectid as $subkey => $subobject)
2755
						{
2756
							if ($subobjecttype == 'facture')
2757
							{
2758
								$linkedInvoices[] = $subobject;
2759
							}
2760
						}
2761
					}
2762
				}
2763
			}
2764
		}
2765
2766
		if (count($linkedInvoices) > 0)
2767
		{
2768
			$sql= "SELECT rowid as facid, facnumber, total, datef as df, fk_user_author, fk_statut, paye";
2769
			$sql.= " FROM ".MAIN_DB_PREFIX."facture";
2770
			$sql.= " WHERE rowid IN (".implode(',',$linkedInvoices).")";
2771
2772
			dol_syslog(get_class($this)."::InvoiceArrayList", LOG_DEBUG);
2773
			$resql=$this->db->query($sql);
2774
2775
			if ($resql)
2776
			{
2777
				$tab_sqlobj=array();
2778
				$nump = $this->db->num_rows($resql);
2779
				for ($i = 0;$i < $nump;$i++)
2780
				{
2781
					$sqlobj = $this->db->fetch_object($resql);
2782
					$tab_sqlobj[] = $sqlobj;
2783
				}
2784
				$this->db->free($resql);
2785
2786
				$nump = count($tab_sqlobj);
2787
2788
				if ($nump)
2789
				{
2790
					$i = 0;
2791
					while ($i < $nump)
2792
					{
2793
						$obj = array_shift($tab_sqlobj);
2794
2795
						$ga[$i] = $obj;
2796
2797
						$i++;
2798
					}
2799
				}
2800
				return $ga;
2801
			}
2802
			else
2803
			{
2804
				return -1;
2805
			}
2806
		}
2807
		else return $ga;
2808
	}
2809
2810
	/**
2811
	 *	Delete proposal
2812
	 *
2813
	 *	@param	User	$user        	Object user that delete
2814
	 *	@param	int		$notrigger		1=Does not execute triggers, 0= execute triggers
2815
	 *	@return	int						1 if ok, otherwise if error
2816
	 */
2817
	function delete($user, $notrigger=0)
2818
	{
2819
		global $conf;
2820
		require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2821
2822
		$error=0;
2823
2824
		$this->db->begin();
2825
2826
		if (! $notrigger)
2827
		{
2828
			// Call trigger
2829
			$result=$this->call_trigger('PROPAL_DELETE',$user);
2830
			if ($result < 0) { $error++; }
2831
			// End call triggers
2832
		}
2833
2834
		if (! $error)
2835
		{
2836
			$sql = "DELETE FROM ".MAIN_DB_PREFIX."propaldet WHERE fk_propal = ".$this->id;
2837
			if ($this->db->query($sql))
2838
			{
2839
				$sql = "DELETE FROM ".MAIN_DB_PREFIX."propal WHERE rowid = ".$this->id;
2840
				if ($this->db->query($sql))
2841
				{
2842
					// Delete linked object
2843
					$res = $this->deleteObjectLinked();
2844
					if ($res < 0) $error++;
2845
2846
					// Delete linked contacts
2847
					$res = $this->delete_linked_contact();
2848
					if ($res < 0) $error++;
2849
2850
					if (! $error)
2851
					{
2852
						// We remove directory
2853
						$ref = dol_sanitizeFileName($this->ref);
2854
						if ($conf->propal->multidir_output[$this->entity] && !empty($this->ref))
2855
						{
2856
							$dir = $conf->propal->multidir_output[$this->entity] . "/" . $ref ;
2857
							$file = $dir . "/" . $ref . ".pdf";
2858
							if (file_exists($file))
2859
							{
2860
								dol_delete_preview($this);
2861
2862
								if (! dol_delete_file($file,0,0,0,$this)) // For triggers
2863
								{
2864
									$this->error='ErrorFailToDeleteFile';
2865
									$this->errors=array('ErrorFailToDeleteFile');
2866
									$this->db->rollback();
2867
									return 0;
2868
								}
2869
							}
2870
							if (file_exists($dir))
2871
							{
2872
								$res=@dol_delete_dir_recursive($dir);
2873
								if (! $res)
2874
								{
2875
									$this->error='ErrorFailToDeleteDir';
2876
									$this->errors=array('ErrorFailToDeleteDir');
2877
									$this->db->rollback();
2878
									return 0;
2879
								}
2880
							}
2881
						}
2882
					}
2883
2884
					// Removed extrafields
2885
					if (! $error)
2886
					{
2887
						if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) // For avoid conflicts if trigger used
2888
						{
2889
							$result=$this->deleteExtraFields();
2890
							if ($result < 0)
2891
							{
2892
								$error++;
2893
								$errorflag=-4;
2894
								dol_syslog(get_class($this)."::delete erreur ".$errorflag." ".$this->error, LOG_ERR);
2895
							}
2896
						}
2897
					}
2898
2899
					if (! $error)
2900
					{
2901
						dol_syslog(get_class($this)."::delete ".$this->id." by ".$user->id, LOG_DEBUG);
2902
						$this->db->commit();
2903
						return 1;
2904
					}
2905
					else
2906
					{
2907
						$this->error=$this->db->lasterror();
2908
						$this->db->rollback();
2909
						return 0;
2910
					}
2911
				}
2912
				else
2913
				{
2914
					$this->error=$this->db->lasterror();
2915
					$this->db->rollback();
2916
					return -3;
2917
				}
2918
			}
2919
			else
2920
			{
2921
				$this->error=$this->db->lasterror();
2922
				$this->db->rollback();
2923
				return -2;
2924
			}
2925
		}
2926
		else
2927
		{
2928
			$this->db->rollback();
2929
			return -1;
2930
		}
2931
	}
2932
2933
	/**
2934
	 *  Change the delivery time
2935
	 *
2936
	 *  @param	int	$availability_id	Id of new delivery time
2937
	 * 	@param	int	$notrigger			1=Does not execute triggers, 0= execute triggers
2938
	 *  @return int                  	>0 if OK, <0 if KO
2939
	 *  @deprecated  use set_availability
2940
	 */
2941
	function availability($availability_id, $notrigger=0)
2942
	{
2943
		global $user;
2944
2945
		if ($this->statut >= self::STATUS_DRAFT)
2946
		{
2947
			$error=0;
2948
2949
			$this->db->begin();
2950
2951
			$sql = 'UPDATE '.MAIN_DB_PREFIX.'propal';
2952
			$sql .= ' SET fk_availability = '.$availability_id;
2953
			$sql .= ' WHERE rowid='.$this->id;
2954
2955
			dol_syslog(__METHOD__.' availability('.$availability_id.')', LOG_DEBUG);
2956
			$resql=$this->db->query($sql);
2957
			if (!$resql)
2958
			{
2959
				$this->errors[]=$this->db->error();
2960
				$error++;
2961
			}
2962
2963
			if (! $error)
2964
			{
2965
				$this->oldcopy= clone $this;
2966
				$this->availability_id = $availability_id;
2967
			}
2968
2969
			if (! $notrigger && empty($error))
2970
			{
2971
				// Call trigger
2972
				$result=$this->call_trigger('PROPAL_MODIFY',$user);
2973
				if ($result < 0) $error++;
2974
				// End call triggers
2975
			}
2976
2977
			if (! $error)
2978
			{
2979
				$this->db->commit();
2980
				return 1;
2981
			}
2982
			else
2983
			{
2984
				foreach($this->errors as $errmsg)
2985
				{
2986
					dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2987
					$this->error.=($this->error?', '.$errmsg:$errmsg);
2988
				}
2989
				$this->db->rollback();
2990
				return -1*$error;
2991
			}
2992
		}
2993
		else
2994
		{
2995
			$error_str='Propal status do not meet requirement '.$this->statut;
2996
			dol_syslog(__METHOD__.$error_str, LOG_ERR);
2997
			$this->error=$error_str;
2998
			$this->errors[]= $this->error;
2999
			return -2;
3000
		}
3001
	}
3002
3003
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
3004
	/**
3005
	 *	Change source demand
3006
	 *
3007
	 *	@param	int $demand_reason_id 	Id of new source demand
3008
	 * 	@param	int	$notrigger			1=Does not execute triggers, 0= execute triggers
3009
	 *	@return int						>0 si ok, <0 si ko
3010
	 *	@deprecated use set_demand_reason
3011
	 */
3012
	function demand_reason($demand_reason_id, $notrigger=0)
3013
	{
3014
        // phpcs:enable
3015
		global $user;
3016
3017
		if ($this->statut >= self::STATUS_DRAFT)
3018
		{
3019
			$error=0;
3020
3021
			$this->db->begin();
3022
3023
			$sql = 'UPDATE '.MAIN_DB_PREFIX.'propal';
3024
			$sql .= ' SET fk_input_reason = '.$demand_reason_id;
3025
			$sql .= ' WHERE rowid='.$this->id;
3026
3027
			dol_syslog(__METHOD__.' demand_reason('.$demand_reason_id.')', LOG_DEBUG);
3028
			$resql=$this->db->query($sql);
3029
			if (!$resql)
3030
			{
3031
				$this->errors[]=$this->db->error();
3032
				$error++;
3033
			}
3034
3035
			if (! $error)
3036
			{
3037
				$this->oldcopy= clone $this;
3038
				$this->demand_reason_id = $demand_reason_id;
3039
			}
3040
3041
			if (! $notrigger && empty($error))
3042
			{
3043
				// Call trigger
3044
				$result=$this->call_trigger('PROPAL_MODIFY',$user);
3045
				if ($result < 0) $error++;
3046
				// End call triggers
3047
			}
3048
3049
			if (! $error)
3050
			{
3051
				$this->db->commit();
3052
				return 1;
3053
			}
3054
			else
3055
			{
3056
				foreach($this->errors as $errmsg)
3057
				{
3058
					dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
3059
					$this->error.=($this->error?', '.$errmsg:$errmsg);
3060
				}
3061
				$this->db->rollback();
3062
				return -1*$error;
3063
			}
3064
		}
3065
		else
3066
		{
3067
			$error_str='Propal status do not meet requirement '.$this->statut;
3068
			dol_syslog(__METHOD__.$error_str, LOG_ERR);
3069
			$this->error=$error_str;
3070
			$this->errors[]= $this->error;
3071
			return -2;
3072
		}
3073
	}
3074
3075
3076
	/**
3077
	 *	Object Proposal Information
3078
	 *
3079
	 * 	@param	int		$id		Proposal id
3080
	 *  @return	void
3081
	 */
3082
	function info($id)
3083
	{
3084
		$sql = "SELECT c.rowid, ";
3085
		$sql.= " c.datec, c.date_valid as datev, c.date_cloture as dateo,";
3086
		$sql.= " c.fk_user_author, c.fk_user_valid, c.fk_user_cloture";
3087
		$sql.= " FROM ".MAIN_DB_PREFIX."propal as c";
3088
		$sql.= " WHERE c.rowid = ".$id;
3089
3090
		$result = $this->db->query($sql);
3091
3092
		if ($result)
3093
		{
3094
			if ($this->db->num_rows($result))
3095
			{
3096
				$obj = $this->db->fetch_object($result);
3097
3098
				$this->id                = $obj->rowid;
3099
3100
				$this->date_creation     = $this->db->jdate($obj->datec);
3101
				$this->date_validation   = $this->db->jdate($obj->datev);
3102
				$this->date_cloture      = $this->db->jdate($obj->dateo);
3103
3104
				$cuser = new User($this->db);
3105
				$cuser->fetch($obj->fk_user_author);
3106
				$this->user_creation     = $cuser;
3107
3108
				if ($obj->fk_user_valid)
3109
				{
3110
					$vuser = new User($this->db);
3111
					$vuser->fetch($obj->fk_user_valid);
3112
					$this->user_validation     = $vuser;
3113
				}
3114
3115
				if ($obj->fk_user_cloture)
3116
				{
3117
					$cluser = new User($this->db);
3118
					$cluser->fetch($obj->fk_user_cloture);
3119
					$this->user_cloture     = $cluser;
3120
				}
3121
3122
3123
			}
3124
			$this->db->free($result);
3125
3126
		}
3127
		else
3128
		{
3129
			dol_print_error($this->db);
3130
		}
3131
	}
3132
3133
3134
	/**
3135
	 *    	Return label of status of proposal (draft, validated, ...)
3136
	 *
3137
	 *    	@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
3138
	 *    	@return     string		Label
3139
	 */
3140
	function getLibStatut($mode=0)
3141
	{
3142
		return $this->LibStatut($this->statut, $mode);
3143
	}
3144
3145
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
3146
	/**
3147
	 *    	Return label of a status (draft, validated, ...)
3148
	 *
3149
	 *    	@param      int			$statut		id statut
3150
	 *    	@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
3151
	 *    	@return     string		Label
3152
	 */
3153
	function LibStatut($statut,$mode=1)
3154
	{
3155
        // phpcs:enable
3156
		global $conf;
3157
3158
		// Init/load array of translation of status
3159
		if (empty($this->labelstatut) || empty($this->labelstatut_short))
3160
		{
3161
			global $langs;
3162
			$langs->load("propal");
3163
			$this->labelstatut[0]=$langs->trans("PropalStatusDraft");
3164
			$this->labelstatut[1]=$langs->trans("PropalStatusValidated");
3165
			$this->labelstatut[2]=$langs->trans("PropalStatusSigned");
3166
			$this->labelstatut[3]=$langs->trans("PropalStatusNotSigned");
3167
			$this->labelstatut[4]=$langs->trans("PropalStatusBilled");
3168
			$this->labelstatut_short[0]=$langs->trans("PropalStatusDraftShort");
3169
			$this->labelstatut_short[1]=$langs->trans("Opened");
3170
			$this->labelstatut_short[2]=$langs->trans("PropalStatusSignedShort");
3171
			$this->labelstatut_short[3]=$langs->trans("PropalStatusNotSignedShort");
3172
			$this->labelstatut_short[4]=$langs->trans("PropalStatusBilledShort");
3173
		}
3174
3175
		$statuttrans='';
3176
		if ($statut==self::STATUS_DRAFT) $statuttrans='statut0';
3177
		elseif ($statut==self::STATUS_VALIDATED) $statuttrans='statut1';
3178
		elseif ($statut==self::STATUS_SIGNED) $statuttrans='statut3';
3179
		elseif ($statut==self::STATUS_NOTSIGNED) $statuttrans='statut5';
3180
		elseif ($statut==self::STATUS_BILLED) $statuttrans='statut6';
3181
3182
		if ($mode == 0)	return $this->labelstatut[$statut];
3183
		elseif ($mode == 1)	return $this->labelstatut_short[$statut];
3184
		elseif ($mode == 2)	return img_picto($this->labelstatut_short[$statut], $statuttrans).' '.$this->labelstatut_short[$statut];
3185
		elseif ($mode == 3)	return img_picto($this->labelstatut[$statut], $statuttrans);
3186
		elseif ($mode == 4)	return img_picto($this->labelstatut[$statut],$statuttrans).' '.$this->labelstatut[$statut];
3187
		elseif ($mode == 5)	return '<span class="hideonsmartphone">'.$this->labelstatut_short[$statut].' </span>'.img_picto($this->labelstatut[$statut],$statuttrans);
3188
		elseif ($mode == 6)	return '<span class="hideonsmartphone">'.$this->labelstatut[$statut].' </span>'.img_picto($this->labelstatut[$statut],$statuttrans);
3189
	}
3190
3191
3192
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
3193
	/**
3194
	 *      Load indicators for dashboard (this->nbtodo and this->nbtodolate)
3195
	 *
3196
	 *      @param          User	$user   Object user
3197
	 *      @param          int		$mode   "opened" for proposal to close, "signed" for proposal to invoice
3198
	 *      @return WorkboardResponse|int <0 if KO, WorkboardResponse if OK
3199
	 */
3200
	function load_board($user,$mode)
3201
	{
3202
        // phpcs:enable
3203
		global $conf, $langs;
3204
3205
		$clause = " WHERE";
3206
3207
		$sql = "SELECT p.rowid, p.ref, p.datec as datec, p.fin_validite as datefin, p.total_ht";
3208
		$sql.= " FROM ".MAIN_DB_PREFIX."propal as p";
3209
		if (!$user->rights->societe->client->voir && !$user->societe_id)
3210
		{
3211
			$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON p.fk_soc = sc.fk_soc";
3212
			$sql.= " WHERE sc.fk_user = " .$user->id;
3213
			$clause = " AND";
3214
		}
3215
		$sql.= $clause." p.entity IN (".getEntity('propal').")";
3216
		if ($mode == 'opened') $sql.= " AND p.fk_statut = ".self::STATUS_VALIDATED;
3217
		if ($mode == 'signed') $sql.= " AND p.fk_statut = ".self::STATUS_SIGNED;
3218
		if ($user->societe_id) $sql.= " AND p.fk_soc = ".$user->societe_id;
3219
3220
		$resql=$this->db->query($sql);
3221
		if ($resql)
3222
		{
3223
			$langs->load("propal");
3224
			$now=dol_now();
3225
3226
			$delay_warning = 0;
3227
			$statut = 0;
3228
			$label = '';
3229
			if ($mode == 'opened') {
3230
				$delay_warning=$conf->propal->cloture->warning_delay;
3231
				$statut = self::STATUS_VALIDATED;
3232
				$label = $langs->trans("PropalsToClose");
3233
			}
3234
			if ($mode == 'signed') {
3235
				$delay_warning=$conf->propal->facturation->warning_delay;
3236
				$statut = self::STATUS_SIGNED;
3237
				$label = $langs->trans("PropalsToBill");         // We set here bill but may be billed or ordered
3238
			}
3239
3240
			$response = new WorkboardResponse();
3241
			$response->warning_delay = $delay_warning/60/60/24;
3242
			$response->label = $label;
3243
			$response->url = DOL_URL_ROOT.'/comm/propal/list.php?viewstatut='.$statut.'&mainmenu=commercial&leftmenu=propals';
3244
			$response->url_late = DOL_URL_ROOT.'/comm/propal/list.php?viewstatut='.$statut.'&mainmenu=commercial&leftmenu=propals&sortfield=p.datep&sortorder=asc';
3245
			$response->img = img_object('',"propal");
3246
3247
			// This assignment in condition is not a bug. It allows walking the results.
3248
			while ($obj=$this->db->fetch_object($resql))
3249
			{
3250
				$response->nbtodo++;
3251
				$response->total+=$obj->total_ht;
3252
3253
				if ($mode == 'opened')
3254
				{
3255
					$datelimit = $this->db->jdate($obj->datefin);
3256
					if ($datelimit < ($now - $delay_warning))
3257
					{
3258
						$response->nbtodolate++;
3259
					}
3260
				}
3261
				// TODO Definir regle des propales a facturer en retard
3262
				// if ($mode == 'signed' && ! count($this->FactureListeArray($obj->rowid))) $this->nbtodolate++;
3263
			}
3264
3265
			return $response;
3266
		}
3267
		else
3268
		{
3269
			$this->error=$this->db->error();
3270
			return -1;
3271
		}
3272
	}
3273
3274
3275
	/**
3276
	 *  Initialise an instance with random values.
3277
	 *  Used to build previews or test instances.
3278
	 *	id must be 0 if object instance is a specimen.
3279
	 *
3280
	 *  @return	void
3281
	 */
3282
	function initAsSpecimen()
3283
	{
3284
		global $langs;
3285
3286
		// Load array of products prodids
3287
		$num_prods = 0;
3288
		$prodids = array();
3289
		$sql = "SELECT rowid";
3290
		$sql.= " FROM ".MAIN_DB_PREFIX."product";
3291
		$sql.= " WHERE entity IN (".getEntity('product').")";
3292
		$resql = $this->db->query($sql);
3293
		if ($resql)
3294
		{
3295
			$num_prods = $this->db->num_rows($resql);
3296
			$i = 0;
3297
			while ($i < $num_prods)
3298
			{
3299
				$i++;
3300
				$row = $this->db->fetch_row($resql);
3301
				$prodids[$i] = $row[0];
3302
			}
3303
		}
3304
3305
		// Initialise parametres
3306
		$this->id=0;
3307
		$this->ref = 'SPECIMEN';
3308
		$this->ref_client='NEMICEPS';
3309
		$this->specimen=1;
3310
		$this->socid = 1;
3311
		$this->date = time();
3312
		$this->fin_validite = $this->date+3600*24*30;
3313
		$this->cond_reglement_id   = 1;
3314
		$this->cond_reglement_code = 'RECEP';
3315
		$this->mode_reglement_id   = 7;
3316
		$this->mode_reglement_code = 'CHQ';
3317
		$this->availability_id     = 1;
3318
		$this->availability_code   = 'AV_NOW';
3319
		$this->demand_reason_id    = 1;
3320
		$this->demand_reason_code  = 'SRC_00';
3321
		$this->note_public='This is a comment (public)';
3322
		$this->note_private='This is a comment (private)';
3323
		// Lines
3324
		$nbp = 5;
3325
		$xnbp = 0;
3326
		while ($xnbp < $nbp)
3327
		{
3328
			$line=new PropaleLigne($this->db);
3329
			$line->desc=$langs->trans("Description")." ".$xnbp;
3330
			$line->qty=1;
3331
			$line->subprice=100;
3332
			$line->price=100;
3333
			$line->tva_tx=20;
3334
			$line->localtax1_tx=0;
3335
			$line->localtax2_tx=0;
3336
			if ($xnbp == 2)
3337
			{
3338
				$line->total_ht=50;
3339
				$line->total_ttc=60;
3340
				$line->total_tva=10;
3341
				$line->remise_percent=50;
3342
			}
3343
			else
3344
			{
3345
				$line->total_ht=100;
3346
				$line->total_ttc=120;
3347
				$line->total_tva=20;
3348
				$line->remise_percent=00;
3349
			}
3350
3351
			if ($num_prods > 0)
3352
			{
3353
				$prodid = mt_rand(1, $num_prods);
3354
				$line->fk_product=$prodids[$prodid];
3355
		$line->product_ref='SPECIMEN';
3356
			}
3357
3358
			$this->lines[$xnbp]=$line;
3359
3360
			$this->total_ht       += $line->total_ht;
3361
			$this->total_tva      += $line->total_tva;
3362
			$this->total_ttc      += $line->total_ttc;
3363
3364
			$xnbp++;
3365
		}
3366
	}
3367
3368
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
3369
	/**
3370
	 *      Charge indicateurs this->nb de tableau de bord
3371
	 *
3372
	 *      @return     int         <0 if ko, >0 if ok
3373
	 */
3374
	function load_state_board()
3375
	{
3376
        // phpcs:enable
3377
		global $user;
3378
3379
		$this->nb=array();
3380
		$clause = "WHERE";
3381
3382
		$sql = "SELECT count(p.rowid) as nb";
3383
		$sql.= " FROM ".MAIN_DB_PREFIX."propal as p";
3384
		$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid";
3385
		if (!$user->rights->societe->client->voir && !$user->societe_id)
3386
		{
3387
			$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
3388
			$sql.= " WHERE sc.fk_user = " .$user->id;
3389
			$clause = "AND";
3390
		}
3391
		$sql.= " ".$clause." p.entity IN (".getEntity('propal').")";
3392
3393
		$resql=$this->db->query($sql);
3394
		if ($resql)
3395
		{
3396
			// This assignment in condition is not a bug. It allows walking the results.
3397
			while ($obj=$this->db->fetch_object($resql))
3398
			{
3399
				$this->nb["proposals"]=$obj->nb;
3400
			}
3401
			$this->db->free($resql);
3402
			return 1;
3403
		}
3404
		else
3405
		{
3406
			dol_print_error($this->db);
3407
			$this->error=$this->db->error();
3408
			return -1;
3409
		}
3410
	}
3411
3412
3413
	/**
3414
	 *  Returns the reference to the following non used Proposal used depending on the active numbering module
3415
	 *  defined into PROPALE_ADDON
3416
	 *
3417
	 *  @param	Societe		$soc  	Object thirdparty
3418
	 *  @return string      		Reference libre pour la propale
3419
	 */
3420
	function getNextNumRef($soc)
3421
	{
3422
		global $conf,$langs;
3423
		$langs->load("propal");
3424
3425
		$constant = 'PROPALE_ADDON_'.$this->entity;
3426
3427
		if (! empty($conf->global->$constant)) {
3428
			$classname = $conf->global->$constant; // for multicompany proposal sharing
3429
		} else {
3430
			$classname = $conf->global->PROPALE_ADDON;
3431
		}
3432
3433
		if (! empty($classname))
3434
		{
3435
			$mybool=false;
3436
3437
			$file = $classname.".php";
3438
3439
			// Include file with class
3440
			$dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
3441
			foreach ($dirmodels as $reldir) {
3442
3443
				$dir = dol_buildpath($reldir."core/modules/propale/");
3444
3445
				// Load file with numbering class (if found)
3446
				$mybool|=@include_once $dir.$file;
3447
			}
3448
3449
			if (! $mybool)
3450
			{
3451
				dol_print_error('',"Failed to include file ".$file);
3452
				return '';
3453
			}
3454
3455
			$obj = new $classname();
3456
			$numref = "";
3457
			$numref = $obj->getNextValue($soc,$this);
3458
3459
			if ($numref != "")
3460
			{
3461
				return $numref;
3462
			}
3463
			else
3464
			{
3465
				$this->error=$obj->error;
3466
				//dol_print_error($db,"Propale::getNextNumRef ".$obj->error);
3467
				return "";
3468
			}
3469
		}
3470
		else
3471
		{
3472
			$langs->load("errors");
3473
			print $langs->trans("Error")." ".$langs->trans("ErrorModuleSetupNotComplete");
3474
			return "";
3475
		}
3476
	}
3477
3478
	/**
3479
	 *	Return clicable link of object (with eventually picto)
3480
	 *
3481
	 *	@param      int		$withpicto		          Add picto into link
3482
	 *	@param      string	$option			          Where point the link ('expedition', 'document', ...)
3483
	 *	@param      string	$get_params    	          Parametres added to url
3484
	 *  @param	    int   	$notooltip		          1=Disable tooltip
3485
	 *  @param      int     $save_lastsearch_value    -1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking
3486
	 *	@return     string          		          String with URL
3487
	 */
3488
	function getNomUrl($withpicto=0, $option='', $get_params='', $notooltip=0, $save_lastsearch_value=-1)
3489
	{
3490
		global $langs, $conf, $user;
3491
3492
		if (! empty($conf->dol_no_mouse_hover)) $notooltip=1;   // Force disable tooltips
3493
3494
		$result='';
3495
		$label='';
3496
		$url='';
3497
3498
		if ($user->rights->propal->lire)
3499
		{
3500
			$label = '<u>' . $langs->trans("ShowPropal") . '</u>';
3501
			if (! empty($this->ref))
3502
				$label.= '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
3503
			if (! empty($this->ref_client))
3504
				$label.= '<br><b>'.$langs->trans('RefCustomer').':</b> '.$this->ref_client;
3505
			if (! empty($this->total_ht))
3506
				$label.= '<br><b>' . $langs->trans('AmountHT') . ':</b> ' . price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
3507
			if (! empty($this->total_tva))
3508
				$label.= '<br><b>' . $langs->trans('VAT') . ':</b> ' . price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
3509
			if (! empty($this->total_ttc))
3510
				$label.= '<br><b>' . $langs->trans('AmountTTC') . ':</b> ' . price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
3511
			if ($option == '') {
3512
				$url = DOL_URL_ROOT.'/comm/propal/card.php?id='.$this->id. $get_params;
3513
			}
3514
			if ($option == 'compta') {  // deprecated
3515
				$url = DOL_URL_ROOT.'/comm/propal/card.php?id='.$this->id. $get_params;
3516
			}
3517
			if ($option == 'expedition') {
3518
				$url = DOL_URL_ROOT.'/expedition/propal.php?id='.$this->id. $get_params;
3519
			}
3520
			if ($option == 'document') {
3521
				$url = DOL_URL_ROOT.'/comm/propal/document.php?id='.$this->id. $get_params;
3522
			}
3523
3524
			if ($option != 'nolink')
3525
			{
3526
				// Add param to save lastsearch_values or not
3527
				$add_save_lastsearch_values=($save_lastsearch_value == 1 ? 1 : 0);
3528
				if ($save_lastsearch_value == -1 && preg_match('/list\.php/',$_SERVER["PHP_SELF"])) $add_save_lastsearch_values=1;
3529
				if ($add_save_lastsearch_values) $url.='&save_lastsearch_values=1';
3530
			}
3531
		}
3532
3533
		$linkclose='';
3534
		if (empty($notooltip) && $user->rights->propal->lire)
3535
		{
3536
			if (! empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER))
3537
			{
3538
				$label=$langs->trans("ShowPropal");
3539
				$linkclose.=' alt="'.dol_escape_htmltag($label, 1).'"';
3540
			}
3541
			$linkclose.= ' title="'.dol_escape_htmltag($label, 1).'"';
3542
			$linkclose.=' class="classfortooltip"';
3543
		}
3544
3545
		$linkstart = '<a href="'.$url.'"';
3546
		$linkstart.=$linkclose.'>';
3547
		$linkend='</a>';
3548
3549
		$result .= $linkstart;
3550
		if ($withpicto) $result.=img_object(($notooltip?'':$label), $this->picto, ($notooltip?(($withpicto != 2) ? 'class="paddingright"' : ''):'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip?0:1);
3551
		if ($withpicto != 2) $result.= $this->ref;
3552
		$result .= $linkend;
3553
3554
		return $result;
3555
	}
3556
3557
	/**
3558
	 * 	Retrieve an array of propal lines
3559
	 *
3560
	 * 	@return int		>0 if OK, <0 if KO
3561
	 */
3562
	function getLinesArray()
3563
	{
3564
		return $this->fetch_lines();
3565
		/*
3566
		$this->lines = array();
3567
3568
		$sql = 'SELECT pt.rowid, pt.label as custom_label, pt.description, pt.fk_product, pt.fk_remise_except,';
3569
		$sql.= ' pt.qty, pt.vat_src_code, pt.tva_tx, pt.localtax1_tx, pt.localtax2_tx, pt.localtax1_type, pt.localtax2_type, pt.remise_percent, pt.subprice, pt.info_bits,';
3570
		$sql.= ' pt.total_ht, pt.total_tva, pt.total_ttc, pt.total_localtax1, pt.total_localtax2, pt.fk_product_fournisseur_price as fk_fournprice, pt.buy_price_ht as pa_ht, pt.special_code,';
3571
		$sql.= ' pt.date_start, pt.date_end, pt.product_type, pt.rang, pt.fk_parent_line,';
3572
		$sql.= ' pt.fk_unit,';
3573
		$sql.= ' p.label as product_label, p.ref, p.fk_product_type, p.rowid as prodid, p.description as product_desc, p.tobatch as product_tobatch,';
3574
		$sql.= ' p.entity,';
3575
		$sql.= ' pt.fk_multicurrency, pt.multicurrency_code, pt.multicurrency_subprice, pt.multicurrency_total_ht, pt.multicurrency_total_tva, pt.multicurrency_total_ttc';
3576
		$sql.= ' FROM '.MAIN_DB_PREFIX.'propaldet as pt';
3577
		$sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON pt.fk_product=p.rowid';
3578
		$sql.= ' WHERE pt.fk_propal = '.$this->id;
3579
		$sql.= ' ORDER BY pt.rang ASC, pt.rowid';
3580
3581
		dol_syslog(get_class($this).'::getLinesArray', LOG_DEBUG);
3582
		$resql = $this->db->query($sql);
3583
		if ($resql)
3584
		{
3585
			$num = $this->db->num_rows($resql);
3586
			$i = 0;
3587
3588
			while ($i < $num)
3589
			{
3590
				$obj = $this->db->fetch_object($resql);
3591
3592
				$this->lines[$i]					= new PropaleLigne($this->db);
3593
				$this->lines[$i]->id				= $obj->rowid; // for backward compatibility
3594
				$this->lines[$i]->rowid				= $obj->rowid;
3595
				$this->lines[$i]->label 			= $obj->custom_label;
3596
				$this->lines[$i]->desc       		= $obj->description;
3597
				$this->lines[$i]->description 		= $obj->description;
3598
				$this->lines[$i]->fk_product		= $obj->fk_product;
3599
				$this->lines[$i]->ref				= $obj->ref;
3600
				$this->lines[$i]->product_ref		= $obj->ref;
3601
				$this->lines[$i]->entity            = $obj->entity;             // Product entity
3602
				$this->lines[$i]->product_label		= $obj->product_label;
3603
				$this->lines[$i]->product_desc		= $obj->product_desc;
3604
				$this->lines[$i]->product_tobatch   = $obj->product_tobatch;
3605
				$this->lines[$i]->fk_product_type	= $obj->fk_product_type;    // deprecated
3606
				$this->lines[$i]->product_type		= $obj->product_type;
3607
				$this->lines[$i]->qty				= $obj->qty;
3608
				$this->lines[$i]->subprice			= $obj->subprice;
3609
				$this->lines[$i]->fk_remise_except 	= $obj->fk_remise_except;
3610
				$this->lines[$i]->remise_percent	= $obj->remise_percent;
3611
3612
				$this->lines[$i]->vat_src_code      = $obj->vat_src_code;
3613
				$this->lines[$i]->tva_tx			= $obj->tva_tx;
3614
				$this->lines[$i]->localtax1_tx		= $obj->localtax1_tx;
3615
				$this->lines[$i]->localtax2_tx		= $obj->localtax2_tx;
3616
				$this->lines[$i]->localtax1_type	= $obj->localtax1_type;
3617
				$this->lines[$i]->localtax2_type	= $obj->localtax2_type;
3618
				$this->lines[$i]->info_bits			= $obj->info_bits;
3619
				$this->lines[$i]->total_ht			= $obj->total_ht;
3620
				$this->lines[$i]->total_tva			= $obj->total_tva;
3621
				$this->lines[$i]->total_ttc			= $obj->total_ttc;
3622
				$this->lines[$i]->total_localtax1	= $obj->total_localtax1;
3623
				$this->lines[$i]->total_localtax2	= $obj->total_localtax2;
3624
				$this->lines[$i]->fk_fournprice		= $obj->fk_fournprice;
3625
				$marginInfos						= getMarginInfos($obj->subprice, $obj->remise_percent, $obj->tva_tx, $obj->localtax1_tx, $obj->localtax2_tx, $this->lines[$i]->fk_fournprice, $obj->pa_ht);
3626
				$this->lines[$i]->pa_ht				= $marginInfos[0];
3627
				$this->lines[$i]->marge_tx			= $marginInfos[1];
3628
				$this->lines[$i]->marque_tx			= $marginInfos[2];
3629
				$this->lines[$i]->fk_parent_line	= $obj->fk_parent_line;
3630
				$this->lines[$i]->special_code		= $obj->special_code;
3631
				$this->lines[$i]->rang				= $obj->rang;
3632
				$this->lines[$i]->date_start		= $this->db->jdate($obj->date_start);
3633
				$this->lines[$i]->date_end			= $this->db->jdate($obj->date_end);
3634
				$this->lines[$i]->fk_unit			= $obj->fk_unit;
3635
3636
				// Multicurrency
3637
				$this->lines[$i]->fk_multicurrency 			= $obj->fk_multicurrency;
3638
				$this->lines[$i]->multicurrency_code 		= $obj->multicurrency_code;
3639
				$this->lines[$i]->multicurrency_subprice 	= $obj->multicurrency_subprice;
3640
				$this->lines[$i]->multicurrency_total_ht 	= $obj->multicurrency_total_ht;
3641
				$this->lines[$i]->multicurrency_total_tva 	= $obj->multicurrency_total_tva;
3642
				$this->lines[$i]->multicurrency_total_ttc 	= $obj->multicurrency_total_ttc;
3643
3644
				$i++;
3645
			}
3646
			$this->db->free($resql);
3647
3648
			return 1;
3649
		}
3650
		else
3651
		{
3652
			$this->error=$this->db->error();
3653
			return -1;
3654
		}*/
3655
	}
3656
3657
	/**
3658
	 *  Create a document onto disk according to template module.
3659
	 *
3660
	 * 	@param	    string		$modele			Force model to use ('' to not force)
3661
	 * 	@param		Translate	$outputlangs	Object langs to use for output
3662
	 *  @param      int			$hidedetails    Hide details of lines
3663
	 *  @param      int			$hidedesc       Hide description
3664
	 *  @param      int			$hideref        Hide ref
3665
         *  @param   null|array  $moreparams     Array to provide more information
3666
	 * 	@return     int         				0 if KO, 1 if OK
3667
	 */
3668
	public function generateDocument($modele, $outputlangs, $hidedetails=0, $hidedesc=0, $hideref=0, $moreparams=null)
3669
	{
3670
		global $conf,$langs;
3671
3672
		$langs->load("propale");
3673
3674
		if (! dol_strlen($modele)) {
3675
3676
			$modele = 'azur';
3677
3678
			if ($this->modelpdf) {
3679
				$modele = $this->modelpdf;
3680
			} elseif (! empty($conf->global->PROPALE_ADDON_PDF)) {
3681
				$modele = $conf->global->PROPALE_ADDON_PDF;
3682
			}
3683
		}
3684
3685
		$modelpath = "core/modules/propale/doc/";
3686
3687
		return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref,$moreparams);
3688
	}
3689
3690
	/**
3691
	 * Function used to replace a thirdparty id with another one.
3692
	 *
3693
	 * @param DoliDB $db Database handler
3694
	 * @param int $origin_id Old thirdparty id
3695
	 * @param int $dest_id New thirdparty id
3696
	 * @return bool
3697
	 */
3698
	public static function replaceThirdparty(DoliDB $db, $origin_id, $dest_id)
3699
	{
3700
		$tables = array(
3701
			'propal'
3702
		);
3703
3704
		return CommonObject::commonReplaceThirdparty($db, $origin_id, $dest_id, $tables);
3705
	}
3706
}
3707
3708
3709
/**
3710
 *	Class to manage commercial proposal lines
3711
 */
3712
class PropaleLigne extends CommonObjectLine
3713
{
3714
	/**
3715
	 * @var string ID to identify managed object
3716
	 */
3717
	public $element='propaldet';
3718
3719
	/**
3720
	 * @var string Name of table without prefix where object is stored
3721
	 */
3722
	public $table_element='propaldet';
3723
3724
	var $oldline;
3725
3726
	// From llx_propaldet
3727
	var $fk_propal;
3728
	var $fk_parent_line;
3729
	var $desc;          	// Description ligne
3730
	var $fk_product;		// Id produit predefini
3731
	/**
3732
	 * @deprecated
3733
	 * @see product_type
3734
	 */
3735
	var $fk_product_type;
3736
	/**
3737
	 * Product type.
3738
	 * @var int
3739
	 * @see Product::TYPE_PRODUCT, Product::TYPE_SERVICE
3740
	 */
3741
	var $product_type = Product::TYPE_PRODUCT;
3742
3743
	var $qty;
3744
	var $tva_tx;
3745
	var $subprice;
3746
	var $remise_percent;
3747
	var $fk_remise_except;
3748
3749
	var $rang = 0;
3750
3751
	var $fk_fournprice;
3752
	var $pa_ht;
3753
	var $marge_tx;
3754
	var $marque_tx;
3755
3756
	var $special_code;	// Tag for special lines (exlusive tags)
3757
	// 1: frais de port
3758
	// 2: ecotaxe
3759
	// 3: option line (when qty = 0)
3760
3761
	var $info_bits = 0;	// Liste d'options cumulables:
3762
	// Bit 0: 	0 si TVA normal - 1 si TVA NPR
3763
	// Bit 1:	0 ligne normale - 1 si ligne de remise fixe
3764
3765
	var $total_ht;			// Total HT  de la ligne toute quantite et incluant la remise ligne
3766
	var $total_tva;			// Total TVA  de la ligne toute quantite et incluant la remise ligne
3767
	var $total_ttc;			// Total TTC de la ligne toute quantite et incluant la remise ligne
3768
3769
	/**
3770
	 * @deprecated
3771
	 * @see $remise_percent, $fk_remise_except
3772
	 */
3773
	var $remise;
3774
	/**
3775
	 * @deprecated
3776
	 * @see subprice
3777
	 */
3778
	var $price;
3779
3780
	// From llx_product
3781
	/**
3782
	 * @deprecated
3783
	 * @see product_ref
3784
	 */
3785
	var $ref;
3786
	/**
3787
	 * Product reference
3788
	 * @var string
3789
	 */
3790
	public $product_ref;
3791
	/**
3792
	 * @deprecated
3793
	 * @see product_label
3794
	 */
3795
	var $libelle;
3796
	/**
3797
	 *  Product label
3798
	 * @var string
3799
	 */
3800
	public $product_label;
3801
	/**
3802
	 * Product description
3803
	 * @var string
3804
	 */
3805
	public $product_desc;
3806
3807
	var $localtax1_tx;		// Local tax 1
3808
	var $localtax2_tx;		// Local tax 2
3809
	var $localtax1_type;	// Local tax 1 type
3810
	var $localtax2_type;	// Local tax 2 type
3811
	var $total_localtax1;  	// Line total local tax 1
3812
	var $total_localtax2;	// Line total local tax 2
3813
3814
	var $date_start;
3815
	var $date_end;
3816
3817
	var $skip_update_total; // Skip update price total for special lines
3818
3819
	// Multicurrency
3820
	var $fk_multicurrency;
3821
	var $multicurrency_code;
3822
	var $multicurrency_subprice;
3823
	var $multicurrency_total_ht;
3824
	var $multicurrency_total_tva;
3825
	var $multicurrency_total_ttc;
3826
3827
	/**
3828
	 * 	Class line Contructor
3829
	 *
3830
	 * 	@param	DoliDB	$db	Database handler
3831
	 */
3832
	function __construct($db)
3833
	{
3834
		$this->db= $db;
3835
	}
3836
3837
	/**
3838
	 *	Retrieve the propal line object
3839
	 *
3840
	 *	@param	int		$rowid		Propal line id
3841
	 *	@return	int					<0 if KO, >0 if OK
3842
	 */
3843
	function fetch($rowid)
3844
	{
3845
		$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,';
3846
		$sql.= ' pd.remise, pd.remise_percent, pd.fk_remise_except, pd.subprice,';
3847
		$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,';
3848
		$sql.= ' pd.fk_unit,';
3849
		$sql.= ' pd.localtax1_tx, pd.localtax2_tx, pd.total_localtax1, pd.total_localtax2,';
3850
		$sql.= ' pd.fk_multicurrency, pd.multicurrency_code, pd.multicurrency_subprice, pd.multicurrency_total_ht, pd.multicurrency_total_tva, pd.multicurrency_total_ttc,';
3851
		$sql.= ' p.ref as product_ref, p.label as product_label, p.description as product_desc,';
3852
		$sql.= ' pd.date_start, pd.date_end, pd.product_type';
3853
		$sql.= ' FROM '.MAIN_DB_PREFIX.'propaldet as pd';
3854
		$sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON pd.fk_product = p.rowid';
3855
		$sql.= ' WHERE pd.rowid = '.$rowid;
3856
3857
		$result = $this->db->query($sql);
3858
		if ($result)
3859
		{
3860
			$objp = $this->db->fetch_object($result);
3861
3862
			if ($objp)
3863
			{
3864
				$this->id               = $objp->rowid;
3865
				$this->rowid			= $objp->rowid;     // deprecated
3866
				$this->fk_propal		= $objp->fk_propal;
3867
				$this->fk_parent_line	= $objp->fk_parent_line;
3868
				$this->label			= $objp->custom_label;
3869
				$this->desc				= $objp->description;
3870
				$this->qty				= $objp->qty;
3871
				$this->price			= $objp->price;		// deprecated
3872
				$this->subprice			= $objp->subprice;
3873
				$this->vat_src_code		= $objp->vat_src_code;
3874
				$this->tva_tx			= $objp->tva_tx;
3875
				$this->remise			= $objp->remise;    // deprecated
3876
				$this->remise_percent	= $objp->remise_percent;
3877
				$this->fk_remise_except = $objp->fk_remise_except;
3878
				$this->fk_product		= $objp->fk_product;
3879
				$this->info_bits		= $objp->info_bits;
3880
3881
				$this->total_ht			= $objp->total_ht;
3882
				$this->total_tva		= $objp->total_tva;
3883
				$this->total_ttc		= $objp->total_ttc;
3884
3885
				$this->fk_fournprice	= $objp->fk_fournprice;
3886
3887
				$marginInfos			= getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $this->fk_fournprice, $objp->pa_ht);
3888
				$this->pa_ht			= $marginInfos[0];
3889
				$this->marge_tx			= $marginInfos[1];
3890
				$this->marque_tx		= $marginInfos[2];
3891
3892
				$this->special_code		= $objp->special_code;
3893
				$this->product_type		= $objp->product_type;
3894
				$this->rang				= $objp->rang;
3895
3896
				$this->ref				= $objp->product_ref;      // deprecated
3897
				$this->product_ref		= $objp->product_ref;
3898
				$this->libelle			= $objp->product_label;  // deprecated
3899
				$this->product_label	= $objp->product_label;
3900
				$this->product_desc		= $objp->product_desc;
3901
				$this->fk_unit          = $objp->fk_unit;
3902
3903
				$this->date_start       = $this->db->jdate($objp->date_start);
3904
				$this->date_end         = $this->db->jdate($objp->date_end);
3905
3906
				// Multicurrency
3907
				$this->fk_multicurrency 		= $objp->fk_multicurrency;
3908
				$this->multicurrency_code 		= $objp->multicurrency_code;
3909
				$this->multicurrency_subprice 	= $objp->multicurrency_subprice;
3910
				$this->multicurrency_total_ht 	= $objp->multicurrency_total_ht;
3911
				$this->multicurrency_total_tva 	= $objp->multicurrency_total_tva;
3912
				$this->multicurrency_total_ttc 	= $objp->multicurrency_total_ttc;
3913
3914
				$this->fetch_optionals();
3915
3916
				$this->db->free($result);
3917
3918
				return 1;
3919
			}
3920
			else
3921
			{
3922
				return 0;
3923
			}
3924
		}
3925
		else
3926
		{
3927
			return -1;
3928
		}
3929
	}
3930
3931
	/**
3932
	 *  Insert object line propal in database
3933
	 *
3934
	 *	@param		int		$notrigger		1=Does not execute triggers, 0= execute triggers
3935
	 *	@return		int						<0 if KO, >0 if OK
3936
	 */
3937
	function insert($notrigger=0)
3938
	{
3939
		global $conf,$user;
3940
3941
		$error=0;
3942
3943
		dol_syslog(get_class($this)."::insert rang=".$this->rang);
3944
3945
		$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'.
3946
3947
		// Clean parameters
3948
		if (empty($this->tva_tx)) $this->tva_tx=0;
3949
		if (empty($this->localtax1_tx)) $this->localtax1_tx=0;
3950
		if (empty($this->localtax2_tx)) $this->localtax2_tx=0;
3951
		if (empty($this->localtax1_type)) $this->localtax1_type=0;
3952
		if (empty($this->localtax2_type)) $this->localtax2_type=0;
3953
		if (empty($this->total_localtax1)) $this->total_localtax1=0;
3954
		if (empty($this->total_localtax2)) $this->total_localtax2=0;
3955
		if (empty($this->rang)) $this->rang=0;
3956
		if (empty($this->remise)) $this->remise=0;
3957
		if (empty($this->remise_percent) || ! is_numeric($this->remise_percent)) $this->remise_percent=0;
3958
		if (empty($this->info_bits)) $this->info_bits=0;
3959
		if (empty($this->special_code)) $this->special_code=0;
3960
		if (empty($this->fk_parent_line)) $this->fk_parent_line=0;
3961
		if (empty($this->fk_fournprice)) $this->fk_fournprice=0;
3962
		if (! is_numeric($this->qty)) $this->qty = 0;
3963
		if (empty($this->pa_ht)) $this->pa_ht=0;
3964
		if (empty($this->multicurrency_subprice))  $this->multicurrency_subprice=0;
3965
		if (empty($this->multicurrency_total_ht))  $this->multicurrency_total_ht=0;
3966
		if (empty($this->multicurrency_total_tva)) $this->multicurrency_total_tva=0;
3967
		if (empty($this->multicurrency_total_ttc)) $this->multicurrency_total_ttc=0;
3968
3969
	   // if buy price not defined, define buyprice as configured in margin admin
3970
		if ($this->pa_ht == 0 && $pa_ht_isemptystring)
3971
		{
3972
			if (($result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product)) < 0)
3973
			{
3974
				return $result;
3975
			}
3976
			else
3977
			{
3978
				$this->pa_ht = $result;
3979
			}
3980
		}
3981
3982
		// Check parameters
3983
		if ($this->product_type < 0) return -1;
3984
3985
		$this->db->begin();
3986
3987
		// Insert line into database
3988
		$sql = 'INSERT INTO '.MAIN_DB_PREFIX.'propaldet';
3989
		$sql.= ' (fk_propal, fk_parent_line, label, description, fk_product, product_type,';
3990
		$sql.= ' fk_remise_except, qty, vat_src_code, tva_tx, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type,';
3991
		$sql.= ' subprice, remise_percent, ';
3992
		$sql.= ' info_bits, ';
3993
		$sql.= ' total_ht, total_tva, total_localtax1, total_localtax2, total_ttc, fk_product_fournisseur_price, buy_price_ht, special_code, rang,';
3994
		$sql.= ' fk_unit,';
3995
		$sql.= ' date_start, date_end';
3996
		$sql.= ', fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc)';
3997
		$sql.= " VALUES (".$this->fk_propal.",";
3998
		$sql.= " ".($this->fk_parent_line>0?"'".$this->db->escape($this->fk_parent_line)."'":"null").",";
3999
		$sql.= " ".(! empty($this->label)?"'".$this->db->escape($this->label)."'":"null").",";
4000
		$sql.= " '".$this->db->escape($this->desc)."',";
4001
		$sql.= " ".($this->fk_product?"'".$this->db->escape($this->fk_product)."'":"null").",";
4002
		$sql.= " '".$this->db->escape($this->product_type)."',";
4003
		$sql.= " ".($this->fk_remise_except?"'".$this->db->escape($this->fk_remise_except)."'":"null").",";
4004
		$sql.= " ".price2num($this->qty).",";
4005
		$sql.= " ".(empty($this->vat_src_code)?"''":"'".$this->db->escape($this->vat_src_code)."'").",";
4006
		$sql.= " ".price2num($this->tva_tx).",";
4007
		$sql.= " ".price2num($this->localtax1_tx).",";
4008
		$sql.= " ".price2num($this->localtax2_tx).",";
4009
		$sql.= " '".$this->db->escape($this->localtax1_type)."',";
4010
		$sql.= " '".$this->db->escape($this->localtax2_type)."',";
4011
		$sql.= " ".(price2num($this->subprice)!==''?price2num($this->subprice):"null").",";
4012
		$sql.= " ".price2num($this->remise_percent).",";
4013
		$sql.= " ".(isset($this->info_bits)?"'".$this->db->escape($this->info_bits)."'":"null").",";
4014
		$sql.= " ".price2num($this->total_ht).",";
4015
		$sql.= " ".price2num($this->total_tva).",";
4016
		$sql.= " ".price2num($this->total_localtax1).",";
4017
		$sql.= " ".price2num($this->total_localtax2).",";
4018
		$sql.= " ".price2num($this->total_ttc).",";
4019
		$sql.= " ".(!empty($this->fk_fournprice)?"'".$this->db->escape($this->fk_fournprice)."'":"null").",";
4020
		$sql.= " ".(isset($this->pa_ht)?"'".price2num($this->pa_ht)."'":"null").",";
4021
		$sql.= ' '.$this->special_code.',';
4022
		$sql.= ' '.$this->rang.',';
4023
		$sql.= ' '.(!$this->fk_unit ? 'NULL' : $this->fk_unit).',';
4024
		$sql.= " ".(! empty($this->date_start)?"'".$this->db->idate($this->date_start)."'":"null").',';
4025
		$sql.= " ".(! empty($this->date_end)?"'".$this->db->idate($this->date_end)."'":"null");
4026
		$sql.= ", ".($this->fk_multicurrency > 0?$this->fk_multicurrency:'null');
4027
		$sql.= ", '".$this->db->escape($this->multicurrency_code)."'";
4028
		$sql.= ", ".$this->multicurrency_subprice;
4029
		$sql.= ", ".$this->multicurrency_total_ht;
4030
		$sql.= ", ".$this->multicurrency_total_tva;
4031
		$sql.= ", ".$this->multicurrency_total_ttc;
4032
		$sql.= ')';
4033
4034
		dol_syslog(get_class($this).'::insert', LOG_DEBUG);
4035
		$resql=$this->db->query($sql);
4036
		if ($resql)
4037
		{
4038
			$this->rowid=$this->db->last_insert_id(MAIN_DB_PREFIX.'propaldet');
4039
4040
			if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) // For avoid conflicts if trigger used
4041
			{
4042
				$this->id=$this->rowid;
4043
				$result=$this->insertExtraFields();
4044
				if ($result < 0)
4045
				{
4046
					$error++;
4047
				}
4048
			}
4049
4050
			if (! $error && ! $notrigger)
4051
			{
4052
				// Call trigger
4053
				$result=$this->call_trigger('LINEPROPAL_INSERT',$user);
4054
				if ($result < 0)
4055
				{
4056
					$this->db->rollback();
4057
					return -1;
4058
				}
4059
				// End call triggers
4060
			}
4061
4062
			$this->db->commit();
4063
			return 1;
4064
		}
4065
		else
4066
		{
4067
			$this->error=$this->db->error()." sql=".$sql;
4068
			$this->db->rollback();
4069
			return -1;
4070
		}
4071
	}
4072
4073
	/**
4074
	 * 	Delete line in database
4075
	 *
4076
	 *  @param	User	$user		Object user
4077
	 *	@param 	int		$notrigger	1=Does not execute triggers, 0= execute triggers
4078
	 *	@return	 int  				<0 if ko, >0 if ok
4079
	 */
4080
	function delete(User $user, $notrigger=0)
4081
	{
4082
		global $conf;
4083
4084
		$error=0;
4085
		$this->db->begin();
4086
4087
		$sql = "DELETE FROM ".MAIN_DB_PREFIX."propaldet WHERE rowid = ".$this->rowid;
4088
		dol_syslog("PropaleLigne::delete", LOG_DEBUG);
4089
		if ($this->db->query($sql) )
4090
		{
4091
4092
			// Remove extrafields
4093
			if ((! $error) && (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED))) // For avoid conflicts if trigger used
4094
			{
4095
				$this->id=$this->rowid;
4096
				$result=$this->deleteExtraFields();
4097
				if ($result < 0)
4098
				{
4099
					$error++;
4100
					dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
4101
				}
4102
			}
4103
4104
			if (! $error && ! $notrigger)
4105
			{
4106
				// Call trigger
4107
				$result=$this->call_trigger('LINEPROPAL_DELETE',$user);
4108
				if ($result < 0)
4109
				{
4110
					$this->db->rollback();
4111
					return -1;
4112
				}
4113
			}
4114
			// End call triggers
4115
4116
			$this->db->commit();
4117
4118
			return 1;
4119
		}
4120
		else
4121
		{
4122
			$this->error=$this->db->error()." sql=".$sql;
4123
			$this->db->rollback();
4124
			return -1;
4125
		}
4126
	}
4127
4128
	/**
4129
	 *	Update propal line object into DB
4130
	 *
4131
	 *	@param 	int		$notrigger	1=Does not execute triggers, 0= execute triggers
4132
	 *	@return	int					<0 if ko, >0 if ok
4133
	 */
4134
	function update($notrigger=0)
4135
	{
4136
		global $conf,$user;
4137
4138
		$error=0;
4139
4140
		$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'.
4141
4142
		// Clean parameters
4143
		if (empty($this->tva_tx)) $this->tva_tx=0;
4144
		if (empty($this->localtax1_tx)) $this->localtax1_tx=0;
4145
		if (empty($this->localtax2_tx)) $this->localtax2_tx=0;
4146
		if (empty($this->total_localtax1)) $this->total_localtax1=0;
4147
		if (empty($this->total_localtax2)) $this->total_localtax2=0;
4148
		if (empty($this->localtax1_type)) $this->localtax1_type=0;
4149
		if (empty($this->localtax2_type)) $this->localtax2_type=0;
4150
		if (empty($this->marque_tx)) $this->marque_tx=0;
4151
		if (empty($this->marge_tx)) $this->marge_tx=0;
4152
		if (empty($this->price)) $this->price=0;	// TODO A virer
4153
		if (empty($this->remise)) $this->remise=0;	// TODO A virer
4154
		if (empty($this->remise_percent)) $this->remise_percent=0;
4155
		if (empty($this->info_bits)) $this->info_bits=0;
4156
		if (empty($this->special_code)) $this->special_code=0;
4157
		if (empty($this->fk_parent_line)) $this->fk_parent_line=0;
4158
		if (empty($this->fk_fournprice)) $this->fk_fournprice=0;
4159
		if (empty($this->subprice)) $this->subprice=0;
4160
		if (empty($this->pa_ht)) $this->pa_ht=0;
4161
4162
		// if buy price not defined, define buyprice as configured in margin admin
4163
		if ($this->pa_ht == 0 && $pa_ht_isemptystring)
4164
		{
4165
			if (($result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product)) < 0)
4166
			{
4167
				return $result;
4168
			}
4169
			else
4170
			{
4171
				$this->pa_ht = $result;
4172
			}
4173
		}
4174
4175
		$this->db->begin();
4176
4177
		// Mise a jour ligne en base
4178
		$sql = "UPDATE ".MAIN_DB_PREFIX."propaldet SET";
4179
		$sql.= " description='".$this->db->escape($this->desc)."'";
4180
		$sql.= ", label=".(! empty($this->label)?"'".$this->db->escape($this->label)."'":"null");
4181
		$sql.= ", product_type=".$this->product_type;
4182
		$sql.= ", vat_src_code = '".(empty($this->vat_src_code)?'':$this->vat_src_code)."'";
4183
		$sql.= ", tva_tx='".price2num($this->tva_tx)."'";
4184
		$sql.= ", localtax1_tx=".price2num($this->localtax1_tx);
4185
		$sql.= ", localtax2_tx=".price2num($this->localtax2_tx);
4186
		$sql.= ", localtax1_type='".$this->db->escape($this->localtax1_type)."'";
4187
		$sql.= ", localtax2_type='".$this->db->escape($this->localtax2_type)."'";
4188
		$sql.= ", qty='".price2num($this->qty)."'";
4189
		$sql.= ", subprice=".price2num($this->subprice)."";
4190
		$sql.= ", remise_percent=".price2num($this->remise_percent)."";
4191
		$sql.= ", price=".price2num($this->price)."";					// TODO A virer
4192
		$sql.= ", remise=".price2num($this->remise)."";				// TODO A virer
4193
		$sql.= ", info_bits='".$this->db->escape($this->info_bits)."'";
4194
		if (empty($this->skip_update_total))
4195
		{
4196
			$sql.= ", total_ht=".price2num($this->total_ht)."";
4197
			$sql.= ", total_tva=".price2num($this->total_tva)."";
4198
			$sql.= ", total_ttc=".price2num($this->total_ttc)."";
4199
			$sql.= ", total_localtax1=".price2num($this->total_localtax1)."";
4200
			$sql.= ", total_localtax2=".price2num($this->total_localtax2)."";
4201
		}
4202
		$sql.= ", fk_product_fournisseur_price=".(! empty($this->fk_fournprice)?"'".$this->db->escape($this->fk_fournprice)."'":"null");
4203
		$sql.= ", buy_price_ht=".price2num($this->pa_ht);
4204
		if (strlen($this->special_code)) $sql.= ", special_code=".$this->special_code;
4205
		$sql.= ", fk_parent_line=".($this->fk_parent_line>0?$this->fk_parent_line:"null");
4206
		if (! empty($this->rang)) $sql.= ", rang=".$this->rang;
4207
		$sql.= ", date_start=".(! empty($this->date_start)?"'".$this->db->idate($this->date_start)."'":"null");
4208
		$sql.= ", date_end=".(! empty($this->date_end)?"'".$this->db->idate($this->date_end)."'":"null");
4209
		$sql.= ", fk_unit=".(!$this->fk_unit ? 'NULL' : $this->fk_unit);
4210
4211
		// Multicurrency
4212
		$sql.= ", multicurrency_subprice=".price2num($this->multicurrency_subprice)."";
4213
		$sql.= ", multicurrency_total_ht=".price2num($this->multicurrency_total_ht)."";
4214
		$sql.= ", multicurrency_total_tva=".price2num($this->multicurrency_total_tva)."";
4215
		$sql.= ", multicurrency_total_ttc=".price2num($this->multicurrency_total_ttc)."";
4216
4217
		$sql.= " WHERE rowid = ".$this->rowid;
4218
4219
		dol_syslog(get_class($this)."::update", LOG_DEBUG);
4220
		$resql=$this->db->query($sql);
4221
		if ($resql)
4222
		{
4223
			if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) // For avoid conflicts if trigger used
4224
			{
4225
				$this->id=$this->rowid;
4226
				$result=$this->insertExtraFields();
4227
				if ($result < 0)
4228
				{
4229
					$error++;
4230
				}
4231
			}
4232
4233
			if (! $error && ! $notrigger)
4234
			{
4235
				// Call trigger
4236
				$result=$this->call_trigger('LINEPROPAL_UPDATE',$user);
4237
				if ($result < 0)
4238
				{
4239
					$this->db->rollback();
4240
					return -1;
4241
				}
4242
				// End call triggers
4243
			}
4244
4245
			$this->db->commit();
4246
			return 1;
4247
		}
4248
		else
4249
		{
4250
			$this->error=$this->db->error();
4251
			$this->db->rollback();
4252
			return -2;
4253
		}
4254
	}
4255
4256
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
4257
	/**
4258
	 *	Update DB line fields total_xxx
4259
	 *	Used by migration
4260
	 *
4261
	 *	@return		int		<0 if KO, >0 if OK
4262
	 */
4263
	function update_total()
4264
	{
4265
        // phpcs:enable
4266
		$this->db->begin();
4267
4268
		// Mise a jour ligne en base
4269
		$sql = "UPDATE ".MAIN_DB_PREFIX."propaldet SET";
4270
		$sql.= " total_ht=".price2num($this->total_ht,'MT')."";
4271
		$sql.= ",total_tva=".price2num($this->total_tva,'MT')."";
4272
		$sql.= ",total_ttc=".price2num($this->total_ttc,'MT')."";
4273
		$sql.= " WHERE rowid = ".$this->rowid;
4274
4275
		dol_syslog("PropaleLigne::update_total", LOG_DEBUG);
4276
4277
		$resql=$this->db->query($sql);
4278
		if ($resql)
4279
		{
4280
			$this->db->commit();
4281
			return 1;
4282
		}
4283
		else
4284
		{
4285
			$this->error=$this->db->error();
4286
			$this->db->rollback();
4287
			return -2;
4288
		}
4289
	}
4290
}
4291