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

CommandeFournisseur::calcAndSetStatusDispatch()   F

Complexity

Conditions 24
Paths 149

Size

Total Lines 132
Code Lines 63

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 24
eloc 63
c 0
b 0
f 0
nc 149
nop 3
dl 0
loc 132
rs 3.7583

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
/* Copyright (C) 2003-2006	Rodolphe Quiedeville	<[email protected]>
3
 * Copyright (C) 2004-2017	Laurent Destailleur		<[email protected]>
4
 * Copyright (C) 2005-2012	Regis Houssin			<[email protected]>
5
 * Copyright (C) 2007		Franky Van Liedekerke	<[email protected]>
6
 * Copyright (C) 2010-2014	Juanjo Menent			<[email protected]>
7
 * Copyright (C) 2010-2018	Philippe Grand			<[email protected]>
8
 * Copyright (C) 2012-2015  Marcos García           <[email protected]>
9
 * Copyright (C) 2013       Florian Henry		  	<[email protected]>
10
 * Copyright (C) 2013       Cédric Salvador         <[email protected]>
11
 * Copyright (C) 2018       Nicolas ZABOURI			<[email protected]>
12
 * Copyright (C) 2018-2019  Frédéric France         <[email protected]>
13
 * Copyright (C) 2018       Ferran Marcet         	<[email protected]>
14
 *
15
 * This program is free software; you can redistribute it and/or modify
16
 * it under the terms of the GNU General Public License as published by
17
 * the Free Software Foundation; either version 3 of the License, or
18
 * (at your option) any later version.
19
 *
20
 * This program is distributed in the hope that it will be useful,
21
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23
 * GNU General Public License for more details.
24
 *
25
 * You should have received a copy of the GNU General Public License
26
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
27
 */
28
29
/**
30
 *	\file       htdocs/fourn/class/fournisseur.commande.class.php
31
 *	\ingroup    fournisseur,commande
32
 *	\brief      File of class to manage suppliers orders
33
 */
34
35
include_once DOL_DOCUMENT_ROOT.'/core/class/commonorder.class.php';
36
require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
37
if (! empty($conf->productbatch->enabled)) require_once DOL_DOCUMENT_ROOT.'/product/class/productbatch.class.php';
38
require_once DOL_DOCUMENT_ROOT.'/multicurrency/class/multicurrency.class.php';
39
40
/**
41
 *	Class to manage predefined suppliers products
42
 */
43
class CommandeFournisseur extends CommonOrder
44
{
45
    /**
46
	 * @var string ID to identify managed object
47
	 */
48
	public $element='order_supplier';
49
50
    /**
51
	 * @var string Name of table without prefix where object is stored
52
	 */
53
	public $table_element='commande_fournisseur';
54
55
    /**
56
	 * @var int    Name of subtable line
57
	 */
58
	public $table_element_line = 'commande_fournisseurdet';
59
60
    /**
61
	 * @var int Field with ID of parent key if this field has a parent
62
	 */
63
	public $fk_element = 'fk_commande';
64
65
    public $picto='order';
66
67
    /**
68
     * 0=No test on entity, 1=Test with field entity, 2=Test with link by societe
69
     * @var int
70
     */
71
    public $ismultientitymanaged = 1;
72
73
    /**
74
     * 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
75
     * @var integer
76
     */
77
    public $restrictiononfksoc = 1;
78
79
    /**
80
     * {@inheritdoc}
81
     */
82
    protected $table_ref_field = 'ref';
83
84
    /**
85
	 * @var int ID
86
	 */
87
	public $id;
88
89
	/**
90
	 * Supplier order reference
91
	 * @var string
92
	 */
93
    public $ref;
94
95
    public $ref_supplier;
96
    public $brouillon;
97
    public $statut;			// 0=Draft -> 1=Validated -> 2=Approved -> 3=Ordered/Process runing -> 4=Received partially -> 5=Received totally -> (reopen) 4=Received partially
98
    //                                                                                          -> 7=Canceled/Never received -> (reopen) 3=Process runing
99
    //									                            -> 6=Canceled -> (reopen) 2=Approved
100
    //  		                                      -> 9=Refused  -> (reopen) 1=Validated
101
    //  Note: billed or not is on another field "billed"
102
    public $statuts;           // List of status
103
104
    public $socid;
105
    public $fourn_id;
106
    public $date;
107
    public $date_valid;
108
    public $date_approve;
109
    public $date_approve2;		// Used when SUPPLIER_ORDER_3_STEPS_TO_BE_APPROVED is set
110
    public $date_commande;
111
112
    /**
113
     * Delivery date
114
     */
115
    public $date_livraison;
116
117
    public $total_ht;
118
    public $total_tva;
119
    public $total_localtax1;   // Total Local tax 1
120
    public $total_localtax2;   // Total Local tax 2
121
    public $total_ttc;
122
    public $source;
123
124
	/**
125
	 * @deprecated
126
	 * @see $note_private, $note_public
127
	 */
128
    public $note;
129
130
	public $note_private;
131
    public $note_public;
132
    public $model_pdf;
133
134
    /**
135
     * @var int ID
136
     */
137
    public $fk_project;
138
139
    public $cond_reglement_id;
140
    public $cond_reglement_code;
141
142
    /**
143
     * @var int ID
144
     */
145
    public $fk_account;
146
147
    public $mode_reglement_id;
148
    public $mode_reglement_code;
149
    public $user_author_id;
150
    public $user_valid_id;
151
    public $user_approve_id;
152
    public $user_approve_id2;	// Used when SUPPLIER_ORDER_3_STEPS_TO_BE_APPROVED is set
153
154
	//Incoterms
155
    public $fk_incoterms;
156
    public $location_incoterms;
157
    public $label_incoterms;  //Used into tooltip
158
159
    public $extraparams=array();
160
161
	/**
162
	 * @var CommandeFournisseurLigne[]
163
	 */
164
	public $lines = array();
165
166
	//Add for supplier_proposal
167
    public $origin;
168
    public $origin_id;
169
    public $linked_objects=array();
170
171
	// Multicurrency
172
	/**
173
     * @var int ID
174
     */
175
    public $fk_multicurrency;
176
177
    public $multicurrency_code;
178
    public $multicurrency_tx;
179
    public $multicurrency_total_ht;
180
    public $multicurrency_total_tva;
181
    public $multicurrency_total_ttc;
182
183
	/**
184
	 * Draft status
185
	 */
186
	const STATUS_DRAFT = 0;
187
188
	/**
189
	 * Validated status
190
	 */
191
	const STATUS_VALIDATED = 1;
192
193
	/**
194
	 * Accepted
195
	 */
196
	const STATUS_ACCEPTED = 2;
197
198
	/**
199
	 * Order sent, shipment on process
200
	 */
201
	const STATUS_ORDERSENT = 3;
202
203
	/**
204
	 * Received partially
205
	 */
206
	const STATUS_RECEIVED_PARTIALLY = 4;
207
208
	/**
209
	 * Received completely
210
	 */
211
	const STATUS_RECEIVED_COMPLETELY = 5;
212
213
	/**
214
	 * Order canceled
215
	 */
216
	const STATUS_CANCELED = 6;
217
218
	/**
219
	 * Order canceled/never received
220
	 */
221
	const STATUS_CANCELED_AFTER_ORDER = 7;
222
223
	/**
224
	 * Refused
225
	 */
226
	const STATUS_REFUSED = 9;
227
228
229
230
231
	/**
232
     * 	Constructor
233
     *
234
     *  @param      DoliDB		$db      Database handler
235
     */
236
    public function __construct($db)
237
    {
238
        $this->db = $db;
239
240
        $this->products = array();
241
    }
242
243
244
    /**
245
     *	Get object and lines from database
246
     *
247
     * 	@param	int		$id			Id of order to load
248
     * 	@param	string	$ref		Ref of object
249
     *	@return int 		        >0 if OK, <0 if KO, 0 if not found
250
     */
251
    public function fetch($id, $ref = '')
252
    {
253
        global $conf;
254
255
        // Check parameters
256
        if (empty($id) && empty($ref)) return -1;
257
258
        $sql = "SELECT c.rowid, c.entity, c.ref, ref_supplier, c.fk_soc, c.fk_statut, c.amount_ht, c.total_ht, c.total_ttc, c.tva as total_vat,";
259
        $sql.= " c.localtax1, c.localtax2, ";
260
        $sql.= " c.date_creation, c.date_valid, c.date_approve, c.date_approve2,";
261
        $sql.= " c.fk_user_author, c.fk_user_valid, c.fk_user_approve, c.fk_user_approve2,";
262
        $sql.= " c.date_commande as date_commande, c.date_livraison as date_livraison, c.fk_cond_reglement, c.fk_mode_reglement, c.fk_projet as fk_project, c.remise_percent, c.source, c.fk_input_method,";
263
        $sql.= " c.fk_account,";
264
        $sql.= " c.note_private, c.note_public, c.model_pdf, c.extraparams, c.billed,";
265
        $sql.= " c.fk_multicurrency, c.multicurrency_code, c.multicurrency_tx, c.multicurrency_total_ht, c.multicurrency_total_tva, c.multicurrency_total_ttc,";
266
        $sql.= " cm.libelle as methode_commande,";
267
        $sql.= " cr.code as cond_reglement_code, cr.libelle as cond_reglement_libelle, cr.libelle_facture as cond_reglement_libelle_doc,";
268
        $sql.= " p.code as mode_reglement_code, p.libelle as mode_reglement_libelle";
269
        $sql.= ', c.fk_incoterms, c.location_incoterms';
270
        $sql.= ', i.libelle as label_incoterms';
271
        $sql.= " FROM ".MAIN_DB_PREFIX."commande_fournisseur as c";
272
        $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."c_payment_term as cr ON c.fk_cond_reglement = cr.rowid";
273
        $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as p ON c.fk_mode_reglement = p.id";
274
        $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."c_input_method as cm ON cm.rowid = c.fk_input_method";
275
        $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_incoterms as i ON c.fk_incoterms = i.rowid';
276
        $sql.= " WHERE c.entity = ".$conf->entity;
277
        if ($ref) $sql.= " AND c.ref='".$this->db->escape($ref)."'";
278
        else $sql.= " AND c.rowid=".$id;
279
280
        dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
281
        $resql = $this->db->query($sql);
282
        if ($resql)
283
        {
284
            $obj = $this->db->fetch_object($resql);
285
            if (! $obj)
286
            {
287
                $this->error='Bill with id '.$id.' not found';
288
                dol_syslog(get_class($this).'::fetch '.$this->error);
289
                return 0;
290
            }
291
292
            $this->id					= $obj->rowid;
293
            $this->entity				= $obj->entity;
294
295
            $this->ref					= $obj->ref;
296
            $this->ref_supplier			= $obj->ref_supplier;
297
            $this->socid				= $obj->fk_soc;
298
            $this->fourn_id				= $obj->fk_soc;
299
            $this->statut				= $obj->fk_statut;
300
            $this->billed				= $obj->billed;
301
            $this->user_author_id		= $obj->fk_user_author;
302
            $this->user_valid_id		= $obj->fk_user_valid;
303
            $this->user_approve_id		= $obj->fk_user_approve;
304
            $this->user_approve_id2		= $obj->fk_user_approve2;
305
            $this->total_ht				= $obj->total_ht;
306
            $this->total_tva			= $obj->total_vat;
307
            $this->total_localtax1		= $obj->localtax1;
308
            $this->total_localtax2		= $obj->localtax2;
309
            $this->total_ttc			= $obj->total_ttc;
310
            $this->date					= $this->db->jdate($obj->date_creation);
311
            $this->date_valid			= $this->db->jdate($obj->date_valid);
312
            $this->date_approve			= $this->db->jdate($obj->date_approve);
313
            $this->date_approve2		= $this->db->jdate($obj->date_approve2);
314
            $this->date_commande		= $this->db->jdate($obj->date_commande); // date we make the order to supplier
315
			$this->date_livraison       = $this->db->jdate($obj->date_livraison);
316
            $this->remise_percent		= $obj->remise_percent;
317
            $this->methode_commande_id	= $obj->fk_input_method;
318
            $this->methode_commande		= $obj->methode_commande;
319
320
            $this->source				= $obj->source;
321
            $this->fk_project			= $obj->fk_project;
322
            $this->cond_reglement_id	= $obj->fk_cond_reglement;
323
            $this->cond_reglement_code	= $obj->cond_reglement_code;
324
            $this->cond_reglement		= $obj->cond_reglement_libelle;
325
            $this->cond_reglement_doc	= $obj->cond_reglement_libelle_doc;
326
            $this->fk_account           = $obj->fk_account;
327
            $this->mode_reglement_id	= $obj->fk_mode_reglement;
328
            $this->mode_reglement_code	= $obj->mode_reglement_code;
329
            $this->mode_reglement		= $obj->mode_reglement_libelle;
330
            $this->note					= $obj->note_private;    // deprecated
331
            $this->note_private			= $obj->note_private;
332
            $this->note_public			= $obj->note_public;
333
            $this->modelpdf				= $obj->model_pdf;
334
335
			//Incoterms
336
			$this->fk_incoterms = $obj->fk_incoterms;
337
			$this->location_incoterms = $obj->location_incoterms;
338
			$this->label_incoterms = $obj->label_incoterms;
339
340
			// Multicurrency
341
			$this->fk_multicurrency 		= $obj->fk_multicurrency;
342
			$this->multicurrency_code 		= $obj->multicurrency_code;
343
			$this->multicurrency_tx 		= $obj->multicurrency_tx;
344
			$this->multicurrency_total_ht 	= $obj->multicurrency_total_ht;
345
			$this->multicurrency_total_tva 	= $obj->multicurrency_total_tva;
346
			$this->multicurrency_total_ttc 	= $obj->multicurrency_total_ttc;
347
348
            $this->extraparams			= (array) json_decode($obj->extraparams, true);
349
350
            $this->db->free($resql);
351
352
            // Retreive all extrafield
353
            // fetch optionals attributes and labels
354
            $this->fetch_optionals();
355
356
            if ($this->statut == 0) $this->brouillon = 1;
357
358
            /*
359
             * Lines
360
             */
361
            $result=$this->fetch_lines();
362
            if ($result < 0)
363
            {
364
            	return -1;
365
            }
366
            else
367
            {
368
            	return 1;
369
            }
370
        }
371
        else
372
        {
373
            $this->error=$this->db->error()." sql=".$sql;
374
            return -1;
375
        }
376
    }
377
378
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
379
    /**
380
     * Load array lines
381
     *
382
     * @param		int		$only_product	Return only physical products
383
     * @return		int						<0 if KO, >0 if OK
384
     */
385
    public function fetch_lines($only_product = 0)
386
    {
387
        // phpcs:enable
388
    	//$result=$this->fetch_lines();
389
    	$this->lines=array();
390
391
    	$sql = "SELECT l.rowid, l.ref as ref_supplier, l.fk_product, l.product_type, l.label, l.description, l.qty,";
392
    	$sql.= " l.vat_src_code, l.tva_tx, l.remise_percent, l.subprice,";
393
    	$sql.= " l.localtax1_tx, l. localtax2_tx, l.localtax1_type, l. localtax2_type, l.total_localtax1, l.total_localtax2,";
394
    	$sql.= " l.total_ht, l.total_tva, l.total_ttc, l.special_code, l.fk_parent_line, l.rang,";
395
    	$sql.= " p.rowid as product_id, p.ref as product_ref, p.label as product_label, p.description as product_desc,";
396
    	$sql.= " l.fk_unit,";
397
    	$sql.= " l.date_start, l.date_end,";
398
    	$sql.= ' l.fk_multicurrency, l.multicurrency_code, l.multicurrency_subprice, l.multicurrency_total_ht, l.multicurrency_total_tva, l.multicurrency_total_ttc';
399
    	$sql.= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet	as l";
400
    	$sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON l.fk_product = p.rowid';
401
    	$sql.= " WHERE l.fk_commande = ".$this->id;
402
    	if ($only_product) $sql .= ' AND p.fk_product_type = 0';
403
    	$sql.= " ORDER BY l.rang, l.rowid";
404
    	//print $sql;
405
406
    	dol_syslog(get_class($this)."::fetch get lines", LOG_DEBUG);
407
    	$result = $this->db->query($sql);
408
    	if ($result)
409
    	{
410
    		$num = $this->db->num_rows($result);
411
    		$i = 0;
412
413
    		while ($i < $num)
414
    		{
415
    			$objp                  = $this->db->fetch_object($result);
416
417
    			$line                 = new CommandeFournisseurLigne($this->db);
418
419
    			$line->id                  = $objp->rowid;
420
    			$line->desc                = $objp->description;
421
    			$line->description         = $objp->description;
422
    			$line->qty                 = $objp->qty;
423
    			$line->tva_tx              = $objp->tva_tx;
424
    			$line->localtax1_tx		   = $objp->localtax1_tx;
425
    			$line->localtax2_tx		   = $objp->localtax2_tx;
426
    			$line->localtax1_type	   = $objp->localtax1_type;
427
    			$line->localtax2_type	   = $objp->localtax2_type;
428
    			$line->subprice            = $objp->subprice;
429
    			$line->pu_ht	           = $objp->subprice;
430
    			$line->remise_percent      = $objp->remise_percent;
431
432
    			$line->vat_src_code        = $objp->vat_src_code;
433
    			$line->total_ht            = $objp->total_ht;
434
    			$line->total_tva           = $objp->total_tva;
435
    			$line->total_localtax1	   = $objp->total_localtax1;
436
    			$line->total_localtax2	   = $objp->total_localtax2;
437
    			$line->total_ttc           = $objp->total_ttc;
438
    			$line->product_type        = $objp->product_type;
439
440
    			$line->fk_product          = $objp->fk_product;
441
442
    			$line->libelle             = $objp->product_label;
443
    			$line->product_label       = $objp->product_label;
444
    			$line->product_desc        = $objp->product_desc;
445
446
    			$line->ref                 = $objp->product_ref;    // Ref of product
447
    			$line->product_ref         = $objp->product_ref;    // Ref of product
448
    			$line->ref_fourn           = $objp->ref_supplier;   // The supplier ref of price when product was added. May have change since
449
    			$line->ref_supplier        = $objp->ref_supplier;   // The supplier ref of price when product was added. May have change since
450
451
    			$line->date_start          = $this->db->jdate($objp->date_start);
452
    			$line->date_end            = $this->db->jdate($objp->date_end);
453
    			$line->fk_unit             = $objp->fk_unit;
454
455
    			// Multicurrency
456
    			$line->fk_multicurrency 		= $objp->fk_multicurrency;
457
    			$line->multicurrency_code 		= $objp->multicurrency_code;
458
    			$line->multicurrency_subprice 	= $objp->multicurrency_subprice;
459
    			$line->multicurrency_total_ht 	= $objp->multicurrency_total_ht;
460
    			$line->multicurrency_total_tva 	= $objp->multicurrency_total_tva;
461
    			$line->multicurrency_total_ttc 	= $objp->multicurrency_total_ttc;
462
463
    			$line->special_code        = $objp->special_code;
464
    			$line->fk_parent_line      = $objp->fk_parent_line;
465
466
    			$line->rang                = $objp->rang;
467
468
    			// Retreive all extrafield
469
    			// fetch optionals attributes and labels
470
    			$line->fetch_optionals();
471
472
    			$this->lines[$i]      = $line;
473
474
    			$i++;
475
    		}
476
    		$this->db->free($result);
477
478
    		return $num;
479
    	}
480
    	else
481
    	{
482
    		$this->error=$this->db->error()." sql=".$sql;
483
    		return -1;
484
    	}
485
    }
486
487
    /**
488
     *	Validate an order
489
     *
490
     *	@param	User	$user			Validator User
491
     *	@param	int		$idwarehouse	Id of warehouse to use for stock decrease
492
     *  @param	int		$notrigger		1=Does not execute triggers, 0= execute triggers
493
     *	@return	int						<0 if KO, >0 if OK
494
     */
495
    public function valid($user, $idwarehouse = 0, $notrigger = 0)
496
    {
497
        global $langs,$conf;
498
        require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
499
500
        $error=0;
501
502
        dol_syslog(get_class($this)."::valid");
503
        $result = 0;
504
        if ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->fournisseur->commande->creer))
