Completed
Branch develop (a0152f)
by
unknown
34:49
created

Propal::InvoiceArrayList()   C

Complexity

Conditions 12
Paths 42

Size

Total Lines 78
Code Lines 38

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 12
eloc 38
nc 42
nop 1
dl 0
loc 78
rs 5.1746
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
/* 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
 *
16
 * This program is free software; you can redistribute it and/or modify
17
 * it under the terms of the GNU General Public License as published by
18
 * the Free Software Foundation; either version 3 of the License, or
19
 * (at your option) any later version.
20
 *
21
 * This program is distributed in the hope that it will be useful,
22
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24
 * GNU General Public License for more details.
25
 *
26
 * You should have received a copy of the GNU General Public License
27
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
28
 */
29
30
/**
31
 *	\file       htdocs/comm/propal/class/propal.class.php
32
 *	\brief      File of class to manage proposals
33
 */
34
35
require_once DOL_DOCUMENT_ROOT .'/core/class/commonobject.class.php';
36
require_once DOL_DOCUMENT_ROOT ."/core/class/commonobjectline.class.php";
37
require_once DOL_DOCUMENT_ROOT .'/product/class/product.class.php';
38
require_once DOL_DOCUMENT_ROOT .'/contact/class/contact.class.php';
39
require_once DOL_DOCUMENT_ROOT .'/margin/lib/margins.lib.php';
40
require_once DOL_DOCUMENT_ROOT .'/multicurrency/class/multicurrency.class.php';
41
42
/**
43
 *	Class to manage proposals
44
 */
45
class Propal extends CommonObject
46
{
47
	public $element='propal';
48
	public $table_element='propal';
49
	public $table_element_line='propaldet';
50
	public $fk_element='fk_propal';
51
	public $picto='propal';
52
	/**
53
	 * 0=No test on entity, 1=Test with field entity, 2=Test with link by societe
54
	 * @var int
55
	 */
56
	public $ismultientitymanaged = 1;
57
	/**
58
	 * 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
59
	 * @var integer
60
	 */
61
	public $restrictiononfksoc = 1;
62
63
	/**
64
	 * {@inheritdoc}
65
	 */
66
	protected $table_ref_field = 'ref';
67
68
	/**
69
	 * ID of the client
70
	 * @var int
71
	 */
72
	public $socid;
73
74
	public $contactid;
75
	public $author;
76
	public $ref_client;
77
78
	/**
79
	 * Status of the quote
80
	 * @var int
81
	 * @see Propal::STATUS_DRAFT, Propal::STATUS_VALIDATED, Propal::STATUS_SIGNED, Propal::STATUS_NOTSIGNED, Propal::STATUS_BILLED
82
	 */
83
	public $statut;
84
85
	/**
86
	 * @deprecated
87
	 * @see date_creation
88
	 */
89
	public $datec;
90
91
	/**
92
	 * Creation date
93
	 * @var int
94
	 */
95
	public $date_creation;
96
97
	/**
98
	 * @deprecated
99
	 * @see date_validation
100
	 */
101
	public $datev;
102
103
	/**
104
	 * Validation date
105
	 * @var int
106
	 */
107
	public $date_validation;
108
109
	/**
110
	 * Date of the quote
111
	 * @var
112
	 */
113
	public $date;
114
115
	/**
116
	 * @deprecated
117
	 * @see date
118
	 */
119
	public $datep;
120
	public $date_livraison;
121
	public $fin_validite;
122
123
	public $user_author_id;
124
	public $user_valid_id;
125
	public $user_close_id;
126
127
	/**
128
	 * @deprecated
129
	 * @see total_ht
130
	 */
131
	public $price;
132
	/**
133
	 * @deprecated
134
	 * @see total_tva
135
	 */
136
	public $tva;
137
	/**
138
	 * @deprecated
139
	 * @see total_ttc
140
	 */
141
	public $total;
142
143
	public $cond_reglement_code;
144
	public $mode_reglement_code;
145
	public $remise = 0;
146
	public $remise_percent = 0;
147
	public $remise_absolue = 0;
148
	public $fk_address;
149
	public $address_type;
150
	public $address;
151
	public $availability_id;
152
	public $availability_code;
153
	public $demand_reason_id;
154
	public $demand_reason_code;
155
156
	public $products=array();
157
	public $extraparams=array();
158
159
	/**
160
	 * @var PropaleLigne[]
161
	 */
162
	public $lines = array();
163
	public $line;
164
165
	public $labelstatut=array();
166
	public $labelstatut_short=array();
167
168
	public $specimen;
169
170
	// Multicurrency
171
	public $fk_multicurrency;
172
	public $multicurrency_code;
173
	public $multicurrency_tx;
174
	public $multicurrency_total_ht;
175
	public $multicurrency_total_tva;
176
	public $multicurrency_total_ttc;
177
178
	public $oldcopy;
179
180
	/**
181
	 * Draft status
182
	 */
183
	const STATUS_DRAFT = 0;
184
	/**
185
	 * Validated status
186
	 */
187
	const STATUS_VALIDATED = 1;
188
	/**
189
	 * Signed quote
190
	 */
191
	const STATUS_SIGNED = 2;
192
	/**
193
	 * Not signed quote
194
	 */
195
	const STATUS_NOTSIGNED = 3;
196
	/**
197
	 * Billed or processed quote
198
	 */
199
	const STATUS_BILLED = 4;   // Todo rename into STATUS_CLOSE ?
200
201
202
	/**
203
	 *	Constructor
204
	 *
205
	 *	@param      DoliDB	$db         Database handler
206
	 *	@param      int		$socid		Id third party
207
	 *	@param      int		$propalid   Id proposal
208
	 */
209
	function __construct($db, $socid="", $propalid=0)
210
	{
211
		global $conf,$langs;
212
213
		$this->db = $db;
214
215
		$this->socid = $socid;
216
		$this->id = $propalid;
217
218
		$this->products = array();
219
220
		$this->duree_validite=$conf->global->PROPALE_VALIDITY_DURATION;
221
	}
222
223
224
	/**
225
	 * 	Add line into array products
226
	 *	$this->thirdparty should be loaded
227
	 *
228
	 * 	@param  int		$idproduct       	Product Id to add
229
	 * 	@param  int		$qty             	Quantity
230
	 * 	@param  int		$remise_percent  	Discount effected on Product
231
	 *  @return	int							<0 if KO, >0 if OK
232
	 *
233
	 *	TODO	Replace calls to this function by generation objet Ligne
234
	 *			inserted into table $this->products
235
	 */
236
	function add_product($idproduct, $qty, $remise_percent=0)
237
	{
238
		global $conf, $mysoc;
239
240
		if (! $qty) $qty = 1;
241
242
		dol_syslog(get_class($this)."::add_product $idproduct, $qty, $remise_percent");
243
		if ($idproduct > 0)
244
		{
245
			$prod=new Product($this->db);
246
			$prod->fetch($idproduct);
247
248
			$productdesc = $prod->description;
249
250
			$tva_tx = get_default_tva($mysoc,$this->thirdparty,$prod->id);
251
			$tva_npr = get_default_npr($mysoc,$this->thirdparty,$prod->id);
252
			if (empty($tva_tx)) $tva_npr=0;
253
			$vat_src_code = '';     // May be defined into tva_tx
254
255
			$localtax1_tx = get_localtax($tva_tx,1,$mysoc,$this->thirdparty,$tva_npr);
256
			$localtax2_tx = get_localtax($tva_tx,2,$mysoc,$this->thirdparty,$tva_npr);
257
258
			// multiprices
259
			if($conf->global->PRODUIT_MULTIPRICES && $this->thirdparty->price_level)
260
			{
261
				$price = $prod->multiprices[$this->thirdparty->price_level];
262
			}
263
			else
264
			{
265
				$price = $prod->price;
266
			}
267
268
			$line = new PropaleLigne($this->db);
269
270
			$line->fk_product=$idproduct;
271
			$line->desc=$productdesc;
272
			$line->qty=$qty;
273
			$line->subprice=$price;
274
			$line->remise_percent=$remise_percent;
275
			$line->vat_src_code=$vat_src_code;
276
			$line->tva_tx=$tva_tx;
277
			$line->fk_unit=$prod->fk_unit;
278
			if ($tva_npr) $line->info_bits = 1;
279
280
			$this->lines[]=$line;
281
		}
282
	}
283
284
	/**
285
	 *	Adding line of fixed discount in the proposal in DB
286
	 *
287
	 *	@param     int		$idremise			Id of fixed discount
288
	 *  @return    int          				>0 if OK, <0 if KO
289
	 */
290
	function insert_discount($idremise)
291
	{
292
		global $langs;
293
294
		include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
295
		include_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
296
297
		$this->db->begin();
298
299
		$remise=new DiscountAbsolute($this->db);
300
		$result=$remise->fetch($idremise);
301
302
		if ($result > 0)
303
		{
304
			if ($remise->fk_facture)	// Protection against multiple submission
305
			{
306
				$this->error=$langs->trans("ErrorDiscountAlreadyUsed");
307
				$this->db->rollback();
308
				return -5;
309
			}
310
311
			$line=new PropaleLigne($this->db);
312
313
			$this->line->context = $this->context;
314
315
			$line->fk_propal=$this->id;
316
			$line->fk_remise_except=$remise->id;
317
			$line->desc=$remise->description;   	// Description ligne
318
			$line->vat_src_code=$remise->vat_src_code;
319
			$line->tva_tx=$remise->tva_tx;
320
			$line->subprice=-$remise->amount_ht;
321
			$line->fk_product=0;					// Id produit predefined
322
			$line->qty=1;
323
			$line->remise=0;
324
			$line->remise_percent=0;
325
			$line->rang=-1;
326
			$line->info_bits=2;
327
328
			// TODO deprecated
329
			$line->price=-$remise->amount_ht;
330
331
			$line->total_ht  = -$remise->amount_ht;
332
			$line->total_tva = -$remise->amount_tva;
333
			$line->total_ttc = -$remise->amount_ttc;
334
335
			$result=$line->insert();
336
			if ($result > 0)
337
			{
338
				$result=$this->update_price(1);
339
				if ($result > 0)
340
				{
341
					$this->db->commit();
342
					return 1;
343
				}
344
				else
345
				{
346
					$this->db->rollback();
347
					return -1;
348
				}
349
			}
350
			else
351
			{
352
				$this->error=$line->error;
353
				$this->db->rollback();
354
				return -2;
355
			}
356
		}
357
		else
358
		{
359
			$this->db->rollback();
360
			return -2;
361
		}
362
	}
363
364
    /**
365
     *    	Add a proposal line into database (linked to product/service or not)
366
     *      The parameters are already supposed to be appropriate and with final values to the call
367
     *      of this method. Also, for the VAT rate, it must have already been defined
368
     *      by whose calling the method get_default_tva (societe_vendeuse, societe_acheteuse, '' product)
369
     *      and desc must already have the right value (it's up to the caller to manage multilanguage)
370
     *
371
     * 		@param    	string		$desc				Description of line
372
     * 		@param    	float		$pu_ht				Unit price
373
     * 		@param    	float		$qty             	Quantity
374
     * 		@param    	float		$txtva           	Force Vat rate, -1 for auto (Can contain the vat_src_code too with syntax '9.9 (CODE)')
375
     * 		@param		float		$txlocaltax1		Local tax 1 rate (deprecated, use instead txtva with code inside)
376
     *  	@param		float		$txlocaltax2		Local tax 2 rate (deprecated, use instead txtva with code inside)
377
     *		@param    	int			$fk_product      	Id du produit/service predefini
378
     * 		@param    	float		$remise_percent  	Pourcentage de remise de la ligne
379
     * 		@param    	string		$price_base_type	HT or TTC
380
     * 		@param    	float		$pu_ttc             Prix unitaire TTC
381
     * 		@param    	int			$info_bits			Bits de type de lignes
382
     *      @param      int			$type               Type of line (0=product, 1=service). Not used if fk_product is defined, the type of product is used.
383
     *      @param      int			$rang               Position of line
384
     *      @param		int			$special_code		Special code (also used by externals modules!)
385
     *      @param		int			$fk_parent_line		Id of parent line
386
     *      @param		int			$fk_fournprice		Id supplier price
387
     *      @param		int			$pa_ht				Buying price without tax
388
     *      @param		string		$label				???
389
     *		@param      int			$date_start       	Start date of the line
390
     *		@param      int			$date_end         	End date of the line
391
     *      @param		array		$array_options		extrafields array
392
     * 		@param 		string		$fk_unit 			Code of the unit to use. Null to use the default one
393
     *      @param		string		$origin				'order', ...
394
     *      @param		int			$origin_id			Id of origin object
395
     * 		@param		double		$pu_ht_devise		Unit price in currency
396
     * 		@param		int    		$fk_remise_except	Id discount if line is from a discount
397
     *    	@return    	int         	    			>0 if OK, <0 if KO
398
     *    	@see       	add_product
399
     */
400
	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)
401
	{
402
		global $mysoc, $conf, $langs;
403
404
		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);
405
		include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
406
407
		// Clean parameters
408
		if (empty($remise_percent)) $remise_percent=0;
409
		if (empty($qty)) $qty=0;
410
		if (empty($info_bits)) $info_bits=0;
411
		if (empty($rang)) $rang=0;
412
		if (empty($fk_parent_line) || $fk_parent_line < 0) $fk_parent_line=0;
413
414
		$remise_percent=price2num($remise_percent);
415
		$qty=price2num($qty);
416
		$pu_ht=price2num($pu_ht);
417
		$pu_ttc=price2num($pu_ttc);
418
		$txtva=price2num($txtva);               // $txtva can have format '5.0(XXX)' or '5'
419
		$txlocaltax1=price2num($txlocaltax1);
420
		$txlocaltax2=price2num($txlocaltax2);
421
		$pa_ht=price2num($pa_ht);
422
		if ($price_base_type=='HT')
423
		{
424
			$pu=$pu_ht;
425
		}
426
		else
427
		{
428
			$pu=$pu_ttc;
429
		}
430
431
		// Check parameters
432
		if ($type < 0) return -1;
433
434
		if ($this->statut == self::STATUS_DRAFT)
435
		{
436
			$this->db->begin();
437
438
			$product_type=$type;
439
			if (!empty($fk_product))
440
			{
441
				$product=new Product($this->db);
442
				$result=$product->fetch($fk_product);
443
				$product_type=$product->type;
444
445
				if (! empty($conf->global->STOCK_MUST_BE_ENOUGH_FOR_PROPOSAL) && $product_type == 0 && $product->stock_reel < $qty) {
446
					$langs->load("errors");
447
					$this->error=$langs->trans('ErrorStockIsNotEnoughToAddProductOnProposal', $product->ref);
448
					$this->db->rollback();
449
					return -3;
450
				}
451
			}
452
453
			// Calcul du total TTC et de la TVA pour la ligne a partir de
454
			// qty, pu, remise_percent et txtva
455
			// TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
456
			// la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
457
458
			$localtaxes_type=getLocalTaxesFromRate($txtva,0,$this->thirdparty,$mysoc);
459
460
			// Clean vat code
461
			$vat_src_code='';
462
			if (preg_match('/\((.*)\)/', $txtva, $reg))
463
			{
464
				$vat_src_code = $reg[1];
465
				$txtva = preg_replace('/\s*\(.*\)/', '', $txtva);    // Remove code into vatrate.
466
			}
467
468
			$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);
469
470
			$total_ht  = $tabprice[0];
471
			$total_tva = $tabprice[1];
472
			$total_ttc = $tabprice[2];
473
			$total_localtax1 = $tabprice[9];
474
			$total_localtax2 = $tabprice[10];
475
			$pu_ht  = $tabprice[3];
476
			$pu_tva = $tabprice[4];
477
			$pu_ttc = $tabprice[5];
478
479
			// MultiCurrency
480
			$multicurrency_total_ht  = $tabprice[16];
481
			$multicurrency_total_tva = $tabprice[17];
482
			$multicurrency_total_ttc = $tabprice[18];
483
			$pu_ht_devise = $tabprice[19];
484
485
			// Rang to use
486
			$rangtouse = $rang;
487
			if ($rangtouse == -1)
488
			{
489
				$rangmax = $this->line_max($fk_parent_line);
490
				$rangtouse = $rangmax + 1;
491
			}
492
493
			// TODO A virer
494
			// Anciens indicateurs: $price, $remise (a ne plus utiliser)
495
			$price = $pu;
496
			$remise = 0;
497
			if ($remise_percent > 0)
498
			{
499
				$remise = round(($pu * $remise_percent / 100), 2);
500
				$price = $pu - $remise;
501
			}
502
503
			// Insert line
504
			$this->line=new PropaleLigne($this->db);
505
506
			$this->line->context = $this->context;
507
508
			$this->line->fk_propal=$this->id;
509
			$this->line->label=$label;
510
			$this->line->desc=$desc;
511
			$this->line->qty=$qty;
512
513
			$this->line->vat_src_code=$vat_src_code;
514
			$this->line->tva_tx=$txtva;
515
			$this->line->localtax1_tx=($total_localtax1?$localtaxes_type[1]:0);
516
			$this->line->localtax2_tx=($total_localtax2?$localtaxes_type[3]:0);
517
			$this->line->localtax1_type = $localtaxes_type[0];
518
			$this->line->localtax2_type = $localtaxes_type[2];
519
			$this->line->fk_product=$fk_product;
520
			$this->line->product_type=$type;
521
			$this->line->fk_remise_except=$fk_remise_except;
522
			$this->line->remise_percent=$remise_percent;
523
			$this->line->subprice=$pu_ht;
524
			$this->line->rang=$rangtouse;
525
			$this->line->info_bits=$info_bits;
526
			$this->line->total_ht=$total_ht;
527
			$this->line->total_tva=$total_tva;
528
			$this->line->total_localtax1=$total_localtax1;
529
			$this->line->total_localtax2=$total_localtax2;
530
			$this->line->total_ttc=$total_ttc;
531
			$this->line->special_code=$special_code;
532
			$this->line->fk_parent_line=$fk_parent_line;
533
			$this->line->fk_unit=$fk_unit;
534
535
			$this->line->date_start=$date_start;
536
			$this->line->date_end=$date_end;
537
538
			$this->line->fk_fournprice = $fk_fournprice;
539
			$this->line->pa_ht = $pa_ht;
540
541
			$this->line->origin_id = $origin_id;
542
			$this->line->origin = $origin;
543
544
			// Multicurrency
545
			$this->line->fk_multicurrency			= $this->fk_multicurrency;
546
			$this->line->multicurrency_code			= $this->multicurrency_code;
547
			$this->line->multicurrency_subprice		= $pu_ht_devise;
548
			$this->line->multicurrency_total_ht 	= $multicurrency_total_ht;
549
			$this->line->multicurrency_total_tva 	= $multicurrency_total_tva;
550
			$this->line->multicurrency_total_ttc 	= $multicurrency_total_ttc;
551
552
			// Mise en option de la ligne
553
			if (empty($qty) && empty($special_code)) $this->line->special_code=3;
554
555
			// TODO deprecated
556
			$this->line->price=$price;
557
			$this->line->remise=$remise;
558
559
			if (is_array($array_options) && count($array_options)>0) {
560
				$this->line->array_options=$array_options;
561
			}
562
563
			$result=$this->line->insert();
564
			if ($result > 0)
565
			{
566
				// Reorder if child line
567
				if (! empty($fk_parent_line)) $this->line_order(true,'DESC');
568
569
                // Mise a jour informations denormalisees au niveau de la propale meme
570
                $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.
571
                if ($result > 0)
572
                {
573
                    $this->db->commit();
574
                    return $this->line->rowid;
575
                }
576
                else
577
                {
578
                    $this->error=$this->db->error();
579
                    $this->db->rollback();
580
                    return -1;
581
                }
582
            }
583
            else
584
            {
585
                $this->error=$this->line->error;
586
                $this->db->rollback();
587
                return -2;
588
            }
589
        }
590
		else
591
		{
592
			dol_syslog(get_class($this)."::addline status of order must be Draft to allow use of ->addline()", LOG_ERR);
593
			return -3;
594
		}
595
    }
