Passed
Pull Request — master (#3)
by
unknown
25:36
created

SupplierProposal   F

Complexity

Total Complexity 319

Size/Duplication

Total Lines 2630
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 1336
dl 0
loc 2630
rs 0.8
c 0
b 0
f 0
wmc 319

How to fix   Complexity   

Complex Class

Complex classes like SupplierProposal often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use SupplierProposal, and based on these observations, apply Extract Interface, too.

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