505
       	|| (! empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->fournisseur->supplier_order_advance->validate)))
506
        {
507
            $this->db->begin();
508
509
            // Definition of supplier order numbering model name
510
            $soc = new Societe($this->db);
511
            $soc->fetch($this->fourn_id);
512
513
            // Check if object has a temporary ref
514
            if (preg_match('/^[\(]?PROV/i', $this->ref) || empty($this->ref)) // empty should not happened, but when it occurs, the test save life
515
            {
516
                $num = $this->getNextNumRef($soc);
517
            }
518
            else
519
			{
520
                $num = $this->ref;
521
            }
522
            $this->newref = $num;
523
524
            $sql = 'UPDATE '.MAIN_DB_PREFIX."commande_fournisseur";
525
            $sql.= " SET ref='".$this->db->escape($num)."',";
526
            $sql.= " fk_statut = ".self::STATUS_VALIDATED.",";
527
            $sql.= " date_valid='".$this->db->idate(dol_now())."',";
528
            $sql.= " fk_user_valid = ".$user->id;
529
            $sql.= " WHERE rowid = ".$this->id;
530
            $sql.= " AND fk_statut = ".self::STATUS_DRAFT;
531
532
            $resql=$this->db->query($sql);
533
            if (! $resql)
534
            {
535
                dol_print_error($this->db);
536
                $error++;
537
            }
538
539
            if (! $error && ! $notrigger)
540
            {
541
				// Call trigger
542
				$result=$this->call_trigger('ORDER_SUPPLIER_VALIDATE', $user);
543
				if ($result < 0) $error++;
544
				// End call triggers
545
            }
546
547
            if (! $error)
548
            {
549
	            $this->oldref = $this->ref;
550
551
                // Rename directory if dir was a temporary ref
552
                if (preg_match('/^[\(]?PROV/i', $this->ref))
553
                {
554
                	// Now we rename also files into index
555
                	$sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filename = CONCAT('".$this->db->escape($this->newref)."', SUBSTR(filename, ".(strlen($this->ref)+1).")), filepath = 'fournisseur/commande/".$this->db->escape($this->newref)."'";
556
                	$sql.= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'fournisseur/commande/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
557
                	$resql = $this->db->query($sql);
558
                	if (! $resql) { $error++; $this->error = $this->db->lasterror(); }
559
560
                	// We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
561
                    $oldref = dol_sanitizeFileName($this->ref);
562
                    $newref = dol_sanitizeFileName($num);
563
                    $dirsource = $conf->fournisseur->commande->dir_output.'/'.$oldref;
564
                    $dirdest = $conf->fournisseur->commande->dir_output.'/'.$newref;
565
                    if (! $error && file_exists($dirsource))
566
                    {
567
                        dol_syslog(get_class($this)."::valid rename dir ".$dirsource." into ".$dirdest);
568
569
                        if (@rename($dirsource, $dirdest))
570
                        {
571
                            dol_syslog("Rename ok");
572
                            // Rename docs starting with $oldref with $newref
573
	                        $listoffiles=dol_dir_list($conf->fournisseur->commande->dir_output.'/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
574
	                        foreach($listoffiles as $fileentry)
575
	                        {
576
	                        	$dirsource=$fileentry['name'];
577
	                        	$dirdest=preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
578
	                        	$dirsource=$fileentry['path'].'/'.$dirsource;
579
	                        	$dirdest=$fileentry['path'].'/'.$dirdest;
580
	                        	@rename($dirsource, $dirdest);
1 ignored issue
show
Security Best Practice introduced by
It seems like you do not handle an error condition for rename(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

580
	                        	/** @scrutinizer ignore-unhandled */ @rename($dirsource, $dirdest);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
581
	                        }
582
                        }
583
                    }
584
                }
585
            }
586
587
            if (! $error)
588
            {
589
                $result = 1;
590
                $this->statut = self::STATUS_VALIDATED;
591
                $this->ref = $num;
592
            }
593
594
            if (! $error)
595
            {
596
                $this->db->commit();
597
                return 1;
598
            }
599
            else
600
            {
601
                $this->db->rollback();
602
                return -1;
603
            }
604
        }
605
        else
606
        {
607
            $this->error='NotAuthorized';
608
            dol_syslog(get_class($this)."::valid ".$this->error, LOG_ERR);
609
            return -1;
610
        }
611
    }
612
613
    /**
614
     *  Return label of the status of object
615
     *
616
	 *  @param      int		$mode			0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=short label + picto
617
     *  @return 	string        			Label
618
     */
619
    public function getLibStatut($mode = 0)
620
    {
621
        return $this->LibStatut($this->statut, $mode, $this->billed);
622
    }
623
624
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
625
    /**
626
     *  Return label of a status
627
     *
628
     * 	@param  int		$statut		Id statut
629
     *  @param  int		$mode       0=Long label, 1=Short label, 2=Picto + Short label, 3=Picto, 4=Picto + Long label, 5=Short label + Picto
630
     *  @param  int     $billed     1=Billed
631
     *  @return string				Label of status
632
     */
633
    public function LibStatut($statut, $mode = 0, $billed = 0)
634
    {
635
        // phpcs:enable
636
    	global $conf, $langs;
637
638
    	if (empty($this->statuts) || empty($this->statutshort))
639
    	{
640
	        $langs->load('orders');
641
642
	        $this->statuts[0] = 'StatusOrderDraft';
643
	        $this->statuts[1] = 'StatusOrderValidated';
644
	        $this->statuts[2] = 'StatusOrderApproved';
645
	        if (empty($conf->global->SUPPLIER_ORDER_USE_DISPATCH_STATUS)) $this->statuts[3] = 'StatusOrderOnProcess';
646
	        else $this->statuts[3] = 'StatusOrderOnProcessWithValidation';
647
	        $this->statuts[4] = 'StatusOrderReceivedPartially';
648
	        $this->statuts[5] = 'StatusOrderReceivedAll';
649
	        $this->statuts[6] = 'StatusOrderCanceled';	// Approved->Canceled
650
	        $this->statuts[7] = 'StatusOrderCanceled';	// Process running->canceled
651
	        //$this->statuts[8] = 'StatusOrderBilled';	// Everything is finished, order received totally and bill received
652
	        $this->statuts[9] = 'StatusOrderRefused';
653
654
	        // List of language codes for status
655
	        $this->statutshort[0] = 'StatusOrderDraftShort';
656
	        $this->statutshort[1] = 'StatusOrderValidatedShort';
657
	        $this->statutshort[2] = 'StatusOrderApprovedShort';
658
	        $this->statutshort[3] = 'StatusOrderOnProcessShort';
659
	        $this->statutshort[4] = 'StatusOrderReceivedPartiallyShort';
660
	        $this->statutshort[5] = 'StatusOrderReceivedAllShort';
661
	        $this->statutshort[6] = 'StatusOrderCanceledShort';
662
	        $this->statutshort[7] = 'StatusOrderCanceledShort';
663
	        $this->statutshort[9] = 'StatusOrderRefusedShort';
664
    	}
665
666
        $billedtext='';
667
		//if ($statut==5 && $this->billed == 1) $statut = 8;
668
        if ($billed == 1) $billedtext=$langs->trans("Billed");
669
670
        if ($mode == 0)
671
        {
672
            return $langs->trans($this->statuts[$statut]);
673
        }
674
        elseif ($mode == 1)
675
        {
676
        	return $langs->trans($this->statutshort[$statut]);
677
        }
678
        elseif ($mode == 2)
679
        {
680
            return $langs->trans($this->statuts[$statut]);
681
        }
682
        elseif ($mode == 3)
683
        {
684
            if ($statut==0) return img_picto($langs->trans($this->statuts[$statut]), 'statut0');
685
            elseif ($statut==1) return img_picto($langs->trans($this->statuts[$statut]), 'statut1');
686
            elseif ($statut==2) return img_picto($langs->trans($this->statuts[$statut]), 'statut3');
687
            elseif ($statut==3) return img_picto($langs->trans($this->statuts[$statut]), 'statut3');
688
            elseif ($statut==4) return img_picto($langs->trans($this->statuts[$statut]), 'statut3');
689
            elseif ($statut==5) return img_picto($langs->trans($this->statuts[$statut]), 'statut6');
690
            elseif ($statut==6 || $statut==7) return img_picto($langs->trans($this->statuts[$statut]), 'statut5');
691
            elseif ($statut==9) return img_picto($langs->trans($this->statuts[$statut]), 'statut5');
692
        }
693
        elseif ($mode == 4)
694
        {
695
            if ($statut==0) return img_picto($langs->trans($this->statuts[$statut]), 'statut0').' '.$langs->trans($this->statuts[$statut]).($billedtext?' - '.$billedtext:'');
696
            elseif ($statut==1) return img_picto($langs->trans($this->statuts[$statut]), 'statut1').' '.$langs->trans($this->statuts[$statut]).($billedtext?' - '.$billedtext:'');
697
            elseif ($statut==2) return img_picto($langs->trans($this->statuts[$statut]), 'statut3').' '.$langs->trans($this->statuts[$statut]).($billedtext?' - '.$billedtext:'');
698
            elseif ($statut==3) return img_picto($langs->trans($this->statuts[$statut]), 'statut3').' '.$langs->trans($this->statuts[$statut]).($billedtext?' - '.$billedtext:'');
699
            elseif ($statut==4) return img_picto($langs->trans($this->statuts[$statut]), 'statut3').' '.$langs->trans($this->statuts[$statut]).($billedtext?' - '.$billedtext:'');
700
            elseif ($statut==5) return img_picto($langs->trans($this->statuts[$statut]), 'statut6').' '.$langs->trans($this->statuts[$statut]).($billedtext?' - '.$billedtext:'');
701
            elseif ($statut==6 || $statut==7) return img_picto($langs->trans($this->statuts[$statut]), 'statut5').' '.$langs->trans($this->statuts[$statut]).($billedtext?' - '.$billedtext:'');
702
            elseif ($statut==9) return img_picto($langs->trans($this->statuts[$statut]), 'statut5').' '.$langs->trans($this->statuts[$statut]).($billedtext?' - '.$billedtext:'');
703
        }
704
        elseif ($mode == 5)
705
        {
706
        	if ($statut==0) return '<span class="hideonsmartphone">'.$langs->trans($this->statutshort[$statut]).' </span>'.img_picto($langs->trans($this->statuts[$statut]), 'statut0');
707
        	elseif ($statut==1) return '<span class="hideonsmartphone">'.$langs->trans($this->statutshort[$statut]).' </span>'.img_picto($langs->trans($this->statuts[$statut]), 'statut1');
708
        	elseif ($statut==2) return '<span class="hideonsmartphone">'.$langs->trans($this->statutshort[$statut]).' </span>'.img_picto($langs->trans($this->statuts[$statut]), 'statut3');
709
        	elseif ($statut==3) return '<span class="hideonsmartphone">'.$langs->trans($this->statutshort[$statut]).' </span>'.img_picto($langs->trans($this->statuts[$statut]), 'statut3');
710
        	elseif ($statut==4) return '<span class="hideonsmartphone">'.$langs->trans($this->statutshort[$statut]).' </span>'.img_picto($langs->trans($this->statuts[$statut]), 'statut3');
711
        	elseif ($statut==5) return '<span class="hideonsmartphone">'.$langs->trans($this->statutshort[$statut]).' </span>'.img_picto($langs->trans($this->statuts[$statut]), 'statut6');
712
        	elseif ($statut==6 || $statut==7) return '<span class="hideonsmartphone">'.$langs->trans($this->statutshort[$statut]).' </span>'.img_picto($langs->trans($this->statuts[$statut]), 'statut5');
713
        	elseif ($statut==9) return '<span class="hideonsmartphone">'.$langs->trans($this->statutshort[$statut]).' </span>'.img_picto($langs->trans($this->statuts[$statut]), 'statut5');
714
        }
715
    }
716
717
718
    /**
719
     *	Return clicable name (with picto eventually)
720
     *
721
     *	@param		int		$withpicto					0=No picto, 1=Include picto into link, 2=Only picto
722
     *	@param		string	$option						On what the link points
723
     *  @param	    int   	$notooltip					1=Disable tooltip
724
     *  @param      int     $save_lastsearch_value		-1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking
725
     *	@return		string								Chain with URL
726
     */
727
    public function getNomUrl($withpicto = 0, $option = '', $notooltip = 0, $save_lastsearch_value = -1)
728
    {
729
        global $langs, $conf;
730
731
        $result='';
732
        $label = '<u>' . $langs->trans("ShowOrder") . '</u>';
733
        if (! empty($this->ref))
734
            $label .= '<br><b>' . $langs->trans('Ref') . ':</b> ' . $this->ref;
735
        if (! empty($this->ref_supplier))
736
            $label.= '<br><b>' . $langs->trans('RefSupplier') . ':</b> ' . $this->ref_supplier;
737
        if (! empty($this->total_ht))
738
            $label.= '<br><b>' . $langs->trans('AmountHT') . ':</b> ' . price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
739
        if (! empty($this->total_tva))
740
            $label.= '<br><b>' . $langs->trans('VAT') . ':</b> ' . price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
741
        if (! empty($this->total_ttc))
742
            $label.= '<br><b>' . $langs->trans('AmountTTC') . ':</b> ' . price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
743
744
        $picto='order';
745
        $url = DOL_URL_ROOT.'/fourn/commande/card.php?id='.$this->id;
746
747
        if ($option !== 'nolink')
748
        {
749
        	// Add param to save lastsearch_values or not
750
        	$add_save_lastsearch_values=($save_lastsearch_value == 1 ? 1 : 0);
751
        	if ($save_lastsearch_value == -1 && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) $add_save_lastsearch_values=1;
752
        	if ($add_save_lastsearch_values) $url.='&save_lastsearch_values=1';
753
        }
754
755
        $linkclose='';
756
        if (empty($notooltip))
757
        {
758
            if (! empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER))
759
            {
760
                $label=$langs->trans("ShowOrder");
761
                $linkclose.=' alt="'.dol_escape_htmltag($label, 1).'"';
762
            }
763
            $linkclose.= ' title="'.dol_escape_htmltag($label, 1).'"';
764
            $linkclose.=' class="classfortooltip"';
765
        }
766
767
        $linkstart = '<a href="'.$url.'"';
768
        $linkstart.=$linkclose.'>';
769
        $linkend='</a>';
770
771
        $result .= $linkstart;
772
        if ($withpicto) $result.=img_object(($notooltip?'':$label), $this->picto, ($notooltip?(($withpicto != 2) ? 'class="paddingright"' : ''):'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip?0:1);
773
        if ($withpicto != 2) $result.= $this->ref;
774
        $result .= $linkend;
775
776
        return $result;
777
    }
778
779
780
    /**
781
     *  Returns the following order reference not used depending on the numbering model activated
782
     *                  defined within COMMANDE_SUPPLIER_ADDON_NUMBER
783
     *
784
     *  @param	    Societe		$soc  		company object
785
     *  @return     string                  free reference for the invoice
786
     */
787
    public function getNextNumRef($soc)
788
    {
789
        global $db, $langs, $conf;
790
        $langs->load("orders");
791
792
        if (! empty($conf->global->COMMANDE_SUPPLIER_ADDON_NUMBER))
793
        {
794
            $mybool = false;
795
796
            $file = $conf->global->COMMANDE_SUPPLIER_ADDON_NUMBER.'.php';
797
            $classname=$conf->global->COMMANDE_SUPPLIER_ADDON_NUMBER;
798
799
            // Include file with class
800
            $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
801
802
            foreach ($dirmodels as $reldir) {
803
804
                $dir = dol_buildpath($reldir."core/modules/supplier_order/");
805
806
                // Load file with numbering class (if found)
807
                $mybool|=@include_once $dir.$file;
808
            }
809
810
            if ($mybool === false) {
811
                dol_print_error('', "Failed to include file ".$file);
812
                return '';
813
            }
814
815
            $obj = new $classname();
816
            $numref = $obj->getNextValue($soc, $this);
817
818
            if ( $numref != "")
819
            {
820
                return $numref;
821
            }
822
            else
823
			{
824
                $this->error = $obj->error;
825
                return -1;
826
            }
827
        }
828
        else
829
		{
830
            $this->error = "Error_COMMANDE_SUPPLIER_ADDON_NotDefined";
831
            return -2;
832
        }
833
    }
834
	/**
835
     *	Class invoiced the supplier order
836
     *
837
     *  @param      User        $user       Object user making the change
838
     *	@return     int     	            <0 if KO, >0 if KO
839
     */
840
    public function classifyBilled(User $user)
841
    {
842
        $error=0;
843
        $this->db->begin();
844
845
        $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande_fournisseur SET billed = 1';
846
        $sql .= ' WHERE rowid = '.$this->id.' AND fk_statut > '.self::STATUS_DRAFT;
847
        if ($this->db->query($sql))
848
        {
849
        	if (! $error)
850
        	{
851
        	    // Call trigger
852
        	    $result=$this->call_trigger('ORDER_SUPPLIER_CLASSIFY_BILLED', $user);
853
        	    if ($result < 0) $error++;
854
        	    // End call triggers
855
        	}
856
857
        	if (! $error)
858
        	{
859
        	    $this->billed=1;
860
861
        	    $this->db->commit();
862
        	    return 1;
863
        	}
864
        	else
865
        	{
866
        	    $this->db->rollback();
867
                return -1;
868
        	}
869
        }
870
        else
871
        {
872
        	dol_print_error($this->db);
873
874
        	$this->db->rollback();
875
			return -1;
876
        }
877
    }
878
879
    /**
880
     * 	Approve a supplier order
881
     *
882
     *	@param	User	$user			Object user
883
     *	@param	int		$idwarehouse	Id of warhouse for stock change
884
     *  @param	int		$secondlevel	0=Standard approval, 1=Second level approval (used when option SUPPLIER_ORDER_3_STEPS_TO_BE_APPROVED is set)
885
     *	@return	int						<0 if KO, >0 if OK
886
     */
887
    public function approve($user, $idwarehouse = 0, $secondlevel = 0)
888
    {
889
        global $langs,$conf;
890
		require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
891
892
        $error=0;
893
894
        dol_syslog(get_class($this)."::approve");
895
896
        if ($user->rights->fournisseur->commande->approuver)
897
        {
898
        	$now = dol_now();
899
900
            $this->db->begin();
901
902
			// Definition of order numbering model name
903
            $soc = new Societe($this->db);
904
            $soc->fetch($this->fourn_id);
905
906
            // Check if object has a temporary ref
907
            if (preg_match('/^[\(]?PROV/i', $this->ref) || empty($this->ref)) // empty should not happened, but when it occurs, the test save life
908
            {
909
                $num = $this->getNextNumRef($soc);
910
            }
911
            else
912
			{
913
                $num = $this->ref;
914
            }
915
            $this->newref = $num;
916
917
            // Do we have to change status now ? (If double approval is required and first approval, we keep status to 1 = validated)
918
			$movetoapprovestatus=true;
919
			$comment='';
920
921
            $sql = "UPDATE ".MAIN_DB_PREFIX."commande_fournisseur";
922
			$sql.= " SET ref='".$this->db->escape($num)."',";
923
			if (empty($secondlevel))	// standard or first level approval
924
			{
925
	            $sql.= " date_approve='".$this->db->idate($now)."',";
926
    	        $sql.= " fk_user_approve = ".$user->id;
927
    	        if (! empty($conf->global->SUPPLIER_ORDER_3_STEPS_TO_BE_APPROVED) && $conf->global->MAIN_FEATURES_LEVEL > 0 && $this->total_ht >= $conf->global->SUPPLIER_ORDER_3_STEPS_TO_BE_APPROVED)
928
    	        {
929
    	        	if (empty($this->user_approve_id2))
930
    	        	{
931
    	        	    $movetoapprovestatus=false;		// second level approval not done
932
    	        	    $comment=' (first level)';
933
    	        	}
934
    	        }
935
			}
936
			else	// request a second level approval
937
			{
938
            	$sql.= " date_approve2='".$this->db->idate($now)."',";
939
            	$sql.= " fk_user_approve2 = ".$user->id;
940
    	        if (empty($this->user_approve_id)) $movetoapprovestatus=false;		// first level approval not done
941
    	        $comment=' (second level)';
942
			}
943
			// If double approval is required and first approval, we keep status to 1 = validated
944
			if ($movetoapprovestatus) $sql.= ", fk_statut = ".self::STATUS_ACCEPTED;
945
			else $sql.= ", fk_statut = ".self::STATUS_VALIDATED;
946
            $sql.= " WHERE rowid = ".$this->id;
947
            $sql.= " AND fk_statut = ".self::STATUS_VALIDATED;
948
949
            if ($this->db->query($sql))
950
            {
951
            	if (! empty($conf->global->SUPPLIER_ORDER_AUTOADD_USER_CONTACT))
952
	            {
953
					$result=$this->add_contact($user->id, 'SALESREPFOLL', 'internal', 1);
954
					if ($result < 0 && $result != -2)	// -2 means already exists
955
					{
956
						$error++;
957
					}
958
	            }
959
960
                // If stock is incremented on validate order, we must increment it
961
                if (! $error && $movetoapprovestatus && ! empty($conf->stock->enabled) && ! empty($conf->global->STOCK_CALCULATE_ON_SUPPLIER_VALIDATE_ORDER))
962
                {
963
                    require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
964
                    $langs->load("agenda");
965
966
                    $cpt=count($this->lines);
967
                    for ($i = 0; $i < $cpt; $i++)
968
                    {
969
                        // Product with reference
970
                        if ($this->lines[$i]->fk_product > 0)
971
                        {
972
                            $this->line = $this->lines[$i];
973
                            $mouvP = new MouvementStock($this->db);
974
                            $mouvP->origin = &$this;
975
                            // We decrement stock of product (and sub-products)
976
	                        $up_ht_disc=$this->lines[$i]->subprice;
977
    	                    if (! empty($this->lines[$i]->remise_percent) && empty($conf->global->STOCK_EXCLUDE_DISCOUNT_FOR_PMP)) $up_ht_disc=price2num($up_ht_disc * (100 - $this->lines[$i]->remise_percent) / 100, 'MU');
978
                            $result=$mouvP->reception($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $up_ht_disc, $langs->trans("OrderApprovedInDolibarr", $this->ref));
979
                            if ($result < 0) { $error++; }
980
                            unset($this->line);
981
                        }
982
                    }
983
                }
984
985
                if (! $error)
986
                {
987
					// Call trigger
988
					$result=$this->call_trigger('ORDER_SUPPLIER_APPROVE', $user);
989
					if ($result < 0) $error++;
990
					// End call triggers
991
                }
992
993
                if (! $error)
994
                {
995
                	$this->ref = $this->newref;
996
997
                	if ($movetoapprovestatus) $this->statut = self::STATUS_ACCEPTED;
998
					else $this->statut = self::STATUS_VALIDATED;
999
           			if (empty($secondlevel))	// standard or first level approval
1000
					{
1001
			            $this->date_approve = $now;
1002
		    	        $this->user_approve_id = $user->id;
1003
					}
1004
					else	// request a second level approval
1005
					{
1006
			            $this->date_approve2 = $now;
1007
		    	        $this->user_approve_id2 = $user->id;
1008
					}
1009
1010
                    $this->db->commit();
1011
                    return 1;
1012
                }
1013
                else
1014
                {
1015
                    $this->db->rollback();
1016
                    return -1;
1017
                }
1018
            }
1019
            else
1020
            {
1021
                $this->db->rollback();
1022
                $this->error=$this->db->lasterror();
1023
                return -1;
1024
            }
1025
        }
1026
        else
1027
        {
1028
            dol_syslog(get_class($this)."::approve Not Authorized", LOG_ERR);
1029
        }
1030
        return -1;
1031
    }
1032
1033
    /**
1034
     * 	Refuse an order
1035
     *
1036
     * 	@param		User	$user		User making action
1037
     *	@return		int					0 if Ok, <0 if Ko
1038
     */
1039
    public function refuse($user)
1040
    {
1041
        global $conf, $langs;
1042
1043
		$error=0;
1044
1045
        dol_syslog(get_class($this)."::refuse");
1046
        $result = 0;
1047
        if ($user->rights->fournisseur->commande->approuver)
1048
        {
1049
            $this->db->begin();
1050
1051
            $sql = "UPDATE ".MAIN_DB_PREFIX."commande_fournisseur SET fk_statut = ".self::STATUS_REFUSED;
1052
            $sql .= " WHERE rowid = ".$this->id;
1053
1054
            if ($this->db->query($sql))
1055
            {
1056
                $result = 0;
1057
1058
                if ($error == 0)
1 ignored issue
show
introduced by
The condition $error == 0 is always true.
Loading history...
1059
                {
1060
					// Call trigger
1061
					$result=$this->call_trigger('ORDER_SUPPLIER_REFUSE', $user);
1062
					if ($result < 0)
1063
                    {
1064
                        $error++;
1065
                        $this->db->rollback();
1066
                    }
1067
                    else
1068
                    	$this->db->commit();
1069
					// End call triggers
1070
                }
1071
            }
1072
            else
1073
            {
1074
                $this->db->rollback();
1075
                $this->error=$this->db->lasterror();
1076
                dol_syslog(get_class($this)."::refuse Error -1");
1077
                $result = -1;
1078
            }
1079
        }
1080
        else
1081
        {
1082
            dol_syslog(get_class($this)."::refuse Not Authorized");
1083
        }
1084
        return $result ;
1085
    }
1086
1087
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1088
    /**
1089
     * 	Cancel an approved order.
1090
     *	The cancellation is done after approval
1091
     *
1092
     * 	@param	User	$user			User making action
1093
     *	@param	int		$idwarehouse	Id warehouse to use for stock change (not used for supplier orders).
1094
     * 	@return	int						>0 if Ok, <0 if Ko
1095
     */
1096
    public function Cancel($user, $idwarehouse = -1)
1097
    {
1098
        // phpcs:enable
1099
        global $langs,$conf;
1100
1101
        $error=0;
1102
1103
        //dol_syslog("CommandeFournisseur::Cancel");
1104
        $result = 0;
1105
        if ($user->rights->fournisseur->commande->commander)
1106
        {
1107
            $statut = self::STATUS_CANCELED;
1108
1109
            $this->db->begin();
1110
1111
            $sql = "UPDATE ".MAIN_DB_PREFIX."commande_fournisseur SET fk_statut = ".$statut;
1112
            $sql .= " WHERE rowid = ".$this->id;
1113
            dol_syslog(get_class($this)."::cancel", LOG_DEBUG);
1114
            if ($this->db->query($sql))
1115
            {
1116
                $result = 0;
1117
1118
				// Call trigger
1119
				$result=$this->call_trigger('ORDER_SUPPLIER_CANCEL', $user);
1120
				if ($result < 0) $error++;
1121
				// End call triggers
1122
1123
                if ($error == 0)
1124
                {
1125
                    $this->db->commit();
1126
                    return 1;
1127
                }
1128
                else
1129
                {
1130
                    $this->db->rollback();
1131
                    return -1;
1132
                }
1133
            }
1134
            else
1135
            {
1136
                $this->db->rollback();
1137
                $this->error=$this->db->lasterror();
1138
                dol_syslog(get_class($this)."::cancel ".$this->error);
1139
                return -1;
1140
            }
1141
        }
1142
        else
1143
        {
1144
            dol_syslog(get_class($this)."::cancel Not Authorized");
1145
            return -1;
1146
        }
1147
    }
1148
1149
    /**
1150
     * 	Submit a supplier order to supplier
1151
     *
1152
     * 	@param		User	$user		User making change
1153
     * 	@param		integer	$date		Date
1154
     * 	@param		int		$methode	Method
1155
     * 	@param		string	$comment	Comment
1156
     * 	@return		int			        <0 if KO, >0 if OK
1157
     */
1158
    public function commande($user, $date, $methode, $comment = '')
1159
    {
1160
        global $langs;
1161
        dol_syslog(get_class($this)."::commande");
1162
        $error = 0;
1163
        if ($user->rights->fournisseur->commande->commander)
1164
        {
1165
            $this->db->begin();
1166
1167
            $sql = "UPDATE ".MAIN_DB_PREFIX."commande_fournisseur SET fk_statut = ".self::STATUS_ORDERSENT.", fk_input_method=".$methode.", date_commande='".$this->db->idate($date)."'";
1168
            $sql .= " WHERE rowid = ".$this->id;
1169
1170
            dol_syslog(get_class($this)."::commande", LOG_DEBUG);
1171
            if ($this->db->query($sql))
1172
            {
1173
                $this->statut = self::STATUS_ORDERSENT;
1174
                $this->methode_commande_id = $methode;
1175
                $this->date_commande = $date;
1176
1177
                // Call trigger
1178
                $result=$this->call_trigger('ORDER_SUPPLIER_SUBMIT', $user);
1179
                if ($result < 0) $error++;
1180
                // End call triggers
1181
            }
1182
            else
1183
            {
1184
                $error++;
1185
                $this->error = $this->db->lasterror();
1186
                $this->errors[] = $this->db->lasterror();
1187
            }
1188
1189
            if (! $error)
1190
            {
1191
                $this->db->commit();
1192
            }
1193
            else
1194
            {
1195
                $this->db->rollback();
1196
            }
1197
        }
1198
        else
1199
        {
1200
            $error++;
1201
            $this->error = $langs->trans('NotAuthorized');
1202
            $this->errors[] = $langs->trans('NotAuthorized');
1203
            dol_syslog(get_class($this)."::commande User not Authorized", LOG_WARNING);
1204
        }
1205
1206
        return ($error ? -1 : 1);
1207
    }
1208
1209
    /**
1210
     *  Create order with draft status
1211
     *
1212
     *  @param      User	$user       User making creation
1213
     *	@param		int		$notrigger	Disable all triggers
1214
     *  @return     int         		<0 if KO, Id of supplier order if OK
1215
     */
1216
    public function create($user, $notrigger = 0)
1217
    {
1218
        global $langs,$conf,$hookmanager;
1219
1220
        $this->db->begin();
1221
1222
		$error=0;
1223
        $now=dol_now();
1224
1225
        // Clean parameters
1226
        if (empty($this->source)) $this->source = 0;
1227
1228
		// Multicurrency (test on $this->multicurrency_tx because we should take the default rate only if not using origin rate)
1229
		if (!empty($this->multicurrency_code) && empty($this->multicurrency_tx)) list($this->fk_multicurrency,$this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code);
1230
		else $this->fk_multicurrency = MultiCurrency::getIdFromCode($this->db, $this->multicurrency_code);
1231
		if (empty($this->fk_multicurrency))
1232
		{
1233
			$this->multicurrency_code = $conf->currency;
1234
			$this->fk_multicurrency = 0;
1235
			$this->multicurrency_tx = 1;
1236
		}
1237
1238
        // We set order into draft status
1239
        $this->brouillon = 1;
1240
1241
        $sql = "INSERT INTO ".MAIN_DB_PREFIX."commande_fournisseur (";
1242
        $sql.= "ref";
1243
        $sql.= ", ref_supplier";
1244
        $sql.= ", note_private";
1245
        $sql.= ", note_public";
1246
        $sql.= ", entity";
1247
        $sql.= ", fk_soc";
1248
        $sql.= ", fk_projet";
1249
        $sql.= ", date_creation";
1250
		$sql.= ", date_livraison";
1251
        $sql.= ", fk_user_author";
1252
        $sql.= ", fk_statut";
1253
        $sql.= ", source";
1254
        $sql.= ", model_pdf";
1255
        $sql.= ", fk_mode_reglement";
1256
		$sql.= ", fk_cond_reglement";
1257
        $sql.= ", fk_account";
1258
		$sql.= ", fk_incoterms, location_incoterms";
1259
        $sql.= ", fk_multicurrency";
1260
        $sql.= ", multicurrency_code";
1261
        $sql.= ", multicurrency_tx";
1262
        $sql.= ") ";
1263
        $sql.= " VALUES (";
1264
        $sql.= "''";
1265
        $sql.= ", '".$this->db->escape($this->ref_supplier)."'";
1266
        $sql.= ", '".$this->db->escape($this->note_private)."'";
1267
        $sql.= ", '".$this->db->escape($this->note_public)."'";
1268
        $sql.= ", ".$conf->entity;
1269
        $sql.= ", ".$this->socid;
1270
        $sql.= ", ".($this->fk_project > 0 ? $this->fk_project : "null");
1271
        $sql.= ", '".$this->db->idate($now)."'";
1272
		$sql.= ", ".($this->date_livraison?"'".$this->db->idate($this->date_livraison)."'":"null");
1273
        $sql.= ", ".$user->id;
1274
        $sql.= ", ".self::STATUS_DRAFT;
1275
        $sql.= ", ".$this->db->escape($this->source);
1276
        $sql.= ", '".$conf->global->COMMANDE_SUPPLIER_ADDON_PDF."'";
1277
        $sql.= ", ".($this->mode_reglement_id > 0 ? $this->mode_reglement_id : 'null');
1278
        $sql.= ", ".($this->cond_reglement_id > 0 ? $this->cond_reglement_id : 'null');
1279
        $sql.= ", ".($this->fk_account>0?$this->fk_account:'NULL');
1280
        $sql.= ", ".(int) $this->fk_incoterms;
1281
        $sql.= ", '".$this->db->escape($this->location_incoterms)."'";
1282
		$sql.= ", ".(int) $this->fk_multicurrency;
1283
		$sql.= ", '".$this->db->escape($this->multicurrency_code)."'";
1284
		$sql.= ", ".(double) $this->multicurrency_tx;
1285
        $sql.= ")";
1286
1287
        dol_syslog(get_class($this)."::create", LOG_DEBUG);
1288
        if ($this->db->query($sql))
1289
        {
1290
            $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."commande_fournisseur");
1291
1292
			if ($this->id) {
1293
				$num=count($this->lines);
1294
1295
	            // insert products details into database
1296
	            for ($i=0;$i<$num;$i++)
1297
	            {
1298
1299
	                $this->special_code = $this->lines[$i]->special_code; // TODO : remove this in 9.0 and add special_code param to addline()
1300
1301
                $result = $this->addline(              // This include test on qty if option SUPPLIER_ORDER_WITH_NOPRICEDEFINED is not set
1302
	                    $this->lines[$i]->desc,
1303
	                    $this->lines[$i]->subprice,
1304
	                    $this->lines[$i]->qty,
1305
	                    $this->lines[$i]->tva_tx,
1306
	                    $this->lines[$i]->localtax1_tx,
1307
	                    $this->lines[$i]->localtax2_tx,
1308
	                    $this->lines[$i]->fk_product,
1309
	                    0,
1310
	                    $this->lines[$i]->ref_fourn,   // $this->lines[$i]->ref_fourn comes from field ref into table of lines. Value may ba a ref that does not exists anymore, so we first try with value of product
1311
	                    $this->lines[$i]->remise_percent,
1312
	                    'HT',
1313
	                    0,
1314
	                    $this->lines[$i]->product_type,
1315
	                    $this->lines[$i]->info_bits,
1316
                        false,
1317
	                    $this->lines[$i]->date_start,
1318
                        $this->lines[$i]->date_end,
1319
                        0,
1320
                        $this->lines[$i]->fk_unit
1321
	                );
1322
	                if ($result < 0)
1323
	                {
1324
	                    dol_syslog(get_class($this)."::create ".$this->error, LOG_WARNING);	// do not use dol_print_error here as it may be a functionnal error
1325
	                    $this->db->rollback();
1326
	                    return -1;
1327
	                }
1328
	            }
1329
1330
	            $sql = "UPDATE ".MAIN_DB_PREFIX."commande_fournisseur";
1331
	            $sql.= " SET ref='(PROV".$this->id.")'";
1332
	            $sql.= " WHERE rowid=".$this->id;
1333
	            dol_syslog(get_class($this)."::create", LOG_DEBUG);
1334
	            if ($this->db->query($sql))
1335
	            {
1336
					// Add link with price request and supplier order
1337
					if ($this->id)
1338
					{
1339
						$this->ref="(PROV".$this->id.")";
1340
1341
						if (! empty($this->linkedObjectsIds) && empty($this->linked_objects))	// To use new linkedObjectsIds instead of old linked_objects
1342
						{
1343
							$this->linked_objects = $this->linkedObjectsIds;	// TODO Replace linked_objects with linkedObjectsIds
1344
						}
1345
1346
						// Add object linked
1347
						if (! $error && $this->id && is_array($this->linked_objects) && ! empty($this->linked_objects))
1348
						{
1349
							foreach($this->linked_objects as $origin => $tmp_origin_id)
1350
							{
1351
							    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, ...))
1352
							    {
1353
							        foreach($tmp_origin_id as $origin_id)
1354
							        {
1355
							            $ret = $this->add_object_linked($origin, $origin_id);
1356
							            if (! $ret)
1357
							            {
1358
							                dol_print_error($this->db);
1359
							                $error++;
1360
							            }
1361
							        }
1362
							    }
1363
							    else                                // Old behaviour, if linked_object has only one link per type, so is something like array('contract'=>id1))
1364
							    {
1365
							        $origin_id = $tmp_origin_id;
1366
									$ret = $this->add_object_linked($origin, $origin_id);
1367
									if (! $ret)
1368
									{
1369
										dol_print_error($this->db);
1370
										$error++;
1371
									}
1372
							    }
1373
							}
1374
						}
1375
					}
1376
1377
	                if (! $error)
1378
                    {
1379
                    	$result=$this->insertExtraFields();
1380
	                    if ($result < 0) $error++;
1381
                    }
1382
1383
					if (! $error && ! $notrigger)
1384
	                {
1385
						// Call trigger
1386
						$result=$this->call_trigger('ORDER_SUPPLIER_CREATE', $user);
1387
						if ($result < 0)
1388
	                    {
1389
	                        $this->db->rollback();
1390
	                        return -1;
1391
	                    }
1392
						// End call triggers
1393
	                }
1394
1395
	                $this->db->commit();
1396
	                return $this->id;
1397
	            }
1398
	            else
1399
	            {
1400
	                $this->error=$this->db->lasterror();
1401
	                $this->db->rollback();
1402
	                return -2;
1403
	            }
1404
            }
1405
        }