596
597
598
	/**
599
	 *  Update a proposal line
600
	 *
601
	 *  @param      int			$rowid           	Id de la ligne
602
	 *  @param      float		$pu		     	  	Prix unitaire (HT ou TTC selon price_base_type)
603
	 *  @param      float		$qty            	Quantity
604
	 *  @param      float		$remise_percent  	Remise effectuee sur le produit
605
	 *  @param      float		$txtva	          	Taux de TVA
606
	 * 	@param	  	float		$txlocaltax1		Local tax 1 rate
607
	 *  @param	  	float		$txlocaltax2		Local tax 2 rate
608
	 *  @param      string		$desc            	Description
609
	 *	@param	  	string		$price_base_type	HT ou TTC
610
	 *	@param      int			$info_bits        	Miscellaneous informations
611
	 *	@param		int			$special_code		Special code (also used by externals modules!)
612
	 * 	@param		int			$fk_parent_line		Id of parent line (0 in most cases, used by modules adding sublevels into lines).
613
	 * 	@param		int			$skip_update_total	Keep fields total_xxx to 0 (used for special lines by some modules)
614
	 *  @param		int			$fk_fournprice		Id of origin supplier price
615
	 *  @param		int			$pa_ht				Price (without tax) of product when it was bought
616
	 *  @param		string		$label				???
617
	 *  @param		int			$type				0/1=Product/service
618
	 *	@param      int			$date_start       	Start date of the line
619
	 *	@param      int			$date_end         	End date of the line
620
	 *  @param		array		$array_options		extrafields array
621
	 * 	@param 		string		$fk_unit 			Code of the unit to use. Null to use the default one
622
	 * 	@param		double		$pu_ht_devise		Unit price in currency
623
	 * 	@param		int			$notrigger			disable line update trigger
624
	 *  @return     int     		        		0 if OK, <0 if KO
625
	 */
626
	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)
627
	{
628
		global $mysoc;
629
630
		dol_syslog(get_class($this)."::updateLine rowid=$rowid, pu=$pu, qty=$qty, remise_percent=$remise_percent,
631
        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");
632
		include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
633
634
		// Clean parameters
635
		$remise_percent=price2num($remise_percent);
636
		$qty=price2num($qty);
637
		$pu = price2num($pu);
638
		$txtva = price2num($txtva);
639
		$txlocaltax1=price2num($txlocaltax1);
640
		$txlocaltax2=price2num($txlocaltax2);
641
		$pa_ht=price2num($pa_ht);
642
		if (empty($qty) && empty($special_code)) $special_code=3;    // Set option tag
643
		if (! empty($qty) && $special_code == 3) $special_code=0;    // Remove option tag
644
		if (empty($type)) $type=0;
645
646
		if ($this->statut == self::STATUS_DRAFT)
647
		{
648
			$this->db->begin();
649
650
			// Calcul du total TTC et de la TVA pour la ligne a partir de
651
			// qty, pu, remise_percent et txtva
652
			// TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
653
			// la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
654
655
			$localtaxes_type=getLocalTaxesFromRate($txtva,0,$this->thirdparty,$mysoc);
656
657
			// Clean vat code
658
			$vat_src_code='';
659
			if (preg_match('/\((.*)\)/', $txtva, $reg))
660
			{
661
				$vat_src_code = $reg[1];
662
				$txtva = preg_replace('/\s*\(.*\)/', '', $txtva);    // Remove code into vatrate.
663
			}
664
665
			$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);
666
			$total_ht  = $tabprice[0];
667
			$total_tva = $tabprice[1];
668
			$total_ttc = $tabprice[2];
669
			$total_localtax1 = $tabprice[9];
670
			$total_localtax2 = $tabprice[10];
671
			$pu_ht  = $tabprice[3];
672
			$pu_tva = $tabprice[4];
673
			$pu_ttc = $tabprice[5];
674
675
			// MultiCurrency
676
			$multicurrency_total_ht  = $tabprice[16];
677
			$multicurrency_total_tva = $tabprice[17];
678
			$multicurrency_total_ttc = $tabprice[18];
679
			$pu_ht_devise = $tabprice[19];
680
681
			// Anciens indicateurs: $price, $remise (a ne plus utiliser)
682
			$price = $pu;
683
			$remise = 0;
684
			if ($remise_percent > 0)
685
			{
686
				$remise = round(($pu * $remise_percent / 100), 2);
687
				$price = $pu - $remise;
688
			}
689
690
			//Fetch current line from the database and then clone the object and set it in $oldline property
691
			$line = new PropaleLigne($this->db);
692
			$line->fetch($rowid);
693
			$line->fetch_optionals(); // Fetch extrafields for oldcopy
694
695
			$staticline = clone $line;
696
697
			$line->oldline = $staticline;
698
			$this->line = $line;
699
			$this->line->context = $this->context;
700
701
			// Reorder if fk_parent_line change
702
			if (! empty($fk_parent_line) && ! empty($staticline->fk_parent_line) && $fk_parent_line != $staticline->fk_parent_line)
703
			{
704
				$rangmax = $this->line_max($fk_parent_line);
705
				$this->line->rang = $rangmax + 1;
706
			}
707
708
			$this->line->rowid				= $rowid;
709
			$this->line->label				= $label;
710
			$this->line->desc				= $desc;
711
			$this->line->qty				= $qty;
712
			$this->line->product_type		= $type;
713
			$this->line->vat_src_code		= $vat_src_code;
714
			$this->line->tva_tx				= $txtva;
715
			$this->line->localtax1_tx		= $txlocaltax1;
716
			$this->line->localtax2_tx		= $txlocaltax2;
717
			$this->line->localtax1_type		= $localtaxes_type[0];
718
			$this->line->localtax2_type		= $localtaxes_type[2];
719
			$this->line->remise_percent		= $remise_percent;
720
			$this->line->subprice			= $pu_ht;
721
			$this->line->info_bits			= $info_bits;
722
723
			$this->line->total_ht			= $total_ht;
724
			$this->line->total_tva			= $total_tva;
725
			$this->line->total_localtax1	= $total_localtax1;
726
			$this->line->total_localtax2	= $total_localtax2;
727
			$this->line->total_ttc			= $total_ttc;
728
			$this->line->special_code		= $special_code;
729
			$this->line->fk_parent_line		= $fk_parent_line;
730
			$this->line->skip_update_total	= $skip_update_total;
731
			$this->line->fk_unit	= $fk_unit;
732
733
			$this->line->fk_fournprice = $fk_fournprice;
734
			$this->line->pa_ht = $pa_ht;
735
736
			$this->line->date_start=$date_start;
737
			$this->line->date_end=$date_end;
738
739
			// TODO deprecated
740
			$this->line->price=$price;
741
			$this->line->remise=$remise;
742
743
			if (is_array($array_options) && count($array_options)>0) {
744
				$this->line->array_options=$array_options;
745
			}
746
747
			// Multicurrency
748
			$this->line->multicurrency_subprice		= $pu_ht_devise;
749
			$this->line->multicurrency_total_ht 	= $multicurrency_total_ht;
750
			$this->line->multicurrency_total_tva 	= $multicurrency_total_tva;
751
			$this->line->multicurrency_total_ttc 	= $multicurrency_total_ttc;
752
753
			$result=$this->line->update($notrigger);
754
			if ($result > 0)
755
			{
756
				// Reorder if child line
757
				if (! empty($fk_parent_line)) $this->line_order(true,'DESC');
758
759
				$this->update_price(1);
760
761
				$this->fk_propal = $this->id;
762
				$this->rowid = $rowid;
763
764
				$this->db->commit();
765
				return $result;
766
			}
767
			else
768
			{
769
				$this->error=$this->line->error;
770
771
				$this->db->rollback();
772
				return -1;
773
			}
774
		}
775
		else
776
		{
777
			dol_syslog(get_class($this)."::updateline Erreur -2 Propal en mode incompatible pour cette action");
778
			return -2;
779
		}
780
	}
781
782
783
	/**
784
	 *  Delete detail line
785
	 *
786
	 *  @param		int		$lineid			Id of line to delete
787
	 *  @return     int         			>0 if OK, <0 if KO
788
	 */
789
	function deleteline($lineid)
790
	{
791
		global $user;
792
793
		if ($this->statut == self::STATUS_DRAFT)
794
		{
795
			$this->db->begin();
796
797
			$line=new PropaleLigne($this->db);
798
799
			// For triggers
800
			$line->fetch($lineid);
801
802
			if ($line->delete($user) > 0)
803
			{
804
				$this->update_price(1);
805
806
				$this->db->commit();
807
				return 1;
808
			}
809
			else
810
			{
811
				$this->db->rollback();
812
				return -1;
813
			}
814
		}
815
		else
816
		{
817
			$this->error='ErrorDeleteLineNotAllowedByObjectStatus';
818
			return -2;
819
		}
820
	}
821
822
823
	/**
824
	 *  Create commercial proposal into database
825
	 * 	this->ref can be set or empty. If empty, we will use "(PROVid)"
826
	 *
827
	 * 	@param		User	$user		User that create
828
	 * 	@param		int		$notrigger	1=Does not execute triggers, 0= execute triggers
829
	 *  @return     int     			<0 if KO, >=0 if OK
830
	 */
831
	function create($user, $notrigger=0)
832
	{
833
		global $conf,$hookmanager;
834
		$error=0;
835
836
		$now=dol_now();
837
838
		// Clean parameters
839
		if (empty($this->date)) $this->date=$this->datep;
840
		$this->fin_validite = $this->date + ($this->duree_validite * 24 * 3600);
841
		if (empty($this->availability_id)) $this->availability_id=0;
842
		if (empty($this->demand_reason_id)) $this->demand_reason_id=0;
843
844
		// Multicurrency (test on $this->multicurrency_tx because we should take the default rate only if not using origin rate)
845
		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);
846
		else $this->fk_multicurrency = MultiCurrency::getIdFromCode($this->db, $this->multicurrency_code);
847
		if (empty($this->fk_multicurrency))
848
		{
849
			$this->multicurrency_code = $conf->currency;
850
			$this->fk_multicurrency = 0;
851
			$this->multicurrency_tx = 1;
852
		}
853
854
		dol_syslog(get_class($this)."::create");
855
856
		// Check parameters
857
		$result=$this->fetch_thirdparty();
858
		if ($result < 0)
859
		{
860
			$this->error="Failed to fetch company";
861
			dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR);
862
			return -3;
863
		}
864
865
		// Check parameters
866
		if (! empty($this->ref))	// We check that ref is not already used
867
		{
868
			$result=self::isExistingObject($this->element, 0, $this->ref);	// Check ref is not yet used
869
			if ($result > 0)
870
			{
871
				$this->error='ErrorRefAlreadyExists';
872
				dol_syslog(get_class($this)."::create ".$this->error,LOG_WARNING);
873
				$this->db->rollback();
874
				return -1;
875
			}
876
		}
877
878
		if (empty($this->date))
879
		{
880
			$this->error="Date of proposal is required";
881
			dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR);
882
			return -4;
883
		}
884
885
886
		$this->db->begin();
887
888
		// Insert into database
889
		$sql = "INSERT INTO ".MAIN_DB_PREFIX."propal (";
890
		$sql.= "fk_soc";
891
		$sql.= ", price";
892
		$sql.= ", remise";
893
		$sql.= ", remise_percent";
894
		$sql.= ", remise_absolue";
895
		$sql.= ", tva";
896
		$sql.= ", total";
897
		$sql.= ", datep";
898
		$sql.= ", datec";
899
		$sql.= ", ref";
900
		$sql.= ", fk_user_author";
901
		$sql.= ", note_private";
902
		$sql.= ", note_public";
903
		$sql.= ", model_pdf";
904
		$sql.= ", fin_validite";
905
		$sql.= ", fk_cond_reglement";
906
		$sql.= ", fk_mode_reglement";
907
		$sql.= ", fk_account";
908
		$sql.= ", ref_client";
909
		$sql.= ", date_livraison";
910
		$sql.= ", fk_shipping_method";
911
		$sql.= ", fk_availability";
912
		$sql.= ", fk_input_reason";
913
		$sql.= ", fk_projet";
914
		$sql.= ", fk_incoterms";
915
		$sql.= ", location_incoterms";
916
		$sql.= ", entity";
917
		$sql.= ", fk_multicurrency";
918
		$sql.= ", multicurrency_code";
919
		$sql.= ", multicurrency_tx";
920
		$sql.= ") ";
921
		$sql.= " VALUES (";
922
		$sql.= $this->socid;
923
		$sql.= ", 0";
924
		$sql.= ", ".$this->remise;
925
		$sql.= ", ".($this->remise_percent?$this->db->escape($this->remise_percent):'NULL');
926
		$sql.= ", ".($this->remise_absolue?$this->db->escape($this->remise_absolue):'NULL');
927
		$sql.= ", 0";
928
		$sql.= ", 0";
929
		$sql.= ", '".$this->db->idate($this->date)."'";
930
		$sql.= ", '".$this->db->idate($now)."'";
931
		$sql.= ", '(PROV)'";
932
		$sql.= ", ".($user->id > 0 ? "'".$user->id."'":"NULL");
933
		$sql.= ", '".$this->db->escape($this->note_private)."'";
934
		$sql.= ", '".$this->db->escape($this->note_public)."'";
935
		$sql.= ", '".$this->db->escape($this->modelpdf)."'";
936
		$sql.= ", ".($this->fin_validite!=''?"'".$this->db->idate($this->fin_validite)."'":"NULL");
937
		$sql.= ", ".($this->cond_reglement_id > 0 ? $this->cond_reglement_id : 'NULL');
938
		$sql.= ", ".($this->mode_reglement_id > 0 ? $this->mode_reglement_id : 'NULL');
939
		$sql.= ", ".($this->fk_account>0?$this->fk_account:'NULL');
940
		$sql.= ", '".$this->db->escape($this->ref_client)."'";
941
		$sql.= ", ".($this->date_livraison!=''?"'".$this->db->idate($this->date_livraison)."'":"NULL");
942
		$sql.= ", ".($this->shipping_method_id>0?$this->shipping_method_id:'NULL');
943
		$sql.= ", ".$this->availability_id;
944
		$sql.= ", ".$this->demand_reason_id;
945
		$sql.= ", ".($this->fk_project?$this->fk_project:"null");
946
		$sql.= ", ".(int) $this->fk_incoterms;
947
		$sql.= ", '".$this->db->escape($this->location_incoterms)."'";
948
		$sql.= ", ".$conf->entity;
949
		$sql.= ", ".(int) $this->fk_multicurrency;
950
		$sql.= ", '".$this->db->escape($this->multicurrency_code)."'";
951
		$sql.= ", ".(double) $this->multicurrency_tx;
952
		$sql.= ")";
953
954
		dol_syslog(get_class($this)."::create", LOG_DEBUG);
955
		$resql=$this->db->query($sql);
956
		if ($resql)
957
		{
958
			$this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."propal");
959
960
			if ($this->id)
961
			{
962
				$this->ref='(PROV'.$this->id.')';
963
				$sql = 'UPDATE '.MAIN_DB_PREFIX."propal SET ref='".$this->db->escape($this->ref)."' WHERE rowid=".$this->id;
964
965
				dol_syslog(get_class($this)."::create", LOG_DEBUG);
966
				$resql=$this->db->query($sql);
967
				if (! $resql) $error++;
968
969
                if (! empty($this->linkedObjectsIds) && empty($this->linked_objects))	// To use new linkedObjectsIds instead of old linked_objects
970
                {
971
                	$this->linked_objects = $this->linkedObjectsIds;	// TODO Replace linked_objects with linkedObjectsIds
972
                }
973
974
                // Add object linked
975
                if (! $error && $this->id && is_array($this->linked_objects) && ! empty($this->linked_objects))
976
                {
977
                	foreach($this->linked_objects as $origin => $tmp_origin_id)
978
                	{
979
                		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, ...))
980
                		{
981
                			foreach($tmp_origin_id as $origin_id)
982
                			{
983
                				$ret = $this->add_object_linked($origin, $origin_id);
984
                				if (! $ret)
985
                				{
986
                					$this->error=$this->db->lasterror();
987
                					$error++;
988
                				}
989
                			}
990
                		}
991
                		else                                // Old behaviour, if linked_object has only one link per type, so is something like array('contract'=>id1))
992
                		{
993
                			$origin_id = $tmp_origin_id;
994
                			$ret = $this->add_object_linked($origin, $origin_id);
995
                			if (! $ret)
996
                			{
997
                				$this->error=$this->db->lasterror();
998
                				$error++;
999
                			}
1000
                		}
1001
                	}
1002
                }
1003
1004
                // Add linked object (deprecated, use ->linkedObjectsIds instead)
1005
                if (! $error && $this->origin && $this->origin_id)
1006
                {
1007
                	$ret = $this->add_object_linked();
1008
                	if (! $ret)	dol_print_error($this->db);
1009
                }
1010
1011
                /*
1012
                 *  Insertion du detail des produits dans la base
1013
                 *  Insert products detail in database
1014
                 */
1015
                if (! $error)
1016
                {
1017
                    $fk_parent_line=0;
1018
                    $num=count($this->lines);
1019
1020
					for ($i=0;$i<$num;$i++)
1021
					{
1022
                        if (! is_object($this->lines[$i]))	// If this->lines is not array of objects, coming from REST API
1023
                            {   // Convert into object this->lines[$i].
1024
                                $line = (object) $this->lines[$i];
1025
                            }
1026
                            else
1027
                            {
1028
                                    $line = $this->lines[$i];
1029
                            }
1030
						// Reset fk_parent_line for line that are not child lines or special product
1031
						if (($line->product_type != 9 && empty($line->fk_parent_line)) || $line->product_type == 9) {
1032
							$fk_parent_line = 0;
1033
						}
1034
                        // Complete vat rate with code
1035
						$vatrate = $line->tva_tx;
1036
						if ($line->vat_src_code && ! preg_match('/\(.*\)/', $vatrate)) $vatrate.=' ('.$line->vat_src_code.')';
1037
1038
						$result = $this->addline(
1039
							$line->desc,
1040
							$line->subprice,
1041
							$line->qty,
1042
							$vatrate,
1043
							$line->localtax1_tx,
1044
							$line->localtax2_tx,
1045
							$line->fk_product,
1046
							$line->remise_percent,
1047
							'HT',
1048
							0,
1049
							$line->info_bits,
1050
							$line->product_type,
1051
							$line->rang,
1052
							$line->special_code,
1053
							$fk_parent_line,
1054
							$line->fk_fournprice,
1055
							$line->pa_ht,
1056
							$line->label,
1057
							$line->date_start,
1058
							$line->date_end,
1059
							$line->array_options,
1060
							$line->fk_unit,
1061
							$this->element,
1062
							$line->id
1063
						);
1064
1065
						if ($result < 0)
1066
						{
1067
							$error++;
1068
							$this->error=$this->db->error;
1069
							dol_print_error($this->db);
1070
							break;
1071
						}
1072
						// Defined the new fk_parent_line
1073
						if ($result > 0 && $line->product_type == 9) {
1074
							$fk_parent_line = $result;
1075
						}
1076
					}
1077
				}
1078
1079
				// Add linked object
1080
				if (! $error && $this->origin && $this->origin_id)
1081
				{
1082
					$ret = $this->add_object_linked();
1083
					if (! $ret)	dol_print_error($this->db);
1084
				}
1085
1086
				// Set delivery address
1087
				if (! $error && $this->fk_delivery_address)
1088
				{
1089
					$sql = "UPDATE ".MAIN_DB_PREFIX."propal";
1090
					$sql.= " SET fk_delivery_address = ".$this->fk_delivery_address;
1091
					$sql.= " WHERE ref = '".$this->db->escape($this->ref)."'";
1092
					$sql.= " AND entity = ".$conf->entity;
1093
1094
					$result=$this->db->query($sql);
1095
				}
1096
1097
				if (! $error)
1098
				{
1099
					// Mise a jour infos denormalisees
1100
					$resql=$this->update_price(1);
1101
					if ($resql)
1102
					{
1103
						$action='update';
1104
1105
						// Actions on extra fields
1106
						if (! $error)
1107
						{
1108
							if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) // For avoid conflicts if trigger used
1109
							{
1110
								$result=$this->insertExtraFields();
1111
								if ($result < 0)
1112
								{
1113
									$error++;
1114
								}
1115
							}
1116
						}
1117
1118
						if (! $error && ! $notrigger)
1119
						{
1120
							// Call trigger
1121
							$result=$this->call_trigger('PROPAL_CREATE',$user);
1122
							if ($result < 0) { $error++; }
1123
							// End call triggers
1124
						}
1125
					}
1126
					else
1127
					{
1128
						$this->error=$this->db->lasterror();
1129
						$error++;
1130
					}
1131
				}
1132
			}
1133
			else
1134
			{
1135
				$this->error=$this->db->lasterror();
1136
				$error++;
1137
			}
1138
1139
			if (! $error)
1140
			{
1141
				$this->db->commit();
1142
				dol_syslog(get_class($this)."::create done id=".$this->id);
1143
				return $this->id;
1144
			}
1145
			else
