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

SupplierProposal::addline()   F

Complexity

Conditions 37
Paths > 20000

Size

Total Lines 232
Code Lines 146

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 37
eloc 146
nc 820736
nop 24
dl 0
loc 232
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity    Many Parameters   

Long Method

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

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

Commonly applied refactorings include:

Many Parameters

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

There are several approaches to avoid long parameter lists:

1
<?php
2
/* Copyright (C) 2002-2004 Rodolphe Quiedeville		<[email protected]>
3
 * Copyright (C) 2004      Eric Seigne				<[email protected]>
4
 * Copyright (C) 2004-2011 Laurent Destailleur		<[email protected]>
5
 * Copyright (C) 2005      Marc Barilley			<[email protected]>
6
 * Copyright (C) 2005-2013 Regis Houssin			<[email protected]>
7
 * Copyright (C) 2006      Andre Cianfarani			<[email protected]>
8
 * Copyright (C) 2008      Raphael Bertrand			<[email protected]>
9
 * Copyright (C) 2010-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