1406
        else
1407
        {
1408
            $this->error=$this->db->lasterror();
1409
            $this->db->rollback();
1410
            return -1;
1411
        }
1412
    }
1413
1414
    /**
1415
     *	Load an object from its id and create a new one in database
1416
     *
1417
	 *  @param	    User	$user		User making the clone
1418
     *	@return		int					New id of clone
1419
     */
1420
    public function createFromClone(User $user)
1421
    {
1422
        global $hookmanager;
1423
1424
        $error=0;
1425
1426
		$this->db->begin();
1427
1428
		// Load source object
1429
		$objFrom = clone $this;
1430
1431
        $this->id=0;
1432
        $this->statut=self::STATUS_DRAFT;
1433
1434
        // Clear fields
1435
        $this->user_author_id     = $user->id;
1436
        $this->user_valid         = '';
1437
        $this->date_creation      = '';
1438
        $this->date_validation    = '';
1439
        $this->ref_supplier       = '';
1440
        $this->user_approve_id    = '';
1441
        $this->user_approve_id2   = '';
1442
        $this->date_approve       = '';
1443
        $this->date_approve2      = '';
1444
1445
        // Create clone
1446
        $this->context['createfromclone'] = 'createfromclone';
1447
        $result=$this->create($user);
1448
        if ($result < 0) $error++;
1449
1450
        if (! $error)
1451
        {
1452
            // Hook of thirdparty module
1453
            if (is_object($hookmanager))
1454
            {
1455
                $parameters=array('objFrom'=>$objFrom);
1456
                $action='';
1457
                $reshook=$hookmanager->executeHooks('createFrom', $parameters, $this, $action);    // Note that $action and $object may have been modified by some hooks
1458
                if ($reshook < 0) $error++;
1459
            }
1460
        }
1461
1462
		unset($this->context['createfromclone']);
1463
1464
		// End
1465
        if (! $error)
1466
        {
1467
            $this->db->commit();
1468
            return $this->id;
1469
        }
1470
        else
1471
        {
1472
            $this->db->rollback();
1473
            return -1;
1474
        }
1475
    }