1146
			{
1147
				$this->db->rollback();
1148
				return -2;
1149
			}
1150
		}
1151
		else
1152
		{
1153
			$this->error=$this->db->lasterror();
1154
			$this->db->rollback();
1155
			return -1;
1156
		}
1157
	}
1158
1159
1160
	/**
1161
	 *	Insert into DB a proposal object completely defined by its data members (ex, results from copy).
1162
	 *
1163
	 *	@param 		User	$user	User that create
1164
	 *	@return    	int				Id of the new object if ok, <0 if ko
1165
	 *	@see       	create
1166
	 */
1167
	function create_from($user)
1168
	{
1169
		// i love this function because $this->products is not used in create function...
1170
		$this->products=$this->lines;
1171
1172
		return $this->create($user);
1173
	}
1174
1175
	/**
1176
	 *		Load an object from its id and create a new one in database
1177
	 *
1178
	 *		@param		int				$socid			Id of thirdparty
1179
	 * 	 	@return		int								New id of clone
1180
	 */
1181
	function createFromClone($socid=0)
1182
	{
1183
		global $user,$conf,$hookmanager;
1184
1185
		dol_include_once('/projet/class/project.class.php');
1186
1187
		$this->context['createfromclone']='createfromclone';
1188
1189
		$error=0;
1190
		$now=dol_now();
1191
1192
		$this->db->begin();
1193
1194
		// get extrafields so they will be clone
1195
		foreach($this->lines as $line)
1196
			$line->fetch_optionals();
1197
1198
		// Load dest object
1199
		$clonedObj = clone $this;
1200
1201
		$objsoc=new Societe($this->db);
1202
1203
		// Change socid if needed
1204
		if (! empty($socid) && $socid != $clonedObj->socid)
1205
		{
1206
			if ($objsoc->fetch($socid) > 0)
1207
			{
1208
				$clonedObj->socid 				= $objsoc->id;
1209
				$clonedObj->cond_reglement_id	= (! empty($objsoc->cond_reglement_id) ? $objsoc->cond_reglement_id : 0);
1210
				$clonedObj->mode_reglement_id	= (! empty($objsoc->mode_reglement_id) ? $objsoc->mode_reglement_id : 0);
1211
				$clonedObj->fk_delivery_address	= '';
1212
1213
				/*if (!empty($conf->projet->enabled))
1214
                {
1215
                    $project = new Project($db);
1216
    				if ($this->fk_project > 0 && $project->fetch($this->fk_project)) {
1217
    					if ($project->socid <= 0) $clonedObj->fk_project = $this->fk_project;
1218
    					else $clonedObj->fk_project = '';
1219
    				} else {
1220
    					$clonedObj->fk_project = '';
1221
    				}
1222
                }*/
1223
				$clonedObj->fk_project = '';    // A cloned proposal is set by default to no project.
1224
			}
1225
1226
			// reset ref_client
1227
			$clonedObj->ref_client  = '';
1228
1229
			// TODO Change product price if multi-prices
1230
		}
1231
		else
1232
		{
1233
			$objsoc->fetch($clonedObj->socid);
1234
		}
1235
1236
		$clonedObj->id=0;
1237
		$clonedObj->ref='';
1238
		$clonedObj->statut=self::STATUS_DRAFT;
1239
1240
		// Clear fields
1241
		$clonedObj->user_author	= $user->id;
1242
		$clonedObj->user_valid	= '';
1243
		$clonedObj->date		= $now;
1244
		$clonedObj->datep		= $now;    // deprecated
1245
		$clonedObj->fin_validite	= $clonedObj->date + ($clonedObj->duree_validite * 24 * 3600);
1246
		if (empty($conf->global->MAIN_KEEP_REF_CUSTOMER_ON_CLONING)) $clonedObj->ref_client	= '';
1247
1248
		// Create clone
1249
		$result=$clonedObj->create($user);
1250
		if ($result < 0) $error++;
1251
		else
1252
		{
1253
			// copy internal contacts
1254
			if ($clonedObj->copy_linked_contact($this, 'internal') < 0)
1255
				$error++;
1256
1257
			// copy external contacts if same company
1258
			elseif ($this->socid == $clonedObj->socid)
1259
			{
1260
				if ($clonedObj->copy_linked_contact($this, 'external') < 0)
1261
					$error++;
1262
			}
1263
		}
1264
1265
		if (! $error)
1266
		{
1267
			// Hook of thirdparty module
1268
			if (is_object($hookmanager))
1269
			{
1270
				$parameters=array('objFrom'=>$this,'clonedObj'=>$clonedObj);
1271
				$action='';
1272
				$reshook=$hookmanager->executeHooks('createFrom',$parameters,$clonedObj,$action);    // Note that $action and $object may have been modified by some hooks
1273
				if ($reshook < 0) $error++;
1274
			}
1275
		}
1276
1277
		unset($this->context['createfromclone']);
1278
1279
		// End
1280
		if (! $error)
1281
		{
1282
			$this->db->commit();
1283
			return $clonedObj->id;
1284
		}
1285
		else
1286
		{
1287
			$this->db->rollback();
1288
			return -1;
1289
		}
1290
	}
1291
1292
	/**
1293
	 *	Load a proposal from database and its ligne array
1294
	 *
1295
	 *	@param      int			$rowid		id of object to load
1296
	 *	@param		string		$ref		Ref of proposal
1297
	 *	@return     int         			>0 if OK, <0 if KO
1298
	 */
1299
	function fetch($rowid,$ref='')
1300
	{
1301
1302
		$sql = "SELECT p.rowid, p.ref, p.entity, p.remise, p.remise_percent, p.remise_absolue, p.fk_soc";
1303
		$sql.= ", p.total, p.tva, p.localtax1, p.localtax2, p.total_ht";
1304
		$sql.= ", p.datec";
1305
		$sql.= ", p.date_valid as datev";
1306
		$sql.= ", p.datep as dp";
1307
		$sql.= ", p.fin_validite as dfv";
1308
		$sql.= ", p.date_livraison as date_livraison";
1309
		$sql.= ", p.model_pdf, p.last_main_doc, p.ref_client, p.extraparams";
1310
		$sql.= ", p.note_private, p.note_public";
1311
		$sql.= ", p.fk_projet, p.fk_statut";
1312
		$sql.= ", p.fk_user_author, p.fk_user_valid, p.fk_user_cloture";
1313
		$sql.= ", p.fk_delivery_address";
1314
		$sql.= ", p.fk_availability";
1315
		$sql.= ", p.fk_input_reason";
1316
		$sql.= ", p.fk_cond_reglement";
1317
		$sql.= ", p.fk_mode_reglement";
1318
		$sql.= ', p.fk_account';
1319
		$sql.= ", p.fk_shipping_method";
1320
		$sql.= ", p.fk_incoterms, p.location_incoterms";
1321
		$sql.= ", p.fk_multicurrency, p.multicurrency_code, p.multicurrency_tx, p.multicurrency_total_ht, p.multicurrency_total_tva, p.multicurrency_total_ttc";
1322
		$sql.= ", i.libelle as libelle_incoterms";
1323
		$sql.= ", c.label as statut_label";
1324
		$sql.= ", ca.code as availability_code, ca.label as availability";
1325
		$sql.= ", dr.code as demand_reason_code, dr.label as demand_reason";
1326
		$sql.= ", cr.code as cond_reglement_code, cr.libelle as cond_reglement, cr.libelle_facture as cond_reglement_libelle_doc";
1327
		$sql.= ", cp.code as mode_reglement_code, cp.libelle as mode_reglement";
1328
		$sql.= " FROM ".MAIN_DB_PREFIX."c_propalst as c, ".MAIN_DB_PREFIX."propal as p";
1329
		$sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as cp ON p.fk_mode_reglement = cp.id';
1330
		$sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_payment_term as cr ON p.fk_cond_reglement = cr.rowid';
1331
		$sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_availability as ca ON p.fk_availability = ca.rowid';
1332
		$sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_input_reason as dr ON p.fk_input_reason = dr.rowid';
1333
		$sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_incoterms as i ON p.fk_incoterms = i.rowid';
1334
		$sql.= " WHERE p.fk_statut = c.id";
1335
		$sql.= " AND p.entity IN (".getEntity('propal').")";
1336
		if ($ref) $sql.= " AND p.ref='".$ref."'";
1337
		else $sql.= " AND p.rowid=".$rowid;
1338
1339
		dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
1340
		$resql=$this->db->query($sql);
1341
		if ($resql)
1342
		{
1343
			if ($this->db->num_rows($resql))
1344
			{
1345
				$obj = $this->db->fetch_object($resql);
1346
1347
				$this->id                   = $obj->rowid;
1348
				$this->entity               = $obj->entity;
1349
1350
				$this->ref                  = $obj->ref;
1351
				$this->ref_client           = $obj->ref_client;
1352
				$this->remise               = $obj->remise;
1353
				$this->remise_percent       = $obj->remise_percent;
1354
				$this->remise_absolue       = $obj->remise_absolue;
1355
				$this->total                = $obj->total; // TODO deprecated
1356
				$this->total_ht             = $obj->total_ht;
1357
				$this->total_tva            = $obj->tva;
1358
				$this->total_localtax1		= $obj->localtax1;
1359
				$this->total_localtax2		= $obj->localtax2;
1360
				$this->total_ttc            = $obj->total;
1361
				$this->socid                = $obj->fk_soc;
1362
				$this->fk_project           = $obj->fk_projet;
1363
				$this->modelpdf             = $obj->model_pdf;
1364
				$this->last_main_doc		= $obj->last_main_doc;
1365
				$this->note                 = $obj->note_private; // TODO deprecated
1366
				$this->note_private         = $obj->note_private;
1367
				$this->note_public          = $obj->note_public;
1368
				$this->statut               = (int) $obj->fk_statut;
1369
				$this->statut_libelle       = $obj->statut_label;
1370
1371
				$this->datec                = $this->db->jdate($obj->datec); // TODO deprecated
1372
				$this->datev                = $this->db->jdate($obj->datev); // TODO deprecated
1373
				$this->date_creation		= $this->db->jdate($obj->datec); //Creation date
1374
				$this->date_validation		= $this->db->jdate($obj->datev); //Validation date
1375
				$this->date                 = $this->db->jdate($obj->dp);	// Proposal date
1376
				$this->datep                = $this->db->jdate($obj->dp);    // deprecated
1377
				$this->fin_validite         = $this->db->jdate($obj->dfv);
1378
				$this->date_livraison       = $this->db->jdate($obj->date_livraison);
1379
				$this->shipping_method_id   = ($obj->fk_shipping_method>0)?$obj->fk_shipping_method:null;
1380
				$this->availability_id      = $obj->fk_availability;
1381
				$this->availability_code    = $obj->availability_code;
1382
				$this->availability         = $obj->availability;
1383
				$this->demand_reason_id     = $obj->fk_input_reason;
1384
				$this->demand_reason_code   = $obj->demand_reason_code;
1385
				$this->demand_reason        = $obj->demand_reason;
1386
				$this->fk_address  			= $obj->fk_delivery_address;
1387
1388
				$this->mode_reglement_id    = $obj->fk_mode_reglement;
1389
				$this->mode_reglement_code  = $obj->mode_reglement_code;
1390
				$this->mode_reglement       = $obj->mode_reglement;
1391
				$this->fk_account           = ($obj->fk_account>0)?$obj->fk_account:null;
1392
				$this->cond_reglement_id    = $obj->fk_cond_reglement;
1393
				$this->cond_reglement_code  = $obj->cond_reglement_code;
1394
				$this->cond_reglement       = $obj->cond_reglement;
1395
				$this->cond_reglement_doc   = $obj->cond_reglement_libelle_doc;
1396
1397
				$this->extraparams			= (array) json_decode($obj->extraparams, true);
1398
1399
				$this->user_author_id = $obj->fk_user_author;
1400
				$this->user_valid_id  = $obj->fk_user_valid;
1401
				$this->user_close_id  = $obj->fk_user_cloture;
1402
1403
				//Incoterms
1404
				$this->fk_incoterms = $obj->fk_incoterms;
1405
				$this->location_incoterms = $obj->location_incoterms;
1406
				$this->libelle_incoterms = $obj->libelle_incoterms;
1407
1408
				// Multicurrency
1409
				$this->fk_multicurrency 		= $obj->fk_multicurrency;
1410
				$this->multicurrency_code 		= $obj->multicurrency_code;
1411
				$this->multicurrency_tx 		= $obj->multicurrency_tx;
1412
				$this->multicurrency_total_ht 	= $obj->multicurrency_total_ht;
1413
				$this->multicurrency_total_tva 	= $obj->multicurrency_total_tva;
1414
				$this->multicurrency_total_ttc 	= $obj->multicurrency_total_ttc;
1415
1416
				if ($obj->fk_statut == self::STATUS_DRAFT)
1417
				{
1418
					$this->brouillon = 1;
1419
				}
1420
1421
				// Retreive all extrafield
1422
				// fetch optionals attributes and labels
1423
				$this->fetch_optionals();
1424
1425
				$this->db->free($resql);
1426
1427
				$this->lines = array();
1428
1429
				/*
1430
                 * Lines
1431
                 */
1432
				$result=$this->fetch_lines();
1433
				if ($result < 0)
1434
				{
1435
					return -3;
1436
				}
1437
1438
				return 1;
1439
			}
1440
1441
			$this->error="Record Not Found";
1442
			return 0;
1443
		}
1444
		else
1445
		{
1446
			$this->error=$this->db->lasterror();
1447
			return -1;
1448
		}
1449
	}
1450
1451
	/**
1452
	 *      Update database
1453
	 *
1454
	 *      @param      User	$user        	User that modify
1455
	 *      @param      int		$notrigger	    0=launch triggers after, 1=disable triggers
1456
	 *      @return     int      			   	<0 if KO, >0 if OK
1457
	 */
1458
	function update(User $user, $notrigger=0)
1459
	{
1460
		$error=0;
1461
1462
		// Clean parameters
1463
		if (isset($this->ref)) $this->ref=trim($this->ref);
1464
		if (isset($this->ref_client)) $this->ref_client=trim($this->ref_client);
1465
		if (isset($this->note) || isset($this->note_private)) $this->note_private=(isset($this->note_private) ? trim($this->note_private) : trim($this->note));
1466
		if (isset($this->note_public)) $this->note_public=trim($this->note_public);
1467
		if (isset($this->modelpdf)) $this->modelpdf=trim($this->modelpdf);
1468
		if (isset($this->import_key)) $this->import_key=trim($this->import_key);
1469
1470
		// Check parameters
1471
		// Put here code to add control on parameters values
1472
1473
		// Update request
1474
		$sql = "UPDATE ".MAIN_DB_PREFIX."propal SET";
1475
1476
		$sql.= " ref=".(isset($this->ref)?"'".$this->db->escape($this->ref)."'":"null").",";
1477
		$sql.= " ref_client=".(isset($this->ref_client)?"'".$this->db->escape($this->ref_client)."'":"null").",";
1478
		$sql.= " ref_ext=".(isset($this->ref_ext)?"'".$this->db->escape($this->ref_ext)."'":"null").",";
1479
		$sql.= " fk_soc=".(isset($this->socid)?$this->socid:"null").",";
1480
		$sql.= " datep=".(strval($this->datep)!='' ? "'".$this->db->idate($this->datep)."'" : 'null').",";
1481
		$sql.= " date_valid=".(strval($this->date_validation)!='' ? "'".$this->db->idate($this->date_validation)."'" : 'null').",";
1482
		$sql.= " tva=".(isset($this->total_tva)?$this->total_tva:"null").",";
1483
		$sql.= " localtax1=".(isset($this->total_localtax1)?$this->total_localtax1:"null").",";
1484
		$sql.= " localtax2=".(isset($this->total_localtax2)?$this->total_localtax2:"null").",";
1485
		$sql.= " total_ht=".(isset($this->total_ht)?$this->total_ht:"null").",";
1486
		$sql.= " total=".(isset($this->total_ttc)?$this->total_ttc:"null").",";
1487
		$sql.= " fk_statut=".(isset($this->statut)?$this->statut:"null").",";
1488
		$sql.= " fk_user_author=".(isset($this->user_author_id)?$this->user_author_id:"null").",";
1489
		$sql.= " fk_user_valid=".(isset($this->user_valid)?$this->user_valid:"null").",";
1490
		$sql.= " fk_projet=".(isset($this->fk_project)?$this->fk_project:"null").",";
1491
		$sql.= " fk_cond_reglement=".(isset($this->cond_reglement_id)?$this->cond_reglement_id:"null").",";
1492
		$sql.= " fk_mode_reglement=".(isset($this->mode_reglement_id)?$this->mode_reglement_id:"null").",";
1493
		$sql.= " note_private=".(isset($this->note_private)?"'".$this->db->escape($this->note_private)."'":"null").",";
1494
		$sql.= " note_public=".(isset($this->note_public)?"'".$this->db->escape($this->note_public)."'":"null").",";
1495
		$sql.= " model_pdf=".(isset($this->modelpdf)?"'".$this->db->escape($this->modelpdf)."'":"null").",";
1496
		$sql.= " import_key=".(isset($this->import_key)?"'".$this->db->escape($this->import_key)."'":"null")."";
1497
1498
		$sql.= " WHERE rowid=".$this->id;
1499
1500
		$this->db->begin();
1501
1502
		dol_syslog(get_class($this)."::update", LOG_DEBUG);
1503
		$resql = $this->db->query($sql);
1504
		if (! $resql) {
1505
			$error++; $this->errors[]="Error ".$this->db->lasterror();
1506
		}
1507
1508
		if (! $error)
1509
		{
1510
			if (! $notrigger)
1511
			{
1512
				// Call trigger
1513
				$result=$this->call_trigger('PROPAL_MODIFY', $user);
1514
				if ($result < 0) $error++;
1515
				// End call triggers
1516
			}
1517
		}
1518
1519
		// Commit or rollback
1520
		if ($error)
1521
		{
1522
			foreach($this->errors as $errmsg)
1523
			{
1524
				dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1525
				$this->error.=($this->error?', '.$errmsg:$errmsg);
1526
			}
1527
			$this->db->rollback();
1528
			return -1*$error;
1529
		}
1530
		else
1531
		{
1532
			$this->db->commit();
1533
			return 1;
1534
		}
1535
	}
1536
1537
1538
	/**
1539
	 * Load array lines
1540
	 *
1541
	 * @param		int		$only_product	Return only physical products
1542
	 * @return		int						<0 if KO, >0 if OK
1543
	 */
1544
	function fetch_lines($only_product=0)