1476
1477
    /**
1478
     *	Add order line
1479
     *
1480
     *	@param      string	$desc            		Description
1481
     *	@param      float	$pu_ht              	Unit price
1482
     *	@param      float	$qty             		Quantity
1483
     *	@param      float	$txtva           		Taux tva
1484
     *	@param      float	$txlocaltax1        	Localtax1 tax
1485
     *  @param      float	$txlocaltax2        	Localtax2 tax
1486
     *	@param      int		$fk_product      		Id product
1487
     *  @param      int		$fk_prod_fourn_price	Id supplier price
1488
     *  @param      string	$ref_supplier			Supplier reference price
1489
     *	@param      float	$remise_percent  		Remise
1490
     *	@param      string	$price_base_type		HT or TTC
1491
     *	@param		float	$pu_ttc					Unit price TTC
1492
     *	@param		int		$type					Type of line (0=product, 1=service)
1493
     *	@param		int		$info_bits				More information
1494
     *  @param		bool	$notrigger				Disable triggers
1495
     *  @param		int		$date_start				Date start of service
1496
     *  @param		int		$date_end				Date end of service
1497
	 *  @param		array	$array_options			extrafields array
1498
     *  @param 		string	$fk_unit 				Code of the unit to use. Null to use the default one
1499
	 *  @param 		string	$pu_ht_devise			Amount in currency
1500
	 *  @param		string	$origin					'order', ...
1501
	 *  @param		int		$origin_id				Id of origin object
1502
     *	@return     int             				<=0 if KO, >0 if OK
1503
     */
1504
	public function addline($desc, $pu_ht, $qty, $txtva, $txlocaltax1 = 0.0, $txlocaltax2 = 0.0, $fk_product = 0, $fk_prod_fourn_price = 0, $ref_supplier = '', $remise_percent = 0.0, $price_base_type = 'HT', $pu_ttc = 0.0, $type = 0, $info_bits = 0, $notrigger = false, $date_start = null, $date_end = null, $array_options = 0, $fk_unit = null, $pu_ht_devise = 0, $origin = '', $origin_id = 0)
1505
    {
1506
        global $langs,$mysoc,$conf;
1507
1508
        $error = 0;
1509
1510
        dol_syslog(get_class($this)."::addline $desc, $pu_ht, $qty, $txtva, $txlocaltax1, $txlocaltax2, $fk_product, $fk_prod_fourn_price, $ref_supplier, $remise_percent, $price_base_type, $pu_ttc, $type, $info_bits, $notrigger, $date_start, $date_end, $fk_unit, $pu_ht_devise, $origin, $origin_id");
1511
        include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
1512
1513
		if ($this->statut == self::STATUS_DRAFT)
1514
		{
1515
			include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
1516
1517
			// Clean parameters
1518
			if (! $qty) $qty=1;
1519
			if (! $info_bits) $info_bits=0;
1520
			if (empty($txtva)) $txtva=0;
1521
			if (empty($txlocaltax1)) $txlocaltax1=0;
1522
			if (empty($txlocaltax2)) $txlocaltax2=0;
1523
			if (empty($remise_percent)) $remise_percent=0;
1524
1525
			$remise_percent=price2num($remise_percent);
1526
			$qty=price2num($qty);
1527
			$pu_ht=price2num($pu_ht);
1528
			$pu_ht_devise=price2num($pu_ht_devise);
1529
			$pu_ttc=price2num($pu_ttc);
1530
			if (!preg_match('/\((.*)\)/', $txtva)) {
1531
				$txtva = price2num($txtva);               // $txtva can have format '5.0(XXX)' or '5'
1532
			}
1533
			$txlocaltax1 = price2num($txlocaltax1);
1534
			$txlocaltax2 = price2num($txlocaltax2);
1535
			if ($price_base_type=='HT')
1536
			{
1537
				$pu=$pu_ht;
1538
			}
1539
			else
1540
			{
1541
				$pu=$pu_ttc;
1542
			}
1543
			$desc=trim($desc);
1544
1545
			// Check parameters
1546
			if ($qty < 0 && ! $fk_product)
1547
			{
1548
				$this->error=$langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Product"));
1549
				return -1;
1550
			}
1551
			if ($type < 0) return -1;
1552
			if ($date_start && $date_end && $date_start > $date_end) {
2 ignored issues
show
Bug Best Practice introduced by
The expression $date_end of type integer|null is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.

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

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

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

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
Bug Best Practice introduced by
The expression $date_start of type integer|null is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.

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

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

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

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
1553
				$langs->load("errors");
1554
				$this->error=$langs->trans('ErrorStartDateGreaterEnd');
1555
				return -1;
1556
			}
1557
1558
1559
            $this->db->begin();
1560
1561
            if ($fk_product > 0)
1562
            {
1563
            	if (! empty($conf->global->SUPPLIER_ORDER_WITH_PREDEFINED_PRICES_ONLY))
1564
                {
1565
                    // Check quantity is enough
1566
                    dol_syslog(get_class($this)."::addline we check supplier prices fk_product=".$fk_product." fk_prod_fourn_price=".$fk_prod_fourn_price." qty=".$qty." ref_supplier=".$ref_supplier);
1567
                    $prod = new Product($this->db, $fk_product);
0 ignored issues
show
Unused Code introduced by
The call to Product::__construct() has too many arguments starting with $fk_product. ( Ignorable by Annotation )

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

1567
                    $prod = /** @scrutinizer ignore-call */ new Product($this->db, $fk_product);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
1568
                    if ($prod->fetch($fk_product) > 0)
1569
                    {
1570
                        $product_type = $prod->type;
1571
                        $label = $prod->label;
1572
1573
                        // We use 'none' instead of $ref_supplier, because fourn_ref may not exists anymore. So we will take the first supplier price ok.
1574
                        // If we want a dedicated supplier price, we must provide $fk_prod_fourn_price.
1575
                        $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
1576
                        // If supplier order created from customer order, we take best supplier price
1577
                        // If $pu (defined previously from pu_ht or pu_ttc) is not defined at all, we also take the best supplier price
1578
                        if ($result > 0 && ($origin == 'commande' || $pu === ''))
1579
                        {
1580
                        	$pu = $prod->fourn_pu;       // Unit price supplier price set by get_buyprice
1581
                        	$ref_supplier = $prod->ref_supplier;   // Ref supplier price set by get_buyprice
1582
                        	// is remise percent not keyed but present for the product we add it
1583
                        	if ($remise_percent == 0 && $prod->remise_percent !=0) $remise_percent =$prod->remise_percent;
1584
                        }
1585
                        if ($result == 0)                   // If result == 0, we failed to found the supplier reference price
1586
                        {
1587
                            $langs->load("errors");
1588
                            $this->error = "Ref " . $prod->ref . " " . $langs->trans("ErrorQtyTooLowForThisSupplier");
1589
                            $this->db->rollback();
1590
                            dol_syslog(get_class($this)."::addline we did not found supplier price, so we can't guess unit price");
1591
                            //$pu    = $prod->fourn_pu;     // We do not overwrite unit price
1592
                            //$ref   = $prod->ref_fourn;    // We do not overwrite ref supplier price
1593
                            return -1;
1594
                        }
1595
                        if ($result == -1)
1596
                        {
1597
                            $langs->load("errors");
1598
                            $this->error = "Ref " . $prod->ref . " " . $langs->trans("ErrorQtyTooLowForThisSupplier");
1599
                            $this->db->rollback();
1600
                            dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_DEBUG);
1601
                            return -1;
1602
                        }
1603
                        if ($result < -1)
1604
                        {
1605
                            $this->error=$prod->error;
1606
                            $this->db->rollback();
1607
                            dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_ERR);
1608
                            return -1;
1609
                        }
1610
                    }
1611
                    else
1612
    				{
1613
                        $this->error=$prod->error;
1614
                        $this->db->rollback();
1615
                        return -1;
1616
                    }
1617
                }
1618
            }
1619
            else
1620
            {
1621
                $product_type = $type;
1622
            }
1623
1624
            if ($conf->multicurrency->enabled && $pu_ht_devise > 0) {
1625
            	$pu = 0;
1626
            }
1627
1628
            $localtaxes_type=getLocalTaxesFromRate($txtva, 0, $mysoc, $this->thirdparty);
1629
1630
            // Clean vat code
1631
            $vat_src_code='';
1632
            if (preg_match('/\((.*)\)/', $txtva, $reg))
1633
            {
1634
                $vat_src_code = $reg[1];
1635
                $txtva = preg_replace('/\s*\(.*\)/', '', $txtva);    // Remove code into vatrate.
1636
            }
1637
1638
            // Calcul du total TTC et de la TVA pour la ligne a partir de
1639
            // qty, pu, remise_percent et txtva
1640
            // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
1641
            // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
1642
1643
            $tabprice = calcul_price_total($qty, $pu, $remise_percent, $txtva, $txlocaltax1, $txlocaltax2, 0, $price_base_type, $info_bits, $product_type, $this->thirdparty, $localtaxes_type, 100, $this->multicurrency_tx, $pu_ht_devise);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $product_type does not seem to be defined for all execution paths leading up to this point.
Loading history...
1644
1645
            $total_ht  = $tabprice[0];
1646
            $total_tva = $tabprice[1];
1647
            $total_ttc = $tabprice[2];
1648
            $total_localtax1 = $tabprice[9];
1649
            $total_localtax2 = $tabprice[10];
1650
            $pu = $pu_ht = $tabprice[3];
1651
1652
			// MultiCurrency
1653
			$multicurrency_total_ht  = $tabprice[16];
1654
            $multicurrency_total_tva = $tabprice[17];
1655
            $multicurrency_total_ttc = $tabprice[18];
1656
			$pu_ht_devise = $tabprice[19];
1657
1658
            $localtax1_type=$localtaxes_type[0];
1659
			$localtax2_type=$localtaxes_type[2];
1660
1661
            $subprice = price2num($pu, 'MU');
1662
1663
            $rangmax = $this->line_max();
1664
            $rang = $rangmax + 1;
1665
1666
            // Insert line
1667
            $this->line=new CommandeFournisseurLigne($this->db);
1668
1669
            $this->line->context = $this->context;
1670
1671
            $this->line->fk_commande=$this->id;
1672
            $this->line->label=$label;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $label does not seem to be defined for all execution paths leading up to this point.
Loading history...
1673
            $this->line->ref_fourn = $ref_supplier;
1674
            $this->line->ref_supplier = $ref_supplier;
1675
            $this->line->desc=$desc;
1676
            $this->line->qty=$qty;
1 ignored issue
show
Documentation Bug introduced by
The property $qty was declared of type double, but $qty is of type string. Maybe add a type cast?

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

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

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
1677
            $this->line->tva_tx=$txtva;
1 ignored issue
show
Documentation Bug introduced by
It seems like $txtva can also be of type string. However, the property $tva_tx is declared as type double. Maybe add an additional type check?

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

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

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

class Id
{
    public $id;

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

}

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

$account_id = false;

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

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
1678
            $this->line->localtax1_tx=($total_localtax1?$localtaxes_type[1]:0);
1679
            $this->line->localtax2_tx=($total_localtax2?$localtaxes_type[3]:0);
1680
            $this->line->localtax1_type = $localtaxes_type[0];
1681
            $this->line->localtax2_type = $localtaxes_type[2];
1682
            $this->line->fk_product=$fk_product;
1683
            $this->line->product_type=$product_type;
1684
            $this->line->remise_percent=$remise_percent;
1685
            $this->line->subprice=$pu_ht;
1686
            $this->line->rang=$rang;
1687
            $this->line->info_bits=$info_bits;
1688
1689
            $this->line->vat_src_code=$vat_src_code;
0 ignored issues
show
Bug introduced by
The property vat_src_code does not seem to exist on CommandeFournisseurLigne.
Loading history...
1690
            $this->line->total_ht=$total_ht;
1691
            $this->line->total_tva=$total_tva;
1692
            $this->line->total_localtax1=$total_localtax1;
1693
            $this->line->total_localtax2=$total_localtax2;
1694
            $this->line->total_ttc=$total_ttc;
1695
            $this->line->product_type=$type;
1696
            $this->line->special_code=$this->special_code;
1697
            $this->line->origin=$origin;
1698
            $this->line->origin_id=$origin_id;
1699
            $this->line->fk_unit=$fk_unit;
1700
1701
            $this->line->date_start=$date_start;
1702
            $this->line->date_end=$date_end;
1703
1704
            // Multicurrency
1705
            $this->line->fk_multicurrency			= $this->fk_multicurrency;
1706
            $this->line->multicurrency_code			= $this->multicurrency_code;
1707
            $this->line->multicurrency_subprice		= $pu_ht_devise;
1708
            $this->line->multicurrency_total_ht 	= $multicurrency_total_ht;
1709
            $this->line->multicurrency_total_tva 	= $multicurrency_total_tva;
1710
            $this->line->multicurrency_total_ttc 	= $multicurrency_total_ttc;
1711
1712
            $this->line->subprice=$pu_ht;
1713
            $this->line->price=$this->line->subprice;
1 ignored issue
show
Deprecated Code introduced by
The property CommonOrderLine::$price has been deprecated. ( Ignorable by Annotation )

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

1713
            /** @scrutinizer ignore-deprecated */ $this->line->price=$this->line->subprice;
Loading history...
1714
1715
            $this->line->remise_percent=$remise_percent;
1716
1717
            if (is_array($array_options) && count($array_options)>0) {
1718
                $this->line->array_options=$array_options;
1719
            }
1720
1721
            $result=$this->line->insert($notrigger);
1722
            if ($result > 0)
1723
            {
1724
                // Reorder if child line
1725
                if (! empty($fk_parent_line)) $this->line_order(true, 'DESC');
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $fk_parent_line seems to never exist and therefore empty should always be true.
Loading history...
1726
1727
                // Mise a jour informations denormalisees au niveau de la commande meme
1728
                $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.
1729
                if ($result > 0)
1730
                {
1731
                    $this->db->commit();
1732
                    return $this->line->id;
1733
                }
1734
                else
1735
                {
1736
                    $this->db->rollback();
1737
                    return -1;
1738
                }
1739
            }
1740
            else
1741
            {
1742
                $this->error=$this->line->error;
1743
                $this->errors=$this->line->errors;
1744
                dol_syslog(get_class($this)."::addline error=".$this->error, LOG_ERR);
1745
                $this->db->rollback();
1746
                return -1;
1747
            }
1748
        }
1749
    }
1750
1751
1752
    /**
1753
     * Save a receiving into the tracking table of receiving (commande_fournisseur_dispatch) and add product into stock warehouse.
1754
     *
1755
     * @param 	User		$user					User object making change
1756
     * @param 	int			$product				Id of product to dispatch
1757
     * @param 	double		$qty					Qty to dispatch
1758
     * @param 	int			$entrepot				Id of warehouse to add product
1759
     * @param 	double		$price					Unit Price for PMP value calculation (Unit price without Tax and taking into account discount)
1760
     * @param	string		$comment				Comment for stock movement
1761
	 * @param	integer		$eatby					eat-by date
1762
	 * @param	integer		$sellby					sell-by date
1763
	 * @param	string		$batch					Lot number
1764
	 * @param	int			$fk_commandefourndet	Id of supplier order line
1765
     * @param	int			$notrigger          	1 = notrigger
1766
     * @return 	int						<0 if KO, >0 if OK
1767
     */
1768
    public function dispatchProduct($user, $product, $qty, $entrepot, $price = 0, $comment = '', $eatby = '', $sellby = '', $batch = '', $fk_commandefourndet = 0, $notrigger = 0)