1545
	{
1546
		$this->lines=array();
1547
1548
		$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,';
1549
		$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,';
1550
		$sql.= ' d.fk_unit,';
1551
		$sql.= ' p.ref as product_ref, p.description as product_desc, p.fk_product_type, p.label as product_label,';
1552
		$sql.= ' p.weight, p.weight_units, p.volume, p.volume_units,';
1553
		$sql.= ' d.date_start, d.date_end';
1554
		$sql.= ' ,d.fk_multicurrency, d.multicurrency_code, d.multicurrency_subprice, d.multicurrency_total_ht, d.multicurrency_total_tva, d.multicurrency_total_ttc';
1555
		$sql.= ' FROM '.MAIN_DB_PREFIX.'propaldet as d';
1556
		$sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON (d.fk_product = p.rowid)';
1557
		$sql.= ' WHERE d.fk_propal = '.$this->id;
1558
		if ($only_product) $sql .= ' AND p.fk_product_type = 0';
1559
		$sql.= ' ORDER by d.rang';
1560
1561
		dol_syslog(get_class($this)."::fetch_lines", LOG_DEBUG);
1562
		$result = $this->db->query($sql);
1563
		if ($result)
1564
		{
1565
			require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
1566
1567
			$num = $this->db->num_rows($result);
1568
1569
			$i = 0;
1570
			while ($i < $num)
1571
			{
1572
				$objp                   = $this->db->fetch_object($result);
1573
1574
				$line                   = new PropaleLigne($this->db);
1575
1576
				$line->rowid			= $objp->rowid; //Deprecated
1577
				$line->id				= $objp->rowid;
1578
				$line->fk_propal		= $objp->fk_propal;
1579
				$line->fk_parent_line	= $objp->fk_parent_line;
1580
				$line->product_type     = $objp->product_type;
1581
				$line->label            = $objp->custom_label;
1582
				$line->desc             = $objp->description;  // Description ligne
1583
				$line->qty              = $objp->qty;
1584
				$line->vat_src_code     = $objp->vat_src_code;
1585
				$line->tva_tx           = $objp->tva_tx;
1586
				$line->localtax1_tx		= $objp->localtax1_tx;
1587
				$line->localtax2_tx		= $objp->localtax2_tx;
1588
				$line->localtax1_type	= $objp->localtax1_type;
1589
				$line->localtax2_type	= $objp->localtax2_type;
1590
				$line->subprice         = $objp->subprice;
1591
				$line->fk_remise_except = $objp->fk_remise_except;
1592
				$line->remise_percent   = $objp->remise_percent;
1593
				$line->price            = $objp->price;		// TODO deprecated
1594
1595
				$line->info_bits        = $objp->info_bits;
1596
				$line->total_ht         = $objp->total_ht;
1597
				$line->total_tva        = $objp->total_tva;
1598
				$line->total_localtax1	= $objp->total_localtax1;
1599
				$line->total_localtax2	= $objp->total_localtax2;
1600
				$line->total_ttc        = $objp->total_ttc;
1601
				$line->fk_fournprice 	= $objp->fk_fournprice;
1602
				$marginInfos			= getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $line->fk_fournprice, $objp->pa_ht);
1603
				$line->pa_ht 			= $marginInfos[0];
1604
				$line->marge_tx			= $marginInfos[1];
1605
				$line->marque_tx		= $marginInfos[2];
1606
				$line->special_code     = $objp->special_code;
1607
				$line->rang             = $objp->rang;
1608
1609
				$line->fk_product       = $objp->fk_product;
1610
1611
				$line->ref				= $objp->product_ref;		// TODO deprecated
1612
				$line->product_ref		= $objp->product_ref;
1613
				$line->libelle			= $objp->product_label;		// TODO deprecated
1614
				$line->product_label	= $objp->product_label;
1615
				$line->product_desc     = $objp->product_desc; 		// Description produit
1616
				$line->fk_product_type  = $objp->fk_product_type;
1617
				$line->fk_unit          = $objp->fk_unit;
1618
				$line->weight = $objp->weight;
1619
				$line->weight_units = $objp->weight_units;
1620
				$line->volume = $objp->volume;
1621
				$line->volume_units = $objp->volume_units;
1622
1623
				$line->date_start  		= $this->db->jdate($objp->date_start);
1624
				$line->date_end  		= $this->db->jdate($objp->date_end);
1625
1626
				// Multicurrency
1627
				$line->fk_multicurrency 		= $objp->fk_multicurrency;
1628
				$line->multicurrency_code 		= $objp->multicurrency_code;
1629
				$line->multicurrency_subprice 	= $objp->multicurrency_subprice;
1630
				$line->multicurrency_total_ht 	= $objp->multicurrency_total_ht;
1631
				$line->multicurrency_total_tva 	= $objp->multicurrency_total_tva;
1632
				$line->multicurrency_total_ttc 	= $objp->multicurrency_total_ttc;
1633
1634
				$line->fetch_optionals();
1635
1636
				$this->lines[$i]        = $line;
1637
				//dol_syslog("1 ".$line->fk_product);
1638
				//print "xx $i ".$this->lines[$i]->fk_product;
1639
				$i++;
1640
			}
1641
1642
			$this->db->free($result);
1643
1644
			return $num;
1645
		}
1646
		else
1647
		{
1648
			$this->error=$this->db->lasterror();
1649
			return -3;
1650
		}
1651
	}
1652
1653
	/**
1654
	 *  Set status to validated
1655
	 *
1656
	 *  @param	User	$user       Object user that validate
1657
	 *  @param	int		$notrigger	1=Does not execute triggers, 0=execute triggers
1658
	 *  @return int         		<0 if KO, 0=Nothing done, >=0 if OK
1659
	 */
1660
	function valid($user, $notrigger=0)
1661
	{
1662
		require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1663
1664
		global $conf;
1665
1666
		$error=0;
1667
1668
		// Protection
1669
		if ($this->statut == self::STATUS_VALIDATED)
1670
		{
1671
			dol_syslog(get_class($this)."::valid action abandonned: already validated", LOG_WARNING);
1672
			return 0;
1673
		}
1674
1675
		if (! ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->propal->creer))
1676
	   	|| (! empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->propal->propal_advance->validate))))
1677
		{
1678
			$this->error='ErrorPermissionDenied';
1679
			dol_syslog(get_class($this)."::valid ".$this->error, LOG_ERR);
1680
			return -1;
1681
		}
1682
1683
		$now=dol_now();
1684
1685
		$this->db->begin();
1686
1687
		// Numbering module definition
1688
		$soc = new Societe($this->db);
1689
		$soc->fetch($this->socid);
1690
1691
		// Define new ref
1692
		if (! $error && (preg_match('/^[\(]?PROV/i', $this->ref) || empty($this->ref))) // empty should not happened, but when it occurs, the test save life
1693
		{
1694
			$num = $this->getNextNumRef($soc);
1695
		}
1696
		else
1697
		{
1698
			$num = $this->ref;
1699
		}
1700
		$this->newref = $num;
1701
1702
		$sql = "UPDATE ".MAIN_DB_PREFIX."propal";
1703
		$sql.= " SET ref = '".$num."',";
1704
		$sql.= " fk_statut = ".self::STATUS_VALIDATED.", date_valid='".$this->db->idate($now)."', fk_user_valid=".$user->id;
1705
		$sql.= " WHERE rowid = ".$this->id." AND fk_statut = ".self::STATUS_DRAFT;
1706
1707
		dol_syslog(get_class($this)."::valid", LOG_DEBUG);
1708
		$resql=$this->db->query($sql);
1709
		if (! $resql)
1710
		{
1711
			dol_print_error($this->db);
1712
			$error++;
1713
		}
1714
1715
		// Trigger calls
1716
		if (! $error && ! $notrigger)
1717
		{
1718
			// Call trigger
1719
			$result=$this->call_trigger('PROPAL_VALIDATE',$user);
1720
			if ($result < 0) { $error++; }
1721
			// End call triggers
1722
		}
1723
1724
		if (! $error)
1725
		{
1726
			$this->oldref = $this->ref;
1727
1728
			// Rename directory if dir was a temporary ref
1729
			if (preg_match('/^[\(]?PROV/i', $this->ref))
1730
			{
1731
				// Rename of propal directory ($this->ref = old ref, $num = new ref)
1732
				// to  not lose the linked files
1733
				$oldref = dol_sanitizeFileName($this->ref);
1734
				$newref = dol_sanitizeFileName($num);
1735
				$dirsource = $conf->propal->multidir_output[$this->entity].'/'.$oldref;
1736
				$dirdest = $conf->propal->multidir_output[$this->entity].'/'.$newref;
1737
1738
				if (file_exists($dirsource))
1739
				{
1740
					dol_syslog(get_class($this)."::validate rename dir ".$dirsource." into ".$dirdest);
1741
					if (@rename($dirsource, $dirdest))
1742
					{
1743
						dol_syslog("Rename ok");
1744
						// Rename docs starting with $oldref with $newref
1745
						$listoffiles=dol_dir_list($dirdest, 'files', 1, '^'.preg_quote($oldref,'/'));
1746
						foreach($listoffiles as $fileentry)
1747
						{
1748
							$dirsource=$fileentry['name'];
1749
							$dirdest=preg_replace('/^'.preg_quote($oldref,'/').'/',$newref, $dirsource);
1750
							$dirsource=$fileentry['path'].'/'.$dirsource;
1751
							$dirdest=$fileentry['path'].'/'.$dirdest;
1752
							@rename($dirsource, $dirdest);
1753
						}
1754
					}
1755
				}
1756
			}
1757
1758
			$this->ref=$num;
1759
			$this->brouillon=0;
1760
			$this->statut = self::STATUS_VALIDATED;
1761
			$this->user_valid_id=$user->id;
1762
			$this->datev=$now;
1763
1764
			$this->db->commit();
1765
			return 1;
1766
		}
1767
		else
1768
		{
1769
			$this->db->rollback();
1770
			return -1;
1771
		}
1772
	}
1773
1774
1775
	/**
1776
	 *  Define proposal date
1777
	 *
1778
	 *  @param  User		$user      	Object user that modify
1779
	 *  @param  int			$date		Date
1780
	 *  @param  int			$notrigger	1=Does not execute triggers, 0= execute triggers
1781
	 *  @return	int         			<0 if KO, >0 if OK
1782
	 */
1783
	function set_date($user, $date, $notrigger=0)
1784
	{
1785
		if (empty($date))
1786
		{
1787
			$this->error='ErrorBadParameter';
1788
			dol_syslog(get_class($this)."::set_date ".$this->error, LOG_ERR);
1789
			return -1;
1790
		}
1791
1792
		if (! empty($user->rights->propal->creer))
1793
		{
1794
			$error=0;
1795
1796
			$this->db->begin();
1797
1798
			$sql = "UPDATE ".MAIN_DB_PREFIX."propal SET datep = '".$this->db->idate($date)."'";
1799
			$sql.= " WHERE rowid = ".$this->id." AND fk_statut = ".self::STATUS_DRAFT;
1800
1801
			dol_syslog(__METHOD__, LOG_DEBUG);
1802
			$resql=$this->db->query($sql);
1803
			if (!$resql)
1804
			{
1805
				$this->errors[]=$this->db->error();
1806
				$error++;
1807
			}
1808
1809
			if (! $error)
1810
			{
1811
				$this->oldcopy= clone $this;
1812
				$this->date = $date;
1813
				$this->datep = $date;    // deprecated
1814
			}
1815
1816
			if (! $notrigger && empty($error))
1817
			{
1818
				// Call trigger
1819
				$result=$this->call_trigger('PROPAL_MODIFY',$user);
1820
				if ($result < 0) $error++;
1821
				// End call triggers
1822
			}
1823
1824
			if (! $error)
1825
			{
1826
				$this->db->commit();
1827
				return 1;
1828
			}
1829
			else
1830
			{
1831
				foreach($this->errors as $errmsg)
1832
				{
1833
					dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
1834
					$this->error.=($this->error?', '.$errmsg:$errmsg);
1835
				}
1836
				$this->db->rollback();
1837
				return -1*$error;
1838
			}
1839
		}
1840
	}
1841
1842
	/**
1843
	 *	Define end validity date
1844
	 *
1845
	 *	@param		User	$user        		Object user that modify
1846
	 *	@param      int		$date_fin_validite	End of validity date
1847
	 *  @param  	int		$notrigger			1=Does not execute triggers, 0= execute triggers
1848
	 *	@return     int         				<0 if KO, >0 if OK
1849
	 */
1850
	function set_echeance($user, $date_fin_validite, $notrigger=0)
1851
	{
1852
		if (! empty($user->rights->propal->creer))
1853
		{
1854
			$error=0;
1855
1856
			$this->db->begin();
1857
1858
			$sql = "UPDATE ".MAIN_DB_PREFIX."propal SET fin_validite = ".($date_fin_validite!=''?"'".$this->db->idate($date_fin_validite)."'":'null');
1859
			$sql.= " WHERE rowid = ".$this->id." AND fk_statut = ".self::STATUS_DRAFT;
1860
1861
			dol_syslog(__METHOD__, LOG_DEBUG);
1862
			$resql=$this->db->query($sql);
1863
			if (!$resql)
1864
			{
1865
				$this->errors[]=$this->db->error();
1866
				$error++;
1867
			}
1868
1869
1870
			if (! $error)
1871
			{
1872
				$this->oldcopy= clone $this;
1873
				$this->fin_validite = $date_fin_validite;
1874
			}
1875
1876
			if (! $notrigger && empty($error))
1877
			{
1878
				// Call trigger
1879
				$result=$this->call_trigger('PROPAL_MODIFY',$user);
1880
				if ($result < 0) $error++;
1881
				// End call triggers
1882
			}
1883
1884
			if (! $error)
1885
			{
1886
				$this->db->commit();
1887
				return 1;
1888
			}
1889
			else
1890
			{
1891
				foreach($this->errors as $errmsg)
1892
				{
1893
					dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
1894
					$this->error.=($this->error?', '.$errmsg:$errmsg);
1895
				}
1896
				$this->db->rollback();
1897
				return -1*$error;
1898
			}
1899
		}
1900
	}
1901
1902
	/**
1903
	 *	Set delivery date
1904
	 *
1905
	 *	@param      User 	$user        		Object user that modify
1906
	 *	@param      int		$date_livraison     Delivery date
1907
	 *  @param  	int		$notrigger			1=Does not execute triggers, 0= execute triggers
1908
	 *	@return     int         				<0 if ko, >0 if ok
1909
	 */
1910
	function set_date_livraison($user, $date_livraison, $notrigger=0)
1911
	{
1912
		if (! empty($user->rights->propal->creer))
1913
		{
1914
			$error=0;
1915
1916
			$this->db->begin();
1917
1918
			$sql = "UPDATE ".MAIN_DB_PREFIX."propal ";
1919
			$sql.= " SET date_livraison = ".($date_livraison!=''?"'".$this->db->idate($date_livraison)."'":'null');
1920
			$sql.= " WHERE rowid = ".$this->id;
1921
1922
			dol_syslog(__METHOD__, LOG_DEBUG);
1923
			$resql=$this->db->query($sql);
1924
			if (!$resql)
1925
			{
1926
				$this->errors[]=$this->db->error();
1927
				$error++;
1928
			}
1929
1930
			if (! $error)
1931
			{
1932
				$this->oldcopy= clone $this;
1933
				$this->date_livraison = $date_livraison;
1934
			}
1935
1936
			if (! $notrigger && empty($error))
1937
			{
1938
				// Call trigger
1939
				$result=$this->call_trigger('PROPAL_MODIFY',$user);
1940
				if ($result < 0) $error++;
1941
				// End call triggers
1942
			}
1943
1944
			if (! $error)
1945
			{
1946
				$this->db->commit();
1947
				return 1;
1948
			}
1949
			else
1950
			{
1951
				foreach($this->errors as $errmsg)
1952
				{
1953
					dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
1954
					$this->error.=($this->error?', '.$errmsg:$errmsg);
1955
				}
1956
				$this->db->rollback();
1957
				return -1*$error;
1958
			}
1959
		}
1960
	}
1961
1962
	/**
1963
	 *  Set delivery
1964
	 *
1965
	 *  @param		User	$user		  	Object user that modify
1966
	 *  @param      int		$id				Availability id
1967
	 *  @param  	int		$notrigger		1=Does not execute triggers, 0= execute triggers
1968
	 *  @return     int           			<0 if KO, >0 if OK
1969
	 */
1970
	function set_availability($user, $id, $notrigger=0)
1971
	{
1972
		if (! empty($user->rights->propal->creer) && $this->statut >= self::STATUS_DRAFT)
1973
		{
1974
			$error=0;
1975
1976
			$this->db->begin();
1977
1978
			$sql = "UPDATE ".MAIN_DB_PREFIX."propal ";
1979
			$sql.= " SET fk_availability = '".$id."'";
1980
			$sql.= " WHERE rowid = ".$this->id;
1981
1982
			dol_syslog(__METHOD__.' availability('.$id.')', LOG_DEBUG);
1983
			$resql=$this->db->query($sql);
1984
			if (!$resql)
1985
			{
1986
				$this->errors[]=$this->db->error();
1987
				$error++;
1988
			}
1989
1990
			if (! $error)
1991
			{
1992
				$this->oldcopy= clone $this;
1993
				$this->fk_availability = $id;
1994
				$this->availability_id = $id;
1995
			}
1996
1997
			if (! $notrigger && empty($error))
1998
			{
1999
				// Call trigger
2000
				$result=$this->call_trigger('PROPAL_MODIFY',$user);
2001
				if ($result < 0) $error++;
2002
				// End call triggers
2003
			}
2004
2005
			if (! $error)
2006
			{
2007
				$this->db->commit();
2008
				return 1;
2009
			}
2010
			else
2011
			{
2012
				foreach($this->errors as $errmsg)
2013
				{
2014
					dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2015
					$this->error.=($this->error?', '.$errmsg:$errmsg);
2016
				}
2017
				$this->db->rollback();
2018
				return -1*$error;
2019
			}
2020
		}
2021
		else
2022
		{
2023
			$error_str='Propal status do not meet requirement '.$this->statut;
2024
			dol_syslog(__METHOD__.$error_str, LOG_ERR);
2025
			$this->error=$error_str;
2026
			$this->errors[]= $this->error;
2027
			return -2;
2028
		}
2029
	}
2030
2031
	/**
2032
	 *  Set source of demand
2033
	 *
2034
	 *  @param		User	$user		Object user that modify
2035
	 *  @param      int		$id			Input reason id
2036
	 *  @param  	int		$notrigger	1=Does not execute triggers, 0= execute triggers
2037
	 *  @return     int           		<0 if KO, >0 if OK
2038
	 */
2039
	function set_demand_reason($user, $id, $notrigger=0)
2040
	{
2041
		if (! empty($user->rights->propal->creer) && $this->statut >= self::STATUS_DRAFT)
2042
		{
2043
			$error=0;
2044
2045
			$this->db->begin();
2046
2047
			$sql = "UPDATE ".MAIN_DB_PREFIX."propal ";
2048
			$sql.= " SET fk_input_reason = ".$id;
2049
			$sql.= " WHERE rowid = ".$this->id;
2050
2051
			dol_syslog(__METHOD__, LOG_DEBUG);
2052
			$resql=$this->db->query($sql);
2053
			if (!$resql)
2054
			{
2055
				$this->errors[]=$this->db->error();
2056
				$error++;
2057
			}
2058
2059
2060
			if (! $error)
2061
			{
2062
				$this->oldcopy= clone $this;
2063
				$this->fk_input_reason = $id;
2064
				$this->demand_reason_id = $id;
2065
			}
2066
2067
2068
			if (! $notrigger && empty($error))
2069
			{
2070
				// Call trigger
2071
				$result=$this->call_trigger('PROPAL_MODIFY',$user);
2072
				if ($result < 0) $error++;
2073
				// End call triggers
2074
			}
2075
2076
			if (! $error)
2077
			{
2078
				$this->db->commit();
2079
				return 1;
2080
			}
2081
			else
2082
			{
2083
				foreach($this->errors as $errmsg)
2084
				{
2085
					dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2086
					$this->error.=($this->error?', '.$errmsg:$errmsg);
2087
				}
2088
				$this->db->rollback();
2089
				return -1*$error;
2090
			}
2091
		}
2092
		else
2093
		{
2094
			$error_str='Propal status do not meet requirement '.$this->statut;
2095
			dol_syslog(__METHOD__.$error_str, LOG_ERR);
2096
			$this->error=$error_str;
2097
			$this->errors[]= $this->error;
2098
			return -2;
2099
		}
2100
	}
2101
2102
	/**
2103
	 * Set customer reference number
2104
	 *
2105
	 *  @param      User	$user			Object user that modify
2106
	 *  @param      string	$ref_client		Customer reference
2107
	 *  @param  	int		$notrigger		1=Does not execute triggers, 0= execute triggers
2108
	 *  @return     int						<0 if ko, >0 if ok
2109
	 */
2110
	function set_ref_client($user, $ref_client, $notrigger=0)
2111
	{
2112
		if (! empty($user->rights->propal->creer))
2113
		{
2114
			$error=0;
2115
2116
			$this->db->begin();
2117
2118
			$sql = 'UPDATE '.MAIN_DB_PREFIX.'propal SET ref_client = '.(empty($ref_client) ? 'NULL' : '\''.$this->db->escape($ref_client).'\'');
2119
			$sql.= ' WHERE rowid = '.$this->id;
2120
2121
			dol_syslog(__METHOD__.' $this->id='.$this->id.', ref_client='.$ref_client, LOG_DEBUG);
2122
			$resql=$this->db->query($sql);
2123
			if (!$resql)
2124
			{
2125
				$this->errors[]=$this->db->error();
2126
				$error++;
2127
			}
2128
2129
			if (! $error)
2130
			{
2131
				$this->oldcopy= clone $this;
2132
				$this->ref_client = $ref_client;
2133
			}
2134
2135
			if (! $notrigger && empty($error))
2136
			{
2137
				// Call trigger
2138
				$result=$this->call_trigger('PROPAL_MODIFY',$user);
2139
				if ($result < 0) $error++;
2140
				// End call triggers
2141
			}
2142
2143
			if (! $error)
2144
			{
2145
				$this->db->commit();
2146
				return 1;
2147
			}
2148
			else
2149
			{
2150
				foreach($this->errors as $errmsg)
2151
				{
2152
					dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2153
					$this->error.=($this->error?', '.$errmsg:$errmsg);
2154
				}
2155
				$this->db->rollback();
2156
				return -1*$error;
2157
			}
2158
		}
2159
		else
2160
		{
2161
			return -1;
2162
		}
2163
	}