1769
    {
1770
        global $conf, $langs;
1771
1772
        $error = 0;
1773
        require_once DOL_DOCUMENT_ROOT .'/product/stock/class/mouvementstock.class.php';
1774
1775
        // Check parameters (if test are wrong here, there is bug into caller)
1776
        if ($entrepot <= 0)
1777
        {
1778
            $this->error='ErrorBadValueForParameterWarehouse';
1779
            return -1;
1780
        }
1781
        if ($qty == 0)
1782
        {
1783
            $this->error='ErrorBadValueForParameterQty';
1784
            return -1;
1785
        }
1786
1787
        $dispatchstatus = 1;
1788
        if (! empty($conf->global->SUPPLIER_ORDER_USE_DISPATCH_STATUS)) $dispatchstatus = 0;	// Setting dispatch status (a validation step after receiving products) will be done manually to 1 or 2 if this option is on
1789
1790
        $now=dol_now();
1791
1792
        if (($this->statut == self::STATUS_ORDERSENT || $this->statut == self::STATUS_RECEIVED_PARTIALLY || $this->statut == self::STATUS_RECEIVED_COMPLETELY))
1793
        {
1794
            $this->db->begin();
1795
1796
            $sql = "INSERT INTO ".MAIN_DB_PREFIX."commande_fournisseur_dispatch";
1797
            $sql.= " (fk_commande, fk_product, qty, fk_entrepot, fk_user, datec, fk_commandefourndet, status, comment, eatby, sellby, batch) VALUES";
1798
            $sql.= " ('".$this->id."','".$product."','".$qty."',".($entrepot>0?"'".$entrepot."'":"null").",'".$user->id."','".$this->db->idate($now)."','".$fk_commandefourndet."', ".$dispatchstatus.", '".$this->db->escape($comment)."', ";
1799
            $sql.= ($eatby?"'".$this->db->idate($eatby)."'":"null").", ".($sellby?"'".$this->db->idate($sellby)."'":"null").", ".($batch?"'".$batch."'":"null");
1800
            $sql.= ")";
1801
1802
            dol_syslog(get_class($this)."::dispatchProduct", LOG_DEBUG);
1803
            $resql = $this->db->query($sql);
1804
            if ($resql)
1805
            {
1806
                if (! $notrigger)
1807
                {
1808
                    global $conf, $langs, $user;
1809
					// Call trigger
1810
					$result=$this->call_trigger('LINEORDER_SUPPLIER_DISPATCH', $user);
1811
					if ($result < 0)
1812
                    {
1813
                        $error++;
1814
                        return -1;
1815
                    }
1816
					// End call triggers
1817
                }
1818
            }
1819
            else
1820
			{
1821
                $this->error=$this->db->lasterror();
1822
                $error++;
1823
            }
1824
1825
            // Si module stock gere et que incrementation faite depuis un dispatching en stock
1826
            if (! $error && $entrepot > 0 && ! empty($conf->stock->enabled) && ! empty($conf->global->STOCK_CALCULATE_ON_SUPPLIER_DISPATCH_ORDER))
1827
            {
1828
1829
                $mouv = new MouvementStock($this->db);
1830
                if ($product > 0)
1831
                {
1832
                	// $price should take into account discount (except if option STOCK_EXCLUDE_DISCOUNT_FOR_PMP is on)
1833
                	$mouv->origin = &$this;
1834
					$result=$mouv->reception($user, $product, $entrepot, $qty, $price, $comment, $eatby, $sellby, $batch);
1835
                    if ($result < 0)
1836
                    {
1837
                        $this->error=$mouv->error;
1838
                        $this->errors=$mouv->errors;
1839
                        dol_syslog(get_class($this)."::dispatchProduct ".$this->error." ".join(',', $this->errors), LOG_ERR);
1840
                        $error++;
1841
                    }
1842
                }
1843
            }
1844
1845
            if ($error == 0)
1846
            {
1847
                $this->db->commit();
1848
                return 1;
1849
            }
1850
            else
1851
            {
1852
                $this->db->rollback();
1853
                return -1;
1854
            }
1855
        }
1856
        else
1857
		{
1858
            $this->error='BadStatusForObject';
1859
            return -2;
1860
        }
1861
    }
1862
1863
    /**
1864
     * 	Delete line
1865
     *
1866
     *	@param	int		$idline		Id of line to delete
1867
     *	@param	int		$notrigger	1=Disable call to triggers
1868
     *	@return	int					<0 if KO, >0 if OK
1869
     */
1870
    public function deleteline($idline, $notrigger = 0)
1871
    {
1872
        if ($this->statut == 0)
1873
        {
1874
            $line = new CommandeFournisseurLigne($this->db);
1875
1876
            if ($line->fetch($idline) <= 0)
1877
            {
1878
                return 0;
1879
            }
1880
1881
            if ($line->delete($notrigger) > 0)
1882
            {
1883
                $this->update_price();
1884
                return 1;
1885
            }
1886
            else
1887
            {
1888
                $this->error = $line->error;
1889
                $this->errors = $line->errors;
1890
                return -1;
1891
            }
1892
        }
1893
        else
1894
        {
1895
            return -2;
1896
        }
1897
    }
1898
1899
    /**
1900
     *  Delete an order
1901
     *
1902
     *	@param	User	$user		Object user
1903
     *	@param	int		$notrigger	1=Does not execute triggers, 0= execute triggers
1904
     *	@return	int					<0 if KO, >0 if OK
1905
     */
1906
    public function delete(User $user, $notrigger = 0)
1907
    {
1908
        global $langs,$conf;
1909
        require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1910
1911
        $error = 0;
1912
1913
        $this->db->begin();
1914
1915
        if (empty($notrigger))
1916
        {
1917
            // Call trigger
1918
            $result=$this->call_trigger('ORDER_SUPPLIER_DELETE', $user);
1919
            if ($result < 0)
1920
            {
1921
            	$this->errors[]='ErrorWhenRunningTrigger';
1922
            	dol_syslog(get_class($this)."::delete ".$this->error, LOG_ERR);
1923
            	return -1;
1924
            }
1925
            // End call triggers
1926
        }
1927
1928
        $sql = "DELETE FROM ".MAIN_DB_PREFIX."commande_fournisseurdet WHERE fk_commande =". $this->id ;
1929
        dol_syslog(get_class($this)."::delete", LOG_DEBUG);
1930
        if (! $this->db->query($sql) )
1931
        {
1932
            $this->error=$this->db->lasterror();
1933
            $this->errors[]=$this->db->lasterror();
1934
            $error++;
1935
        }
1936
1937
        $sql = "DELETE FROM ".MAIN_DB_PREFIX."commande_fournisseur WHERE rowid =".$this->id;
1938
        dol_syslog(get_class($this)."::delete", LOG_DEBUG);
1939
        if ($resql = $this->db->query($sql) )
1940
        {
1941
            if ($this->db->affected_rows($resql) < 1)
1942
            {
1943
                $this->error=$this->db->lasterror();
1944
                $this->errors[]=$this->db->lasterror();
1945
                $error++;
1946
            }
1947
        }
1948
        else
1949
        {
1950
            $this->error=$this->db->lasterror();
1951
            $this->errors[]=$this->db->lasterror();
1952
            $error++;
1953
        }
1954
1955
        // Remove extrafields
1956
        if ((! $error) && (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED))) // For avoid conflicts if trigger used
1957
        {
1958
        	$result=$this->deleteExtraFields();
1959
        	if ($result < 0)
1960
        	{
1961
        		$this->error='FailToDeleteExtraFields';
1962
        		$this->errors[]='FailToDeleteExtraFields';
1963
        		$error++;
1964
        		dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
1965
        	}
1966
        }
1967
1968
		// Delete linked object
1969
    	$res = $this->deleteObjectLinked();
1970
    	if ($res < 0) {
1971
    		$this->error='FailToDeleteObjectLinked';
1972
    		$this->errors[]='FailToDeleteObjectLinked';
1973
    		$error++;
1974
    	}
1975
1976
        if (! $error)
1977
        {
1978
        	// We remove directory
1979
        	$ref = dol_sanitizeFileName($this->ref);
1980
        	if ($conf->fournisseur->commande->dir_output)
1981
        	{
1982
        		$dir = $conf->fournisseur->commande->dir_output . "/" . $ref ;
1983
        		$file = $dir . "/" . $ref . ".pdf";
1984
        		if (file_exists($file))
1985
        		{
1986
        			if (! dol_delete_file($file, 0, 0, 0, $this)) // For triggers
1987
        			{
1988
        				$this->error='ErrorFailToDeleteFile';
1989
        				$this->errors[]='ErrorFailToDeleteFile';
1990
        				$error++;
1991
        			}
1992
        		}
1993
        		if (file_exists($dir))
1994
        		{
1995
        			$res=@dol_delete_dir_recursive($dir);
1996
        			if (! $res)
1997
        			{
1998
        				$this->error='ErrorFailToDeleteDir';
1999
        				$this->errors[]='ErrorFailToDeleteDir';
2000
        				$error++;
2001
        			}
2002
        		}
2003
        	}
2004
        }
2005
2006
		if (! $error)
2007
		{
2008
			dol_syslog(get_class($this)."::delete $this->id by $user->id", LOG_DEBUG);
2009
			$this->db->commit();
2010
			return 1;
2011
		}
2012
		else
2013
		{
2014
			dol_syslog(get_class($this)."::delete ".$this->error, LOG_ERR);
2015
			$this->db->rollback();
2016
			return -$error;
2017
		}
2018
    }
2019
2020
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2021
    /**
2022
     *	Get list of order methods
2023
     *
2024
     *	@return int 0 if OK, <0 if KO
2025
     */
2026
    public function get_methodes_commande()
2027
    {
2028
        // phpcs:enable
2029
        $sql = "SELECT rowid, libelle";
2030
        $sql.= " FROM ".MAIN_DB_PREFIX."c_input_method";
2031
        $sql.= " WHERE active = 1";
2032
2033
        $resql=$this->db->query($sql);
2034
        if ($resql)
2035
        {
2036
            $i = 0;
2037
            $num = $this->db->num_rows($resql);
2038
            $this->methodes_commande = array();
2039
            while ($i < $num)
2040
            {
2041
                $row = $this->db->fetch_row($resql);
2042
2043
                $this->methodes_commande[$row[0]] = $row[1];
2044
2045
                $i++;
2046
            }
2047
            return 0;
2048
        }
2049
        else
2050
        {
2051
            return -1;
2052
        }
2053
    }
2054
2055
    /**
2056
	 * Return array of dispatched lines waiting to be approved for this order
2057
     *
2058
     * @since 8.0 Return dispatched quantity (qty).
2059
	 *
2060
	 * @param	int		$status		Filter on stats (-1 = no filter, 0 = lines draft to be approved, 1 = approved lines)
2061
	 * @return	array				Array of lines
2062
     */
2063
    public function getDispachedLines($status = -1)
2064
    {
2065
    	$ret = array();
2066
2067
    	// List of already dispatched lines
2068
		$sql = "SELECT p.ref, p.label,";
2069
		$sql.= " e.rowid as warehouse_id, e.ref as entrepot,";
2070
		$sql.= " cfd.rowid as dispatchedlineid, cfd.fk_product, cfd.qty, cfd.eatby, cfd.sellby, cfd.batch, cfd.comment, cfd.status";
2071
		$sql.= " FROM ".MAIN_DB_PREFIX."product as p,";
2072
		$sql.= " ".MAIN_DB_PREFIX."commande_fournisseur_dispatch as cfd";
2073
		$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."entrepot as e ON cfd.fk_entrepot = e.rowid";
2074
		$sql.= " WHERE cfd.fk_commande = ".$this->id;
2075
		$sql.= " AND cfd.fk_product = p.rowid";
2076
		if ($status >= 0) $sql.=" AND cfd.status = ".$status;
2077
		$sql.= " ORDER BY cfd.rowid ASC";
2078
2079
		$resql = $this->db->query($sql);
2080
		if ($resql)
2081
		{
2082
			$num = $this->db->num_rows($resql);
2083
			$i = 0;
2084
2085
			while ($i < $num)
2086
			{
2087
				$objp = $this->db->fetch_object($resql);
2088
				if ($objp)
2089
				{
2090
					$ret[] = array(
2091
						'id' => $objp->dispatchedlineid,
2092
						'productid' => $objp->fk_product,
2093
						'warehouseid' => $objp->warehouse_id,
2094
						'qty' => $objp->qty,
2095
					);
2096
				}
2097
2098
				$i++;
2099
			}
2100
		}
2101
		else dol_print_error($this->db, 'Failed to execute request to get dispatched lines');
2102
2103
		return $ret;
2104
    }
2105
2106
2107
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2108
    /**
2109
     * 	Set a delivery in database for this supplier order
2110
     *
2111
     *	@param	User	$user		User that input data
2112
     *	@param	integer	$date		Date of reception
2113
     *	@param	string	$type		Type of receipt ('tot' = total/done, 'par' = partial, 'nev' = never, 'can' = cancel)
2114
     *	@param	string	$comment	Comment
2115
     *	@return	int					<0 if KO, >0 if OK
2116
     */
2117
    public function Livraison($user, $date, $type, $comment)
2118
    {
2119
        // phpcs:enable
2120
    	global $conf, $langs;
2121
2122
        $result = 0;
2123
		$error = 0;
2124
2125
        dol_syslog(get_class($this)."::Livraison");
2126
2127
        if ($user->rights->fournisseur->commande->receptionner)
2128
        {
2129
        	// Define the new status
2130
            if ($type == 'par') $statut = self::STATUS_RECEIVED_PARTIALLY;
2131
            elseif ($type == 'tot')	$statut = self::STATUS_RECEIVED_COMPLETELY;
2132
            elseif ($type == 'nev') $statut = self::STATUS_CANCELED_AFTER_ORDER;
2133
            elseif ($type == 'can') $statut = self::STATUS_CANCELED_AFTER_ORDER;
2134
			else {
2135
            	$error++;
2136
                dol_syslog(get_class($this)."::Livraison Error -2", LOG_ERR);
2137
                return -2;
2138
			}
2139
2140
            // Some checks to accept the record
2141
            if (! empty($conf->global->SUPPLIER_ORDER_USE_DISPATCH_STATUS))
2142
            {
2143
				// If option SUPPLIER_ORDER_USE_DISPATCH_STATUS is on, we check all reception are approved to allow status "total/done"
2144
	        	if (! $error && ($type == 'tot'))
2145
		    	{
2146
		    		$dispatchedlinearray=$this->getDispachedLines(0);
2147
		    		if (count($dispatchedlinearray) > 0)
2148
		    		{
2149
		    			$result=-1;
2150
		    			$error++;
2151
		    			$this->errors[]='ErrorCantSetReceptionToTotalDoneWithReceptionToApprove';
2152
		    			dol_syslog('ErrorCantSetReceptionToTotalDoneWithReceptionToApprove', LOG_DEBUG);
2153
		    		}
2154
		    	}
2155
	    		if (! $error && ! empty($conf->global->SUPPLIER_ORDER_USE_DISPATCH_STATUS_NEED_APPROVE) && ($type == 'tot'))	// Accept to move to reception done, only if status of all line are ok (refuse denied)
2156
	    		{
2157
	    			$dispatcheddenied=$this->getDispachedLines(2);
2158
	    			if (count($dispatchedlinearray) > 0)
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $dispatchedlinearray does not seem to be defined for all execution paths leading up to this point.
Loading history...
2159
	    			{
2160
		    			$result=-1;
2161
		    			$error++;
2162
		    			$this->errors[]='ErrorCantSetReceptionToTotalDoneWithReceptionDenied';
2163
		    			dol_syslog('ErrorCantSetReceptionToTotalDoneWithReceptionDenied', LOG_DEBUG);
2164
	    			}
2165
	    		}
2166
            }
2167
2168
            // TODO LDR01 Add a control test to accept only if ALL predefined products are received (same qty).
2169
2170
2171
            if (! $error)
2172
            {
2173
                $this->db->begin();
2174
2175
                $sql = "UPDATE ".MAIN_DB_PREFIX."commande_fournisseur";
2176
                $sql.= " SET fk_statut = ".$statut;
2177
                $sql.= " WHERE rowid = ".$this->id;
2178
                $sql.= " AND fk_statut IN (".self::STATUS_ORDERSENT.",".self::STATUS_RECEIVED_PARTIALLY.")";	// Process running or Partially received
2179
2180
                dol_syslog(get_class($this)."::Livraison", LOG_DEBUG);
2181
                $resql=$this->db->query($sql);
2182
                if ($resql)
2183
                {
2184
                    $result = 0;
2185
                    $old_statut = $this->statut;
2186
                    $this->statut = $statut;
2187
					$this->actionmsg2 = $comment;
2188
2189
                    // Call trigger
2190
                    $result=$this->call_trigger('ORDER_SUPPLIER_RECEIVE', $user);
2191
                    if ($result < 0) $error++;
2192
                    // End call triggers
2193
2194
                    if (! $error)
2195
                    {
2196
                        $this->db->commit();
2197
                    }
2198
                    else
2199
                    {
2200
                        $this->statut = $old_statut;
2201
                        $this->db->rollback();
2202
                        $this->error=$this->db->lasterror();
2203
                        $result = -1;
2204
                    }
2205
                }
2206
                else
2207
                {
2208
                    $this->db->rollback();
2209
                    $this->error=$this->db->lasterror();
2210
                    $result = -1;
2211
                }
2212
            }
2213
        }
2214
        else
2215
        {
2216
            $this->error = $langs->trans('NotAuthorized');
2217
            $this->errors[] = $langs->trans('NotAuthorized');
2218
            dol_syslog(get_class($this)."::Livraison Not Authorized");
2219
            $result = -3;
2220
        }
2221
        return $result ;
2222
    }
2223
2224
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2225
    /**
2226
     *	Set the planned delivery date
2227
     *
2228
     *	@param      User			$user        		Objet user making change
2229
     *	@param      integer  		$date_livraison     Planned delivery date
2230
     *  @param     	int				$notrigger			1=Does not execute triggers, 0= execute triggers
2231
     *	@return     int         						<0 if KO, >0 if OK
2232
     */
2233
    public function set_date_livraison($user, $date_livraison, $notrigger = 0)
2234
    {
2235
        // phpcs:enable
2236
        if ($user->rights->fournisseur->commande->creer)
2237
        {
2238
        	$error=0;
2239
2240
        	$this->db->begin();
2241
2242
        	$sql = "UPDATE ".MAIN_DB_PREFIX."commande_fournisseur";
2243
            $sql.= " SET date_livraison = ".($date_livraison ? "'".$this->db->idate($date_livraison)."'" : 'null');
2244
            $sql.= " WHERE rowid = ".$this->id;
2245
2246
        	dol_syslog(__METHOD__, LOG_DEBUG);
2247
        	$resql=$this->db->query($sql);
2248
        	if (!$resql)
2249
        	{
2250
        		$this->errors[]=$this->db->error();
2251
        		$error++;
2252
        	}
2253
2254
        	if (! $error)
2255
        	{
2256
        		$this->oldcopy= clone $this;
2257
        		$this->date_livraison = $date_livraison;
2258
        	}
2259
2260
        	if (! $notrigger && empty($error))
2261
        	{
2262
        		// Call trigger
2263
        		$result=$this->call_trigger('ORDER_SUPPLIER_MODIFY', $user);
2264
        		if ($result < 0) $error++;
2265
        		// End call triggers
2266
        	}
2267
2268
        	if (! $error)
2269
        	{
2270
        		$this->db->commit();
2271
        		return 1;
2272
        	}
2273
        	else
2274
        	{
2275
        		foreach($this->errors as $errmsg)
2276
        		{
2277
        			dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2278
        			$this->error.=($this->error?', '.$errmsg:$errmsg);
2279
        		}
2280
        		$this->db->rollback();
2281
        		return -1*$error;
2282
        	}
2283
        }
2284
        else
2285
        {
2286
            return -2;
2287
        }
2288
    }
2289
2290
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2291
	/**
2292
     *	Set the id projet
2293
     *
2294
     *	@param      User			$user        		Objet utilisateur qui modifie
2295
     *	@param      int				$id_projet    	 	Date de livraison
2296
     *  @param     	int				$notrigger			1=Does not execute triggers, 0= execute triggers
2297
     *	@return     int         						<0 si ko, >0 si ok
2298
     */
2299
    public function set_id_projet($user, $id_projet, $notrigger = 0)