2164
2165
	/**
2166
	 *	Set an overall discount on the proposal
2167
	 *
2168
	 *	@param      User	$user       Object user that modify
2169
	 *	@param      double	$remise     Amount discount
2170
	 *  @param  	int		$notrigger	1=Does not execute triggers, 0= execute triggers
2171
	 *	@return     int         		<0 if ko, >0 if ok
2172
	 */
2173
	function set_remise_percent($user, $remise, $notrigger=0)
2174
	{
2175
		$remise=trim($remise)?trim($remise):0;
2176
2177
		if (! empty($user->rights->propal->creer))
2178
		{
2179
			$remise = price2num($remise);
2180
2181
			$error=0;
2182
2183
			$this->db->begin();
2184
2185
			$sql = "UPDATE ".MAIN_DB_PREFIX."propal SET remise_percent = ".$remise;
2186
			$sql.= " WHERE rowid = ".$this->id." AND fk_statut = ".self::STATUS_DRAFT;
2187
2188
			dol_syslog(__METHOD__, LOG_DEBUG);
2189
			$resql=$this->db->query($sql);
2190
			if (!$resql)
2191
			{
2192
				$this->errors[]=$this->db->error();
2193
				$error++;
2194
			}
2195
2196
			if (! $error)
2197
			{
2198
				$this->oldcopy= clone $this;
2199
				$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...
2200
				$this->update_price(1);
2201
			}
2202
2203
			if (! $notrigger && empty($error))
2204
			{
2205
				// Call trigger
2206
				$result=$this->call_trigger('PROPAL_MODIFY',$user);
2207
				if ($result < 0) $error++;
2208
				// End call triggers
2209
			}
2210
2211
			if (! $error)
2212
			{
2213
				$this->db->commit();
2214
				return 1;
2215
			}
2216
			else
2217
			{
2218
				foreach($this->errors as $errmsg)
2219
				{
2220
					dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2221
					$this->error.=($this->error?', '.$errmsg:$errmsg);
2222
				}
2223
				$this->db->rollback();
2224
				return -1*$error;
2225
			}
2226
		}
2227
	}
2228
2229
2230
	/**
2231
	 *	Set an absolute overall discount on the proposal
2232
	 *
2233
	 *	@param      User	$user       Object user that modify
2234
	 *	@param      double	$remise     Amount discount
2235
	 *  @param  	int		$notrigger	1=Does not execute triggers, 0= execute triggers
2236
	 *	@return     int         		<0 if ko, >0 if ok
2237
	 */
2238
	function set_remise_absolue($user, $remise, $notrigger=0)
2239
	{
2240
		$remise=trim($remise)?trim($remise):0;
2241
2242
		if (! empty($user->rights->propal->creer))
2243
		{
2244
			$remise = price2num($remise);
2245
2246
			$error=0;
2247
2248
			$this->db->begin();
2249
2250
			$sql = "UPDATE ".MAIN_DB_PREFIX."propal ";
2251
			$sql.= " SET remise_absolue = ".$remise;
2252
			$sql.= " WHERE rowid = ".$this->id." AND fk_statut = ".self::STATUS_DRAFT;
2253
2254
			dol_syslog(__METHOD__, LOG_DEBUG);
2255
			$resql=$this->db->query($sql);
2256
			if (!$resql)
2257
			{
2258
				$this->errors[]=$this->db->error();
2259
				$error++;
2260
			}
2261
2262
			if (! $error)
2263
			{
2264
				$this->oldcopy= clone $this;
2265
				$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...
2266
				$this->update_price(1);
2267
			}
2268
2269
			if (! $notrigger && empty($error))
2270
			{
2271
				// Call trigger
2272
				$result=$this->call_trigger('PROPAL_MODIFY',$user);
2273
				if ($result < 0) $error++;
2274
				// End call triggers
2275
			}
2276
2277
			if (! $error)
2278
			{
2279
				$this->db->commit();
2280
				return 1;
2281
			}
2282
			else
2283
			{
2284
				foreach($this->errors as $errmsg)
2285
				{
2286
					dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2287
					$this->error.=($this->error?', '.$errmsg:$errmsg);
2288
				}
2289
				$this->db->rollback();
2290
				return -1*$error;
2291
			}
2292
		}
2293
	}
2294
2295
2296
2297
	/**
2298
	 *	Reopen the commercial proposal
2299
	 *
2300
	 *	@param      User	$user		Object user that close
2301
	 *	@param      int		$statut		Statut
2302
	 *	@param      string	$note		Comment
2303
	 *  @param		int		$notrigger	1=Does not execute triggers, 0= execute triggers
2304
	 *	@return     int         		<0 if KO, >0 if OK
2305
	 */
2306
	function reopen($user, $statut, $note='', $notrigger=0)
2307
	{
2308
2309
		$this->statut = $statut;
2310
		$error=0;
2311
2312
		$sql = "UPDATE ".MAIN_DB_PREFIX."propal";
2313
		$sql.= " SET fk_statut = ".$this->statut.",";
2314
		if (! empty($note)) $sql.= " note_private = '".$this->db->escape($note)."',";
2315
		$sql.= " date_cloture=NULL, fk_user_cloture=NULL";
2316
		$sql.= " WHERE rowid = ".$this->id;
2317
2318
		$this->db->begin();
2319
2320
		dol_syslog(get_class($this)."::reopen", LOG_DEBUG);
2321
		$resql = $this->db->query($sql);
2322
		if (! $resql) {
2323
			$error++; $this->errors[]="Error ".$this->db->lasterror();
2324
		}
2325
		if (! $error)
2326
		{
2327
			if (! $notrigger)
2328
			{
2329
				// Call trigger
2330
				$result=$this->call_trigger('PROPAL_REOPEN',$user);
2331
				if ($result < 0) { $error++; }
2332
				// End call triggers
2333
			}
2334
		}
2335
2336
		// Commit or rollback
2337
		if ($error)
2338
		{
2339
			if (!empty($this->errors))
2340
			{
2341
				foreach($this->errors as $errmsg)
2342
				{
2343
					dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
2344
					$this->error.=($this->error?', '.$errmsg:$errmsg);
2345
				}
2346
			}
2347
			$this->db->rollback();
2348
			return -1*$error;
2349
		}
2350
		else
2351
		{
2352
			$this->db->commit();
2353
			return 1;
2354
		}
2355
	}
2356
2357
2358
	/**
2359
	 *	Close the commercial proposal
2360
	 *
2361
	 *	@param      User	$user		Object user that close
2362
	 *	@param      int		$statut		Statut
2363
	 *	@param      string	$note		Complete private note with this note
2364
	 *  @param		int		$notrigger	1=Does not execute triggers, 0=Execute triggers
2365
	 *	@return     int         		<0 if KO, >0 if OK
2366
	 */
2367
	function cloture($user, $statut, $note, $notrigger=0)
2368
	{
2369
		global $langs,$conf;
2370
2371
		$error=0;
2372
		$now=dol_now();
2373
2374
		$this->db->begin();
2375
2376
		$newprivatenote = dol_concatdesc($this->note_private, $note);
2377
2378
		$sql = "UPDATE ".MAIN_DB_PREFIX."propal";
2379
		$sql.= " SET fk_statut = ".$statut.", note_private = '".$this->db->escape($newprivatenote)."', date_cloture='".$this->db->idate($now)."', fk_user_cloture=".$user->id;
2380
		$sql.= " WHERE rowid = ".$this->id;
2381
2382
		$resql=$this->db->query($sql);
2383
		if ($resql)
2384
		{
2385
			$modelpdf=$conf->global->PROPALE_ADDON_PDF_ODT_CLOSED?$conf->global->PROPALE_ADDON_PDF_ODT_CLOSED:$this->modelpdf;
2386
			$trigger_name='PROPAL_CLOSE_REFUSED';
2387
2388
			if ($statut == self::STATUS_SIGNED)
2389
			{
2390
				$trigger_name='PROPAL_CLOSE_SIGNED';
2391
				$modelpdf=$conf->global->PROPALE_ADDON_PDF_ODT_TOBILL?$conf->global->PROPALE_ADDON_PDF_ODT_TOBILL:$this->modelpdf;
2392
2393
				// The connected company is classified as a client
2394
				$soc=new Societe($this->db);
2395
				$soc->id = $this->socid;
2396
				$result=$soc->set_as_client();
2397
2398
				if ($result < 0)
2399
				{
2400
					$this->error=$this->db->lasterror();
2401
					$this->db->rollback();
2402
					return -2;
2403
				}
2404
			}
2405
			if ($statut == self::STATUS_BILLED)	// Why this ?
2406
			{
2407
				$trigger_name='PROPAL_CLASSIFY_BILLED';
2408
			}
2409
2410
			if (empty($conf->global->MAIN_DISABLE_PDF_AUTOUPDATE))
2411
			{
2412
			 	// Define output language
2413
			  	$outputlangs = $langs;
2414
			   	if (! empty($conf->global->MAIN_MULTILANGS))
2415
			   	{
2416
			   		$outputlangs = new Translate("",$conf);
2417
			   		$newlang=(GETPOST('lang_id','aZ09') ? GETPOST('lang_id','aZ09') : $this->thirdparty->default_lang);
2418
			   		$outputlangs->setDefaultLang($newlang);
2419
			   	}
2420
			   	//$ret=$object->fetch($id);    // Reload to get new records
2421
				   $this->generateDocument($modelpdf, $outputlangs);
2422
			}
2423
2424
			if (! $error)
2425
			{
2426
				$this->oldcopy= clone $this;
2427
				$this->statut = $statut;
2428
				$this->date_cloture = $now;
2429
				$this->note_private = $note;
2430
			}
2431
2432
			if (! $notrigger && empty($error))
2433
			{
2434
				// Call trigger
2435
				$result=$this->call_trigger($trigger_name,$user);
2436
				if ($result < 0) { $error++; }
2437
				// End call triggers
2438
			}
2439
2440
			if ( ! $error )
2441
			{
2442
				$this->db->commit();
2443
				return 1;
2444
			}
2445
			else
2446
			{
2447
				$this->db->rollback();
2448
				return -1;
2449
			}
2450
		}
2451
		else
2452
		{
2453
			$this->error=$this->db->lasterror();
2454
			$this->db->rollback();
2455
			return -1;
2456
		}
2457
	}
2458
2459
	/**
2460
	 *	Class invoiced the Propal
2461
	 *
2462
	 *	@param  	User	$user    	Object user
2463
	 *  @param		int		$notrigger	1=Does not execute triggers, 0= execute triggers
2464
	 *	@return     int     			<0 si ko, >0 si ok
2465
	 */
2466
	function classifyBilled(User $user, $notrigger=0)
2467
	{
2468
		$error=0;
2469
2470
		$this->db->begin();
2471
2472
		$sql = 'UPDATE '.MAIN_DB_PREFIX.'propal SET fk_statut = '.self::STATUS_BILLED;
2473
		$sql .= ' WHERE rowid = '.$this->id.' AND fk_statut > '.self::STATUS_DRAFT;
2474
2475
		dol_syslog(__METHOD__, LOG_DEBUG);
2476
		$resql=$this->db->query($sql);
2477
		if (!$resql)
2478
		{
2479
			$this->errors[]=$this->db->error();
2480
			$error++;
2481
		}
2482
2483
		if (! $error)
2484
		{
2485
			$this->oldcopy= clone $this;
2486
			$this->statut=self::STATUS_BILLED;
2487
		}
2488
2489
		if (! $notrigger && empty($error))
2490
		{
2491
			// Call trigger
2492
			$result=$this->call_trigger('PROPAL_MODIFY',$user);
2493
			if ($result < 0) $error++;
2494
			// End call triggers
2495
		}
2496
2497
		if (! $error)
2498
		{
2499
			$this->db->commit();
2500
			return 1;
2501
		}
2502
		else
2503
		{
2504
			foreach($this->errors as $errmsg)
2505
			{
2506
				dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2507
				$this->error.=($this->error?', '.$errmsg:$errmsg);
2508
			}
2509
			$this->db->rollback();
2510
			return -1*$error;
2511
		}
2512
	}
2513
2514
	/**
2515
	 *	Set draft status
2516
	 *
2517
	 *	@param		User	$user		Object user that modify
2518
	 *  @param		int		$notrigger	1=Does not execute triggers, 0= execute triggers
2519
	 *	@return		int					<0 if KO, >0 if OK
2520
	 */
2521
	function set_draft($user, $notrigger=0)
2522
	{
2523
		$error=0;
2524
2525
		$this->db->begin();
2526
2527
		$sql = "UPDATE ".MAIN_DB_PREFIX."propal SET fk_statut = ".self::STATUS_DRAFT;
2528
		$sql.= " WHERE rowid = ".$this->id;
2529
2530
		dol_syslog(__METHOD__, LOG_DEBUG);
2531
		$resql=$this->db->query($sql);
2532
		if (!$resql)
2533
		{
2534
			$this->errors[]=$this->db->error();
2535
			$error++;
2536
		}
2537
2538
		if (! $error)
2539
		{
2540
			$this->oldcopy= clone $this;
2541
			$this->statut = self::STATUS_DRAFT;
2542
			$this->brouillon = 1;
2543
		}
2544
2545
		if (! $notrigger && empty($error))
2546
		{
2547
			// Call trigger
2548
			$result=$this->call_trigger('PROPAL_MODIFY',$user);
2549
			if ($result < 0) $error++;
2550
			// End call triggers
2551
		}
2552
2553
		if (! $error)
2554
		{
2555
			$this->db->commit();
2556
			return 1;
2557
		}
2558
		else
2559
		{
2560
			foreach($this->errors as $errmsg)
2561
			{
2562
				dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2563
				$this->error.=($this->error?', '.$errmsg:$errmsg);
2564
			}
2565
			$this->db->rollback();
2566
			return -1*$error;
2567
		}
2568
	}
2569
2570
2571
	/**
2572
	 *    Return list of proposal (eventually filtered on user) into an array
2573
	 *
2574
	 *    @param	int		$shortlist			0=Return array[id]=ref, 1=Return array[](id=>id,ref=>ref,name=>name)
2575
	 *    @param	int		$draft				0=not draft, 1=draft
2576
	 *    @param	int		$notcurrentuser		0=all user, 1=not current user
2577
	 *    @param    int		$socid				Id third pary
2578
	 *    @param    int		$limit				For pagination
2579
	 *    @param    int		$offset				For pagination
2580
	 *    @param    string	$sortfield			Sort criteria
2581
	 *    @param    string	$sortorder			Sort order
2582
	 *    @return	int		       				-1 if KO, array with result if OK
2583
	 */
2584
	function liste_array($shortlist=0, $draft=0, $notcurrentuser=0, $socid=0, $limit=0, $offset=0, $sortfield='p.datep', $sortorder='DESC')
2585
	{
2586
		global $user;
2587
2588
		$ga = array();
2589
2590
		$sql = "SELECT s.rowid, s.nom as name, s.client,";
2591
		$sql.= " p.rowid as propalid, p.fk_statut, p.total_ht, p.ref, p.remise, ";
2592
		$sql.= " p.datep as dp, p.fin_validite as datelimite";
2593
		if (! $user->rights->societe->client->voir && ! $socid) $sql .= ", sc.fk_soc, sc.fk_user";
2594
		$sql.= " FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."propal as p, ".MAIN_DB_PREFIX."c_propalst as c";
2595
		if (! $user->rights->societe->client->voir && ! $socid) $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc";
2596
		$sql.= " WHERE p.entity IN (".getEntity('propal').")";
2597
		$sql.= " AND p.fk_soc = s.rowid";
2598
		$sql.= " AND p.fk_statut = c.id";
2599
		if (! $user->rights->societe->client->voir && ! $socid) //restriction
2600
		{
2601
			$sql.= " AND s.rowid = sc.fk_soc AND sc.fk_user = " .$user->id;
2602
		}
2603
		if ($socid) $sql.= " AND s.rowid = ".$socid;
2604
		if ($draft)	$sql.= " AND p.fk_statut = ".self::STATUS_DRAFT;
2605
		if ($notcurrentuser > 0) $sql.= " AND p.fk_user_author <> ".$user->id;
2606
		$sql.= $this->db->order($sortfield,$sortorder);
2607
		$sql.= $this->db->plimit($limit,$offset);
2608
2609
		$result=$this->db->query($sql);
2610
		if ($result)
2611
		{
2612
			$num = $this->db->num_rows($result);
2613
			if ($num)
2614
			{
2615
				$i = 0;
2616
				while ($i < $num)
2617
				{
2618
					$obj = $this->db->fetch_object($result);
2619
2620
					if ($shortlist == 1)
2621
					{
2622
						$ga[$obj->propalid] = $obj->ref;
2623
					}
2624
					else if ($shortlist == 2)
2625
					{
2626
						$ga[$obj->propalid] = $obj->ref.' ('.$obj->name.')';
2627
					}
2628
					else
2629
					{
2630
						$ga[$i]['id']	= $obj->propalid;
2631
						$ga[$i]['ref'] 	= $obj->ref;
2632
						$ga[$i]['name'] = $obj->name;
2633
					}
2634
2635
					$i++;
2636
				}
2637
			}
2638
			return $ga;
2639
		}
2640
		else
2641
		{
2642
			dol_print_error($this->db);
2643
			return -1;
2644
		}
2645
	}
2646
2647
	/**
2648
	 *  Returns an array with the numbers of related invoices
2649
	 *
2650
	 *	@return	array		Array of invoices
2651
	 */
2652
	function getInvoiceArrayList()
2653
	{
2654
		return $this->InvoiceArrayList($this->id);
2655
	}
2656
2657
	/**
2658
	 *  Returns an array with id and ref of related invoices
2659
	 *
2660
	 *	@param		int		$id			Id propal
2661
	 *	@return		array				Array of invoices id
2662
	 */
2663
	function InvoiceArrayList($id)
2664
	{
2665
		$ga = array();
2666
		$linkedInvoices = array();
2667
2668
		$this->fetchObjectLinked($id,$this->element);
2669
		foreach($this->linkedObjectsIds as $objecttype => $objectid)
2670
		{
2671
			// Nouveau système du comon object renvoi des rowid et non un id linéaire de 1 à n
2672
			// On parcourt donc une liste d'objets en tant qu'objet unique
2673
			foreach($objectid as $key => $object)
2674
			{
2675
				// Cas des factures liees directement
2676
				if ($objecttype == 'facture')
2677
				{
2678
					$linkedInvoices[] = $object;
2679
				}
2680
				// Cas des factures liees par un autre objet (ex: commande)
2681
				else
2682
				{
2683
					$this->fetchObjectLinked($object,$objecttype);
2684
					foreach($this->linkedObjectsIds as $subobjecttype => $subobjectid)
2685
					{
2686
						foreach($subobjectid as $subkey => $subobject)
2687
						{
2688
							if ($subobjecttype == 'facture')
2689
							{
2690
								$linkedInvoices[] = $subobject;
2691
							}
2692
						}
2693
					}
2694
				}
2695
			}
2696
		}
2697
2698
		if (count($linkedInvoices) > 0)
2699
		{
2700
			$sql= "SELECT rowid as facid, facnumber, total, datef as df, fk_user_author, fk_statut, paye";
2701
			$sql.= " FROM ".MAIN_DB_PREFIX."facture";
2702
			$sql.= " WHERE rowid IN (".implode(',',$linkedInvoices).")";
2703
2704
			dol_syslog(get_class($this)."::InvoiceArrayList", LOG_DEBUG);
2705
			$resql=$this->db->query($sql);
2706
2707
			if ($resql)
2708
			{
2709
				$tab_sqlobj=array();
2710
				$nump = $this->db->num_rows($resql);
2711
				for ($i = 0;$i < $nump;$i++)
2712
				{
2713
					$sqlobj = $this->db->fetch_object($resql);
2714
					$tab_sqlobj[] = $sqlobj;
2715
				}
2716
				$this->db->free($resql);
2717
2718
				$nump = count($tab_sqlobj);
2719
2720
				if ($nump)
2721
				{
2722
					$i = 0;
2723
					while ($i < $nump)
2724
					{
2725
						$obj = array_shift($tab_sqlobj);
2726
2727
						$ga[$i] = $obj;
2728
2729
						$i++;
2730
					}
2731
				}
2732
				return $ga;
2733
			}
2734
			else
2735
			{
2736
				return -1;
2737
			}
2738
		}
2739
		else return $ga;
2740
	}
2741
2742
	/**
2743
	 *	Delete proposal
2744
	 *
2745
	 *	@param	User	$user        	Object user that delete
2746
	 *	@param	int		$notrigger		1=Does not execute triggers, 0= execute triggers
2747
	 *	@return	int						1 if ok, otherwise if error
2748
	 */