2300
    {
2301
        // phpcs:enable
2302
        if ($user->rights->fournisseur->commande->creer)
2303
        {
2304
        	$error=0;
2305
2306
        	$this->db->begin();
2307
2308
            $sql = "UPDATE ".MAIN_DB_PREFIX."commande_fournisseur";
2309
            $sql.= " SET fk_projet = ".($id_projet > 0 ? (int) $id_projet : 'null');
2310
            $sql.= " WHERE rowid = ".$this->id;
2311
2312
            dol_syslog(__METHOD__, LOG_DEBUG);
2313
            $resql=$this->db->query($sql);
2314
            if (!$resql)
2315
            {
2316
            	$this->errors[]=$this->db->error();
2317
            	$error++;
2318
            }
2319
2320
            if (! $error)
2321
            {
2322
            	$this->oldcopy= clone $this;
2323
            	$this->fk_projet = $id_projet;
2324
            	$this->fk_project = $id_projet;
2325
            }
2326
2327
            if (! $notrigger && empty($error))
2328
            {
2329
            	// Call trigger
2330
            	$result=$this->call_trigger('ORDER_SUPPLIER_MODIFY', $user);
2331
            	if ($result < 0) $error++;
2332
            	// End call triggers
2333
            }
2334
2335
            if (! $error)
2336
            {
2337
            	$this->db->commit();
2338
            	return 1;
2339
            }
2340
            else
2341
            {
2342
            	foreach($this->errors as $errmsg)
2343
            	{
2344
            		dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2345
            		$this->error.=($this->error?', '.$errmsg:$errmsg);
2346
            	}
2347
            	$this->db->rollback();
2348
            	return -1*$error;
2349
            }
2350
        }
2351
        else
2352
        {
2353
            return -2;
2354
        }
2355
    }
2356
2357
    /**
2358
     *  Update a supplier order from a customer order
2359
     *
2360
     *  @param  User	$user           User that create
2361
     *  @param  int		$idc			Id of supplier order to update
2362
     *  @param	int		$comclientid	Id of customer order to use as template
2363
     *	@return	int						<0 if KO, >0 if OK
2364
     */
2365
    public function updateFromCommandeClient($user, $idc, $comclientid)
2366
    {
2367
        $comclient = new Commande($this->db);
2368
        $comclient->fetch($comclientid);
2369
2370
        $this->id = $idc;
2371
2372
        $this->lines = array();
2373
2374
        $num=count($comclient->lines);
2375
        for ($i = 0; $i < $num; $i++)
2376
        {
2377
            $prod = new Product($this->db);
2378
            $libelle = '';
2379
            $ref = '';
2380
            if ($prod->fetch($comclient->lines[$i]->fk_product) > 0)
2381
            {
2382
                $libelle  = $prod->libelle;
1 ignored issue
show
Deprecated Code introduced by
The property Product::$libelle has been deprecated. ( Ignorable by Annotation )

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

2382
                $libelle  = /** @scrutinizer ignore-deprecated */ $prod->libelle;
Loading history...
2383
                $ref      = $prod->ref;
2384
            }
2385
2386
            $sql = "INSERT INTO ".MAIN_DB_PREFIX."commande_fournisseurdet";
2387
            $sql .= " (fk_commande,label,description,fk_product, price, qty, tva_tx, localtax1_tx, localtax2_tx, remise_percent, subprice, remise, ref)";
2388
            $sql .= " VALUES (".$idc.", '" . $this->db->escape($libelle) . "','" . $this->db->escape($comclient->lines[$i]->desc) . "'";
2389
            $sql .= ",".$comclient->lines[$i]->fk_product.",'".price2num($comclient->lines[$i]->price)."'";
1 ignored issue
show
Deprecated Code introduced by
The property CommonOrderLine::$price has been deprecated. ( Ignorable by Annotation )

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

2389
            $sql .= ",".$comclient->lines[$i]->fk_product.",'".price2num(/** @scrutinizer ignore-deprecated */ $comclient->lines[$i]->price)."'";
Loading history...
2390
            $sql .= ", '".$comclient->lines[$i]->qty."', ".$comclient->lines[$i]->tva_tx.", ".$comclient->lines[$i]->localtax1_tx.", ".$comclient->lines[$i]->localtax2_tx.", ".$comclient->lines[$i]->remise_percent;
2391
            $sql .= ", '".price2num($comclient->lines[$i]->subprice)."','0','".$ref."');";
2392
            if ($this->db->query($sql))
2393
            {
2394
                $this->update_price();
2395
            }
2396
        }
2397
2398
        return 1;
2399
    }
2400
2401
    /**
2402
     *  Tag order with a particular status
2403
     *
2404
     *  @param      User	$user       Object user that change status
2405
     *  @param      int		$status		New status
2406
     *  @return     int         		<0 if KO, >0 if OK
2407
     */
2408
    public function setStatus($user, $status)
2409
    {
2410
        global $conf,$langs;
2411
        $error=0;
2412
2413
        $this->db->begin();
2414
2415
        $sql = 'UPDATE '.MAIN_DB_PREFIX.'commande_fournisseur';
2416
        $sql.= ' SET fk_statut='.$status;
2417
        $sql.= ' WHERE rowid = '.$this->id;
2418
2419
        dol_syslog(get_class($this)."::setStatus", LOG_DEBUG);
2420
        $resql = $this->db->query($sql);
2421
        if ($resql)
2422
        {
2423
            // Trigger names for each status
2424
            $trigger_name[0] = 'DRAFT';
0 ignored issues
show
Comprehensibility Best Practice introduced by
$trigger_name was never initialized. Although not strictly required by PHP, it is generally a good practice to add $trigger_name = array(); before regardless.
Loading history...
2425
            $trigger_name[1] = 'VALIDATED';
2426
            $trigger_name[2] = 'APPROVED';
2427
            $trigger_name[3] = 'ORDERED';				// Ordered
2428
            $trigger_name[4] = 'RECEIVED_PARTIALLY';
2429
            $trigger_name[5] = 'RECEIVED_COMPLETELY';
2430
            $trigger_name[6] = 'CANCELED';
2431
            $trigger_name[7] = 'CANCELED';
2432
            $trigger_name[9] = 'REFUSED';
2433
2434
            // Call trigger
2435
            $result=$this->call_trigger("ORDER_SUPPLIER_STATUS_".$trigger_name[$status], $user);
2436
            if ($result < 0) { $error++; }
2437
            // End call triggers
2438
        }
2439
        else
2440
        {
2441
            $error++;
2442
            $this->error=$this->db->lasterror();
2443
            dol_syslog(get_class($this)."::setStatus ".$this->error);
2444
        }
2445
2446
        if (! $error)
2447
        {
2448
            $this->statut = $status;
2449
            $this->db->commit();
2450
            return 1;
2451
        }
2452
        else
2453
        {
2454
            $this->db->rollback();
2455
            return -1;
2456
        }
2457
    }
2458
2459
    /**
2460
     *	Update line
2461
     *
2462
     *	@param     	int			$rowid           	Id de la ligne de facture
2463
     *	@param     	string		$desc            	Description de la ligne
2464
     *	@param     	double		$pu              	Prix unitaire
2465
     *	@param     	double		$qty             	Quantity
2466
     *	@param     	double		$remise_percent  	Percent discount on line
2467
     *	@param     	double		$txtva          	VAT rate
2468
     *  @param     	double		$txlocaltax1	    Localtax1 tax
2469
     *  @param     	double		$txlocaltax2   		Localtax2 tax
2470
     *  @param     	double		$price_base_type 	Type of price base
2471
     *	@param		int			$info_bits			Miscellaneous informations
2472
     *	@param		int			$type				Type of line (0=product, 1=service)
2473
     *  @param		int			$notrigger			Disable triggers
2474
     *  @param      integer     $date_start     	Date start of service
2475
     *  @param      integer     $date_end       	Date end of service
2476
	 *  @param		array		$array_options		Extrafields array
2477
     * 	@param 		string		$fk_unit 			Code of the unit to use. Null to use the default one
2478
	 * 	@param		double		$pu_ht_devise		Unit price in currency
2479
	 *  @param		string		$ref_supplier		Supplier ref
2480
     *	@return    	int         	    			< 0 if error, > 0 if ok
2481
     */
2482
    public function updateline($rowid, $desc, $pu, $qty, $remise_percent, $txtva, $txlocaltax1 = 0, $txlocaltax2 = 0, $price_base_type = 'HT', $info_bits = 0, $type = 0, $notrigger = 0, $date_start = '', $date_end = '', $array_options = 0, $fk_unit = null, $pu_ht_devise = 0, $ref_supplier = '')
2483
    {
2484
        global $mysoc, $conf, $langs;
2485
        dol_syslog(get_class($this)."::updateline $rowid, $desc, $pu, $qty, $remise_percent, $txtva, $price_base_type, $info_bits, $type, $fk_unit");
2486
        include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2487
2488
        $error = 0;
2489
2490
        if ($this->brouillon)
2491
        {
2492
            // Clean parameters
2493
            if (empty($qty)) $qty=0;
2494
            if (empty($info_bits)) $info_bits=0;
2495
            if (empty($txtva)) $txtva=0;
2496
            if (empty($txlocaltax1)) $txlocaltax1=0;
2497
            if (empty($txlocaltax2)) $txlocaltax2=0;
2498
            if (empty($remise)) $remise=0;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $remise does not exist. Did you maybe mean $remise_percent?
Loading history...
2499
            if (empty($remise_percent)) $remise_percent=0;
2500
2501
            $remise_percent=price2num($remise_percent);
2502
            $qty=price2num($qty);
2503
            if (! $qty) $qty=1;
2504
            $pu = price2num($pu);
2505
        	$pu_ht_devise=price2num($pu_ht_devise);
2506
            $txtva=price2num($txtva);
2507
            $txlocaltax1=price2num($txlocaltax1);
2508
            $txlocaltax2=price2num($txlocaltax2);
2509
2510
            // Check parameters
2511
            if ($type < 0) return -1;
2512
            if ($date_start && $date_end && $date_start > $date_end) {
2513
                $langs->load("errors");
2514
                $this->error=$langs->trans('ErrorStartDateGreaterEnd');
2515
                return -1;
2516
            }
2517
2518
            $this->db->begin();
2519
2520
            // Calcul du total TTC et de la TVA pour la ligne a partir de
2521
            // qty, pu, remise_percent et txtva
2522
            // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
2523
            // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
2524
2525
            $localtaxes_type=getLocalTaxesFromRate($txtva, 0, $mysoc, $this->thirdparty);
2526
2527
            // Clean vat code
2528
            $vat_src_code='';
2529
            if (preg_match('/\((.*)\)/', $txtva, $reg))
2530
            {
2531
                $vat_src_code = $reg[1];
2532
                $txtva = preg_replace('/\s*\(.*\)/', '', $txtva);    // Remove code into vatrate.
2533
            }
2534
2535
            $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);
2536
            $total_ht  = $tabprice[0];
2537
            $total_tva = $tabprice[1];
2538
            $total_ttc = $tabprice[2];
2539
            $total_localtax1 = $tabprice[9];
2540
            $total_localtax2 = $tabprice[10];
2541
			$pu_ht  = $tabprice[3];
2542
			$pu_tva = $tabprice[4];
2543
			$pu_ttc = $tabprice[5];
2544
2545
			// MultiCurrency
2546
			$multicurrency_total_ht  = $tabprice[16];
2547
            $multicurrency_total_tva = $tabprice[17];
2548
            $multicurrency_total_ttc = $tabprice[18];
2549
			$pu_ht_devise = $tabprice[19];
2550
2551
            $localtax1_type=$localtaxes_type[0];
2552
			$localtax2_type=$localtaxes_type[2];
2553
2554
            $subprice = price2num($pu_ht, 'MU');
2555
2556
            //Fetch current line from the database and then clone the object and set it in $oldline property
2557
            $this->line=new CommandeFournisseurLigne($this->db);
2558
            $this->line->fetch($rowid);
2559
            $oldline = clone $this->line;
2560
            $this->line->oldline = $oldline;
2561
2562
            $this->line->context = $this->context;
2563
2564
            $this->line->fk_commande=$this->id;
2565
            //$this->line->label=$label;
2566
            $this->line->desc=$desc;
2567
            $this->line->qty=$qty;
1 ignored issue
show
Documentation Bug introduced by
It seems like $qty can also be of type string. However, the property $qty is declared as type double. Maybe add an additional type check?

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

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

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

class Id
{
    public $id;

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

}

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

$account_id = false;

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

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
2568
			$this->line->ref_supplier=$ref_supplier;
2569
2570
	        $this->line->vat_src_code   = $vat_src_code;
0 ignored issues
show
Bug introduced by
The property vat_src_code does not seem to exist on CommandeFournisseurLigne.
Loading history...
2571
            $this->line->tva_tx         = $txtva;
1 ignored issue
show
Documentation Bug introduced by
The property $tva_tx was declared of type double, but $txtva is of type string. Maybe add a type cast?

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

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

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
2572
            $this->line->localtax1_tx   = $txlocaltax1;
1 ignored issue
show
Documentation Bug introduced by
The property $localtax1_tx was declared of type double, but $txlocaltax1 is of type string. Maybe add a type cast?

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

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

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
2573
            $this->line->localtax2_tx   = $txlocaltax2;
1 ignored issue
show
Documentation Bug introduced by
The property $localtax2_tx was declared of type double, but $txlocaltax2 is of type string. Maybe add a type cast?

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

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

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
2574
            $this->line->localtax1_type = $localtaxes_type[0];
2575
            $this->line->localtax2_type = $localtaxes_type[2];
2576
            $this->line->remise_percent = $remise_percent;
1 ignored issue
show
Documentation Bug introduced by
The property $remise_percent was declared of type double, but $remise_percent is of type string. Maybe add a type cast?

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

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

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
2577
            $this->line->subprice       = $pu_ht;
2578
            $this->line->rang           = $this->rang;
2579
            $this->line->info_bits      = $info_bits;
2580
            $this->line->total_ht       = $total_ht;
2581
            $this->line->total_tva      = $total_tva;
2582
            $this->line->total_localtax1= $total_localtax1;
2583
            $this->line->total_localtax2= $total_localtax2;
2584
            $this->line->total_ttc      = $total_ttc;
2585
            $this->line->product_type   = $type;
2586
            $this->line->special_code   = $this->special_code;
2587
            $this->line->origin         = $this->origin;
2588
            $this->line->fk_unit        = $fk_unit;
2589
2590
            $this->line->date_start     = $date_start;
2591
            $this->line->date_end       = $date_end;
2592
2593
            // Multicurrency
2594
            $this->line->fk_multicurrency			= $this->fk_multicurrency;
2595
            $this->line->multicurrency_code			= $this->multicurrency_code;
2596
            $this->line->multicurrency_subprice		= $pu_ht_devise;
2597
            $this->line->multicurrency_total_ht 	= $multicurrency_total_ht;
2598
            $this->line->multicurrency_total_tva 	= $multicurrency_total_tva;
2599
            $this->line->multicurrency_total_ttc 	= $multicurrency_total_ttc;
2600
2601
            $this->line->subprice=$pu_ht;
2602
            $this->line->price=$this->line->subprice;
1 ignored issue
show
Deprecated Code introduced by
The property CommonOrderLine::$price has been deprecated. ( Ignorable by Annotation )

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

2602
            /** @scrutinizer ignore-deprecated */ $this->line->price=$this->line->subprice;
Loading history...
2603
2604
            $this->line->remise_percent=$remise_percent;
2605
2606
            if (is_array($array_options) && count($array_options)>0) {
2607
                $this->line->array_options=$array_options;
2608
            }
2609
2610
            $result=$this->line->update($notrigger);
2611
2612
2613
            // Mise a jour info denormalisees au niveau facture
2614
            if ($result >= 0)
2615
            {
2616
                $this->update_price('', 'auto');
2617
				$this->db->commit();
2618
				return $result;
2619
            }
2620
            else
2621
            {
2622
                $this->error=$this->db->lasterror();
2623
                $this->db->rollback();
2624
                return -1;
2625
            }
2626
        }
2627
        else
2628
        {
2629
            $this->error="Order status makes operation forbidden";
2630
            dol_syslog(get_class($this)."::updateline ".$this->error, LOG_ERR);
2631
            return -2;
2632
        }
2633
    }
2634
2635
2636
    /**
2637
     *  Initialise an instance with random values.
2638
     *  Used to build previews or test instances.
2639
     *	id must be 0 if object instance is a specimen.
2640
     *
2641
     *  @return	void
2642
     */
2643
    public function initAsSpecimen()
2644
    {
2645
        global $user,$langs,$conf;
2646
2647
        include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
2648
2649
        dol_syslog(get_class($this)."::initAsSpecimen");
2650
2651
        $now=dol_now();
2652
2653
        // Find first product
2654
        $prodid=0;
2655
        $product=new ProductFournisseur($this->db);
2656
        $sql = "SELECT rowid";
2657
        $sql.= " FROM ".MAIN_DB_PREFIX."product";
2658
        $sql.= " WHERE entity IN (".getEntity('product').")";
2659
        $sql.=$this->db->order("rowid", "ASC");
2660
        $sql.=$this->db->plimit(1);
2661
        $resql = $this->db->query($sql);
2662
        if ($resql)
2663
        {
2664
            $obj = $this->db->fetch_object($resql);
2665
            $prodid = $obj->rowid;
2666
        }
2667
2668
        // Initialise parametres
2669
        $this->id=0;
2670
        $this->ref = 'SPECIMEN';
2671
        $this->specimen=1;
2672
        $this->socid = 1;
2673
        $this->date = $now;
2674
        $this->date_commande = $now;
2675
        $this->date_lim_reglement=$this->date+3600*24*30;
2676
        $this->cond_reglement_code = 'RECEP';
2677
        $this->mode_reglement_code = 'CHQ';
2678
        $this->note_public='This is a comment (public)';
2679
        $this->note_private='This is a comment (private)';
2680
        $this->statut=0;
2681
2682
        // Lines
2683
        $nbp = 5;
2684
        $xnbp = 0;
2685
        while ($xnbp < $nbp)
2686
        {
2687
            $line=new CommandeFournisseurLigne($this->db);
2688
            $line->desc=$langs->trans("Description")." ".$xnbp;
2689
            $line->qty=1;
2690
            $line->subprice=100;
2691
            $line->price=100;
1 ignored issue
show
Deprecated Code introduced by
The property CommonOrderLine::$price has been deprecated. ( Ignorable by Annotation )

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

2691
            /** @scrutinizer ignore-deprecated */ $line->price=100;
Loading history...
2692
            $line->tva_tx=19.6;
2693
            $line->localtax1_tx=0;
2694
            $line->localtax2_tx=0;
2695
            if ($xnbp == 2)
2696
            {
2697
                $line->total_ht=50;
2698
                $line->total_ttc=59.8;
2699
                $line->total_tva=9.8;
2700
                $line->remise_percent=50;
2701
            }
2702
            else
2703
            {
2704
                $line->total_ht=100;
2705
                $line->total_ttc=119.6;
2706
                $line->total_tva=19.6;
2707
                $line->remise_percent=00;
2708
            }
2709
            $line->fk_product=$prodid;
2710
2711
            $this->lines[$xnbp]=$line;
2712
2713
            $this->total_ht       += $line->total_ht;
2714
            $this->total_tva      += $line->total_tva;
2715
            $this->total_ttc      += $line->total_ttc;
2716
2717
            $xnbp++;
2718
        }
2719
    }
2720
2721
    /**
2722
     *	Charge les informations d'ordre info dans l'objet facture
2723
     *
2724
     *	@param  int		$id       	Id de la facture a charger
2725
     *	@return	void
2726
     */
2727
    public function info($id)
2728
    {
2729
        $sql = 'SELECT c.rowid, date_creation as datec, tms as datem, date_valid as date_validation, date_approve as datea, date_approve2 as datea2,';
2730
        $sql.= ' fk_user_author, fk_user_modif, fk_user_valid, fk_user_approve, fk_user_approve2';
2731
        $sql.= ' FROM '.MAIN_DB_PREFIX.'commande_fournisseur as c';
2732
        $sql.= ' WHERE c.rowid = '.$id;
2733
2734
        $result=$this->db->query($sql);
2735
        if ($result)
2736
        {
2737
            if ($this->db->num_rows($result))
2738
            {
2739
                $obj = $this->db->fetch_object($result);
2740
                $this->id = $obj->rowid;
2741
                if ($obj->fk_user_author)   $this->user_creation_id = $obj->fk_user_author;
2742
                if ($obj->fk_user_valid)    $this->user_validation_id = $obj->fk_user_valid;
2743
                if ($obj->fk_user_modif)    $this->user_modification_id =$obj->fk_user_modif;
2744
                if ($obj->fk_user_approve)  $this->user_approve_id = $obj->fk_user_approve;
2745
                if ($obj->fk_user_approve2) $this->user_approve_id2 = $obj->fk_user_approve2;
2746
2747
                $this->date_creation     = $this->db->idate($obj->datec);
2748
                $this->date_modification = $this->db->idate($obj->datem);
2749
                $this->date_approve      = $this->db->idate($obj->datea);
2750
                $this->date_approve2     = $this->db->idate($obj->datea2);
2751
                $this->date_validation   = $this->db->idate($obj->date_validation);
2752
            }
2753
            $this->db->free($result);
2754
        }
2755
        else
2756
        {
2757
            dol_print_error($this->db);
2758
        }
2759
    }
2760
2761
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2762
    /**
2763
     *	Charge indicateurs this->nb de tableau de bord
2764
     *
2765
     *	@return     int         <0 si ko, >0 si ok
2766
     */
2767
    public function load_state_board()
2768
    {
2769
        // phpcs:enable
2770
        global $conf, $user;
2771
2772
        $this->nb=array();
2773
        $clause = "WHERE";
2774
2775
        $sql = "SELECT count(co.rowid) as nb";
2776
        $sql.= " FROM ".MAIN_DB_PREFIX."commande_fournisseur as co";
2777
        $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON co.fk_soc = s.rowid";
2778
        if (!$user->rights->societe->client->voir && !$user->societe_id)
2779
        {
2780
            $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
2781
            $sql.= " WHERE sc.fk_user = " .$user->id;
2782
            $clause = "AND";
2783
        }
2784
        $sql.= " ".$clause." co.entity = ".$conf->entity;
2785
2786
        $resql=$this->db->query($sql);
2787
        if ($resql)
2788
        {
2789
            while ($obj=$this->db->fetch_object($resql))
2790
            {
2791
                $this->nb["supplier_orders"]=$obj->nb;
2792
            }
2793
            $this->db->free($resql);
2794
            return 1;
2795
        }
2796
        else
2797
        {
2798
            dol_print_error($this->db);
2799
            $this->error=$this->db->error();
2800
            return -1;
2801
        }
2802
    }
2803
2804
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2805
    /**
2806
     *	Load indicators for dashboard (this->nbtodo and this->nbtodolate)
2807
     *
2808
     *	@param          User	$user   Objet user
2809
     *	@return WorkboardResponse|int 	<0 if KO, WorkboardResponse if OK
2810
     */
2811
    public function load_board($user)
2812
    {
2813
        // phpcs:enable
2814
        global $conf, $langs;
2815
2816
        $clause = " WHERE";
2817
2818
        $sql = "SELECT c.rowid, c.date_creation as datec, c.date_commande, c.fk_statut, c.date_livraison as delivery_date";
2819
        $sql.= " FROM ".MAIN_DB_PREFIX."commande_fournisseur as c";
2820
        if (!$user->rights->societe->client->voir && !$user->societe_id)
1 ignored issue
show
Deprecated Code introduced by
The property User::$societe_id has been deprecated. ( Ignorable by Annotation )

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

2820
        if (!$user->rights->societe->client->voir && !/** @scrutinizer ignore-deprecated */ $user->societe_id)
Loading history...
2821
        {
2822
            $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON c.fk_soc = sc.fk_soc";
2823
            $sql.= " WHERE sc.fk_user = " .$user->id;
2824
            $clause = " AND";
2825
        }
2826
        $sql.= $clause." c.entity = ".$conf->entity;
2827
        $sql.= " AND c.fk_statut IN (".self::STATUS_VALIDATED.", ".self::STATUS_ACCEPTED.")";
2828
        if ($user->societe_id) $sql.=" AND c.fk_soc = ".$user->societe_id;
1 ignored issue
show
Deprecated Code introduced by
The property User::$societe_id has been deprecated. ( Ignorable by Annotation )

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

2828
        if (/** @scrutinizer ignore-deprecated */ $user->societe_id) $sql.=" AND c.fk_soc = ".$user->societe_id;
Loading history...
2829
2830
        $resql=$this->db->query($sql);
2831
        if ($resql)
2832
        {
2833
            $commandestatic = new CommandeFournisseur($this->db);
2834
2835
	        $response = new WorkboardResponse();
2836
	        $response->warning_delay=$conf->commande->fournisseur->warning_delay/60/60/24;
2837
	        $response->label=$langs->trans("SuppliersOrdersToProcess");
2838
	        $response->url=DOL_URL_ROOT.'/fourn/commande/list.php?statut=1,2,3&mainmenu=commercial&leftmenu=orders_suppliers';
2839
	        $response->img=img_object('', "order");
2840
2841
            while ($obj=$this->db->fetch_object($resql))
2842
            {
2843
                $response->nbtodo++;
2844
2845
                $commandestatic->date_livraison = $this->db->jdate($obj->delivery_date);
2846
                $commandestatic->date_commande = $this->db->jdate($obj->date_commande);
2847
                $commandestatic->statut = $obj->fk_statut;
2848
2849
                if ($commandestatic->hasDelay()) {
2850
	                $response->nbtodolate++;
2851
                }
2852
            }
2853
2854
            return $response;
2855
        }
2856
        else
2857
        {
2858
            $this->error=$this->db->error();
2859
            return -1;
2860
        }
2861
    }
2862
2863
    /**
2864
     * Returns the translated input method of object (defined if $this->methode_commande_id > 0).
2865
     * This function make a sql request to get translation. No cache yet, try to not use it inside a loop.
2866
     *
2867
     * @return string
2868
     */
2869
    public function getInputMethod()
2870
    {
2871
        global $db, $langs;
2872
2873
        if ($this->methode_commande_id > 0)
2874
        {
2875
            $sql = "SELECT rowid, code, libelle as label";
2876
            $sql.= " FROM ".MAIN_DB_PREFIX.'c_input_method';
2877
            $sql.= " WHERE active=1 AND rowid = ".$db->escape($this->methode_commande_id);
2878
2879
            $resql = $db->query($sql);
2880
            if ($resql)
2881
            {
2882
                if ($db->num_rows($query))
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $query seems to be never defined.
Loading history...
2883
                {
2884
                    $obj = $db->fetch_object($query);
2885
2886
                    $string = $langs->trans($obj->code);
2887
                    if ($string == $obj->code)
2888
                    {
2889
                        $string = $obj->label != '-' ? $obj->label : '';
2890
                    }
2891
                    return $string;
2892
                }
2893
            }
2894
            else dol_print_error($db);
2895
        }
2896
2897
        return '';
2898
    }
2899
2900
	/**
2901
	 *  Create a document onto disk according to template model.
2902
	 *
2903
	 *  @param	    string		$modele			Force template to use ('' to not force)
2904
	 *  @param		Translate	$outputlangs	Object lang to use for traduction
2905
	 *  @param      int			$hidedetails    Hide details of lines
2906
	 *  @param      int			$hidedesc       Hide description
2907
	 *  @param      int			$hideref        Hide ref
2908
     *  @param      null|array  $moreparams     Array to provide more information
2909
	 *  @return     int          				0 if KO, 1 if OK
2910
	 */
2911
	public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
2912
	{
2913
		global $conf, $langs;
2914
2915
		$langs->load("suppliers");
2916
2917
		if (! dol_strlen($modele)) {
2918
2919
			$modele = 'muscadet';
2920
2921
			if ($this->modelpdf) {
2922
				$modele = $this->modelpdf;
2923
			} elseif (! empty($conf->global->COMMANDE_SUPPLIER_ADDON_PDF)) {
2924
				$modele = $conf->global->COMMANDE_SUPPLIER_ADDON_PDF;
2925
			}
2926
		}
2927
2928
		$modelpath = "core/modules/supplier_order/pdf/";
2929
2930
		return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
2931
	}
2932
2933
	/**
2934
     * Return the max number delivery delay in day
2935
     *
2936
     * @param	Translate	$langs		Language object
2937
     * @return 	string                  Translated string
2938
     */
2939
    public function getMaxDeliveryTimeDay($langs)
2940
    {
2941
		if (empty($this->lines)) return '';
2942
2943
		$obj = new ProductFournisseur($this->db);
2944
2945
		$nb = 0;
2946
		foreach ($this->lines as $line)
2947
		{
2948
			if ($line->fk_product > 0)
2949
			{
2950
				$idp = $obj->find_min_price_product_fournisseur($line->fk_product, $line->qty);
2951
				if ($idp)
2952
				{
2953
					$obj->fetch($idp);
2954
					if ($obj->delivery_time_days > $nb) $nb = $obj->delivery_time_days;
2955
				}
2956
			}
2957
		}
2958
2959
		if ($nb === 0) return '';
2960
		else return $nb.' '.$langs->trans('Days');
2961
	}
2962
2963
	/**
2964
	 * Returns the rights used for this class
2965
	 * @return stdClass
2966
	 */
2967
	public function getRights()
2968
	{
2969
		global $user;
2970
2971
		return $user->rights->fournisseur->commande;
2972
	}
2973
2974
2975
	/**
2976
	 * Function used to replace a thirdparty id with another one.
2977
	 *
2978
	 * @param DoliDB $db Database handler
2979
	 * @param int $origin_id Old thirdparty id
2980
	 * @param int $dest_id New thirdparty id
2981
	 * @return bool
2982
	 */
2983
	public static function replaceThirdparty(DoliDB $db, $origin_id, $dest_id)
2984
	{
2985
		$tables = array(
2986
			'commande_fournisseur'
2987
		);
2988
2989
		return CommonObject::commonReplaceThirdparty($db, $origin_id, $dest_id, $tables);
2990
	}
2991
2992
    /**
2993
     * Is the supplier order delayed?
2994
     *
2995
     * @return bool
2996
     */
2997
    public function hasDelay()
2998
    {
2999
        global $conf;
3000
3001
        if (empty($this->date_delivery) && ! empty($this->date_livraison)) $this->date_delivery = $this->date_livraison;    // For backward compatibility
3002
3003
        $now = dol_now();
3004
        $date_to_test = empty($this->date_delivery) ? $this->date_commande : $this->date_delivery;
3005
3006
        return ($this->statut > 0 && $this->statut < 4) && $date_to_test && $date_to_test < ($now - $conf->commande->fournisseur->warning_delay);
3007
    }
3008
3009
    /**
3010
     * Show the customer delayed info
3011
     *
3012
     * @return string       Show delayed information
3013
     */
3014
    public function showDelay()
3015
    {
3016
        global $conf, $langs;
3017
3018
        if (empty($this->date_delivery) && ! empty($this->date_livraison)) $this->date_delivery = $this->date_livraison;    // For backward compatibility
3019
3020
        if (empty($this->date_delivery)) $text=$langs->trans("OrderDate").' '.dol_print_date($this->date_commande, 'day');
3021
        else $text=$text=$langs->trans("DeliveryDate").' '.dol_print_date($this->date_delivery, 'day');
3022
        $text.=' '.($conf->commande->fournisseur->warning_delay>0?'+':'-').' '.round(abs($conf->commande->fournisseur->warning_delay)/3600/24, 1).' '.$langs->trans("days").' < '.$langs->trans("Today");
3023
3024
        return $text;
3025
    }
3026
3027
3028
    /**
3029
     * Calc status regarding to dispatched stock
3030
     *
3031
     * @param 		User 	$user                   User action
3032
     * @param       int     $closeopenorder         Close if received
3033
     * @param		string	$comment				Comment
3034
     * @return		int		                        <0 if KO, 0 if not applicable, >0 if OK
3035
     */
3036
    public function calcAndSetStatusDispatch(User $user, $closeopenorder = 1, $comment = '')
3037
    {
3038
    	global $conf, $langs;
3039
3040
    	if (! empty($conf->fournisseur->enabled))
3041
    	{
3042
    		require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.dispatch.class.php';
3043
3044
    		$qtydelivered=array();
3045
    		$qtywished=array();
3046
3047
    		$supplierorderdispatch = new CommandeFournisseurDispatch($this->db);
3048
    		$filter=array('t.fk_commande'=>$this->id);
3049
    		if (! empty($conf->global->SUPPLIER_ORDER_USE_DISPATCH_STATUS)) {
3050
    			$filter['t.status']=1;	// Restrict to lines with status validated
3051
    		}
3052
3053
    		$ret=$supplierorderdispatch->fetchAll('', '', 0, 0, $filter);
3054
    		if ($ret<0)
3055
    		{
3056
    			$this->error=$supplierorderdispatch->error; $this->errors=$supplierorderdispatch->errors;
3057
    			return $ret;
3058
    		}
3059
    		else
3060
    		{
3061
    			if (is_array($supplierorderdispatch->lines) && count($supplierorderdispatch->lines)>0)
3062
    			{
3063
    				$date_liv = dol_now();
3064
3065
    				// Build array with quantity deliverd by product
3066
    				foreach($supplierorderdispatch->lines as $line) {
3067
    					$qtydelivered[$line->fk_product]+=$line->qty;
3068
    				}
3069
    				foreach($this->lines as $line) {
3070
    					$qtywished[$line->fk_product]+=$line->qty;
3071
    				}
3072
    				//Compare array
3073
    				$diff_array=array_diff_assoc($qtydelivered, $qtywished);		// Warning: $diff_array is done only on common keys.
3074
    				$keysinwishednotindelivered=array_diff(array_keys($qtywished), array_keys($qtydelivered));		// To check we also have same number of keys
3075
    				$keysindeliverednotinwished=array_diff(array_keys($qtydelivered), array_keys($qtywished));		// To check we also have same number of keys
3076
    				/*var_dump(array_keys($qtydelivered));
3077
    				var_dump(array_keys($qtywished));
3078
    				var_dump($diff_array);
3079
    				var_dump($keysinwishednotindelivered);
3080
    				var_dump($keysindeliverednotinwished);
3081
    				exit;*/
3082
3083
    				if (count($diff_array)==0 && count($keysinwishednotindelivered)==0 && count($keysindeliverednotinwished)==0) //No diff => mean everythings is received
3084
    				{
3085
    					if ($closeopenorder)
3086
    					{
3087
        					//$ret=$this->setStatus($user,5);
3088
    						$ret = $this->Livraison($user, $date_liv, 'tot', $comment);   // GETPOST("type") is 'tot', 'par', 'nev', 'can'
3089
        					if ($ret<0) {
3090
        						return -1;
3091
        					}
3092
    					    return 5;
3093
    					}
3094
    					else
3095
    					{
3096
    					    //Diff => received partially
3097
    					    //$ret=$this->setStatus($user,4);
3098
    						$ret = $this->Livraison($user, $date_liv, 'par', $comment);   // GETPOST("type") is 'tot', 'par', 'nev', 'can'
3099
    					    if ($ret<0) {
3100
    					        return -1;
3101
    					    }
3102
    					    return 4;
3103
    					}
3104
    				} elseif (! empty($conf->global->SUPPLIER_ORDER_MORE_THAN_WISHED) ) {
3105
                        //set livraison to 'tot' if more products received than wished. (and if $closeopenorder is set to 1 of course...)
3106
3107
					    $close=0;
3108
3109
					    if( count($diff_array) > 0 )
3110
					    {
3111
                            //there are some difference between  the two arrays
3112
3113
						    //scan the array of results
3114
						    foreach($diff_array as $key => $value)
3115
						    {
3116
                                //if the quantity delivered is greater or equal to wish quantity
3117
							    if($qtydelivered[$key] >= $qtywished[$key] )
3118
							    {
3119
								    $close++;
3120
							    }
3121
						    }
3122
					    }
3123
3124
3125
					    if($close == count($diff_array)) {
3126
                            //all the products are received equal or more than the wished quantity
3127
						    if ($closeopenorder) {
3128
    						    $ret = $this->Livraison($user, $date_liv, 'tot', $comment);   // GETPOST("type") is 'tot', 'par', 'nev', 'can'
3129
        					    if ($ret<0) {
3130
        						    return -1;
3131
        					    }
3132
    					        return 5;
3133
    					    }
3134
    					    else
3135
    					    {
3136
    					        //Diff => received partially
3137
    					        $ret = $this->Livraison($user, $date_liv, 'par', $comment);   // GETPOST("type") is 'tot', 'par', 'nev', 'can'
3138
							    if ($ret<0) {
3139
								    return -1;
3140
							    }
3141
							    return 4;
3142
						    }
3143
					    }
3144
					    else
3145
					    {
3146
                            //all the products are not received
3147
						    $ret = $this->Livraison($user, $date_liv, 'par', $comment);   // GETPOST("type") is 'tot', 'par', 'nev', 'can'
3148
						    if ($ret<0) {
3149
							    return -1;
3150
						    }
3151
						    return 4;
3152
					    }
3153
				    }
3154
    				else
3155
    				{
3156
    					//Diff => received partially
3157
    					$ret = $this->Livraison($user, $date_liv, 'par', $comment);   // GETPOST("type") is 'tot', 'par', 'nev', 'can'
3158
    					if ($ret<0) {
3159
    						return -1;
3160
    					}
3161
    					return 4;
3162
    				}
3163
    			}
3164
    			return 1;
3165
    		}
3166
    	}
3167
    	return 0;
3168
    }
3169
3170
    /**
3171
     *	Load array this->receptions of lines of shipments with nb of products sent for each order line
3172
     *  Note: For a dedicated shipment, the fetch_lines can be used to load the qty_asked and qty_shipped. This function is use to return qty_shipped cumulated for the order
3173
     *
3174
     *	@param      int		$filtre_statut      Filter on shipment status
3175
     * 	@return     int                			<0 if KO, Nb of lines found if OK
3176
     */
3177
    public function loadReceptions($filtre_statut = -1)
3178
    {
3179
        $this->receptions = array();
3180
3181
        $sql = 'SELECT cd.rowid, cd.fk_product,';
3182
        $sql.= ' sum(cfd.qty) as qty';
3183
        $sql.= ' FROM '.MAIN_DB_PREFIX.'commande_fournisseur_dispatch as cfd,';
3184
        if ($filtre_statut >= 0) $sql.= ' '.MAIN_DB_PREFIX.'reception as e,';
3185
        $sql.= ' '.MAIN_DB_PREFIX.'commande_fournisseurdet as cd';
3186
        $sql.= ' WHERE';
3187
        if ($filtre_statut >= 0) $sql.= ' cfd.fk_reception = e.rowid AND';
3188
        $sql.= ' cfd.fk_commandefourndet = cd.rowid';
3189
        $sql.= ' AND cd.fk_commande =' .$this->id;
3190
        if ($this->fk_product > 0) $sql.= ' AND cd.fk_product = '.$this->fk_product;
3191
        if ($filtre_statut >= 0) $sql.=' AND e.fk_statut >= '.$filtre_statut;
3192
        $sql.= ' GROUP BY cd.rowid, cd.fk_product';
3193
3194
3195
        dol_syslog(get_class($this)."::loadReceptions", LOG_DEBUG);
3196
        $result = $this->db->query($sql);
3197
        if ($result)
3198
        {
3199
            $num = $this->db->num_rows($result);
3200
            $i = 0;
3201
            while ($i < $num)
3202
            {
3203
                $obj = $this->db->fetch_object($result);
3204
                empty($this->receptions[$obj->rowid])?$this->receptions[$obj->rowid] = $obj->qty:$this->receptions[$obj->rowid] += $obj->qty;
3205
                $i++;
3206
            }
3207
            $this->db->free();
3208
3209
            return $num;
3210
        }
3211
        else
3212
        {
3213
            $this->error=$this->db->lasterror();
3214
            return -1;
3215
        }
3216
    }