2749
	function delete($user, $notrigger=0)
2750
	{
2751
		global $conf;
2752
		require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2753
2754
		$error=0;
2755
2756
		$this->db->begin();
2757
2758
		if (! $notrigger)
2759
		{
2760
			// Call trigger
2761
			$result=$this->call_trigger('PROPAL_DELETE',$user);
2762
			if ($result < 0) { $error++; }
2763
			// End call triggers
2764
		}
2765
2766
		if (! $error)
2767
		{
2768
			$sql = "DELETE FROM ".MAIN_DB_PREFIX."propaldet WHERE fk_propal = ".$this->id;
2769
			if ($this->db->query($sql))
2770
			{
2771
				$sql = "DELETE FROM ".MAIN_DB_PREFIX."propal WHERE rowid = ".$this->id;
2772
				if ($this->db->query($sql))
2773
				{
2774
					// Delete linked object
2775
					$res = $this->deleteObjectLinked();
2776
					if ($res < 0) $error++;
2777
2778
					// Delete linked contacts
2779
					$res = $this->delete_linked_contact();
2780
					if ($res < 0) $error++;
2781
2782
					if (! $error)
2783
					{
2784
						// We remove directory
2785
						$ref = dol_sanitizeFileName($this->ref);
2786
						if ($conf->propal->multidir_output[$this->entity] && !empty($this->ref))
2787
						{
2788
							$dir = $conf->propal->multidir_output[$this->entity] . "/" . $ref ;
2789
							$file = $dir . "/" . $ref . ".pdf";
2790
							if (file_exists($file))
2791
							{
2792
								dol_delete_preview($this);
2793
2794
								if (! dol_delete_file($file,0,0,0,$this)) // For triggers
2795
								{
2796
									$this->error='ErrorFailToDeleteFile';
2797
									$this->errors=array('ErrorFailToDeleteFile');
2798
									$this->db->rollback();
2799
									return 0;
2800
								}
2801
							}
2802
							if (file_exists($dir))
2803
							{
2804
								$res=@dol_delete_dir_recursive($dir);
2805
								if (! $res)
2806
								{
2807
									$this->error='ErrorFailToDeleteDir';
2808
									$this->errors=array('ErrorFailToDeleteDir');
2809
									$this->db->rollback();
2810
									return 0;
2811
								}
2812
							}
2813
						}
2814
					}
2815
2816
					// Removed extrafields
2817
					if (! $error)
2818
					{
2819
						if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) // For avoid conflicts if trigger used
2820
						{
2821
							$result=$this->deleteExtraFields();
2822
							if ($result < 0)
2823
							{
2824
								$error++;
2825
								$errorflag=-4;
2826
								dol_syslog(get_class($this)."::delete erreur ".$errorflag." ".$this->error, LOG_ERR);
2827
							}
2828
						}
2829
					}
2830
2831
					if (! $error)
2832
					{
2833
						dol_syslog(get_class($this)."::delete ".$this->id." by ".$user->id, LOG_DEBUG);
2834
						$this->db->commit();
2835
						return 1;
2836
					}
2837
					else
2838
					{
2839
						$this->error=$this->db->lasterror();
2840
						$this->db->rollback();
2841
						return 0;
2842
					}
2843
				}
2844
				else
2845
				{
2846
					$this->error=$this->db->lasterror();
2847
					$this->db->rollback();
2848
					return -3;
2849
				}
2850
			}
2851
			else
2852
			{
2853
				$this->error=$this->db->lasterror();
2854
				$this->db->rollback();
2855
				return -2;
2856
			}
2857
		}
2858
		else
2859
		{
2860
			$this->db->rollback();
2861
			return -1;
2862
		}
2863
	}
2864
2865
	/**
2866
	 *  Change the delivery time
2867
	 *
2868
	 *  @param	int	$availability_id	Id of new delivery time
2869
	 * 	@param	int	$notrigger			1=Does not execute triggers, 0= execute triggers
2870
	 *  @return int                  	>0 if OK, <0 if KO
2871
	 *  @deprecated  use set_availability
2872
	 */
2873
	function availability($availability_id, $notrigger=0)
2874
	{
2875
		global $user;
2876
2877
		if ($this->statut >= self::STATUS_DRAFT)
2878
		{
2879
			$error=0;
2880
2881
			$this->db->begin();
2882
2883
			$sql = 'UPDATE '.MAIN_DB_PREFIX.'propal';
2884
			$sql .= ' SET fk_availability = '.$availability_id;
2885
			$sql .= ' WHERE rowid='.$this->id;
2886
2887
			dol_syslog(__METHOD__.' availability('.$availability_id.')', LOG_DEBUG);
2888
			$resql=$this->db->query($sql);
2889
			if (!$resql)
2890
			{
2891
				$this->errors[]=$this->db->error();
2892
				$error++;
2893
			}
2894
2895
			if (! $error)
2896
			{
2897
				$this->oldcopy= clone $this;
2898
				$this->availability_id = $availability_id;
2899
			}
2900
2901
			if (! $notrigger && empty($error))
2902
			{
2903
				// Call trigger
2904
				$result=$this->call_trigger('PROPAL_MODIFY',$user);
2905
				if ($result < 0) $error++;
2906
				// End call triggers
2907
			}
2908
2909
			if (! $error)
2910
			{
2911
				$this->db->commit();
2912
				return 1;
2913
			}
2914
			else
2915
			{
2916
				foreach($this->errors as $errmsg)
2917
				{
2918
					dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2919
					$this->error.=($this->error?', '.$errmsg:$errmsg);
2920
				}
2921
				$this->db->rollback();
2922
				return -1*$error;
2923
			}
2924
		}
2925
		else
2926
		{
2927
			$error_str='Propal status do not meet requirement '.$this->statut;
2928
			dol_syslog(__METHOD__.$error_str, LOG_ERR);
2929
			$this->error=$error_str;
2930
			$this->errors[]= $this->error;
2931
			return -2;
2932
		}
2933
	}
2934
2935
	/**
2936
	 *	Change source demand
2937
	 *
2938
	 *	@param	int $demand_reason_id 	Id of new source demand
2939
	 * 	@param	int	$notrigger			1=Does not execute triggers, 0= execute triggers
2940
	 *	@return int						>0 si ok, <0 si ko
2941
	 *	@deprecated use set_demand_reason
2942
	 */
2943
	function demand_reason($demand_reason_id, $notrigger=0)
2944
	{
2945
		global $user;
2946
2947
		if ($this->statut >= self::STATUS_DRAFT)
2948
		{
2949
			$error=0;
2950
2951
			$this->db->begin();
2952
2953
			$sql = 'UPDATE '.MAIN_DB_PREFIX.'propal';
2954
			$sql .= ' SET fk_input_reason = '.$demand_reason_id;
2955
			$sql .= ' WHERE rowid='.$this->id;
2956
2957
			dol_syslog(__METHOD__.' demand_reason('.$demand_reason_id.')', LOG_DEBUG);
2958
			$resql=$this->db->query($sql);
2959
			if (!$resql)
2960
			{
2961
				$this->errors[]=$this->db->error();
2962
				$error++;
2963
			}
2964
2965
			if (! $error)
2966
			{
2967
				$this->oldcopy= clone $this;
2968
				$this->demand_reason_id = $demand_reason_id;
2969
			}
2970
2971
			if (! $notrigger && empty($error))
2972
			{
2973
				// Call trigger
2974
				$result=$this->call_trigger('PROPAL_MODIFY',$user);
2975
				if ($result < 0) $error++;
2976
				// End call triggers
2977
			}
2978
2979
			if (! $error)
2980
			{
2981
				$this->db->commit();
2982
				return 1;
2983
			}
2984
			else
2985
			{
2986
				foreach($this->errors as $errmsg)
2987
				{
2988
					dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2989
					$this->error.=($this->error?', '.$errmsg:$errmsg);
2990
				}
2991
				$this->db->rollback();
2992
				return -1*$error;
2993
			}
2994
		}
2995
		else
2996
		{
2997
			$error_str='Propal status do not meet requirement '.$this->statut;
2998
			dol_syslog(__METHOD__.$error_str, LOG_ERR);
2999
			$this->error=$error_str;
3000
			$this->errors[]= $this->error;
3001
			return -2;
3002
		}
3003
	}
3004
3005
3006
	/**
3007
	 *	Object Proposal Information
3008
	 *
3009
	 * 	@param	int		$id		Proposal id
3010
	 *  @return	void
3011
	 */
3012
	function info($id)
3013
	{
3014
		$sql = "SELECT c.rowid, ";
3015
		$sql.= " c.datec, c.date_valid as datev, c.date_cloture as dateo,";
3016
		$sql.= " c.fk_user_author, c.fk_user_valid, c.fk_user_cloture";
3017
		$sql.= " FROM ".MAIN_DB_PREFIX."propal as c";
3018
		$sql.= " WHERE c.rowid = ".$id;
3019
3020
		$result = $this->db->query($sql);
3021
3022
		if ($result)
3023
		{
3024
			if ($this->db->num_rows($result))
3025
			{
3026
				$obj = $this->db->fetch_object($result);
3027
3028
				$this->id                = $obj->rowid;
3029
3030
				$this->date_creation     = $this->db->jdate($obj->datec);
3031
				$this->date_validation   = $this->db->jdate($obj->datev);
3032
				$this->date_cloture      = $this->db->jdate($obj->dateo);
3033
3034
				$cuser = new User($this->db);
3035
				$cuser->fetch($obj->fk_user_author);
3036
				$this->user_creation     = $cuser;
3037
3038
				if ($obj->fk_user_valid)
3039
				{
3040
					$vuser = new User($this->db);
3041
					$vuser->fetch($obj->fk_user_valid);
3042
					$this->user_validation     = $vuser;
3043
				}
3044
3045
				if ($obj->fk_user_cloture)
3046
				{
3047
					$cluser = new User($this->db);
3048
					$cluser->fetch($obj->fk_user_cloture);
3049
					$this->user_cloture     = $cluser;
3050
				}
3051
3052
3053
			}
3054
			$this->db->free($result);
3055
3056
		}
3057
		else
3058
		{
3059
			dol_print_error($this->db);
3060
		}
3061
	}
3062
3063
3064
	/**
3065
	 *    	Return label of status of proposal (draft, validated, ...)
3066
	 *
3067
	 *    	@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
3068
	 *    	@return     string		Label
3069
	 */
3070
	function getLibStatut($mode=0)
3071
	{
3072
		return $this->LibStatut($this->statut, $mode);
3073
	}
3074
3075
	/**
3076
	 *    	Return label of a status (draft, validated, ...)
3077
	 *
3078
	 *    	@param      int			$statut		id statut
3079
	 *    	@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
3080
	 *    	@return     string		Label
3081
	 */
3082
	function LibStatut($statut,$mode=1)
3083
	{
3084
		// Init/load array of translation of status
3085
		if (empty($this->labelstatut) || empty($this->labelstatut_short))
3086
		{
3087
			global $langs;
3088
			$langs->load("propal");
3089
			$this->labelstatut[0]=(! empty($conf->global->PROPAL_STATUS_DRAFT_LABEL) ? $conf->global->PROPAL_STATUS_DRAFT_LABEL : $langs->trans("PropalStatusDraft"));
0 ignored issues
show
Bug introduced by
The variable $conf does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
3090
			$this->labelstatut[1]=(! empty($conf->global->PROPAL_STATUS_VALIDATED_LABEL) ? $conf->global->PROPAL_STATUS_VALIDATED_LABEL : $langs->trans("PropalStatusValidated"));
3091
			$this->labelstatut[2]=(! empty($conf->global->PROPAL_STATUS_SIGNED_LABEL) ? $conf->global->PROPAL_STATUS_SIGNED_LABEL : $langs->trans("PropalStatusSigned"));
3092
			$this->labelstatut[3]=(! empty($conf->global->PROPAL_STATUS_NOTSIGNED_LABEL) ? $conf->global->PROPAL_STATUS_NOTSIGNED_LABEL : $langs->trans("PropalStatusNotSigned"));
3093
			$this->labelstatut[4]=(! empty($conf->global->PROPAL_STATUS_BILLED_LABEL) ? $conf->global->PROPAL_STATUS_BILLED_LABEL : $langs->trans("PropalStatusBilled"));
3094
			$this->labelstatut_short[0]=(! empty($conf->global->PROPAL_STATUS_DRAFTSHORT_LABEL) ? $conf->global->PROPAL_STATUS_DRAFTSHORT_LABEL : $langs->trans("PropalStatusDraftShort"));
3095
			$this->labelstatut_short[1]=(! empty($conf->global->PROPAL_STATUS_VALIDATEDSHORT_LABEL) ? $conf->global->PROPAL_STATUS_VALIDATEDSHORT_LABEL : $langs->trans("Opened"));
3096
			$this->labelstatut_short[2]=(! empty($conf->global->PROPAL_STATUS_SIGNEDSHORT_LABEL) ? $conf->global->PROPAL_STATUS_SIGNEDSHORT_LABEL : $langs->trans("PropalStatusSignedShort"));
3097
			$this->labelstatut_short[3]=(! empty($conf->global->PROPAL_STATUS_NOTSIGNEDSHORT_LABEL) ? $conf->global->PROPAL_STATUS_NOTSIGNEDSHORT_LABEL : $langs->trans("PropalStatusNotSignedShort"));
3098
			$this->labelstatut_short[4]=(! empty($conf->global->PROPAL_STATUS_BILLEDSHORT_LABEL) ? $conf->global->PROPAL_STATUS_BILLEDSHORT_LABEL : $langs->trans("PropalStatusBilledShort"));
3099
		}
3100
3101
		$statuttrans='';
3102
		if ($statut==self::STATUS_DRAFT) $statuttrans='statut0';
3103
		if ($statut==self::STATUS_VALIDATED) $statuttrans='statut1';
3104
		if ($statut==self::STATUS_SIGNED) $statuttrans='statut3';
3105
		if ($statut==self::STATUS_NOTSIGNED) $statuttrans='statut5';
3106
		if ($statut==self::STATUS_BILLED) $statuttrans='statut6';
3107
3108
		if ($mode == 0)	return $this->labelstatut[$statut];
3109
		if ($mode == 1)	return $this->labelstatut_short[$statut];
3110
		if ($mode == 2)	return img_picto($this->labelstatut_short[$statut], $statuttrans).' '.$this->labelstatut_short[$statut];
3111
		if ($mode == 3)	return img_picto($this->labelstatut[$statut], $statuttrans);
3112
		if ($mode == 4)	return img_picto($this->labelstatut[$statut],$statuttrans).' '.$this->labelstatut[$statut];
3113
		if ($mode == 5)	return '<span class="hideonsmartphone">'.$this->labelstatut_short[$statut].' </span>'.img_picto($this->labelstatut[$statut],$statuttrans);
3114
		if ($mode == 6)	return '<span class="hideonsmartphone">'.$this->labelstatut[$statut].' </span>'.img_picto($this->labelstatut[$statut],$statuttrans);
3115
	 }
3116
3117
3118
	/**
3119
	 *      Load indicators for dashboard (this->nbtodo and this->nbtodolate)
3120
	 *
3121
	 *      @param          User	$user   Object user
3122
	 *      @param          int		$mode   "opened" for proposal to close, "signed" for proposal to invoice
3123
	 *      @return WorkboardResponse|int <0 if KO, WorkboardResponse if OK
3124
	 */
3125
	function load_board($user,$mode)
3126
	{
3127
		global $conf, $langs;
3128
3129
		$clause = " WHERE";
3130
3131
		$sql = "SELECT p.rowid, p.ref, p.datec as datec, p.fin_validite as datefin";
3132
		$sql.= " FROM ".MAIN_DB_PREFIX."propal as p";
3133
		if (!$user->rights->societe->client->voir && !$user->societe_id)
3134
		{
3135
			$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON p.fk_soc = sc.fk_soc";
3136
			$sql.= " WHERE sc.fk_user = " .$user->id;
3137
			$clause = " AND";
3138
		}
3139
		$sql.= $clause." p.entity IN (".getEntity('propal').")";
3140
		if ($mode == 'opened') $sql.= " AND p.fk_statut = ".self::STATUS_VALIDATED;
3141
		if ($mode == 'signed') $sql.= " AND p.fk_statut = ".self::STATUS_SIGNED;
3142
		if ($user->societe_id) $sql.= " AND p.fk_soc = ".$user->societe_id;
3143
3144
		$resql=$this->db->query($sql);
3145
		if ($resql)
3146
		{
3147
			$langs->load("propal");
3148
			$now=dol_now();
3149
3150
			$delay_warning = 0;
3151
			$statut = 0;
3152
			$label = '';
3153
			if ($mode == 'opened') {
3154
				$delay_warning=$conf->propal->cloture->warning_delay;
3155
				$statut = self::STATUS_VALIDATED;
3156
				$label = $langs->trans("PropalsToClose");
3157
			}
3158
			if ($mode == 'signed') {
3159
				$delay_warning=$conf->propal->facturation->warning_delay;
3160
				$statut = self::STATUS_SIGNED;
3161
				$label = $langs->trans("PropalsToBill");         // We set here bill but may be billed or ordered
3162
			}
3163
3164
			$response = new WorkboardResponse();
3165
			$response->warning_delay = $delay_warning/60/60/24;
3166
			$response->label = $label;
3167
			$response->url = DOL_URL_ROOT.'/comm/propal/list.php?viewstatut='.$statut.'&mainmenu=commercial&leftmenu=propals';
3168
			$response->url_late = DOL_URL_ROOT.'/comm/propal/list.php?viewstatut='.$statut.'&mainmenu=commercial&leftmenu=propals&sortfield=p.datep&sortorder=asc';
3169
			$response->img = img_object('',"propal");
3170
3171
			// This assignment in condition is not a bug. It allows walking the results.
3172
			while ($obj=$this->db->fetch_object($resql))
3173
			{
3174
				$response->nbtodo++;
3175
				if ($mode == 'opened')
3176
				{
3177
					$datelimit = $this->db->jdate($obj->datefin);
3178
					if ($datelimit < ($now - $delay_warning))
3179
					{
3180
						$response->nbtodolate++;
3181
					}
3182
				}
3183
				// TODO Definir regle des propales a facturer en retard
3184
				// if ($mode == 'signed' && ! count($this->FactureListeArray($obj->rowid))) $this->nbtodolate++;
3185
			}
3186
3187
			return $response;
3188
		}
3189
		else
3190
		{
3191
			$this->error=$this->db->error();
3192
			return -1;
3193
		}
3194
	}
3195
3196
3197
	/**
3198
	 *  Initialise an instance with random values.
3199
	 *  Used to build previews or test instances.
3200
	 *	id must be 0 if object instance is a specimen.
3201
	 *
3202
	 *  @return	void
3203
	 */
3204
	function initAsSpecimen()
3205
	{
3206
		global $langs;
3207
3208
		// Load array of products prodids
3209
		$num_prods = 0;
3210
		$prodids = array();
3211
		$sql = "SELECT rowid";
3212
		$sql.= " FROM ".MAIN_DB_PREFIX."product";
3213
		$sql.= " WHERE entity IN (".getEntity('product').")";
3214
		$resql = $this->db->query($sql);
3215
		if ($resql)
3216
		{
3217
			$num_prods = $this->db->num_rows($resql);
3218
			$i = 0;
3219
			while ($i < $num_prods)
3220
			{
3221
				$i++;
3222
				$row = $this->db->fetch_row($resql);
3223
				$prodids[$i] = $row[0];
3224
			}
3225
		}
3226
3227
		// Initialise parametres
3228
		$this->id=0;
3229
		$this->ref = 'SPECIMEN';
3230
		$this->ref_client='NEMICEPS';
3231
		$this->specimen=1;
3232
		$this->socid = 1;
3233
		$this->date = time();
3234
		$this->fin_validite = $this->date+3600*24*30;
3235
		$this->cond_reglement_id   = 1;
3236
		$this->cond_reglement_code = 'RECEP';
3237
		$this->mode_reglement_id   = 7;
3238
		$this->mode_reglement_code = 'CHQ';
3239
		$this->availability_id     = 1;
3240
		$this->availability_code   = 'AV_NOW';
3241
		$this->demand_reason_id    = 1;
3242
		$this->demand_reason_code  = 'SRC_00';
3243
		$this->note_public='This is a comment (public)';
3244
		$this->note_private='This is a comment (private)';
3245
		// Lines
3246
		$nbp = 5;
3247
		$xnbp = 0;
3248
		while ($xnbp < $nbp)
3249
		{
3250
			$line=new PropaleLigne($this->db);
3251
			$line->desc=$langs->trans("Description")." ".$xnbp;
3252
			$line->qty=1;
3253
			$line->subprice=100;
3254
			$line->price=100;
3255
			$line->tva_tx=20;
3256
			$line->localtax1_tx=0;
3257
			$line->localtax2_tx=0;
3258
			if ($xnbp == 2)
3259
			{
3260
				$line->total_ht=50;
3261
				$line->total_ttc=60;
3262
				$line->total_tva=10;
3263
				$line->remise_percent=50;
3264
			}
3265
			else
3266
			{
3267
				$line->total_ht=100;
3268
				$line->total_ttc=120;
3269
				$line->total_tva=20;
3270
				$line->remise_percent=00;
3271
			}
3272
3273
			if ($num_prods > 0)
3274
			{
3275
				$prodid = mt_rand(1, $num_prods);
3276
				$line->fk_product=$prodids[$prodid];
3277
		$line->product_ref='SPECIMEN';
3278
			}
3279
3280
			$this->lines[$xnbp]=$line;
3281
3282
			$this->total_ht       += $line->total_ht;
3283
			$this->total_tva      += $line->total_tva;
3284
			$this->total_ttc      += $line->total_ttc;
3285
3286
			$xnbp++;
3287
		}
3288
	}