3217
}
3218
3219
3220
3221
/**
3222
 *  Class to manage line orders
3223
 */
3224
class CommandeFournisseurLigne extends CommonOrderLine
3225
{
3226
    /**
3227
	 * @var string ID to identify managed object
3228
	 */
3229
	public $element='commande_fournisseurdet';
3230
3231
	/**
3232
	 * @var string Name of table without prefix where object is stored
3233
	 */
3234
	public $table_element='commande_fournisseurdet';
3235
3236
    public $oldline;
3237
3238
    /**
3239
     * Id of parent order
3240
     * @var int
3241
     */
3242
    public $fk_commande;
3243
3244
    // From llx_commande_fournisseurdet
3245
    /**
3246
     * @var int ID
3247
     */
3248
    public $fk_parent_line;
3249
3250
    /**
3251
     * @var int ID
3252
     */
3253
    public $fk_facture;
3254
3255
    /**
3256
     * @var string supplier order line label
3257
     */
3258
    public $label;
3259
3260
    public $rang = 0;
3261
    public $special_code = 0;
3262
3263
	/**
3264
	 * Unit price without taxes
3265
	 * @var float
3266
	 */
3267
	public $pu_ht;
3268
3269
    public $date_start;
3270
    public $date_end;
3271
3272
    // From llx_product_fournisseur_price
3273
3274
	/**
3275
	 * Supplier reference of price when we added the line. May have been changed after line was added.
3276
	 * @var string
3277
	 */
3278
    public $ref_supplier;
3279
    public $remise;
3280
    public $product_libelle;
3281
3282
3283
    /**
3284
     *	Constructor
3285
     *
3286
     *  @param		DoliDB		$db      Database handler
3287
     */
3288
    public function __construct($db)
3289
    {
3290
        $this->db= $db;
3291
    }
3292
3293
    /**
3294
     *  Load line order
3295
     *
3296
     *  @param  int		$rowid      Id line order
3297
     *	@return	int					<0 if KO, >0 if OK
3298
     */
3299
    public function fetch($rowid)
3300
    {
3301
        $sql = 'SELECT cd.rowid, cd.fk_commande, cd.fk_product, cd.product_type, cd.description, cd.qty, cd.tva_tx, cd.special_code,';
3302
        $sql.= ' cd.localtax1_tx, cd.localtax2_tx, cd.localtax1_type, cd.localtax2_type, cd.ref,';
3303
        $sql.= ' cd.remise, cd.remise_percent, cd.subprice,';
3304
        $sql.= ' cd.info_bits, cd.total_ht, cd.total_tva, cd.total_ttc,';
3305
        $sql.= ' cd.total_localtax1, cd.total_localtax2,';
3306
        $sql.= ' p.ref as product_ref, p.label as product_libelle, p.description as product_desc,';
3307
        $sql.= ' cd.date_start, cd.date_end, cd.fk_unit,';
3308
		$sql.= ' cd.multicurrency_subprice, cd.multicurrency_total_ht, cd.multicurrency_total_tva, cd.multicurrency_total_ttc';
3309
        $sql.= ' FROM '.MAIN_DB_PREFIX.'commande_fournisseurdet as cd';
3310
        $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON cd.fk_product = p.rowid';
3311
        $sql.= ' WHERE cd.rowid = '.$rowid;
3312
        $result = $this->db->query($sql);
3313
        if ($result)
3314
        {
3315
            $objp = $this->db->fetch_object($result);
3316
3317
            if (!empty($objp))
3318
            {
3319
                $this->rowid            = $objp->rowid;
3320
	            $this->id               = $objp->rowid;
3321
	            $this->fk_commande      = $objp->fk_commande;
3322
	            $this->desc             = $objp->description;
3323
	            $this->qty              = $objp->qty;
3324
	            $this->ref_fourn        = $objp->ref;
3325
	            $this->ref_supplier     = $objp->ref;
3326
	            $this->subprice         = $objp->subprice;
3327
	            $this->tva_tx           = $objp->tva_tx;
3328
	            $this->localtax1_tx		= $objp->localtax1_tx;
3329
	            $this->localtax2_tx		= $objp->localtax2_tx;
3330
	            $this->localtax1_type	= $objp->localtax1_type;
3331
	            $this->localtax2_type	= $objp->localtax2_type;
3332
	            $this->remise           = $objp->remise;
3333
	            $this->remise_percent   = $objp->remise_percent;
3334
	            $this->fk_product       = $objp->fk_product;
3335
	            $this->info_bits        = $objp->info_bits;
3336
	            $this->total_ht         = $objp->total_ht;
3337
	            $this->total_tva        = $objp->total_tva;
3338
    	        $this->total_localtax1	= $objp->total_localtax1;
3339
	            $this->total_localtax2	= $objp->total_localtax2;
3340
	            $this->total_ttc        = $objp->total_ttc;
3341
	            $this->product_type     = $objp->product_type;
3342
	            $this->special_code     = $objp->special_code;
3343
3344
    	        $this->ref	            = $objp->product_ref;
3345
	            $this->product_ref      = $objp->product_ref;
3346
	            $this->product_libelle  = $objp->product_libelle;
3347
	            $this->product_desc     = $objp->product_desc;
3348
3349
	            $this->date_start       		= $this->db->jdate($objp->date_start);
3350
	            $this->date_end         		= $this->db->jdate($objp->date_end);
3351
		        $this->fk_unit          		= $objp->fk_unit;
3352
3353
				$this->multicurrency_subprice	= $objp->multicurrency_subprice;
3354
				$this->multicurrency_total_ht	= $objp->multicurrency_total_ht;
3355
				$this->multicurrency_total_tva	= $objp->multicurrency_total_tva;
3356
				$this->multicurrency_total_ttc	= $objp->multicurrency_total_ttc;
3357
3358
				$this->fetch_optionals();
3359
3360
	            $this->db->free($result);
3361
    	        return 1;
3362
            }
3363
    	    else
3364
    	    {
3365
    	        $this->error='Supplier order line  with id='.$rowid.' not found';
3366
    	        dol_syslog(get_class($this)."::fetch Error ".$this->error, LOG_ERR);
3367
    	        return 0;
3368
    	    }
3369
        }
3370
        else
3371
        {
3372
            dol_print_error($this->db);
3373
            return -1;
3374
        }
3375
    }
3376
3377
    /**
3378
     *	Insert line into database
3379
     *
3380
     *	@param      int		$notrigger		1 = disable triggers
3381
     *	@return		int						<0 if KO, >0 if OK
3382
     */
3383
    public function insert($notrigger = 0)
3384
    {
3385
        global $conf, $user;
3386
3387
        $error=0;
3388
3389
        dol_syslog(get_class($this)."::insert rang=".$this->rang);
3390
3391
        // Clean parameters
3392
        if (empty($this->tva_tx)) $this->tva_tx=0;
3393
        if (empty($this->localtax1_tx)) $this->localtax1_tx=0;
3394
        if (empty($this->localtax2_tx)) $this->localtax2_tx=0;
3395
        if (empty($this->localtax1_type)) $this->localtax1_type='0';
3396
        if (empty($this->localtax2_type)) $this->localtax2_type='0';
3397
        if (empty($this->total_localtax1)) $this->total_localtax1=0;
3398
        if (empty($this->total_localtax2)) $this->total_localtax2=0;
3399
        if (empty($this->rang)) $this->rang=0;
3400
        if (empty($this->remise)) $this->remise=0;
3401
        if (empty($this->remise_percent)) $this->remise_percent=0;
3402
        if (empty($this->info_bits)) $this->info_bits=0;
3403
        if (empty($this->special_code)) $this->special_code=0;
3404
        if (empty($this->fk_parent_line)) $this->fk_parent_line=0;
3405
        if (empty($this->pa_ht)) $this->pa_ht=0;
3406
3407
        // Multicurrency
3408
        if (!empty($this->multicurrency_code)) list($this->fk_multicurrency,$this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code);
3409
        if (empty($this->fk_multicurrency))
3410
        {
3411
            $this->multicurrency_code = $conf->currency;
3412
            $this->fk_multicurrency = 0;
3413
            $this->multicurrency_tx = 1;
3414
        }
3415
3416
        // Check parameters
3417
        if ($this->product_type < 0) return -1;
3418
3419
        $this->db->begin();
3420
3421
        // Insertion dans base de la ligne
3422
        $sql = 'INSERT INTO '.MAIN_DB_PREFIX.$this->table_element;
3423
        $sql.= " (fk_commande, label, description, date_start, date_end,";
3424
        $sql.= " fk_product, product_type, special_code, rang,";
3425
        $sql.= " qty, vat_src_code, tva_tx, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type, remise_percent, subprice, ref,";
3426
        $sql.= " total_ht, total_tva, total_localtax1, total_localtax2, total_ttc, fk_unit,";
3427
        $sql.= " fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc";
3428
        $sql.= ")";
3429
        $sql.= " VALUES (".$this->fk_commande.", '" . $this->db->escape($this->label) . "','" . $this->db->escape($this->desc) . "',";
3430
        $sql.= " ".($this->date_start?"'".$this->db->idate($this->date_start)."'":"null").",";
3431
        $sql.= " ".($this->date_end?"'".$this->db->idate($this->date_end)."'":"null").",";
3432
        if ($this->fk_product) { $sql.= $this->fk_product.","; }
3433
        else { $sql.= "null,"; }
3434
        $sql.= "'".$this->db->escape($this->product_type)."',";
3435
        $sql.= "'".$this->db->escape($this->special_code)."',";
3436
        $sql.= "'".$this->db->escape($this->rang)."',";
3437
        $sql.= "'".$this->db->escape($this->qty)."', ";
3438
        $sql.= " ".(empty($this->vat_src_code)?"''":"'".$this->db->escape($this->vat_src_code)."'").",";
0 ignored issues
show
Bug Best Practice introduced by
The property vat_src_code does not exist on CommandeFournisseurLigne. Did you maybe forget to declare it?
Loading history...
3439
        $sql.= " ".$this->tva_tx.", ";
3440
        $sql.= " ".$this->localtax1_tx.",";
3441
        $sql.= " ".$this->localtax2_tx.",";
3442
        $sql.= " '".$this->db->escape($this->localtax1_type)."',";
3443
        $sql.= " '".$this->db->escape($this->localtax2_type)."',";
3444
        $sql.= " ".$this->remise_percent.", ".price2num($this->subprice, 'MU').", '".$this->db->escape($this->ref_supplier)."',";
3445
        $sql.= " ".price2num($this->total_ht).",";
3446
        $sql.= " ".price2num($this->total_tva).",";
3447
        $sql.= " ".price2num($this->total_localtax1).",";
3448
        $sql.= " ".price2num($this->total_localtax2).",";
3449
        $sql.= " ".price2num($this->total_ttc).",";
3450
        $sql.= ($this->fk_unit ? "'".$this->db->escape($this->fk_unit)."'":"null");
3451
        $sql.= ", ".($this->fk_multicurrency ? $this->fk_multicurrency : "null");
3452
        $sql.= ", '".$this->db->escape($this->multicurrency_code)."'";
3453
        $sql.= ", ".($this->multicurrency_subprice ? price2num($this->multicurrency_subprice) : '0');
3454
        $sql.= ", ".($this->multicurrency_total_ht ? price2num($this->multicurrency_total_ht) : '0');
3455
        $sql.= ", ".($this->multicurrency_total_tva ? price2num($this->multicurrency_total_tva) : '0');
3456
        $sql.= ", ".($this->multicurrency_total_ttc ? price2num($this->multicurrency_total_ttc) : '0');
3457
        $sql.= ")";
3458
3459
        dol_syslog(get_class($this)."::insert", LOG_DEBUG);
3460
        $resql=$this->db->query($sql);
3461
        if ($resql)
3462
        {
3463
            $this->id=$this->db->last_insert_id(MAIN_DB_PREFIX.$this->table_element);
3464
            $this->rowid =$this->id;
3465
3466
            if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) // For avoid conflicts if trigger used
3467
            {
3468
                $result=$this->insertExtraFields();
3469
                if ($result < 0)
3470
                {
3471
                    $error++;
3472
                }
3473
            }
3474
3475
            if (! $error && ! $notrigger)
3476
            {
3477
                // Call trigger
3478
                $result=$this->call_trigger('LINEORDER_SUPPLIER_CREATE', $user);
3479
                if ($result < 0) $error++;
3480
                // End call triggers
3481
            }
3482
3483
            if (!$error) {
3484
                $this->db->commit();
3485
                return 1;
3486
            }
3487
3488
            foreach($this->errors as $errmsg)
3489
            {
3490
                dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
3491
                $this->errors[]=($this->errors?', '.$errmsg:$errmsg);
3492
            }
3493
            $this->db->rollback();
3494
            return -1*$error;
3495
        }
3496
        else
3497
        {
3498
            $this->errors[]=$this->db->error();
3499
            $this->db->rollback();
3500
            return -2;
3501
        }
3502
    }
3503
    /**
3504
     *	Update the line object into db
3505
     *
3506
     *	@param      int		$notrigger		1 = disable triggers
3507
     *	@return		int		<0 si ko, >0 si ok
3508
     */
3509
    public function update($notrigger = 0)
3510
    {
3511
        global $conf,$user;
3512
3513
        $error=0;
3514
3515
        // Mise a jour ligne en base
3516
        $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET";
3517
        $sql.= "  description='".$this->db->escape($this->desc)."'";
3518
        $sql.= ", ref='".$this->db->escape($this->ref_supplier)."'";
3519
        $sql.= ", subprice='".price2num($this->subprice)."'";
3520
        //$sql.= ",remise='".price2num($remise)."'";
3521
        $sql.= ", remise_percent='".price2num($this->remise_percent)."'";
3522
3523
		$sql.= ", vat_src_code = '".(empty($this->vat_src_code)?'':$this->vat_src_code)."'";
0 ignored issues
show
Bug Best Practice introduced by
The property vat_src_code does not exist on CommandeFournisseurLigne. Did you maybe forget to declare it?
Loading history...
3524
        $sql.= ", tva_tx='".price2num($this->tva_tx)."'";
3525
        $sql.= ", localtax1_tx='".price2num($this->total_localtax1)."'";
3526
        $sql.= ", localtax2_tx='".price2num($this->total_localtax2)."'";
3527
        $sql.= ", localtax1_type='".$this->db->escape($this->localtax1_type)."'";
3528
        $sql.= ", localtax2_type='".$this->db->escape($this->localtax2_type)."'";
3529
        $sql.= ", qty='".price2num($this->qty)."'";
3530
        $sql.= ", date_start=".(! empty($this->date_start)?"'".$this->db->idate($this->date_start)."'":"null");
3531
        $sql.= ", date_end=".(! empty($this->date_end)?"'".$this->db->idate($this->date_end)."'":"null");
3532
        $sql.= ", info_bits='".$this->db->escape($this->info_bits)."'";
3533
        $sql.= ", total_ht='".price2num($this->total_ht)."'";
3534
        $sql.= ", total_tva='".price2num($this->total_tva)."'";
3535
        $sql.= ", total_localtax1='".price2num($this->total_localtax1)."'";
3536
        $sql.= ", total_localtax2='".price2num($this->total_localtax2)."'";
3537
        $sql.= ", total_ttc='".price2num($this->total_ttc)."'";
3538
        $sql.= ", product_type=".$this->product_type;
3539
        $sql.= ", special_code=".(!empty($this->special_code) ? $this->special_code : 0);
3540
        $sql.= ($this->fk_unit ? ", fk_unit='".$this->db->escape($this->fk_unit)."'":", fk_unit=null");
3541
3542
        // Multicurrency
3543
        $sql.= ", multicurrency_subprice=".price2num($this->multicurrency_subprice)."";
3544
        $sql.= ", multicurrency_total_ht=".price2num($this->multicurrency_total_ht)."";
3545
        $sql.= ", multicurrency_total_tva=".price2num($this->multicurrency_total_tva)."";
3546
        $sql.= ", multicurrency_total_ttc=".price2num($this->multicurrency_total_ttc)."";
3547
3548
        $sql.= " WHERE rowid = ".$this->id;
3549
3550
        dol_syslog(get_class($this)."::updateline", LOG_DEBUG);
3551
        $result = $this->db->query($sql);
3552
        if ($result > 0)
3553
        {
3554
            if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) // For avoid conflicts if trigger used
3555
            {
3556
                $result=$this->insertExtraFields();
3557
                if ($result < 0)
3558
                {
3559
                    $error++;
3560
                }
3561
            }
3562
3563
            if (! $error && ! $notrigger)
3564
            {
3565
                global $user;
3566
                // Call trigger
3567
                $result=$this->call_trigger('LINEORDER_SUPPLIER_UPDATE', $user);
3568
                if ($result < 0)
3569
                {
3570
                    $this->db->rollback();
3571
                    return -1;
3572
                }
3573
                // End call triggers
3574
            }
3575
3576
            if (! $error)
3577
            {
3578
                $this->db->commit();
3579
                return $result;
3580
            }
3581
            else
3582
            {
3583
                $this->db->rollback();
3584
                return -1;
3585
            }
3586
        }
3587
        else
3588
        {
3589
            $this->error=$this->db->lasterror();
3590
            $this->db->rollback();
3591
            return -1;
3592
        }
3593
    }
3594
3595
    /**
3596
     * 	Delete line in database
3597
     *
3598
     *	@param      int     $notrigger  1=Disable call to triggers
3599
     *	@return     int                 <0 if KO, >0 if OK
3600
     */
3601
    public function delete($notrigger)
3602
    {
3603
        global $user;
3604
3605
        $error=0;
3606
3607
        $this->db->begin();
3608
3609
        $sql = 'DELETE FROM '.MAIN_DB_PREFIX."commande_fournisseurdet WHERE rowid=".$this->rowid;
1 ignored issue
show
Deprecated Code introduced by
The property CommonObjectLine::$rowid has been deprecated: Try to use id property as possible (even if field into database is still rowid) ( Ignorable by Annotation )

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

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

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

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

Loading history...
3610
3611
        dol_syslog(__METHOD__, LOG_DEBUG);
3612
        $resql=$this->db->query($sql);
3613
        if ($resql)
3614
        {
3615
3616
            if (!$notrigger)
3617
            {
3618
                // Call trigger
3619
                $result=$this->call_trigger('LINEORDER_SUPPLIER_DELETE', $user);
3620
                if ($result < 0) $error++;
3621
                // End call triggers
3622
            }
3623
3624
            if (!$error)
3625
            {
3626
                $this->db->commit();
3627
                return 1;
3628
            }
3629
3630
            foreach($this->errors as $errmsg)
3631
            {
3632
                dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
3633
                $this->error.=($this->error?', '.$errmsg:$errmsg);
3634
            }
3635
            $this->db->rollback();
3636
            return -1*$error;
3637
        }
3638
        else
3639
        {
3640
            $this->error=$this->db->lasterror();
3641
            return -1;
3642
        }
3643
    }
3644
}
3645