3289
3290
	/**
3291
	 *      Charge indicateurs this->nb de tableau de bord
3292
	 *
3293
	 *      @return     int         <0 if ko, >0 if ok
3294
	 */
3295
	function load_state_board()
3296
	{
3297
		global $user;
3298
3299
		$this->nb=array();
3300
		$clause = "WHERE";
3301
3302
		$sql = "SELECT count(p.rowid) as nb";
3303
		$sql.= " FROM ".MAIN_DB_PREFIX."propal as p";
3304
		$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid";
3305
		if (!$user->rights->societe->client->voir && !$user->societe_id)
3306
		{
3307
			$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
3308
			$sql.= " WHERE sc.fk_user = " .$user->id;
3309
			$clause = "AND";
3310
		}
3311
		$sql.= " ".$clause." p.entity IN (".getEntity('propal').")";
3312
3313
		$resql=$this->db->query($sql);
3314
		if ($resql)
3315
		{
3316
			// This assignment in condition is not a bug. It allows walking the results.
3317
			while ($obj=$this->db->fetch_object($resql))
3318
			{
3319
				$this->nb["proposals"]=$obj->nb;
3320
			}
3321
			$this->db->free($resql);
3322
			return 1;
3323
		}
3324
		else
3325
		{
3326
			dol_print_error($this->db);
3327
			$this->error=$this->db->error();
3328
			return -1;
3329
		}
3330
	}
3331
3332
3333
	/**
3334
	 *  Returns the reference to the following non used Proposal used depending on the active numbering module
3335
	 *  defined into PROPALE_ADDON
3336
	 *
3337
	 *  @param	Societe		$soc  	Object thirdparty
3338
	 *  @return string      		Reference libre pour la propale
3339
	 */
3340
	function getNextNumRef($soc)
3341
	{
3342
		global $conf,$langs;
3343
		$langs->load("propal");
3344
3345
		if (! empty($conf->global->PROPALE_ADDON))
3346
		{
3347
			$mybool=false;
3348
3349
			$file = $conf->global->PROPALE_ADDON.".php";
3350
			$classname = $conf->global->PROPALE_ADDON;
3351
3352
			// Include file with class
3353
			$dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
3354
			foreach ($dirmodels as $reldir) {
3355
3356
				$dir = dol_buildpath($reldir."core/modules/propale/");
3357
3358
				// Load file with numbering class (if found)
3359
				$mybool|=@include_once $dir.$file;
3360
			}
3361
3362
			if (! $mybool)
3363
			{
3364
				dol_print_error('',"Failed to include file ".$file);
3365
				return '';
3366
			}
3367
3368
			$obj = new $classname();
3369
			$numref = "";
3370
			$numref = $obj->getNextValue($soc,$this);
3371
3372
			if ($numref != "")
3373
			{
3374
				return $numref;
3375
			}
3376
			else
3377
			{
3378
				$this->error=$obj->error;
3379
				//dol_print_error($db,"Propale::getNextNumRef ".$obj->error);
3380
				return "";
3381
			}
3382
		}
3383
		else
3384
		{
3385
			$langs->load("errors");
3386
			print $langs->trans("Error")." ".$langs->trans("ErrorModuleSetupNotComplete");
3387
			return "";
3388
		}
3389
	}
3390
3391
	/**
3392
	 *	Return clicable link of object (with eventually picto)
3393
	 *
3394
	 *	@param      int		$withpicto		          Add picto into link
3395
	 *	@param      string	$option			          Where point the link ('expedition', 'document', ...)
3396
	 *	@param      string	$get_params    	          Parametres added to url
3397
	 *  @param	    int   	$notooltip		          1=Disable tooltip
3398
	 *  @param      int     $save_lastsearch_value    -1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking
3399
	 *	@return     string          		          String with URL
3400
	 */
3401
	function getNomUrl($withpicto=0, $option='', $get_params='', $notooltip=0, $save_lastsearch_value=-1)
3402
	{
3403
		global $langs, $conf, $user;
3404
3405
		if (! empty($conf->dol_no_mouse_hover)) $notooltip=1;   // Force disable tooltips
3406
3407
		$result='';
3408
		$label='';
3409
		$url='';
3410
3411
		if ($user->rights->propal->lire)
3412
		{
3413
			$label = '<u>' . $langs->trans("ShowPropal") . '</u>';
3414
			if (! empty($this->ref))
3415
				$label.= '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
3416
			if (! empty($this->ref_client))
3417
				$label.= '<br><b>'.$langs->trans('RefCustomer').':</b> '.$this->ref_client;
3418
			if (! empty($this->total_ht))
3419
				$label.= '<br><b>' . $langs->trans('AmountHT') . ':</b> ' . price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
3420
			if (! empty($this->total_tva))
3421
				$label.= '<br><b>' . $langs->trans('VAT') . ':</b> ' . price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
3422
			if (! empty($this->total_ttc))
3423
				$label.= '<br><b>' . $langs->trans('AmountTTC') . ':</b> ' . price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
3424
			if ($option == '') {
3425
				$url = DOL_URL_ROOT.'/comm/propal/card.php?id='.$this->id. $get_params;
3426
			}
3427
			if ($option == 'compta') {  // deprecated
3428
				$url = DOL_URL_ROOT.'/comm/propal/card.php?id='.$this->id. $get_params;
3429
			}
3430
			if ($option == 'expedition') {
3431
				$url = DOL_URL_ROOT.'/expedition/propal.php?id='.$this->id. $get_params;
3432
			}
3433
			if ($option == 'document') {
3434
				$url = DOL_URL_ROOT.'/comm/propal/document.php?id='.$this->id. $get_params;
3435
			}
3436
3437
			if ($option != 'nolink')
3438
			{
3439
				// Add param to save lastsearch_values or not
3440
				$add_save_lastsearch_values=($save_lastsearch_value == 1 ? 1 : 0);
3441
				if ($save_lastsearch_value == -1 && preg_match('/list\.php/',$_SERVER["PHP_SELF"])) $add_save_lastsearch_values=1;
3442
				if ($add_save_lastsearch_values) $url.='&save_lastsearch_values=1';
3443
			}
3444
		}
3445
3446
		$linkclose='';
3447
		if (empty($notooltip) && $user->rights->propal->lire)
3448
		{
3449
			if (! empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER))
3450
			{
3451
				$label=$langs->trans("ShowPropal");
3452
				$linkclose.=' alt="'.dol_escape_htmltag($label, 1).'"';
3453
			}
3454
			$linkclose.= ' title="'.dol_escape_htmltag($label, 1).'"';
3455
			$linkclose.=' class="classfortooltip"';
3456
		}
3457
3458
		$linkstart = '<a href="'.$url.'"';
3459
		$linkstart.=$linkclose.'>';
3460
		$linkend='</a>';
3461
3462
		$result .= $linkstart;
3463
		if ($withpicto) $result.=img_object(($notooltip?'':$label), $this->picto, ($notooltip?(($withpicto != 2) ? 'class="paddingright"' : ''):'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip?0:1);
3464
		if ($withpicto != 2) $result.= $this->ref;
3465
		$result .= $linkend;
3466
3467
		return $result;
3468
	}
3469
3470
	/**
3471
	 * 	Retrieve an array of propal lines
3472
	 *
3473
	 * 	@return int		>0 if OK, <0 if KO
3474
	 */
3475
	function getLinesArray()
3476
	{
3477
		// TODO Duplicate with fetch_lines ? Wich one to keep ?
3478
3479
		$this->lines = array();
3480
3481
		$sql = 'SELECT pt.rowid, pt.label as custom_label, pt.description, pt.fk_product, pt.fk_remise_except,';
3482
		$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,';
3483
		$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,';
3484
		$sql.= ' pt.date_start, pt.date_end, pt.product_type, pt.rang, pt.fk_parent_line,';
3485
		$sql.= ' pt.fk_unit,';
3486
		$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,';
3487
		$sql.= ' p.entity,';
3488
		$sql.= ' pt.fk_multicurrency, pt.multicurrency_code, pt.multicurrency_subprice, pt.multicurrency_total_ht, pt.multicurrency_total_tva, pt.multicurrency_total_ttc';
3489
		$sql.= ' FROM '.MAIN_DB_PREFIX.'propaldet as pt';
3490
		$sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON pt.fk_product=p.rowid';
3491
		$sql.= ' WHERE pt.fk_propal = '.$this->id;
3492
		$sql.= ' ORDER BY pt.rang ASC, pt.rowid';
3493
3494
		dol_syslog(get_class($this).'::getLinesArray', LOG_DEBUG);
3495
		$resql = $this->db->query($sql);
3496
		if ($resql)
3497
		{
3498
			$num = $this->db->num_rows($resql);
3499
			$i = 0;
3500
3501
			while ($i < $num)
3502
			{
3503
				$obj = $this->db->fetch_object($resql);
3504
3505
				$this->lines[$i]					= new PropaleLigne($this->db);
3506
				$this->lines[$i]->id				= $obj->rowid; // for backward compatibility
3507
				$this->lines[$i]->rowid				= $obj->rowid;
3508
				$this->lines[$i]->label 			= $obj->custom_label;
3509
				$this->lines[$i]->desc       		= $obj->description;
3510
				$this->lines[$i]->description 		= $obj->description;
3511
				$this->lines[$i]->fk_product		= $obj->fk_product;
3512
				$this->lines[$i]->ref				= $obj->ref;
3513
				$this->lines[$i]->product_ref		= $obj->ref;
3514
				$this->lines[$i]->entity            = $obj->entity;             // Product entity
3515
				$this->lines[$i]->product_label		= $obj->product_label;
3516
				$this->lines[$i]->product_desc		= $obj->product_desc;
3517
				$this->lines[$i]->product_tobatch   = $obj->product_tobatch;
3518
				$this->lines[$i]->fk_product_type	= $obj->fk_product_type;    // deprecated
3519
				$this->lines[$i]->product_type		= $obj->product_type;
3520
				$this->lines[$i]->qty				= $obj->qty;
3521
				$this->lines[$i]->subprice			= $obj->subprice;
3522
				$this->lines[$i]->fk_remise_except 	= $obj->fk_remise_except;
3523
				$this->lines[$i]->remise_percent	= $obj->remise_percent;
3524
3525
				$this->lines[$i]->vat_src_code      = $obj->vat_src_code;
3526
				$this->lines[$i]->tva_tx			= $obj->tva_tx;
3527
				$this->lines[$i]->localtax1_tx		= $obj->localtax1_tx;
3528
				$this->lines[$i]->localtax2_tx		= $obj->localtax2_tx;
3529
				$this->lines[$i]->localtax1_type	= $obj->localtax1_type;
3530
				$this->lines[$i]->localtax2_type	= $obj->localtax2_type;
3531
				$this->lines[$i]->info_bits			= $obj->info_bits;
3532
				$this->lines[$i]->total_ht			= $obj->total_ht;
3533
				$this->lines[$i]->total_tva			= $obj->total_tva;
3534
				$this->lines[$i]->total_ttc			= $obj->total_ttc;
3535
				$this->lines[$i]->total_localtax1	= $obj->total_localtax1;
3536
				$this->lines[$i]->total_localtax2	= $obj->total_localtax2;
3537
				$this->lines[$i]->fk_fournprice		= $obj->fk_fournprice;
3538
				$marginInfos						= getMarginInfos($obj->subprice, $obj->remise_percent, $obj->tva_tx, $obj->localtax1_tx, $obj->localtax2_tx, $this->lines[$i]->fk_fournprice, $obj->pa_ht);
3539
				$this->lines[$i]->pa_ht				= $marginInfos[0];
3540
				$this->lines[$i]->marge_tx			= $marginInfos[1];
3541
				$this->lines[$i]->marque_tx			= $marginInfos[2];
3542
				$this->lines[$i]->fk_parent_line	= $obj->fk_parent_line;
3543
				$this->lines[$i]->special_code		= $obj->special_code;
3544
				$this->lines[$i]->rang				= $obj->rang;
3545
				$this->lines[$i]->date_start		= $this->db->jdate($obj->date_start);
3546
				$this->lines[$i]->date_end			= $this->db->jdate($obj->date_end);
3547
				$this->lines[$i]->fk_unit			= $obj->fk_unit;
3548
3549
				// Multicurrency
3550
				$this->lines[$i]->fk_multicurrency 			= $obj->fk_multicurrency;
3551
				$this->lines[$i]->multicurrency_code 		= $obj->multicurrency_code;
3552
				$this->lines[$i]->multicurrency_subprice 	= $obj->multicurrency_subprice;
3553
				$this->lines[$i]->multicurrency_total_ht 	= $obj->multicurrency_total_ht;
3554
				$this->lines[$i]->multicurrency_total_tva 	= $obj->multicurrency_total_tva;
3555
				$this->lines[$i]->multicurrency_total_ttc 	= $obj->multicurrency_total_ttc;
3556
3557
				$i++;
3558
			}
3559
			$this->db->free($resql);
3560
3561
			return 1;
3562
		}
3563
		else
3564
		{
3565
			$this->error=$this->db->error();
3566
			return -1;
3567
		}
3568
	}
3569
3570
	/**
3571
	 *  Create a document onto disk according to template module.
3572
	 *
3573
	 * 	@param	    string		$modele			Force model to use ('' to not force)
3574
	 * 	@param		Translate	$outputlangs	Object langs to use for output
3575
	 *  @param      int			$hidedetails    Hide details of lines
3576
	 *  @param      int			$hidedesc       Hide description
3577
	 *  @param      int			$hideref        Hide ref
3578
	 * 	@return     int         				0 if KO, 1 if OK
3579
	 */
3580
	public function generateDocument($modele, $outputlangs, $hidedetails=0, $hidedesc=0, $hideref=0)
3581
	{
3582
		global $conf,$langs;
3583
3584
		$langs->load("propale");
3585
3586
		if (! dol_strlen($modele)) {
3587
3588
			$modele = 'azur';
3589
3590
			if ($this->modelpdf) {
3591
				$modele = $this->modelpdf;
3592
			} elseif (! empty($conf->global->PROPALE_ADDON_PDF)) {
3593
				$modele = $conf->global->PROPALE_ADDON_PDF;
3594
			}
3595
		}
3596
3597
		$modelpath = "core/modules/propale/doc/";
3598
3599
		return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref);
3600
	}
3601
3602
	/**
3603
	 * Function used to replace a thirdparty id with another one.
3604
	 *
3605
	 * @param DoliDB $db Database handler
3606
	 * @param int $origin_id Old thirdparty id
3607
	 * @param int $dest_id New thirdparty id
3608
	 * @return bool
3609
	 */
3610
	public static function replaceThirdparty(DoliDB $db, $origin_id, $dest_id)
3611
	{
3612
		$tables = array(
3613
			'propal'
3614
		);
3615
3616
		return CommonObject::commonReplaceThirdparty($db, $origin_id, $dest_id, $tables);
3617
	}
3618
}
3619
3620
3621
/**
3622
 *	Class to manage commercial proposal lines
3623
 */
3624
class PropaleLigne extends CommonObjectLine
3625
{
3626
	public $element='propaldet';
3627
	public $table_element='propaldet';
3628
3629
	var $oldline;
3630
3631
	// From llx_propaldet
3632
	var $fk_propal;
3633
	var $fk_parent_line;
3634
	var $desc;          	// Description ligne
3635
	var $fk_product;		// Id produit predefini
3636
	/**
3637
	 * @deprecated
3638
	 * @see product_type
3639
	 */
3640
	var $fk_product_type;
3641
	/**
3642
	 * Product type.
3643
	 * @var int
3644
	 * @see Product::TYPE_PRODUCT, Product::TYPE_SERVICE
3645
	 */
3646
	var $product_type = Product::TYPE_PRODUCT;
3647
3648
	var $qty;
3649
	var $tva_tx;
3650
	var $subprice;
3651
	var $remise_percent;
3652
	var $fk_remise_except;
3653
3654
	var $rang = 0;
3655
3656
	var $fk_fournprice;
3657
	var $pa_ht;
3658
	var $marge_tx;
3659
	var $marque_tx;
3660
3661
	var $special_code;	// Tag for special lines (exlusive tags)
3662
	// 1: frais de port
3663
	// 2: ecotaxe
3664
	// 3: option line (when qty = 0)
3665
3666
	var $info_bits = 0;	// Liste d'options cumulables:
3667
	// Bit 0: 	0 si TVA normal - 1 si TVA NPR
3668
	// Bit 1:	0 ligne normale - 1 si ligne de remise fixe
3669
3670
	var $total_ht;			// Total HT  de la ligne toute quantite et incluant la remise ligne
3671
	var $total_tva;			// Total TVA  de la ligne toute quantite et incluant la remise ligne
3672
	var $total_ttc;			// Total TTC de la ligne toute quantite et incluant la remise ligne
3673
3674
	/**
3675
	 * @deprecated
3676
	 * @see $remise_percent, $fk_remise_except
3677
	 */
3678
	var $remise;
3679
	/**
3680
	 * @deprecated
3681
	 * @see subprice
3682
	 */
3683
	var $price;
3684
3685
	// From llx_product
3686
	/**
3687
	 * @deprecated
3688
	 * @see product_ref
3689
	 */
3690
	var $ref;
3691
	/**
3692
	 * Product reference
3693
	 * @var string
3694
	 */
3695
	public $product_ref;
3696
	/**
3697
	 * @deprecated
3698
	 * @see product_label
3699
	 */
3700
	var $libelle;
3701
	/**
3702
	 *  Product label
3703
	 * @var string
3704
	 */
3705
	public $product_label;
3706
	/**
3707
	 * Product description
3708
	 * @var string
3709
	 */
3710
	public $product_desc;
3711
3712
	var $localtax1_tx;		// Local tax 1
3713
	var $localtax2_tx;		// Local tax 2
3714
	var $localtax1_type;	// Local tax 1 type
3715
	var $localtax2_type;	// Local tax 2 type
3716
	var $total_localtax1;  	// Line total local tax 1
3717
	var $total_localtax2;	// Line total local tax 2
3718
3719
	var $date_start;
3720
	var $date_end;
3721
3722
	var $skip_update_total; // Skip update price total for special lines
3723
3724
	// Multicurrency
3725
	var $fk_multicurrency;
3726
	var $multicurrency_code;
3727
	var $multicurrency_subprice;
3728
	var $multicurrency_total_ht;
3729
	var $multicurrency_total_tva;
3730
	var $multicurrency_total_ttc;
3731
3732
	/**
3733
	 * 	Class line Contructor
3734
	 *
3735
	 * 	@param	DoliDB	$db	Database handler
3736
	 */
3737
	function __construct($db)
3738
	{
3739
		$this->db= $db;
3740
	}
3741
3742
	/**
3743
	 *	Retrieve the propal line object
3744
	 *
3745
	 *	@param	int		$rowid		Propal line id
3746
	 *	@return	int					<0 if KO, >0 if OK
3747
	 */
3748
	function fetch($rowid)
3749
	{
3750
		$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,';
3751
		$sql.= ' pd.remise, pd.remise_percent, pd.fk_remise_except, pd.subprice,';
3752
		$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,';
3753
		$sql.= ' pd.fk_unit,';
3754
		$sql.= ' pd.localtax1_tx, pd.localtax2_tx, pd.total_localtax1, pd.total_localtax2,';
3755
		$sql.= ' pd.fk_multicurrency, pd.multicurrency_code, pd.multicurrency_subprice, pd.multicurrency_total_ht, pd.multicurrency_total_tva, pd.multicurrency_total_ttc,';
3756
		$sql.= ' p.ref as product_ref, p.label as product_label, p.description as product_desc,';
3757
		$sql.= ' pd.date_start, pd.date_end, pd.product_type';
3758
		$sql.= ' FROM '.MAIN_DB_PREFIX.'propaldet as pd';
3759
		$sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON pd.fk_product = p.rowid';
3760
		$sql.= ' WHERE pd.rowid = '.$rowid;
3761
3762
		$result = $this->db->query($sql);
3763
		if ($result)
3764
		{
3765
			$objp = $this->db->fetch_object($result);
3766
3767
			if ($objp)
3768
			{
3769
				$this->id               = $objp->rowid;
3770
				$this->rowid			= $objp->rowid;     // deprecated
3771
				$this->fk_propal		= $objp->fk_propal;
3772
				$this->fk_parent_line	= $objp->fk_parent_line;
3773
				$this->label			= $objp->custom_label;
3774
				$this->desc				= $objp->description;
3775
				$this->qty				= $objp->qty;
3776
				$this->price			= $objp->price;		// deprecated
3777
				$this->subprice			= $objp->subprice;
3778
				$this->vat_src_code		= $objp->vat_src_code;
3779
				$this->tva_tx			= $objp->tva_tx;
3780
				$this->remise			= $objp->remise;    // deprecated
3781
				$this->remise_percent	= $objp->remise_percent;
3782
				$this->fk_remise_except = $objp->fk_remise_except;
3783
				$this->fk_product		= $objp->fk_product;
3784
				$this->info_bits		= $objp->info_bits;
3785
3786
				$this->total_ht			= $objp->total_ht;
3787
				$this->total_tva		= $objp->total_tva;
3788
				$this->total_ttc		= $objp->total_ttc;
3789
3790
				$this->fk_fournprice	= $objp->fk_fournprice;
3791
3792
				$marginInfos			= getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $this->fk_fournprice, $objp->pa_ht);
3793
				$this->pa_ht			= $marginInfos[0];
3794
				$this->marge_tx			= $marginInfos[1];
3795
				$this->marque_tx		= $marginInfos[2];
3796
3797
				$this->special_code		= $objp->special_code;
3798
				$this->product_type		= $objp->product_type;
3799
				$this->rang				= $objp->rang;
3800
3801
				$this->ref				= $objp->product_ref;      // deprecated
3802
				$this->product_ref		= $objp->product_ref;
3803
				$this->libelle			= $objp->product_label;  // deprecated
3804
				$this->product_label	= $objp->product_label;
3805
				$this->product_desc		= $objp->product_desc;
3806
				$this->fk_unit          = $objp->fk_unit;
3807
3808
				$this->date_start       = $this->db->jdate($objp->date_start);
3809
				$this->date_end         = $this->db->jdate($objp->date_end);
3810
3811
				// Multicurrency
3812
				$this->fk_multicurrency 		= $objp->fk_multicurrency;
3813
				$this->multicurrency_code 		= $objp->multicurrency_code;
3814
				$this->multicurrency_subprice 	= $objp->multicurrency_subprice;
3815
				$this->multicurrency_total_ht 	= $objp->multicurrency_total_ht;
3816
				$this->multicurrency_total_tva 	= $objp->multicurrency_total_tva;
3817
				$this->multicurrency_total_ttc 	= $objp->multicurrency_total_ttc;
3818
3819
				$this->fetch_optionals();
3820
3821
				$this->db->free($result);
3822
3823
				return 1;
3824
			}
3825
			else
3826
			{
3827
				return 0;
3828
			}
3829
		}
3830
		else
3831
		{
3832
			return -1;
3833
		}
3834
	}
3835
3836
	/**
3837
	 *  Insert object line propal in database
3838
	 *
3839
	 *	@param		int		$notrigger		1=Does not execute triggers, 0= execute triggers
3840
	 *	@return		int						<0 if KO, >0 if OK
3841
	 */
3842
	function insert($notrigger=0)
3843
	{
3844
		global $conf,$user;
3845
3846
		$error=0;
3847
3848
		dol_syslog(get_class($this)."::insert rang=".$this->rang);
3849
3850
		$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'.
3851
3852
		// Clean parameters
3853
		if (empty($this->tva_tx)) $this->tva_tx=0;
3854
		if (empty($this->localtax1_tx)) $this->localtax1_tx=0;
3855
		if (empty($this->localtax2_tx)) $this->localtax2_tx=0;
3856
		if (empty($this->localtax1_type)) $this->localtax1_type=0;
3857
		if (empty($this->localtax2_type)) $this->localtax2_type=0;
3858
		if (empty($this->total_localtax1)) $this->total_localtax1=0;
3859
		if (empty($this->total_localtax2)) $this->total_localtax2=0;
3860
		if (empty($this->rang)) $this->rang=0;
3861
		if (empty($this->remise)) $this->remise=0;
3862
		if (empty($this->remise_percent) || ! is_numeric($this->remise_percent)) $this->remise_percent=0;
3863
		if (empty($this->info_bits)) $this->info_bits=0;
3864
		if (empty($this->special_code)) $this->special_code=0;
3865
		if (empty($this->fk_parent_line)) $this->fk_parent_line=0;
3866
		if (empty($this->fk_fournprice)) $this->fk_fournprice=0;
3867
		if (! is_numeric($this->qty)) $this->qty = 0;
3868
		if (empty($this->pa_ht)) $this->pa_ht=0;
3869
		if (empty($this->multicurrency_subprice))  $this->multicurrency_subprice=0;
3870
		if (empty($this->multicurrency_total_ht))  $this->multicurrency_total_ht=0;
3871
		if (empty($this->multicurrency_total_tva)) $this->multicurrency_total_tva=0;
3872
		if (empty($this->multicurrency_total_ttc)) $this->multicurrency_total_ttc=0;
3873
3874
	   // if buy price not defined, define buyprice as configured in margin admin
3875
		if ($this->pa_ht == 0 && $pa_ht_isemptystring)
3876
		{
3877
			if (($result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product)) < 0)
3878
			{
3879
				return $result;
3880
			}
3881
			else
3882
			{
3883
				$this->pa_ht = $result;
3884
			}
3885
		}
3886
3887
		// Check parameters
3888
		if ($this->product_type < 0) return -1;
3889
3890
		$this->db->begin();
3891
3892
		// Insert line into database
3893
		$sql = 'INSERT INTO '.MAIN_DB_PREFIX.'propaldet';
3894
		$sql.= ' (fk_propal, fk_parent_line, label, description, fk_product, product_type,';
3895
		$sql.= ' fk_remise_except, qty, vat_src_code, tva_tx, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type,';
3896
		$sql.= ' subprice, remise_percent, ';
3897
		$sql.= ' info_bits, ';
3898
		$sql.= ' total_ht, total_tva, total_localtax1, total_localtax2, total_ttc, fk_product_fournisseur_price, buy_price_ht, special_code, rang,';
3899
		$sql.= ' fk_unit,';
3900
		$sql.= ' date_start, date_end';
3901
		$sql.= ', fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc)';
3902
		$sql.= " VALUES (".$this->fk_propal.",";
3903
		$sql.= " ".($this->fk_parent_line>0?"'".$this->db->escape($this->fk_parent_line)."'":"null").",";
3904
		$sql.= " ".(! empty($this->label)?"'".$this->db->escape($this->label)."'":"null").",";
3905
		$sql.= " '".$this->db->escape($this->desc)."',";
3906
		$sql.= " ".($this->fk_product?"'".$this->db->escape($this->fk_product)."'":"null").",";
3907
		$sql.= " '".$this->db->escape($this->product_type)."',";
3908
		$sql.= " ".($this->fk_remise_except?"'".$this->db->escape($this->fk_remise_except)."'":"null").",";
3909
		$sql.= " ".price2num($this->qty).",";
3910
		$sql.= " ".(empty($this->vat_src_code)?"''":"'".$this->db->escape($this->vat_src_code)."'").",";
3911
		$sql.= " ".price2num($this->tva_tx).",";
3912
		$sql.= " ".price2num($this->localtax1_tx).",";
3913
		$sql.= " ".price2num($this->localtax2_tx).",";
3914
		$sql.= " '".$this->db->escape($this->localtax1_type)."',";
3915
		$sql.= " '".$this->db->escape($this->localtax2_type)."',";
3916
		$sql.= " ".($this->subprice?price2num($this->subprice):"null").",";
3917
		$sql.= " ".price2num($this->remise_percent).",";
3918
		$sql.= " ".(isset($this->info_bits)?"'".$this->db->escape($this->info_bits)."'":"null").",";
3919
		$sql.= " ".price2num($this->total_ht).",";
3920
		$sql.= " ".price2num($this->total_tva).",";
3921
		$sql.= " ".price2num($this->total_localtax1).",";
3922
		$sql.= " ".price2num($this->total_localtax2).",";
3923
		$sql.= " ".price2num($this->total_ttc).",";
3924
		$sql.= " ".(!empty($this->fk_fournprice)?"'".$this->db->escape($this->fk_fournprice)."'":"null").",";
3925
		$sql.= " ".(isset($this->pa_ht)?"'".price2num($this->pa_ht)."'":"null").",";
3926
		$sql.= ' '.$this->special_code.',';
3927
		$sql.= ' '.$this->rang.',';
3928
		$sql.= ' '.(!$this->fk_unit ? 'NULL' : $this->fk_unit).',';
3929
		$sql.= " ".(! empty($this->date_start)?"'".$this->db->idate($this->date_start)."'":"null").',';
3930
		$sql.= " ".(! empty($this->date_end)?"'".$this->db->idate($this->date_end)."'":"null");
3931
		$sql.= ", ".($this->fk_multicurrency > 0?$this->fk_multicurrency:'null');
3932
		$sql.= ", '".$this->db->escape($this->multicurrency_code)."'";
3933
		$sql.= ", ".$this->multicurrency_subprice;
3934
		$sql.= ", ".$this->multicurrency_total_ht;
3935
		$sql.= ", ".$this->multicurrency_total_tva;
3936
		$sql.= ", ".$this->multicurrency_total_ttc;
3937
		$sql.= ')';
3938
3939
		dol_syslog(get_class($this).'::insert', LOG_DEBUG);
3940
		$resql=$this->db->query($sql);
3941
		if ($resql)
3942
		{
3943
			$this->rowid=$this->db->last_insert_id(MAIN_DB_PREFIX.'propaldet');
3944
3945
			if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) // For avoid conflicts if trigger used
3946
			{
3947
				$this->id=$this->rowid;
3948
				$result=$this->insertExtraFields();
3949
				if ($result < 0)
3950
				{
3951
					$error++;
3952
				}
3953
			}
3954
3955
			if (! $error && ! $notrigger)
3956
			{
3957
				// Call trigger
3958
				$result=$this->call_trigger('LINEPROPAL_INSERT',$user);
3959
				if ($result < 0)
3960
				{
3961
					$this->db->rollback();
3962
					return -1;
3963
				}
3964
				// End call triggers
3965
			}
3966
3967
			$this->db->commit();
3968
			return 1;
3969
		}
3970
		else
3971
		{
3972
			$this->error=$this->db->error()." sql=".$sql;
3973
			$this->db->rollback();
3974
			return -1;
3975
		}
3976
	}
3977
3978
	/**
3979
	 * 	Delete line in database
3980
	 *
3981
	 *  @param	User	$user		Object user
3982
	 *	@param 	int		$notrigger	1=Does not execute triggers, 0= execute triggers
3983
	 *	@return	 int  				<0 if ko, >0 if ok
3984
	 */
3985
	function delete(User $user, $notrigger=0)
3986
	{
3987
		global $conf;
3988
3989
		$error=0;
3990
		$this->db->begin();
3991
3992
		$sql = "DELETE FROM ".MAIN_DB_PREFIX."propaldet WHERE rowid = ".$this->rowid;
3993
		dol_syslog("PropaleLigne::delete", LOG_DEBUG);
3994
		if ($this->db->query($sql) )
3995
		{
3996
3997
			// Remove extrafields
3998
			if ((! $error) && (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED))) // For avoid conflicts if trigger used
3999
			{
4000
				$this->id=$this->rowid;
4001
				$result=$this->deleteExtraFields();
4002
				if ($result < 0)
4003
				{
4004
					$error++;
4005
					dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
4006
				}
4007
			}
4008
4009
			if (! $error && ! $notrigger)
4010
			{
4011
				// Call trigger
4012
				$result=$this->call_trigger('LINEPROPAL_DELETE',$user);
4013
				if ($result < 0)
4014
				{
4015
					$this->db->rollback();
4016
					return -1;
4017
				}
4018
			}
4019
			// End call triggers
4020
4021
			$this->db->commit();
4022
4023
			return 1;
4024
		}
4025
		else
4026
		{
4027
			$this->error=$this->db->error()." sql=".$sql;
4028
			$this->db->rollback();
4029
			return -1;
4030
		}
4031
	}
4032
4033
	/**
4034
	 *	Update propal line object into DB
4035
	 *
4036
	 *	@param 	int		$notrigger	1=Does not execute triggers, 0= execute triggers
4037
	 *	@return	int					<0 if ko, >0 if ok
4038
	 */
4039
	function update($notrigger=0)
4040
	{
4041
		global $conf,$user;
4042
4043
		$error=0;
4044
4045
		$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'.
4046
4047
		// Clean parameters
4048
		if (empty($this->tva_tx)) $this->tva_tx=0;
4049
		if (empty($this->localtax1_tx)) $this->localtax1_tx=0;
4050
		if (empty($this->localtax2_tx)) $this->localtax2_tx=0;
4051
		if (empty($this->total_localtax1)) $this->total_localtax1=0;
4052
		if (empty($this->total_localtax2)) $this->total_localtax2=0;
4053
		if (empty($this->localtax1_type)) $this->localtax1_type=0;
4054
		if (empty($this->localtax2_type)) $this->localtax2_type=0;
4055
		if (empty($this->marque_tx)) $this->marque_tx=0;
4056
		if (empty($this->marge_tx)) $this->marge_tx=0;
4057
		if (empty($this->price)) $this->price=0;	// TODO A virer
4058
		if (empty($this->remise)) $this->remise=0;	// TODO A virer
4059
		if (empty($this->remise_percent)) $this->remise_percent=0;
4060
		if (empty($this->info_bits)) $this->info_bits=0;
4061
		if (empty($this->special_code)) $this->special_code=0;
4062
		if (empty($this->fk_parent_line)) $this->fk_parent_line=0;
4063
		if (empty($this->fk_fournprice)) $this->fk_fournprice=0;
4064
		if (empty($this->subprice)) $this->subprice=0;
4065
		if (empty($this->pa_ht)) $this->pa_ht=0;
4066
4067
		// if buy price not defined, define buyprice as configured in margin admin
4068
		if ($this->pa_ht == 0 && $pa_ht_isemptystring)
4069
		{
4070
			if (($result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product)) < 0)
4071
			{
4072
				return $result;
4073
			}
4074
			else
4075
			{
4076
				$this->pa_ht = $result;
4077
			}
4078
		}
4079
4080
		$this->db->begin();
4081
4082
		// Mise a jour ligne en base
4083
		$sql = "UPDATE ".MAIN_DB_PREFIX."propaldet SET";
4084
		$sql.= " description='".$this->db->escape($this->desc)."'";
4085
		$sql.= ", label=".(! empty($this->label)?"'".$this->db->escape($this->label)."'":"null");
4086
		$sql.= ", product_type=".$this->product_type;
4087
		$sql.= ", vat_src_code = '".(empty($this->vat_src_code)?'':$this->vat_src_code)."'";
4088
		$sql.= ", tva_tx='".price2num($this->tva_tx)."'";
4089
		$sql.= ", localtax1_tx=".price2num($this->localtax1_tx);
4090
		$sql.= ", localtax2_tx=".price2num($this->localtax2_tx);
4091
		$sql.= ", localtax1_type='".$this->db->escape($this->localtax1_type)."'";
4092
		$sql.= ", localtax2_type='".$this->db->escape($this->localtax2_type)."'";
4093
		$sql.= ", qty='".price2num($this->qty)."'";
4094
		$sql.= ", subprice=".price2num($this->subprice)."";
4095
		$sql.= ", remise_percent=".price2num($this->remise_percent)."";
4096
		$sql.= ", price=".price2num($this->price)."";					// TODO A virer
4097
		$sql.= ", remise=".price2num($this->remise)."";				// TODO A virer
4098
		$sql.= ", info_bits='".$this->db->escape($this->info_bits)."'";
4099
		if (empty($this->skip_update_total))
4100
		{
4101
			$sql.= ", total_ht=".price2num($this->total_ht)."";
4102
			$sql.= ", total_tva=".price2num($this->total_tva)."";
4103
			$sql.= ", total_ttc=".price2num($this->total_ttc)."";
4104
			$sql.= ", total_localtax1=".price2num($this->total_localtax1)."";
4105
			$sql.= ", total_localtax2=".price2num($this->total_localtax2)."";
4106
		}
4107
		$sql.= ", fk_product_fournisseur_price=".(! empty($this->fk_fournprice)?"'".$this->db->escape($this->fk_fournprice)."'":"null");
4108
		$sql.= ", buy_price_ht=".price2num($this->pa_ht);
4109
		if (strlen($this->special_code)) $sql.= ", special_code=".$this->special_code;
4110
		$sql.= ", fk_parent_line=".($this->fk_parent_line>0?$this->fk_parent_line:"null");
4111
		if (! empty($this->rang)) $sql.= ", rang=".$this->rang;
4112
		$sql.= ", date_start=".(! empty($this->date_start)?"'".$this->db->idate($this->date_start)."'":"null");
4113
		$sql.= ", date_end=".(! empty($this->date_end)?"'".$this->db->idate($this->date_end)."'":"null");
4114
		$sql.= ", fk_unit=".(!$this->fk_unit ? 'NULL' : $this->fk_unit);
4115
4116
		// Multicurrency
4117
		$sql.= ", multicurrency_subprice=".price2num($this->multicurrency_subprice)."";
4118
		$sql.= ", multicurrency_total_ht=".price2num($this->multicurrency_total_ht)."";
4119
		$sql.= ", multicurrency_total_tva=".price2num($this->multicurrency_total_tva)."";
4120
		$sql.= ", multicurrency_total_ttc=".price2num($this->multicurrency_total_ttc)."";
4121
4122
		$sql.= " WHERE rowid = ".$this->rowid;
4123
4124
		dol_syslog(get_class($this)."::update", LOG_DEBUG);
4125
		$resql=$this->db->query($sql);
4126
		if ($resql)
4127
		{
4128
			if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) // For avoid conflicts if trigger used
4129
			{
4130
				$this->id=$this->rowid;
4131
				$result=$this->insertExtraFields();
4132
				if ($result < 0)
4133
				{
4134
					$error++;
4135
				}
4136
			}
4137
4138
			if (! $error && ! $notrigger)
4139
			{
4140
				// Call trigger
4141
				$result=$this->call_trigger('LINEPROPAL_UPDATE',$user);
4142
				if ($result < 0)
4143
				{
4144
					$this->db->rollback();
4145
					return -1;
4146
				}
4147
				// End call triggers
4148
			}
4149
4150
			$this->db->commit();
4151
			return 1;
4152
		}
4153
		else
4154
		{
4155
			$this->error=$this->db->error();
4156
			$this->db->rollback();
4157
			return -2;
4158
		}
4159
	}
4160
4161
	/**
4162
	 *	Update DB line fields total_xxx
4163
	 *	Used by migration
4164
	 *
4165
	 *	@return		int		<0 if ko, >0 if ok
4166
	 */
4167
	function update_total()
4168
	{
4169
		$this->db->begin();
4170
4171
		// Mise a jour ligne en base
4172
		$sql = "UPDATE ".MAIN_DB_PREFIX."propaldet SET";
4173
		$sql.= " total_ht=".price2num($this->total_ht,'MT')."";
4174
		$sql.= ",total_tva=".price2num($this->total_tva,'MT')."";
4175
		$sql.= ",total_ttc=".price2num($this->total_ttc,'MT')."";
4176
		$sql.= " WHERE rowid = ".$this->rowid;
4177
4178
		dol_syslog("PropaleLigne::update_total", LOG_DEBUG);
4179
4180
		$resql=$this->db->query($sql);
4181
		if ($resql)
4182
		{
4183
			$this->db->commit();
4184
			return 1;
4185
		}
4186
		else
4187
		{
4188
			$this->error=$this->db->error();
4189
			$this->db->rollback();
4190
			return -2;
4191
		}
4192
	}
4193
4194
}
4195
4196