Passed
Branch develop (f6c1a1)
by
unknown
28:54
created

Commande::updateline()   F

Complexity

Conditions 28
Paths > 20000

Size

Total Lines 185
Code Lines 122

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 28
eloc 122
nc 25729
nop 23
dl 0
loc 185
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity    Many Parameters   

Long Method

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

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

Commonly applied refactorings include:

Many Parameters

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

There are several approaches to avoid long parameter lists:

1
<?php
2
/* Copyright (C) 2003-2006 Rodolphe Quiedeville <[email protected]>
3
 * Copyright (C) 2004-2012 Laurent Destailleur  <[email protected]>
4
 * Copyright (C) 2005-2014 Regis Houssin        <[email protected]>
5
 * Copyright (C) 2006      Andre Cianfarani     <[email protected]>
6
 * Copyright (C) 2010-2016 Juanjo Menent        <[email protected]>
7
 * Copyright (C) 2011      Jean Heimburger      <[email protected]>
8
 * Copyright (C) 2012-2014 Christophe Battarel  <[email protected]>
9
 * Copyright (C) 2012      Cedric Salvador      <[email protected]>
10
 * Copyright (C) 2013      Florian Henry		<[email protected]>
11
 * Copyright (C) 2014-2015 Marcos García        <[email protected]>
12
 * Copyright (C) 2018      Nicolas ZABOURI	<[email protected]>
13
 * Copyright (C) 2016-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 <https://www.gnu.org/licenses/>.
27
 */
28
29
/**
30
 *  \file       htdocs/commande/class/commande.class.php
31
 *  \ingroup    commande
32
 *  \brief      Fichier des classes de commandes
33
 */
34
include_once DOL_DOCUMENT_ROOT.'/core/class/commonorder.class.php';
35
require_once DOL_DOCUMENT_ROOT.'/core/class/commonobjectline.class.php';
36
require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
37
require_once DOL_DOCUMENT_ROOT.'/margin/lib/margins.lib.php';
38
require_once DOL_DOCUMENT_ROOT.'/multicurrency/class/multicurrency.class.php';
39
40
/**
41
 *  Class to manage customers orders
42
 */
43
class Commande extends CommonOrder
44
{
45
	/**
46
	 * @var string ID to identify managed object
47
	 */
48
	public $element = 'commande';
49
50
	/**
51
	 * @var string Name of table without prefix where object is stored
52
	 */
53
	public $table_element = 'commande';
54
55
	/**
56
	 * @var string Name of subtable line
57
	 */
58
	public $table_element_line = 'commandedet';
59
60
	/**
61
	 * @var string Name of class line
62
	 */
63
	public $class_element_line = 'OrderLine';
64
65
	/**
66
	 * @var string Field name with ID of parent key if this field has a parent
67
	 */
68
	public $fk_element = 'fk_commande';
69
70
	/**
71
	 * @var string String with name of icon for commande class. Here is object_order.png
72
	 */
73
	public $picto = 'order';
74
75
	/**
76
	 * 0=No test on entity, 1=Test with field entity, 2=Test with link by societe
77
	 * @var int
78
	 */
79
	public $ismultientitymanaged = 1;
80
81
	/**
82
	 * 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
83
	 * @var integer
84
	 */
85
	public $restrictiononfksoc = 1;
86
87
	/**
88
	 * {@inheritdoc}
89
	 */
90
	protected $table_ref_field = 'ref';
91
92
	/**
93
	 * @var int Thirparty ID
94
	 */
95
	public $socid;
96
97
	/**
98
	 * @var string Thirparty ref of order
99
	 */
100
	public $ref_client;
101
102
    /**
103
	 * @var string Internal ref for order
104
	 * @deprecated
105
	 */
106
	public $ref_int;
107
108
    /**
109
	 * @var int Contact ID
110
	 */
111
	public $contactid;
112
113
	/**
114
	 * Status of the order
115
	 * @var int
116
	 */
117
	public $statut;
118
119
	/**
120
	 * @var int Status Billed or not
121
	 */
122
	public $billed;
123
124
    /**
125
     * @var int Draft Status of the order
126
     */
127
    public $brouillon;
128
    public $cond_reglement_code;
129
130
	/**
131
     * @var int bank account ID
132
     */
133
	public $fk_account;
134
135
	/**
136
	 * @var string It holds the label of the payment mode. Use it in case translation cannot be found.
137
	 */
138
	public $mode_reglement;
139
140
	/**
141
	 * @var int Payment mode id
142
	 */
143
	public $mode_reglement_id;
144
145
	/**
146
	 * @var string Payment mode code
147
	 */
148
	public $mode_reglement_code;
149
150
	/**
151
	 * Availability delivery time id
152
	 * @var int
153
	 */
154
	public $availability_id;
155
156
	/**
157
	 * Availability delivery time code
158
	 * @var string
159
	 */
160
	public $availability_code;
161
162
	/**
163
	 * Label of availability delivery time. Use it in case translation cannot be found.
164
	 * @var string
165
	 */
166
	public $availability;
167
168
	public $demand_reason_id; // Source reason. Why we receive order (after a phone campaign, ...)
169
	public $demand_reason_code;
170
	/**
171
     * @var int Date of order
172
	 */
173
	public $date;
174
175
	/**
176
     * @var int Date of order
177
	 * @deprecated
178
	 * @see $date
179
	 */
180
	public $date_commande;
181
182
	public $date_livraison; // Date expected of shipment (date starting shipment, not the reception that occurs some days after)
183
184
	/**
185
     * @var int ID
186
     */
187
	public $fk_remise_except;
188
189
	public $remise_percent;
190
	public $remise_absolue;
191
	public $info_bits;
192
	public $rang;
193
	public $special_code;
194
	public $source; // Order mode. How we received order (by phone, by email, ...)
195
	public $extraparams = array();
196
197
	public $linked_objects = array();
198
199
	/**
200
     * @var int User author ID
201
     */
202
	public $user_author_id;
203
204
    /**
205
     * @var int User validator ID
206
     */
207
	public $user_valid;
208
209
	/**
210
	 * @var OrderLine[]
211
	 */
212
	public $lines = array();
213
214
	// Multicurrency
215
	/**
216
     * @var int Currency ID
217
     */
218
	public $fk_multicurrency;
219
220
	public $multicurrency_code;
221
	public $multicurrency_tx;
222
	public $multicurrency_total_ht;
223
	public $multicurrency_total_tva;
224
	public $multicurrency_total_ttc;
225
226
    /**
227
     * @var Commande clone of order object
228
     */
229
	public $oldcopy;
230
231
	//! key of module source when order generated from a dedicated module ('cashdesk', 'takepos', ...)
232
	public $module_source;
233
	//! key of pos source ('0', '1', ...)
234
	public $pos_source;
235
236
237
	/**
238
	 *  'type' if the field format ('integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter]]', 'varchar(x)', 'double(24,8)', 'real', 'price', 'text', 'html', 'date', 'datetime', 'timestamp', 'duration', 'mail', 'phone', 'url', 'password')
239
	 *         Note: Filter can be a string like "(t.ref:like:'SO-%') or (t.date_creation:<:'20160101') or (t.nature:is:NULL)"
240
	 *  'label' the translation key.
241
	 *  'enabled' is a condition when the field must be managed.
242
	 *  'position' is the sort order of field.
243
	 *  'notnull' is set to 1 if not null in database. Set to -1 if we must set data to null if empty ('' or 0).
244
	 *  'visible' says if field is visible in list (Examples: 0=Not visible, 1=Visible on list and create/update/view forms, 2=Visible on list only, 3=Visible on create/update/view form only (not list), 4=Visible on list and update/view form only (not create). 5=Visible on list and view only (not create/not update). Using a negative value means field is not shown by default on list but can be selected for viewing)
245
	 *  'noteditable' says if field is not editable (1 or 0)
246
	 *  'default' is a default value for creation (can still be overwrote by the Setup of Default Values if field is editable in creation form). Note: If default is set to '(PROV)' and field is 'ref', the default value will be set to '(PROVid)' where id is rowid when a new record is created.
247
	 *  'index' if we want an index in database.
248
	 *  'foreignkey'=>'tablename.field' if the field is a foreign key (it is recommanded to name the field fk_...).
249
	 *  'searchall' is 1 if we want to search in this field when making a search from the quick search button.
250
	 *  'isameasure' must be set to 1 if you want to have a total on list for this field. Field type must be summable like integer or double(24,8).
251
	 *  'css' is the CSS style to use on field. For example: 'maxwidth200'
252
	 *  'help' is a string visible as a tooltip on field
253
	 *  'showoncombobox' if value of the field must be visible into the label of the combobox that list record
254
	 *  'disabled' is 1 if we want to have the field locked by a 'disabled' attribute. In most cases, this is never set into the definition of $fields into class, but is set dynamically by some part of code.
255
	 *  'arraykeyval' to set list of value if type is a list of predefined values. For example: array("0"=>"Draft","1"=>"Active","-1"=>"Cancel")
256
	 *  'comment' is not used. You can store here any text of your choice. It is not used by application.
257
	 *
258
	 *  Note: To have value dynamic, you can set value to 0 in definition and edit the value on the fly into the constructor.
259
	 */
260
261
	// BEGIN MODULEBUILDER PROPERTIES
262
	/**
263
	 * @var array  Array with all fields and their property. Do not use it as a static var. It may be modified by constructor.
264
	 */
265
	public $fields = array(
266
		'rowid' =>array('type'=>'integer', 'label'=>'TechnicalID', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>10),
267
		'entity' =>array('type'=>'integer', 'label'=>'Entity', 'default'=>1, 'enabled'=>1, 'visible'=>-2, 'notnull'=>1, 'position'=>20, 'index'=>1),
268
		'ref' =>array('type'=>'varchar(30)', 'label'=>'Ref', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'showoncombobox'=>1, 'position'=>25),
269
		'ref_ext' =>array('type'=>'varchar(255)', 'label'=>'RefExt', 'enabled'=>1, 'visible'=>0, 'position'=>26),
270
		'ref_int' =>array('type'=>'varchar(255)', 'label'=>'RefInt', 'enabled'=>1, 'visible'=>0, 'position'=>27), // deprecated
271
		'ref_client' =>array('type'=>'varchar(255)', 'label'=>'RefCustomer', 'enabled'=>1, 'visible'=>-1, 'position'=>28),
272
		'fk_soc' =>array('type'=>'integer:Societe:societe/class/societe.class.php', 'label'=>'ThirdParty', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>20),
273
		'fk_projet' =>array('type'=>'integer:Project:projet/class/project.class.php:1:fk_statut=1', 'label'=>'Project', 'enabled'=>1, 'visible'=>-1, 'position'=>25),
274
		'date_creation' =>array('type'=>'datetime', 'label'=>'DateCreation', 'enabled'=>1, 'visible'=>-1, 'position'=>55),
275
		'tms' =>array('type'=>'timestamp', 'label'=>'DateModification', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>56),
276
		'date_valid' =>array('type'=>'datetime', 'label'=>'DateValidation', 'enabled'=>1, 'visible'=>-1, 'position'=>60),
277
		'date_cloture' =>array('type'=>'datetime', 'label'=>'DateClosing', 'enabled'=>1, 'visible'=>-1, 'position'=>65),
278
		'date_commande' =>array('type'=>'date', 'label'=>'Date', 'enabled'=>1, 'visible'=>-1, 'position'=>70),
279
		'fk_user_author' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserAuthor', 'enabled'=>1, 'visible'=>-1, 'position'=>75),
280
		'fk_user_modif' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserModif', 'enabled'=>1, 'visible'=>-2, 'notnull'=>-1, 'position'=>80),
281
		'fk_user_valid' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserValidation', 'enabled'=>1, 'visible'=>-1, 'position'=>85),
282
		'fk_user_cloture' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserClosing', 'enabled'=>1, 'visible'=>-1, 'position'=>90),
283
		'source' =>array('type'=>'smallint(6)', 'label'=>'Source', 'enabled'=>1, 'visible'=>-1, 'position'=>95),
284
		//'amount_ht' =>array('type'=>'double(24,8)', 'label'=>'AmountHT', 'enabled'=>1, 'visible'=>-1, 'position'=>105),
285
		'remise_percent' =>array('type'=>'double', 'label'=>'RelativeDiscount', 'enabled'=>1, 'visible'=>-1, 'position'=>110),
286
		'remise_absolue' =>array('type'=>'double', 'label'=>'CustomerRelativeDiscount', 'enabled'=>1, 'visible'=>-1, 'position'=>115),
287
		//'remise' =>array('type'=>'double', 'label'=>'Remise', 'enabled'=>1, 'visible'=>-1, 'position'=>120),
288
		'tva' =>array('type'=>'double(24,8)', 'label'=>'VAT', 'enabled'=>1, 'visible'=>-1, 'position'=>125, 'isameasure'=>1),
289
		'localtax1' =>array('type'=>'double(24,8)', 'label'=>'LocalTax1', 'enabled'=>1, 'visible'=>-1, 'position'=>130, 'isameasure'=>1),
290
		'localtax2' =>array('type'=>'double(24,8)', 'label'=>'LocalTax2', 'enabled'=>1, 'visible'=>-1, 'position'=>135, 'isameasure'=>1),
291
		'total_ht' =>array('type'=>'double(24,8)', 'label'=>'TotalHT', 'enabled'=>1, 'visible'=>-1, 'position'=>140, 'isameasure'=>1),
292
		'total_ttc' =>array('type'=>'double(24,8)', 'label'=>'TotalTTC', 'enabled'=>1, 'visible'=>-1, 'position'=>145, 'isameasure'=>1),
293
		'note_private' =>array('type'=>'text', 'label'=>'NotePublic', 'enabled'=>1, 'visible'=>0, 'position'=>150),
294
		'note_public' =>array('type'=>'text', 'label'=>'NotePrivate', 'enabled'=>1, 'visible'=>0, 'position'=>155),
295
		'model_pdf' =>array('type'=>'varchar(255)', 'label'=>'PDFTemplate', 'enabled'=>1, 'visible'=>0, 'position'=>160),
296
		//'facture' =>array('type'=>'tinyint(4)', 'label'=>'ParentInvoice', 'enabled'=>1, 'visible'=>-1, 'position'=>165),
297
		'fk_account' =>array('type'=>'integer', 'label'=>'BankAccount', 'enabled'=>1, 'visible'=>-1, 'position'=>170),
298
		'fk_currency' =>array('type'=>'varchar(3)', 'label'=>'MulticurrencyID', 'enabled'=>1, 'visible'=>-1, 'position'=>175),
299
		'fk_cond_reglement' =>array('type'=>'integer', 'label'=>'PaymentTerm', 'enabled'=>1, 'visible'=>-1, 'position'=>180),
300
		'fk_mode_reglement' =>array('type'=>'integer', 'label'=>'PaymentMode', 'enabled'=>1, 'visible'=>-1, 'position'=>185),
301
		'date_livraison' =>array('type'=>'date', 'label'=>'DateDeliveryPlanned', 'enabled'=>1, 'visible'=>-1, 'position'=>190),
302
		'fk_shipping_method' =>array('type'=>'integer', 'label'=>'ShippingMethod', 'enabled'=>1, 'visible'=>-1, 'position'=>195),
303
		'fk_warehouse' =>array('type'=>'integer:Entrepot:product/stock/class/entrepot.class.php', 'label'=>'Fk warehouse', 'enabled'=>1, 'visible'=>-1, 'position'=>200),
304
		'fk_availability' =>array('type'=>'integer', 'label'=>'Availability', 'enabled'=>1, 'visible'=>-1, 'position'=>205),
305
		'fk_input_reason' =>array('type'=>'integer', 'label'=>'InputReason', 'enabled'=>1, 'visible'=>-1, 'position'=>210),
306
		//'fk_delivery_address' =>array('type'=>'integer', 'label'=>'DeliveryAddress', 'enabled'=>1, 'visible'=>-1, 'position'=>215),
307
		'extraparams' =>array('type'=>'varchar(255)', 'label'=>'Extraparams', 'enabled'=>1, 'visible'=>-1, 'position'=>225),
308
		'fk_incoterms' =>array('type'=>'integer', 'label'=>'IncotermCode', 'enabled'=>'$conf->incoterm->enabled', 'visible'=>-1, 'position'=>230),
309
		'location_incoterms' =>array('type'=>'varchar(255)', 'label'=>'IncotermLabel', 'enabled'=>'$conf->incoterm->enabled', 'visible'=>-1, 'position'=>235),
310
		'fk_multicurrency' =>array('type'=>'integer', 'label'=>'Fk multicurrency', 'enabled'=>'$conf->multicurrency->enabled', 'visible'=>-1, 'position'=>240),
311
		'multicurrency_code' =>array('type'=>'varchar(255)', 'label'=>'MulticurrencyCurrency', 'enabled'=>'$conf->multicurrency->enabled', 'visible'=>-1, 'position'=>245),
312
		'multicurrency_tx' =>array('type'=>'double(24,8)', 'label'=>'MulticurrencyRate', 'enabled'=>'$conf->multicurrency->enabled', 'visible'=>-1, 'position'=>250, 'isameasure'=>1),
313
		'multicurrency_total_ht' =>array('type'=>'double(24,8)', 'label'=>'MulticurrencyAmountHT', 'enabled'=>'$conf->multicurrency->enabled', 'visible'=>-1, 'position'=>255, 'isameasure'=>1),
314
		'multicurrency_total_tva' =>array('type'=>'double(24,8)', 'label'=>'MulticurrencyAmountVAT', 'enabled'=>'$conf->multicurrency->enabled', 'visible'=>-1, 'position'=>260, 'isameasure'=>1),
315
		'multicurrency_total_ttc' =>array('type'=>'double(24,8)', 'label'=>'MulticurrencyAmountTTC', 'enabled'=>'$conf->multicurrency->enabled', 'visible'=>-1, 'position'=>265, 'isameasure'=>1),
316
		'last_main_doc' =>array('type'=>'varchar(255)', 'label'=>'LastMainDoc', 'enabled'=>1, 'visible'=>-1, 'position'=>270),
317
		'module_source' =>array('type'=>'varchar(32)', 'label'=>'POSModule', 'enabled'=>1, 'visible'=>-1, 'position'=>275),
318
		'pos_source' =>array('type'=>'varchar(32)', 'label'=>'POSTerminal', 'enabled'=>1, 'visible'=>-1, 'position'=>280),
319
		'fk_statut' =>array('type'=>'smallint(6)', 'label'=>'Status', 'enabled'=>1, 'visible'=>-1, 'position'=>500),
320
		'import_key' =>array('type'=>'varchar(14)', 'label'=>'ImportId', 'enabled'=>1, 'visible'=>-2, 'position'=>900),
321
	);
322
	// END MODULEBUILDER PROPERTIES
323
324
	/**
325
	 * ERR Not enough stock
326
	 */
327
	const STOCK_NOT_ENOUGH_FOR_ORDER = -3;
328
329
	/**
330
	 * Canceled status
331
	 */
332
	const STATUS_CANCELED = -1;
333
	/**
334
	 * Draft status
335
	 */
336
	const STATUS_DRAFT = 0;
337
	/**
338
	 * Validated status
339
	 */
340
	const STATUS_VALIDATED = 1;
341
	/**
342
	 * Shipment on process
343
	 */
344
	const STATUS_SHIPMENTONPROCESS = 2;
345
	const STATUS_ACCEPTED = 2; // For backward compatibility. Use key STATUS_SHIPMENTONPROCESS instead.
346
347
	/**
348
	 * Closed (Sent, billed or not)
349
	 */
350
	const STATUS_CLOSED = 3;
351
352
353
	/**
354
	 *	Constructor
355
	 *
356
	 *  @param		DoliDB		$db      Database handler
357
	 */
358
	public function __construct($db)
359
	{
360
		$this->db = $db;
361
362
		$this->remise = 0;
363
		$this->remise_percent = 0;
364
365
		$this->products = array();
366
	}
367
368
	/**
369
	 *  Returns the reference to the following non used Order depending on the active numbering module
370
	 *  defined into COMMANDE_ADDON
371
	 *
372
	 *  @param	Societe		$soc  	Object thirdparty
373
	 *  @return string      		Order free reference
374
	 */
375
	public function getNextNumRef($soc)
376
	{
377
		global $langs, $conf;
378
		$langs->load("order");
379
380
		if (!empty($conf->global->COMMANDE_ADDON))
381
		{
382
			$mybool = false;
383
384
			$file = $conf->global->COMMANDE_ADDON.".php";
385
			$classname = $conf->global->COMMANDE_ADDON;
386
387
			// Include file with class
388
			$dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
389
			foreach ($dirmodels as $reldir)
390
			{
391
				$dir = dol_buildpath($reldir."core/modules/commande/");
392
393
				// Load file with numbering class (if found)
394
				$mybool |= @include_once $dir.$file;
395
			}
396
397
            if ($mybool === false)
398
            {
399
                dol_print_error('', "Failed to include file ".$file);
400
                return '';
401
            }
402
403
			$obj = new $classname();
404
			$numref = $obj->getNextValue($soc, $this);
405
406
			if ($numref != "")
407
			{
408
				return $numref;
409
			} else {
410
				$this->error = $obj->error;
411
				//dol_print_error($this->db,get_class($this)."::getNextNumRef ".$obj->error);
412
				return "";
413
			}
414
		} else {
415
			print $langs->trans("Error")." ".$langs->trans("Error_COMMANDE_ADDON_NotDefined");
416
			return "";
417
		}
418
	}
419
420
421
	/**
422
	 *	Validate order
423
	 *
424
	 *	@param		User	$user     		User making status change
425
	 *	@param		int		$idwarehouse	Id of warehouse to use for stock decrease
426
	 *  @param		int		$notrigger		1=Does not execute triggers, 0= execute triggers
427
	 *	@return  	int						<=0 if OK, 0=Nothing done, >0 if KO
428
	 */
429
	public function valid($user, $idwarehouse = 0, $notrigger = 0)
430
	{
431
		global $conf, $langs;
432
433
		require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
434
435
		$error = 0;
436
437
		// Protection
438
		if ($this->statut == self::STATUS_VALIDATED)
439
		{
440
			dol_syslog(get_class($this)."::valid action abandonned: already validated", LOG_WARNING);
441
			return 0;
442
		}
443
444
		if (!((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->commande->creer))
445
			|| (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->commande->order_advance->validate))))
446
		{
447
			$this->error = 'NotEnoughPermissions';
448
			dol_syslog(get_class($this)."::valid ".$this->error, LOG_ERR);
449
			return -1;
450
		}
451
452
		$now = dol_now();
453
454
		$this->db->begin();
455
456
		// Definition du nom de module de numerotation de commande
457
		$soc = new Societe($this->db);
458
		$soc->fetch($this->socid);
459
460
		// Class of company linked to order
461
		$result = $soc->set_as_client();
462
463
		// Define new ref
464
		if (!$error && (preg_match('/^[\(]?PROV/i', $this->ref) || empty($this->ref))) // empty should not happened, but when it occurs, the test save life
465
		{
466
			$num = $this->getNextNumRef($soc);
467
		} else {
468
			$num = $this->ref;
469
		}
470
		$this->newref = dol_sanitizeFileName($num);
471
472
		// Validate
473
		$sql = "UPDATE ".MAIN_DB_PREFIX."commande";
474
		$sql .= " SET ref = '".$this->db->escape($num)."',";
475
		$sql .= " fk_statut = ".self::STATUS_VALIDATED.",";
476
		$sql .= " date_valid='".$this->db->idate($now)."',";
477
		$sql .= " fk_user_valid = ".$user->id;
478
		$sql .= " WHERE rowid = ".$this->id;
479
480
		dol_syslog(get_class($this)."::valid()", LOG_DEBUG);
481
		$resql = $this->db->query($sql);
482
		if (!$resql)
483
		{
484
			dol_print_error($this->db);
485
			$this->error = $this->db->lasterror();
486
			$error++;
487
		}
488
489
		if (!$error)
490
		{
491
			// If stock is incremented on validate order, we must increment it
492
			if ($result >= 0 && !empty($conf->stock->enabled) && $conf->global->STOCK_CALCULATE_ON_VALIDATE_ORDER == 1)
493
			{
494
				require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
495
				$langs->load("agenda");
496
497
				// Loop on each line
498
				$cpt = count($this->lines);
499
				for ($i = 0; $i < $cpt; $i++)
500
				{
501
					if ($this->lines[$i]->fk_product > 0)
502
					{
503
						$mouvP = new MouvementStock($this->db);
504
						$mouvP->origin = &$this;
505
						// We decrement stock of product (and sub-products)
506
						$result = $mouvP->livraison($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $this->lines[$i]->subprice, $langs->trans("OrderValidatedInDolibarr", $num));
507
						if ($result < 0)
508
						{
509
							$error++;
510
							$this->error = $mouvP->error;
511
						}
512
					}
513
					if ($error) break;
514
				}
515
			}
516
		}
517
518
		if (!$error && !$notrigger)
519
		{
520
			// Call trigger
521
			$result = $this->call_trigger('ORDER_VALIDATE', $user);
522
			if ($result < 0) $error++;
523
			// End call triggers
524
		}
525
526
		if (!$error)
527
		{
528
			$this->oldref = $this->ref;
529
530
			// Rename directory if dir was a temporary ref
531
			if (preg_match('/^[\(]?PROV/i', $this->ref))
532
			{
533
				// Now we rename also files into index
534
				$sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filename = CONCAT('".$this->db->escape($this->newref)."', SUBSTR(filename, ".(strlen($this->ref) + 1).")), filepath = 'commande/".$this->db->escape($this->newref)."'";
535
				$sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'commande/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
536
				$resql = $this->db->query($sql);
537
				if (!$resql) { $error++; $this->error = $this->db->lasterror(); }
538
539
				// We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
540
				$oldref = dol_sanitizeFileName($this->ref);
541
				$newref = dol_sanitizeFileName($num);
542
				$dirsource = $conf->commande->multidir_output[$this->entity].'/'.$oldref;
543
				$dirdest = $conf->commande->multidir_output[$this->entity].'/'.$newref;
544
				if (!$error && file_exists($dirsource))
545
				{
546
					dol_syslog(get_class($this)."::valid() rename dir ".$dirsource." into ".$dirdest);
547
548
					if (@rename($dirsource, $dirdest))
549
					{
550
						dol_syslog("Rename ok");
551
						// Rename docs starting with $oldref with $newref
552
						$listoffiles = dol_dir_list($conf->commande->multidir_output[$this->entity].'/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
553
						foreach ($listoffiles as $fileentry)
554
						{
555
							$dirsource = $fileentry['name'];
556
							$dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
557
							$dirsource = $fileentry['path'].'/'.$dirsource;
558
							$dirdest = $fileentry['path'].'/'.$dirdest;
559
							@rename($dirsource, $dirdest);
560
						}
561
					}
562
				}
563
			}
564
		}
565
566
		// Set new ref and current status
567
		if (!$error)
568
		{
569
			$this->ref = $num;
570
			$this->statut = self::STATUS_VALIDATED;
571
            $this->brouillon = 0;
572
		}
573
574
		if (!$error)
575
		{
576
			$this->db->commit();
577
			return 1;
578
		} else {
579
			$this->db->rollback();
580
			return -1;
581
		}
582
	}
583
584
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
585
	/**
586
	 *	Set draft status
587
	 *
588
	 *	@param	User	$user			Object user that modify
589
	 *	@param	int		$idwarehouse	Warehouse ID to use for stock change (Used only if option STOCK_CALCULATE_ON_VALIDATE_ORDER is on)
590
	 *	@return	int						<0 if KO, >0 if OK
591
	 */
592
    public function setDraft($user, $idwarehouse = -1)
593
    {
594
        //phpcs:enable
595
		global $conf, $langs;
596
597
		$error = 0;
598
599
		// Protection
600
		if ($this->statut <= self::STATUS_DRAFT)
601
		{
602
			return 0;
603
		}
604
605
		if (!((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->commande->creer))
606
			|| (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->commande->order_advance->validate))))
607
		{
608
			$this->error = 'Permission denied';
609
			return -1;
610
		}
611
612
		dol_syslog(__METHOD__, LOG_DEBUG);
613
614
		$this->db->begin();
615
616
		$sql = "UPDATE ".MAIN_DB_PREFIX."commande";
617
		$sql .= " SET fk_statut = ".self::STATUS_DRAFT;
618
		$sql .= " WHERE rowid = ".$this->id;
619
620
		if ($this->db->query($sql))
621
		{
622
		    if (!$error)
623
		    {
624
		        $this->oldcopy = clone $this;
625
		    }
626
627
		    // If stock is decremented on validate order, we must reincrement it
628
			if (!empty($conf->stock->enabled) && $conf->global->STOCK_CALCULATE_ON_VALIDATE_ORDER == 1)
629
			{
630
				$result = 0;
631
632
				require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
633
				$langs->load("agenda");
634
635
				$num = count($this->lines);
636
				for ($i = 0; $i < $num; $i++)
637
				{
638
					if ($this->lines[$i]->fk_product > 0)
639
					{
640
						$mouvP = new MouvementStock($this->db);
641
						$mouvP->origin = &$this;
642
						// We increment stock of product (and sub-products)
643
						$result = $mouvP->reception($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, 0, $langs->trans("OrderBackToDraftInDolibarr", $this->ref));
644
						if ($result < 0) { $error++; $this->error = $mouvP->error; break; }
645
					}
646
				}
647
			}
648
649
			if (!$error) {
650
				// Call trigger
651
				$result = $this->call_trigger('ORDER_UNVALIDATE', $user);
652
				if ($result < 0) $error++;
653
			}
654
655
			if (!$error) {
656
				$this->statut = self::STATUS_DRAFT;
657
				$this->db->commit();
658
				return 1;
659
			} else {
660
				$this->db->rollback();
661
				return -1;
662
			}
663
		} else {
664
			$this->error = $this->db->error();
665
			$this->db->rollback();
666
			return -1;
667
		}
668
    }
669
670
671
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
672
	/**
673
	 *	Tag the order as validated (opened)
674
	 *	Function used when order is reopend after being closed.
675
	 *
676
	 *	@param      User	$user       Object user that change status
677
	 *	@return     int         		<0 if KO, 0 if nothing is done, >0 if OK
678
	 */
679
	public function set_reopen($user)
680
	{
681
        // phpcs:enable
682
		$error = 0;
683
684
		if ($this->statut != self::STATUS_CANCELED && $this->statut != self::STATUS_CLOSED)
685
		{
686
			dol_syslog(get_class($this)."::set_reopen order has not status closed", LOG_WARNING);
687
			return 0;
688
		}
689
690
		$this->db->begin();
691
692
		$sql = 'UPDATE '.MAIN_DB_PREFIX.'commande';
693
		$sql .= ' SET fk_statut='.self::STATUS_VALIDATED.', facture=0';
694
		$sql .= ' WHERE rowid = '.$this->id;
695
696
		dol_syslog(get_class($this)."::set_reopen", LOG_DEBUG);
697
		$resql = $this->db->query($sql);
698
		if ($resql)
699
		{
700
			// Call trigger
701
			$result = $this->call_trigger('ORDER_REOPEN', $user);
702
			if ($result < 0) $error++;
703
			// End call triggers
704
		} else {
705
			$error++;
706
			$this->error = $this->db->lasterror();
707
			dol_print_error($this->db);
708
		}
709
710
		if (!$error)
711
		{
712
			$this->statut = self::STATUS_VALIDATED;
713
			$this->billed = 0;
714
715
			$this->db->commit();
716
			return 1;
717
		} else {
718
			foreach ($this->errors as $errmsg)
719
			{
720
				dol_syslog(get_class($this)."::set_reopen ".$errmsg, LOG_ERR);
721
				$this->error .= ($this->error ? ', '.$errmsg : $errmsg);
722
			}
723
			$this->db->rollback();
724
			return -1 * $error;
725
		}
726
	}
727
728
	/**
729
	 *  Close order
730
	 *
731
	 * 	@param      User	$user       Objet user that close
732
	 *  @param		int		$notrigger	1=Does not execute triggers, 0=Execute triggers
733
	 *	@return		int					<0 if KO, >0 if OK
734
	 */
735
	public function cloture($user, $notrigger = 0)
736
	{
737
		global $conf;
738
739
		$error = 0;
740
741
		$usercanclose = ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->commande->creer))
742
			|| (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->commande->order_advance->close)));
743
744
		if ($usercanclose)
745
		{
746
			$this->db->begin();
747
748
			$now = dol_now();
749
750
			$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
751
			$sql .= ' SET fk_statut = '.self::STATUS_CLOSED.',';
752
			$sql .= ' fk_user_cloture = '.$user->id.',';
753
			$sql .= " date_cloture = '".$this->db->idate($now)."'";
754
			$sql .= ' WHERE rowid = '.$this->id.' AND fk_statut > '.self::STATUS_DRAFT;
755
756
			if ($this->db->query($sql))
757
			{
758
				if (!$notrigger)
759
				{
760
					// Call trigger
761
					$result = $this->call_trigger('ORDER_CLOSE', $user);
762
					if ($result < 0) $error++;
763
					// End call triggers
764
				}
765
766
				if (!$error)
767
				{
768
					$this->statut = self::STATUS_CLOSED;
769
770
					$this->db->commit();
771
					return 1;
772
				} else {
773
					$this->db->rollback();
774
					return -1;
775
				}
776
			} else {
777
				$this->error = $this->db->lasterror();
778
779
				$this->db->rollback();
780
				return -1;
781
			}
782
		}
783
		return 0;
784
	}
785
786
	/**
787
	 * 	Cancel an order
788
	 * 	If stock is decremented on order validation, we must reincrement it
789
	 *
790
	 *	@param	int		$idwarehouse	Id warehouse to use for stock change.
791
	 *	@return	int						<0 if KO, >0 if OK
792
	 */
793
	public function cancel($idwarehouse = -1)
794
	{
795
		global $conf, $user, $langs;
796
797
		$error = 0;
798
799
		$this->db->begin();
800
801
		$sql = "UPDATE ".MAIN_DB_PREFIX."commande";
802
		$sql .= " SET fk_statut = ".self::STATUS_CANCELED;
803
		$sql .= " WHERE rowid = ".$this->id;
804
		$sql .= " AND fk_statut = ".self::STATUS_VALIDATED;
805
806
		dol_syslog(get_class($this)."::cancel", LOG_DEBUG);
807
		if ($this->db->query($sql))
808
		{
809
			// If stock is decremented on validate order, we must reincrement it
810
			if (!empty($conf->stock->enabled) && $conf->global->STOCK_CALCULATE_ON_VALIDATE_ORDER == 1)
811
			{
812
				require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
813
				$langs->load("agenda");
814
815
				$num = count($this->lines);
816
				for ($i = 0; $i < $num; $i++)
817
				{
818
					if ($this->lines[$i]->fk_product > 0)
819
					{
820
						$mouvP = new MouvementStock($this->db);
821
						// We increment stock of product (and sub-products)
822
						$result = $mouvP->reception($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, 0, $langs->trans("OrderCanceledInDolibarr", $this->ref)); // price is 0, we don't want WAP to be changed
823
						if ($result < 0)
824
						{
825
							$error++;
826
							$this->error = $mouvP->error;
827
							break;
828
						}
829
					}
830
				}
831
			}
832
833
			if (!$error)
834
			{
835
				// Call trigger
836
				$result = $this->call_trigger('ORDER_CANCEL', $user);
837
				if ($result < 0) $error++;
838
				// End call triggers
839
			}
840
841
			if (!$error)
842
			{
843
				$this->statut = self::STATUS_CANCELED;
844
				$this->db->commit();
845
				return 1;
846
			} else {
847
				foreach ($this->errors as $errmsg)
848
				{
849
					dol_syslog(get_class($this)."::cancel ".$errmsg, LOG_ERR);
850
					$this->error .= ($this->error ? ', '.$errmsg : $errmsg);
851
				}
852
				$this->db->rollback();
853
				return -1 * $error;
854
			}
855
		} else {
856
			$this->error = $this->db->error();
857
			$this->db->rollback();
858
			return -1;
859
		}
860
	}
861
862
	/**
863
	 *	Create order
864
	 *	Note that this->ref can be set or empty. If empty, we will use "(PROV)"
865
	 *
866
	 *	@param		User	$user 		Objet user that make creation
867
	 *	@param		int	    $notrigger	Disable all triggers
868
	 *	@return 	int			        <0 if KO, >0 if OK
869
	 */
870
	public function create($user, $notrigger = 0)
871
	{
872
		global $conf, $langs;
873
		$error = 0;
874
875
		// Clean parameters
876
		$this->brouillon = 1; // set command as draft
877
878
		// $date_commande is deprecated
879
		$date = ($this->date_commande ? $this->date_commande : $this->date);
1 ignored issue
show
Deprecated Code introduced by
The property Commande::$date_commande 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

879
		$date = ($this->date_commande ? /** @scrutinizer ignore-deprecated */ $this->date_commande : $this->date);
Loading history...
880
881
		// Multicurrency (test on $this->multicurrency_tx because we should take the default rate only if not using origin rate)
882
		if (!empty($this->multicurrency_code) && empty($this->multicurrency_tx)) list($this->fk_multicurrency, $this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $date);
883
		else $this->fk_multicurrency = MultiCurrency::getIdFromCode($this->db, $this->multicurrency_code);
884
		if (empty($this->fk_multicurrency))
885
		{
886
			$this->multicurrency_code = $conf->currency;
887
			$this->fk_multicurrency = 0;
888
			$this->multicurrency_tx = 1;
889
		}
890
891
		dol_syslog(get_class($this)."::create user=".$user->id);
892
893
		// Check parameters
894
		if (!empty($this->ref))	// We check that ref is not already used
895
		{
896
			$result = self::isExistingObject($this->element, 0, $this->ref); // Check ref is not yet used
897
			if ($result > 0)
898
			{
899
				$this->error = 'ErrorRefAlreadyExists';
900
				dol_syslog(get_class($this)."::create ".$this->error, LOG_WARNING);
901
				$this->db->rollback();
902
				return -1;
903
			}
904
		}
905
906
		$soc = new Societe($this->db);
907
		$result = $soc->fetch($this->socid);
908
		if ($result < 0)
909
		{
910
			$this->error = "Failed to fetch company";
911
			dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR);
912
			return -2;
913
		}
914
		if (!empty($conf->global->ORDER_REQUIRE_SOURCE) && $this->source < 0)
915
		{
916
			$this->error = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Source"));
917
			dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR);
918
			return -1;
919
		}
920
921
		$now = dol_now();
922
923
		$this->db->begin();
924
925
		$sql = "INSERT INTO ".MAIN_DB_PREFIX."commande (";
926
		$sql .= " ref, fk_soc, date_creation, fk_user_author, fk_projet, date_commande, source, note_private, note_public, ref_ext, ref_client, ref_int";
927
		$sql .= ", model_pdf, fk_cond_reglement, fk_mode_reglement, fk_account, fk_availability, fk_input_reason, date_livraison, fk_delivery_address";
928
		$sql .= ", fk_shipping_method";
929
		$sql .= ", fk_warehouse";
930
		$sql .= ", remise_absolue, remise_percent";
931
		$sql .= ", fk_incoterms, location_incoterms";
932
		$sql .= ", entity, module_source, pos_source";
933
		$sql .= ", fk_multicurrency";
934
		$sql .= ", multicurrency_code";
935
		$sql .= ", multicurrency_tx";
936
		$sql .= ")";
937
		$sql .= " VALUES ('(PROV)', ".$this->socid.", '".$this->db->idate($now)."', ".$user->id;
938
		$sql .= ", ".($this->fk_project > 0 ? $this->fk_project : "null");
939
		$sql .= ", '".$this->db->idate($date)."'";
940
		$sql .= ", ".($this->source >= 0 && $this->source != '' ? $this->db->escape($this->source) : 'null');
941
		$sql .= ", '".$this->db->escape($this->note_private)."'";
942
		$sql .= ", '".$this->db->escape($this->note_public)."'";
943
		$sql .= ", ".($this->ref_ext ? "'".$this->db->escape($this->ref_ext)."'" : "null");
944
		$sql .= ", ".($this->ref_client ? "'".$this->db->escape($this->ref_client)."'" : "null");
945
		$sql .= ", ".($this->ref_int ? "'".$this->db->escape($this->ref_int)."'" : "null");
946
		$sql .= ", '".$this->db->escape($this->modelpdf)."'";
947
		$sql .= ", ".($this->cond_reglement_id > 0 ? $this->cond_reglement_id : "null");
948
		$sql .= ", ".($this->mode_reglement_id > 0 ? $this->mode_reglement_id : "null");
949
		$sql .= ", ".($this->fk_account > 0 ? $this->fk_account : 'NULL');
950
		$sql .= ", ".($this->availability_id > 0 ? $this->availability_id : "null");
951
		$sql .= ", ".($this->demand_reason_id > 0 ? $this->demand_reason_id : "null");
952
		$sql .= ", ".($this->date_livraison ? "'".$this->db->idate($this->date_livraison)."'" : "null");
953
		$sql .= ", ".($this->fk_delivery_address > 0 ? $this->fk_delivery_address : 'NULL');
1 ignored issue
show
Deprecated Code introduced by
The property CommonObject::$fk_delivery_address 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

953
		$sql .= ", ".(/** @scrutinizer ignore-deprecated */ $this->fk_delivery_address > 0 ? $this->fk_delivery_address : 'NULL');
Loading history...
954
		$sql .= ", ".($this->shipping_method_id > 0 ? $this->shipping_method_id : 'NULL');
955
		$sql .= ", ".($this->warehouse_id > 0 ? $this->warehouse_id : 'NULL');
956
		$sql .= ", ".($this->remise_absolue > 0 ? $this->db->escape($this->remise_absolue) : 'NULL');
957
		$sql .= ", ".($this->remise_percent > 0 ? $this->db->escape($this->remise_percent) : 0);
958
		$sql .= ", ".(int) $this->fk_incoterms;
959
		$sql .= ", '".$this->db->escape($this->location_incoterms)."'";
960
		$sql .= ", ".setEntity($this);
961
        $sql .= ", ".($this->module_source ? "'".$this->db->escape($this->module_source)."'" : "null");
962
		$sql .= ", ".($this->pos_source != '' ? "'".$this->db->escape($this->pos_source)."'" : "null");
963
		$sql .= ", ".(int) $this->fk_multicurrency;
964
		$sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
965
		$sql .= ", ".(double) $this->multicurrency_tx;
966
		$sql .= ")";
967
968
		dol_syslog(get_class($this)."::create", LOG_DEBUG);
969
		$resql = $this->db->query($sql);
970
		if ($resql)
971
		{
972
			$this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'commande');
973
974
			if ($this->id)
975
			{
976
				$fk_parent_line = 0;
977
				$num = count($this->lines);
978
979
				/*
980
				 *  Insert products details into db
981
				 */
982
				for ($i = 0; $i < $num; $i++)
983
				{
984
					$line = $this->lines[$i];
985
986
					// Test and convert into object this->lines[$i]. When coming from REST API, we may still have an array
987
					//if (! is_object($line)) $line=json_decode(json_encode($line), false);  // convert recursively array into object.
988
					if (!is_object($line)) $line = (object) $line;
989
990
					// Reset fk_parent_line for no child products and special product
991
					if (($line->product_type != 9 && empty($line->fk_parent_line)) || $line->product_type == 9) {
992
						$fk_parent_line = 0;
993
					}
994
995
					// Complete vat rate with code
996
					$vatrate = $line->tva_tx;
997
					if ($line->vat_src_code && !preg_match('/\(.*\)/', $vatrate)) $vatrate .= ' ('.$line->vat_src_code.')';
998
999
					if (!empty($conf->global->MAIN_CREATEFROM_KEEP_LINE_ORIGIN_INFORMATION)) {
1000
						$originid = $line->origin_id;
1001
						$origintype = $line->origin;
1002
					} else {
1003
						$originid = $line->id;
1004
						$origintype = $this->element;
1005
					}
1006
1007
                    $result = $this->addline(
1008
						$line->desc,
1009
						$line->subprice,
1010
						$line->qty,
1011
						$vatrate,
1012
						$line->localtax1_tx,
1013
						$line->localtax2_tx,
1014
						$line->fk_product,
1015
						$line->remise_percent,
1016
						$line->info_bits,
1017
						$line->fk_remise_except,
1018
						'HT',
1019
						0,
1020
						$line->date_start,
1021
						$line->date_end,
1022
						$line->product_type,
1023
						$line->rang,
1024
						$line->special_code,
1025
						$fk_parent_line,
1026
						$line->fk_fournprice,
1027
						$line->pa_ht,
1028
						$line->label,
1029
						$line->array_options,
1030
						$line->fk_unit,
1031
	                    $origintype,
1032
	                    $originid
1033
					);
1034
					if ($result < 0)
1035
					{
1036
						if ($result != self::STOCK_NOT_ENOUGH_FOR_ORDER)
1037
						{
1038
							$this->error = $this->db->lasterror();
1039
							$this->errors[] = $this->error;
1040
							dol_print_error($this->db);
1041
						}
1042
						$this->db->rollback();
1043
						return -1;
1044
					}
1045
					// Defined the new fk_parent_line
1046
					if ($result > 0 && $line->product_type == 9) {
1047
						$fk_parent_line = $result;
1048
					}
1049
				}
1050
1051
				// update ref
1052
				$initialref = '(PROV'.$this->id.')';
1053
				if (!empty($this->ref)) $initialref = $this->ref;
1054
1055
				$sql = 'UPDATE '.MAIN_DB_PREFIX."commande SET ref='".$this->db->escape($initialref)."' WHERE rowid=".$this->id;
1056
				if ($this->db->query($sql))
1057
				{
1058
					$this->ref = $initialref;
1059
1060
					if (!empty($this->linkedObjectsIds) && empty($this->linked_objects))	// To use new linkedObjectsIds instead of old linked_objects
1061
					{
1062
						$this->linked_objects = $this->linkedObjectsIds; // TODO Replace linked_objects with linkedObjectsIds
1063
					}
1064
1065
					// Add object linked
1066
					if (!$error && $this->id && is_array($this->linked_objects) && !empty($this->linked_objects))
1067
					{
1068
						foreach ($this->linked_objects as $origin => $tmp_origin_id)
1069
						{
1070
							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, ...))
1071
							{
1072
								foreach ($tmp_origin_id as $origin_id)
1073
								{
1074
									$ret = $this->add_object_linked($origin, $origin_id);
1075
									if (!$ret)
1076
									{
1077
										$this->error = $this->db->lasterror();
1078
										$error++;
1079
									}
1080
								}
1081
							} else // Old behaviour, if linked_object has only one link per type, so is something like array('contract'=>id1))
1082
							{
1083
								$origin_id = $tmp_origin_id;
1084
								$ret = $this->add_object_linked($origin, $origin_id);
1085
								if (!$ret)
1086
								{
1087
									$this->error = $this->db->lasterror();
1088
									$error++;
1089
								}
1090
							}
1091
						}
1092
					}
1093
1094
					if (!$error && $this->id && !empty($conf->global->MAIN_PROPAGATE_CONTACTS_FROM_ORIGIN) && !empty($this->origin) && !empty($this->origin_id))   // Get contact from origin object
1095
					{
1096
						$originforcontact = $this->origin;
1097
						$originidforcontact = $this->origin_id;
1098
						if ($originforcontact == 'shipping')     // shipment and order share the same contacts. If creating from shipment we take data of order
1099
						{
1100
							require_once DOL_DOCUMENT_ROOT.'/expedition/class/expedition.class.php';
1101
							$exp = new Expedition($this->db);
1102
							$exp->fetch($this->origin_id);
1103
							$exp->fetchObjectLinked();
1104
							if (count($exp->linkedObjectsIds['commande']) > 0)
1105
							{
1106
								foreach ($exp->linkedObjectsIds['commande'] as $key => $value)
1107
								{
1108
									$originforcontact = 'commande';
1109
									if (is_object($value)) $originidforcontact = $value->id;
1110
									else $originidforcontact = $value;
1111
									break; // We take first one
1112
								}
1113
							}
1114
						}
1115
1116
						$sqlcontact = "SELECT ctc.code, ctc.source, ec.fk_socpeople FROM ".MAIN_DB_PREFIX."element_contact as ec, ".MAIN_DB_PREFIX."c_type_contact as ctc";
1117
						$sqlcontact .= " WHERE element_id = ".$originidforcontact." AND ec.fk_c_type_contact = ctc.rowid AND ctc.element = '".$originforcontact."'";
1118
1119
						$resqlcontact = $this->db->query($sqlcontact);
1120
						if ($resqlcontact)
1121
						{
1122
							while ($objcontact = $this->db->fetch_object($resqlcontact))
1123
							{
1124
								//print $objcontact->code.'-'.$objcontact->source.'-'.$objcontact->fk_socpeople."\n";
1125
								$this->add_contact($objcontact->fk_socpeople, $objcontact->code, $objcontact->source); // May failed because of duplicate key or because code of contact type does not exists for new object
1126
							}
1127
						} else dol_print_error($resqlcontact);
1128
					}
1129
1130
					if (!$error)
1131
					{
1132
						$result = $this->insertExtraFields();
1133
						if ($result < 0) $error++;
1134
					}
1135
1136
					if (!$error && !$notrigger)
1137
					{
1138
						// Call trigger
1139
						$result = $this->call_trigger('ORDER_CREATE', $user);
1140
						if ($result < 0) $error++;
1141
						// End call triggers
1142
					}
1143
1144
					if (!$error)
1145
					{
1146
						$this->db->commit();
1147
						return $this->id;
1148
					} else {
1149
						$this->db->rollback();
1150
						return -1 * $error;
1151
					}
1152
				} else {
1153
					$this->error = $this->db->lasterror();
1154
					$this->db->rollback();
1155
					return -1;
1156
				}
1157
			}
1158
		} else {
1159
			dol_print_error($this->db);
1160
			$this->db->rollback();
1161
			return -1;
1162
		}
1163
	}
1164
1165
1166
	/**
1167
	 *	Load an object from its id and create a new one in database
1168
	 *
1169
	 *  @param	    User	$user		User making the clone
1170
	 *	@param		int		$socid		Id of thirdparty
1171
	 *	@return		int					New id of clone
1172
	 */
1173
	public function createFromClone(User $user, $socid = 0)
1174
	{
1175
		global $conf, $user, $hookmanager;
1176
1177
		$error = 0;
1178
1179
		$this->db->begin();
1180
1181
		// get lines so they will be clone
1182
		foreach ($this->lines as $line)
1183
			$line->fetch_optionals();
1184
1185
		// Load source object
1186
		$objFrom = clone $this;
1187
1188
		// Change socid if needed
1189
		if (!empty($socid) && $socid != $this->socid)
1190
		{
1191
			$objsoc = new Societe($this->db);
1192
1193
			if ($objsoc->fetch($socid) > 0)
1194
			{
1195
				$this->socid = $objsoc->id;
1196
				$this->cond_reglement_id	= (!empty($objsoc->cond_reglement_id) ? $objsoc->cond_reglement_id : 0);
1197
				$this->mode_reglement_id	= (!empty($objsoc->mode_reglement_id) ? $objsoc->mode_reglement_id : 0);
1198
				$this->fk_project = 0;
1199
				$this->fk_delivery_address = 0;
1 ignored issue
show
Deprecated Code introduced by
The property CommonObject::$fk_delivery_address 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

1199
				/** @scrutinizer ignore-deprecated */ $this->fk_delivery_address = 0;
Loading history...
1200
			}
1201
1202
			// TODO Change product price if multi-prices
1203
		}
1204
1205
		$this->id = 0;
1206
		$this->ref = '';
1207
		$this->statut = self::STATUS_DRAFT;
1208
1209
		// Clear fields
1210
		$this->user_author_id     = $user->id;
1211
		$this->user_valid         = '';
1212
		$this->date = dol_now();
1213
		$this->date_commande = dol_now();
1 ignored issue
show
Deprecated Code introduced by
The property Commande::$date_commande 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

1213
		/** @scrutinizer ignore-deprecated */ $this->date_commande = dol_now();
Loading history...
1214
		$this->date_creation      = '';
1215
		$this->date_validation    = '';
1216
		if (empty($conf->global->MAIN_KEEP_REF_CUSTOMER_ON_CLONING)) $this->ref_client = '';
1217
1218
		// Create clone
1219
		$this->context['createfromclone'] = 'createfromclone';
1220
		$result = $this->create($user);
1221
		if ($result < 0) $error++;
1222
1223
		if (!$error)
1224
		{
1225
			// copy internal contacts
1226
			if ($this->copy_linked_contact($objFrom, 'internal') < 0)
1227
			{
1228
				$error++;
1229
			}
1230
		}
1231
1232
		if (!$error)
1233
		{
1234
			// copy external contacts if same company
1235
			if ($this->socid == $objFrom->socid)
1236
			{
1237
				if ($this->copy_linked_contact($objFrom, 'external') < 0)
1238
					$error++;
1239
			}
1240
		}
1241
1242
		if (!$error)
1243
		{
1244
			// Hook of thirdparty module
1245
			if (is_object($hookmanager))
1246
			{
1247
				$parameters = array('objFrom'=>$objFrom);
1248
				$action = '';
1249
				$reshook = $hookmanager->executeHooks('createFrom', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1250
				if ($reshook < 0) $error++;
1251
			}
1252
		}
1253
1254
		unset($this->context['createfromclone']);
1255
1256
		// End
1257
		if (!$error)
1258
		{
1259
			$this->db->commit();
1260
			return $this->id;
1261
		} else {
1262
			$this->db->rollback();
1263
			return -1;
1264
		}
1265
	}
1266
1267
1268
	/**
1269
	 *  Load an object from a proposal and create a new order into database
1270
	 *
1271
	 *  @param      Object			$object 	        Object source
1272
	 *  @param		User			$user				User making creation
1273
	 *  @return     int             					<0 if KO, 0 if nothing done, 1 if OK
1274
	 */
1275
	public function createFromProposal($object, User $user)
1276
	{
1277
		global $conf, $hookmanager;
1278
1279
		dol_include_once('/core/class/extrafields.class.php');
1280
1281
		$error = 0;
1282
1283
1284
		$this->date_commande = dol_now();
1 ignored issue
show
Deprecated Code introduced by
The property Commande::$date_commande 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

1284
		/** @scrutinizer ignore-deprecated */ $this->date_commande = dol_now();
Loading history...
1285
		$this->source = 0;
1286
1287
		$num = count($object->lines);
1288
		for ($i = 0; $i < $num; $i++)
1289
		{
1290
			$line = new OrderLine($this->db);
1291
1292
			$line->libelle           = $object->lines[$i]->libelle;
1 ignored issue
show
Deprecated Code introduced by
The property CommonOrderLine::$libelle has been deprecated: Use product_label ( Ignorable by Annotation )

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

1292
			/** @scrutinizer ignore-deprecated */ $line->libelle           = $object->lines[$i]->libelle;

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...
1293
			$line->label             = $object->lines[$i]->label;
1294
			$line->desc              = $object->lines[$i]->desc;
1295
			$line->price             = $object->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

1295
			/** @scrutinizer ignore-deprecated */ $line->price             = $object->lines[$i]->price;
Loading history...
1296
			$line->subprice          = $object->lines[$i]->subprice;
1297
			$line->vat_src_code      = $object->lines[$i]->vat_src_code;
1298
			$line->tva_tx            = $object->lines[$i]->tva_tx;
1299
			$line->localtax1_tx      = $object->lines[$i]->localtax1_tx;
1300
			$line->localtax2_tx      = $object->lines[$i]->localtax2_tx;
1301
			$line->qty               = $object->lines[$i]->qty;
1302
			$line->fk_remise_except  = $object->lines[$i]->fk_remise_except;
1303
			$line->remise_percent    = $object->lines[$i]->remise_percent;
1304
			$line->fk_product        = $object->lines[$i]->fk_product;
1305
			$line->info_bits         = $object->lines[$i]->info_bits;
1306
			$line->product_type      = $object->lines[$i]->product_type;
1307
			$line->rang              = $object->lines[$i]->rang;
1308
			$line->special_code      = $object->lines[$i]->special_code;
1309
			$line->fk_parent_line    = $object->lines[$i]->fk_parent_line;
1310
			$line->fk_unit = $object->lines[$i]->fk_unit;
1311
1312
			$line->date_start = $object->lines[$i]->date_start;
1313
			$line->date_end    		= $object->lines[$i]->date_end;
1314
1315
			$line->fk_fournprice	= $object->lines[$i]->fk_fournprice;
1316
			$marginInfos			= getMarginInfos($object->lines[$i]->subprice, $object->lines[$i]->remise_percent, $object->lines[$i]->tva_tx, $object->lines[$i]->localtax1_tx, $object->lines[$i]->localtax2_tx, $object->lines[$i]->fk_fournprice, $object->lines[$i]->pa_ht);
1317
			$line->pa_ht			= $marginInfos[0];
1318
			$line->marge_tx			= $marginInfos[1];
1319
			$line->marque_tx		= $marginInfos[2];
1320
1321
			// get extrafields from original line
1322
			$object->lines[$i]->fetch_optionals();
1323
			foreach ($object->lines[$i]->array_options as $options_key => $value)
1324
				$line->array_options[$options_key] = $value;
1325
1326
				$this->lines[$i] = $line;
1327
		}
1328
1329
		$this->entity               = $object->entity;
1330
		$this->socid                = $object->socid;
1331
		$this->fk_project           = $object->fk_project;
1332
		$this->cond_reglement_id    = $object->cond_reglement_id;
1333
		$this->mode_reglement_id    = $object->mode_reglement_id;
1334
		$this->fk_account           = $object->fk_account;
1335
		$this->availability_id      = $object->availability_id;
1336
		$this->demand_reason_id     = $object->demand_reason_id;
1337
		$this->date_livraison       = $object->date_livraison;
1338
		$this->shipping_method_id   = $object->shipping_method_id;
1339
		$this->warehouse_id         = $object->warehouse_id;
1340
		$this->fk_delivery_address  = $object->fk_delivery_address;
1 ignored issue
show
Deprecated Code introduced by
The property CommonObject::$fk_delivery_address 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

1340
		/** @scrutinizer ignore-deprecated */ $this->fk_delivery_address  = $object->fk_delivery_address;
Loading history...
1341
		$this->contact_id            = $object->contact_id;
1342
		$this->ref_client           = $object->ref_client;
1343
1344
		if (empty($conf->global->MAIN_DISABLE_PROPAGATE_NOTES_FROM_ORIGIN))
1345
		{
1346
            $this->note_private         = $object->note_private;
1347
            $this->note_public          = $object->note_public;
1348
		}
1349
1350
		$this->origin = $object->element;
1351
		$this->origin_id = $object->id;
1352
1353
		// get extrafields from original line
1354
		$object->fetch_optionals();
1355
1356
		$e = new ExtraFields($this->db);
1357
		$element_extrafields = $e->fetch_name_optionals_label($this->table_element);
1358
1359
		foreach ($object->array_options as $options_key => $value) {
1360
			if (array_key_exists(str_replace('options_', '', $options_key), $element_extrafields)) {
1361
				$this->array_options[$options_key] = $value;
1362
			}
1363
		}
1364
		// Possibility to add external linked objects with hooks
1365
		$this->linked_objects[$this->origin] = $this->origin_id;
1366
		if (is_array($object->other_linked_objects) && !empty($object->other_linked_objects))
1367
		{
1368
			$this->linked_objects = array_merge($this->linked_objects, $object->other_linked_objects);
1369
		}
1370
1371
		$ret = $this->create($user);
1372
1373
		if ($ret > 0)
1374
		{
1375
			// Actions hooked (by external module)
1376
			$hookmanager->initHooks(array('orderdao'));
1377
1378
			$parameters = array('objFrom'=>$object);
1379
			$action = '';
1380
			$reshook = $hookmanager->executeHooks('createFrom', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1381
			if ($reshook < 0) $error++;
1382
1383
			if (!$error)
1384
			{
1385
				// Validate immediatly the order
1386
				if (!empty($conf->global->ORDER_VALID_AFTER_CLOSE_PROPAL))
1387
				{
1388
					$this->fetch($ret);
1389
					$this->valid($user);
1390
				}
1391
				return $ret;
1392
			} else return -1;
1393
		} else return -1;
1394
	}
1395
1396
1397
	/**
1398
	 *	Add an order line into database (linked to product/service or not)
1399
	 *
1400
	 *	@param      string			$desc            	Description of line
1401
	 *	@param      float			$pu_ht    	        Unit price (without tax)
1402
	 *	@param      float			$qty             	Quantite
1403
	 * 	@param    	float			$txtva           	Force Vat rate, -1 for auto (Can contain the vat_src_code too with syntax '9.9 (CODE)')
1404
	 * 	@param		float			$txlocaltax1		Local tax 1 rate (deprecated, use instead txtva with code inside)
1405
	 * 	@param		float			$txlocaltax2		Local tax 2 rate (deprecated, use instead txtva with code inside)
1406
	 *	@param      int				$fk_product      	Id of product
1407
	 *	@param      float			$remise_percent  	Percentage discount of the line
1408
	 *	@param      int				$info_bits			Bits of type of lines
1409
	 *	@param      int				$fk_remise_except	Id remise
1410
	 *	@param      string			$price_base_type	HT or TTC
1411
	 *	@param      float			$pu_ttc    		    Prix unitaire TTC
1412
	 *	@param      int				$date_start       	Start date of the line - Added by Matelli (See http://matelli.fr/showcases/patchs-dolibarr/add-dates-in-order-lines.html)
1413
	 *	@param      int				$date_end         	End date of the line - Added by Matelli (See http://matelli.fr/showcases/patchs-dolibarr/add-dates-in-order-lines.html)
1414
	 *	@param      int				$type				Type of line (0=product, 1=service). Not used if fk_product is defined, the type of product is used.
1415
	 *	@param      int				$rang             	Position of line
1416
	 *	@param		int				$special_code		Special code (also used by externals modules!)
1417
	 *	@param		int				$fk_parent_line		Parent line
1418
	 *  @param		int				$fk_fournprice		Id supplier price
1419
	 *  @param		int				$pa_ht				Buying price (without tax)
1420
	 *  @param		string			$label				Label
1421
	 *  @param		array			$array_options		extrafields array. Example array('options_codeforfield1'=>'valueforfield1', 'options_codeforfield2'=>'valueforfield2', ...)
1422
	 * 	@param 		string			$fk_unit 			Code of the unit to use. Null to use the default one
1423
	 * 	@param		string		    $origin				Depend on global conf MAIN_CREATEFROM_KEEP_LINE_ORIGIN_INFORMATION can be 'orderdet', 'propaldet'..., else 'order','propal,'....
1424
	 *  @param		int			    $origin_id			Depend on global conf MAIN_CREATEFROM_KEEP_LINE_ORIGIN_INFORMATION can be Id of origin object (aka line id), else object id
1425
	 * 	@param		double			$pu_ht_devise		Unit price in currency
1426
	 *	@return     int             					>0 if OK, <0 if KO
1427
	 *
1428
	 *	@see        add_product()
1429
	 *
1430
	 *	Les parametres sont deja cense etre juste et avec valeurs finales a l'appel
1431
	 *	de cette methode. Aussi, pour le taux tva, il doit deja avoir ete defini
1432
	 *	par l'appelant par la methode get_default_tva(societe_vendeuse,societe_acheteuse,produit)
1433
	 *	et le desc doit deja avoir la bonne valeur (a l'appelant de gerer le multilangue)
1434
	 */
1435
	public function addline($desc, $pu_ht, $qty, $txtva, $txlocaltax1 = 0, $txlocaltax2 = 0, $fk_product = 0, $remise_percent = 0, $info_bits = 0, $fk_remise_except = 0, $price_base_type = 'HT', $pu_ttc = 0, $date_start = '', $date_end = '', $type = 0, $rang = -1, $special_code = 0, $fk_parent_line = 0, $fk_fournprice = null, $pa_ht = 0, $label = '', $array_options = 0, $fk_unit = null, $origin = '', $origin_id = 0, $pu_ht_devise = 0)
1436
	{
1437
		global $mysoc, $conf, $langs, $user;
1438
1439
		$logtext = "::addline commandeid=$this->id, desc=$desc, pu_ht=$pu_ht, qty=$qty, txtva=$txtva, fk_product=$fk_product, remise_percent=$remise_percent";
1440
		$logtext .= ", info_bits=$info_bits, fk_remise_except=$fk_remise_except, price_base_type=$price_base_type, pu_ttc=$pu_ttc, date_start=$date_start";
1441
		$logtext .= ", date_end=$date_end, type=$type special_code=$special_code, fk_unit=$fk_unit, origin=$origin, origin_id=$origin_id, pu_ht_devise=$pu_ht_devise";
1442
		dol_syslog(get_class($this).$logtext, LOG_DEBUG);
1443
1444
		if ($this->statut == self::STATUS_DRAFT)
1445
		{
1446
			include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
1447
1448
			// Clean parameters
1449
			if (empty($remise_percent)) $remise_percent = 0;
1450
			if (empty($qty)) $qty = 0;
1451
			if (empty($info_bits)) $info_bits = 0;
1452
			if (empty($rang)) $rang = 0;
1453
			if (empty($txtva)) $txtva = 0;
1454
			if (empty($txlocaltax1)) $txlocaltax1 = 0;
1455
			if (empty($txlocaltax2)) $txlocaltax2 = 0;
1456
			if (empty($fk_parent_line) || $fk_parent_line < 0) $fk_parent_line = 0;
1457
			if (empty($this->fk_multicurrency)) $this->fk_multicurrency = 0;
1458
1459
			$remise_percent = price2num($remise_percent);
1460
			$qty = price2num($qty);
1461
			$pu_ht = price2num($pu_ht);
1462
			$pu_ht_devise = price2num($pu_ht_devise);
1463
			$pu_ttc = price2num($pu_ttc);
1464
			$pa_ht = price2num($pa_ht);
1465
			if (!preg_match('/\((.*)\)/', $txtva)) {
1466
				$txtva = price2num($txtva); // $txtva can have format '5,1' or '5.1' or '5.1(XXX)', we must clean only if '5,1'
1467
			}
1468
			$txlocaltax1 = price2num($txlocaltax1);
1469
			$txlocaltax2 = price2num($txlocaltax2);
1470
			if ($price_base_type == 'HT')
1471
			{
1472
				$pu = $pu_ht;
1473
			} else {
1474
				$pu = $pu_ttc;
1475
			}
1476
			$label = trim($label);
1477
			$desc = trim($desc);
1478
1479
			// Check parameters
1480
			if ($type < 0) return -1;
1481
1482
			if ($date_start && $date_end && $date_start > $date_end) {
1483
				$langs->load("errors");
1484
				$this->error = $langs->trans('ErrorStartDateGreaterEnd');
1485
				return -1;
1486
			}
1487
1488
            $this->db->begin();
1489
1490
			$product_type = $type;
1491
			if (!empty($fk_product))
1492
			{
1493
				$product = new Product($this->db);
1494
				$result = $product->fetch($fk_product);
1495
				$product_type = $product->type;
1496
1497
				if (!empty($conf->global->STOCK_MUST_BE_ENOUGH_FOR_ORDER) && $product_type == 0 && $product->stock_reel < $qty)
1498
				{
1499
					$langs->load("errors");
1500
					$this->error = $langs->trans('ErrorStockIsNotEnoughToAddProductOnOrder', $product->ref);
1501
					$this->errors[] = $this->error;
1502
					dol_syslog(get_class($this)."::addline error=Product ".$product->ref.": ".$this->error, LOG_ERR);
1503
					$this->db->rollback();
1504
					return self::STOCK_NOT_ENOUGH_FOR_ORDER;
1505
				}
1506
			}
1507
			// Calcul du total TTC et de la TVA pour la ligne a partir de
1508
			// qty, pu, remise_percent et txtva
1509
			// TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
1510
			// la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
1511
1512
			$localtaxes_type = getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc);
1513
1514
			// Clean vat code
1515
			$reg = array();
1516
			$vat_src_code = '';
1517
			if (preg_match('/\((.*)\)/', $txtva, $reg))
1518
			{
1519
				$vat_src_code = $reg[1];
1520
				$txtva = preg_replace('/\s*\(.*\)/', '', $txtva); // Remove code into vatrate.
1521
			}
1522
1523
			$tabprice = calcul_price_total($qty, $pu, $remise_percent, $txtva, $txlocaltax1, $txlocaltax2, 0, $price_base_type, $info_bits, $product_type, $mysoc, $localtaxes_type, 100, $this->multicurrency_tx, $pu_ht_devise);
1524
1525
			/*var_dump($txlocaltax1);
1526
			 var_dump($txlocaltax2);
1527
			 var_dump($localtaxes_type);
1528
			 var_dump($tabprice);
1529
			 var_dump($tabprice[9]);
1530
			 var_dump($tabprice[10]);
1531
			 exit;*/
1532
1533
			$total_ht  = $tabprice[0];
1534
			$total_tva = $tabprice[1];
1535
			$total_ttc = $tabprice[2];
1536
			$total_localtax1 = $tabprice[9];
1537
			$total_localtax2 = $tabprice[10];
1538
			$pu_ht = $tabprice[3];
1539
1540
			// MultiCurrency
1541
			$multicurrency_total_ht  = $tabprice[16];
1542
			$multicurrency_total_tva = $tabprice[17];
1543
			$multicurrency_total_ttc = $tabprice[18];
1544
			$pu_ht_devise = $tabprice[19];
1545
1546
			// Rang to use
1547
			$ranktouse = $rang;
1548
			if ($ranktouse == -1)
1549
			{
1550
				$rangmax = $this->line_max($fk_parent_line);
1551
				$ranktouse = $rangmax + 1;
1552
			}
1553
1554
			// TODO A virer
1555
			// Anciens indicateurs: $price, $remise (a ne plus utiliser)
1556
			$price = $pu;
1557
			$remise = 0;
1558
			if ($remise_percent > 0)
1559
			{
1560
				$remise = round(($pu * $remise_percent / 100), 2);
1561
				$price = $pu - $remise;
1562
			}
1563
1564
			// Insert line
1565
			$this->line = new OrderLine($this->db);
1566
1567
			$this->line->context = $this->context;
1568
1569
			$this->line->fk_commande = $this->id;
1570
			$this->line->label = $label;
1571
			$this->line->desc = $desc;
1572
			$this->line->qty = $qty;
1573
1574
			$this->line->vat_src_code = $vat_src_code;
1575
			$this->line->tva_tx = $txtva;
1576
			$this->line->localtax1_tx = ($total_localtax1 ? $localtaxes_type[1] : 0);
1577
			$this->line->localtax2_tx = ($total_localtax2 ? $localtaxes_type[3] : 0);
1578
			$this->line->localtax1_type = $localtaxes_type[0];
1579
			$this->line->localtax2_type = $localtaxes_type[2];
1580
			$this->line->fk_product = $fk_product;
1581
			$this->line->product_type = $product_type;
1582
			$this->line->fk_remise_except = $fk_remise_except;
1583
			$this->line->remise_percent = $remise_percent;
1584
			$this->line->subprice = $pu_ht;
1585
			$this->line->rang = $ranktouse;
1586
			$this->line->info_bits = $info_bits;
1587
			$this->line->total_ht = $total_ht;
1588
			$this->line->total_tva = $total_tva;
1589
			$this->line->total_localtax1 = $total_localtax1;
1590
			$this->line->total_localtax2 = $total_localtax2;
1591
			$this->line->total_ttc = $total_ttc;
1592
			$this->line->special_code = $special_code;
1593
			$this->line->origin = $origin;
1594
			$this->line->origin_id = $origin_id;
1595
			$this->line->fk_parent_line = $fk_parent_line;
1596
			$this->line->fk_unit = $fk_unit;
1597
1598
			$this->line->date_start = $date_start;
1599
			$this->line->date_end = $date_end;
1600
1601
			$this->line->fk_fournprice = $fk_fournprice;
1602
			$this->line->pa_ht = $pa_ht;
1603
1604
			// Multicurrency
1605
			$this->line->fk_multicurrency = $this->fk_multicurrency;
1606
			$this->line->multicurrency_code = $this->multicurrency_code;
1607
			$this->line->multicurrency_subprice		= $pu_ht_devise;
1608
			$this->line->multicurrency_total_ht 	= $multicurrency_total_ht;
1609
			$this->line->multicurrency_total_tva 	= $multicurrency_total_tva;
1610
			$this->line->multicurrency_total_ttc 	= $multicurrency_total_ttc;
1611
1612
			// TODO Ne plus utiliser
1613
			$this->line->price = $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

1613
			/** @scrutinizer ignore-deprecated */ $this->line->price = $price;
Loading history...
1614
			$this->line->remise = $remise;
1 ignored issue
show
Deprecated Code introduced by
The property OrderLine::$remise 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

1614
			/** @scrutinizer ignore-deprecated */ $this->line->remise = $remise;
Loading history...
1615
1616
			if (is_array($array_options) && count($array_options) > 0) {
1617
				$this->line->array_options = $array_options;
1618
			}
1619
1620
			$result = $this->line->insert($user);
1621
			if ($result > 0)
1622
			{
1623
				// Reorder if child line
1624
				if (!empty($fk_parent_line)) $this->line_order(true, 'DESC');
1625
1626
				// Mise a jour informations denormalisees au niveau de la commande meme
1627
				$result = $this->update_price(1, 'auto', 0, $mysoc); // This method is designed to add line from user input so total calculation must be done using 'auto' mode.
1628
				if ($result > 0)
1629
				{
1630
					$this->db->commit();
1631
					return $this->line->id;
1632
				} else {
1633
					$this->db->rollback();
1634
					return -1;
1635
				}
1636
			} else {
1637
				$this->error = $this->line->error;
1638
				dol_syslog(get_class($this)."::addline error=".$this->error, LOG_ERR);
1639
				$this->db->rollback();
1640
				return -2;
1641
			}
1642
		} else {
1643
			dol_syslog(get_class($this)."::addline status of order must be Draft to allow use of ->addline()", LOG_ERR);
1644
			return -3;
1645
		}
1646
	}
1647
1648
1649
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1650
	/**
1651
	 *	Add line into array
1652
	 *	$this->client must be loaded
1653
	 *
1654
	 *	@param  int     $idproduct          Product Id
1655
	 *	@param  float   $qty                Quantity
1656
	 *	@param  float   $remise_percent     Product discount relative
1657
	 * 	@param  int     $date_start         Start date of the line
1658
	 * 	@param  int     $date_end           End date of the line
1659
	 * 	@return void
1660
	 *
1661
	 *	TODO	Remplacer les appels a cette fonction par generation objet Ligne
1662
	 *			insere dans tableau $this->products
1663
	 */
1664
	public function add_product($idproduct, $qty, $remise_percent = 0.0, $date_start = '', $date_end = '')
1665
	{
1666
        // phpcs:enable
1667
		global $conf, $mysoc;
1668
1669
		if (!$qty) $qty = 1;
1670
1671
		if ($idproduct > 0)
1672
		{
1673
			$prod = new Product($this->db);
1674
			$prod->fetch($idproduct);
1675
1676
			$tva_tx = get_default_tva($mysoc, $this->thirdparty, $prod->id);
1677
			$tva_npr = get_default_npr($mysoc, $this->thirdparty, $prod->id);
1678
			if (empty($tva_tx)) $tva_npr = 0;
1679
			$vat_src_code = ''; // May be defined into tva_tx
1680
1681
			$localtax1_tx = get_localtax($tva_tx, 1, $this->thirdparty, $mysoc, $tva_npr);
1682
			$localtax2_tx = get_localtax($tva_tx, 2, $this->thirdparty, $mysoc, $tva_npr);
1683
1684
			// multiprix
1685
			if ($conf->global->PRODUIT_MULTIPRICES && $this->thirdparty->price_level) {
1686
				$price = $prod->multiprices[$this->thirdparty->price_level];
1687
			} else {
1688
				$price = $prod->price;
1689
			}
1690
1691
			$line = new OrderLine($this->db);
1692
1693
			$line->context = $this->context;
1694
1695
			$line->fk_product = $idproduct;
1696
			$line->desc = $prod->description;
1697
			$line->qty = $qty;
1698
			$line->subprice = $price;
1699
			$line->remise_percent = $remise_percent;
1700
			$line->vat_src_code = $vat_src_code;
1701
			$line->tva_tx = $tva_tx;
1702
			$line->localtax1_tx = $localtax1_tx;
1703
			$line->localtax2_tx = $localtax2_tx;
1704
			$line->ref = $prod->ref;
1 ignored issue
show
Deprecated Code introduced by
The property CommonOrderLine::$ref has been deprecated: Use product_ref ( Ignorable by Annotation )

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

1704
			/** @scrutinizer ignore-deprecated */ $line->ref = $prod->ref;

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...
1705
			$line->libelle = $prod->label;
1 ignored issue
show
Deprecated Code introduced by
The property CommonOrderLine::$libelle has been deprecated: Use product_label ( Ignorable by Annotation )

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

1705
			/** @scrutinizer ignore-deprecated */ $line->libelle = $prod->label;

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...
1706
			$line->product_desc = $prod->description;
1707
			$line->fk_unit = $prod->fk_unit;
1708
1709
			// Save the start and end date of the line in the object
1710
			if ($date_start) { $line->date_start = $date_start; }
1711
			if ($date_end) { $line->date_end = $date_end; }
1712
1713
			$this->lines[] = $line;
1714
1715
			/** POUR AJOUTER AUTOMATIQUEMENT LES SOUSPRODUITS a LA COMMANDE
1716
			 if (! empty($conf->global->PRODUIT_SOUSPRODUITS))
1717
			 {
1718
			 $prod = new Product($this->db);
1719
			 $prod->fetch($idproduct);
1720
			 $prod -> get_sousproduits_arbo();
1721
			 $prods_arbo = $prod->get_arbo_each_prod();
1722
			 if(count($prods_arbo) > 0)
1723
			 {
1724
			 foreach($prods_arbo as $key => $value)
1725
			 {
1726
			 // print "id : ".$value[1].' :qty: '.$value[0].'<br>';
1727
			 if(! in_array($value[1],$this->products))
1728
			 $this->add_product($value[1], $value[0]);
1729
1730
			 }
1731
			 }
1732
1733
			 }
1734
			 **/
1735
		}
1736
	}
1737
1738
1739
	/**
1740
	 *	Get object from database. Get also lines.
1741
	 *
1742
	 *	@param      int			$id       		Id of object to load
1743
	 * 	@param		string		$ref			Ref of object
1744
	 * 	@param		string		$ref_ext		External reference of object
1745
	 * 	@param		string		$notused		Internal reference of other object
1746
	 *	@return     int         				>0 if OK, <0 if KO, 0 if not found
1747
	 */
1748
	public function fetch($id, $ref = '', $ref_ext = '', $notused = '')
1749
	{
1750
		// Check parameters
1751
		if (empty($id) && empty($ref) && empty($ref_ext)) return -1;
1752
1753
		$sql = 'SELECT c.rowid, c.entity, c.date_creation, c.ref, c.fk_soc, c.fk_user_author, c.fk_user_valid, c.fk_statut';
1754
		$sql .= ', c.amount_ht, c.total_ht, c.total_ttc, c.tva as total_tva, c.localtax1 as total_localtax1, c.localtax2 as total_localtax2, c.fk_cond_reglement, c.fk_mode_reglement, c.fk_availability, c.fk_input_reason';
1755
		$sql .= ', c.fk_account';
1756
		$sql .= ', c.date_commande, c.date_valid, c.tms';
1757
		$sql .= ', c.date_livraison';
1758
		$sql .= ', c.fk_shipping_method';
1759
		$sql .= ', c.fk_warehouse';
1760
		$sql .= ', c.fk_projet as fk_project, c.remise_percent, c.remise, c.remise_absolue, c.source, c.facture as billed';
1761
		$sql .= ', c.note_private, c.note_public, c.ref_client, c.ref_ext, c.ref_int, c.model_pdf, c.last_main_doc, c.fk_delivery_address, c.extraparams';
1762
		$sql .= ', c.fk_incoterms, c.location_incoterms';
1763
		$sql .= ", c.fk_multicurrency, c.multicurrency_code, c.multicurrency_tx, c.multicurrency_total_ht, c.multicurrency_total_tva, c.multicurrency_total_ttc";
1764
		$sql .= ", c.module_source, c.pos_source";
1765
        $sql .= ", i.libelle as label_incoterms";
1766
		$sql .= ', p.code as mode_reglement_code, p.libelle as mode_reglement_libelle';
1767
		$sql .= ', cr.code as cond_reglement_code, cr.libelle as cond_reglement_libelle, cr.libelle_facture as cond_reglement_libelle_doc';
1768
		$sql .= ', ca.code as availability_code, ca.label as availability_label';
1769
		$sql .= ', dr.code as demand_reason_code';
1770
		$sql .= ' FROM '.MAIN_DB_PREFIX.'commande as c';
1771
		$sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_payment_term as cr ON c.fk_cond_reglement = cr.rowid';
1772
		$sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as p ON c.fk_mode_reglement = p.id';
1773
		$sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_availability as ca ON c.fk_availability = ca.rowid';
1774
		$sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_input_reason as dr ON c.fk_input_reason = dr.rowid';
1775
		$sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_incoterms as i ON c.fk_incoterms = i.rowid';
1776
1777
		if ($id) $sql .= " WHERE c.rowid=".$id;
1778
		else $sql .= " WHERE c.entity IN (".getEntity('commande').")"; // Dont't use entity if you use rowid
1779
1780
		if ($ref)     $sql .= " AND c.ref='".$this->db->escape($ref)."'";
1781
		if ($ref_ext) $sql .= " AND c.ref_ext='".$this->db->escape($ref_ext)."'";
1782
		if ($notused) $sql .= " AND c.ref_int='".$this->db->escape($notused)."'";
1783
1784
		dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
1785
		$result = $this->db->query($sql);
1786
		if ($result)
1787
		{
1788
			$obj = $this->db->fetch_object($result);
1789
			if ($obj)
1790
			{
1791
				$this->id = $obj->rowid;
1792
				$this->entity = $obj->entity;
1793
1794
				$this->ref = $obj->ref;
1795
				$this->ref_client = $obj->ref_client;
1796
				$this->ref_customer = $obj->ref_client;
1797
				$this->ref_ext				= $obj->ref_ext;
1798
				$this->ref_int				= $obj->ref_int;
1799
1800
				$this->socid = $obj->fk_soc;
1801
				$this->thirdparty = null; // Clear if another value was already set by fetch_thirdparty
1802
1803
				$this->fk_project = $obj->fk_project;
1804
				$this->project = null; // Clear if another value was already set by fetch_projet
1805
1806
				$this->statut = $obj->fk_statut;
1807
				$this->user_author_id = $obj->fk_user_author;
1808
				$this->user_valid = $obj->fk_user_valid;
1809
				$this->total_ht				= $obj->total_ht;
1810
				$this->total_tva			= $obj->total_tva;
1811
				$this->total_localtax1		= $obj->total_localtax1;
1812
				$this->total_localtax2		= $obj->total_localtax2;
1813
				$this->total_ttc			= $obj->total_ttc;
1814
				$this->date = $this->db->jdate($obj->date_commande);
1815
				$this->date_commande		= $this->db->jdate($obj->date_commande);
1816
				$this->date_creation		= $this->db->jdate($obj->date_creation);
1817
				$this->date_validation = $this->db->jdate($obj->date_valid);
1818
				$this->date_modification = $this->db->jdate($obj->tms);
1819
				$this->remise				= $obj->remise;
1820
				$this->remise_percent		= $obj->remise_percent;
1821
				$this->remise_absolue		= $obj->remise_absolue;
1822
				$this->source				= $obj->source;
1823
				$this->billed				= $obj->billed;
1824
				$this->note = $obj->note_private; // deprecated
1825
				$this->note_private = $obj->note_private;
1826
				$this->note_public = $obj->note_public;
1827
				$this->modelpdf = $obj->model_pdf;
1828
				$this->last_main_doc = $obj->last_main_doc;
1829
				$this->mode_reglement_id	= $obj->fk_mode_reglement;
1830
				$this->mode_reglement_code	= $obj->mode_reglement_code;
1831
				$this->mode_reglement		= $obj->mode_reglement_libelle;
1832
				$this->cond_reglement_id	= $obj->fk_cond_reglement;
1833
				$this->cond_reglement_code	= $obj->cond_reglement_code;
1834
				$this->cond_reglement		= $obj->cond_reglement_libelle;
1835
				$this->cond_reglement_doc = $obj->cond_reglement_libelle_doc;
1836
				$this->fk_account = $obj->fk_account;
1837
				$this->availability_id = $obj->fk_availability;
1838
				$this->availability_code	= $obj->availability_code;
1839
				$this->availability	    	= $obj->availability_label;
1840
				$this->demand_reason_id		= $obj->fk_input_reason;
1841
				$this->demand_reason_code = $obj->demand_reason_code;
1842
				$this->date_livraison = $this->db->jdate($obj->date_livraison);
1843
				$this->shipping_method_id   = ($obj->fk_shipping_method > 0) ? $obj->fk_shipping_method : null;
1844
				$this->warehouse_id         = ($obj->fk_warehouse > 0) ? $obj->fk_warehouse : null;
1845
				$this->fk_delivery_address = $obj->fk_delivery_address;
1846
				$this->module_source        = $obj->module_source;
1847
				$this->pos_source           = $obj->pos_source;
1848
1849
				//Incoterms
1850
				$this->fk_incoterms         = $obj->fk_incoterms;
1851
				$this->location_incoterms   = $obj->location_incoterms;
1852
				$this->label_incoterms    = $obj->label_incoterms;
1853
1854
				// Multicurrency
1855
				$this->fk_multicurrency 		= $obj->fk_multicurrency;
1856
				$this->multicurrency_code = $obj->multicurrency_code;
1857
				$this->multicurrency_tx 		= $obj->multicurrency_tx;
1858
				$this->multicurrency_total_ht = $obj->multicurrency_total_ht;
1859
				$this->multicurrency_total_tva 	= $obj->multicurrency_total_tva;
1860
				$this->multicurrency_total_ttc 	= $obj->multicurrency_total_ttc;
1861
1862
				$this->extraparams = (array) json_decode($obj->extraparams, true);
1863
1864
				$this->lines = array();
1865
1866
				if ($this->statut == self::STATUS_DRAFT) $this->brouillon = 1;
1867
1868
				// Retreive all extrafield
1869
				// fetch optionals attributes and labels
1870
				$this->fetch_optionals();
1871
1872
				$this->db->free($result);
1873
1874
				// Lines
1875
				$result = $this->fetch_lines();
1876
				if ($result < 0)
1877
				{
1878
					return -3;
1879
				}
1880
				return 1;
1881
			} else {
1882
				$this->error = 'Order with id '.$id.' not found sql='.$sql;
1883
				return 0;
1884
			}
1885
		} else {
1886
			$this->error = $this->db->error();
1887
			return -1;
1888
		}
1889
	}
1890
1891
1892
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1893
	/**
1894
	 *	Adding line of fixed discount in the order in DB
1895
	 *
1896
	 *	@param     int	$idremise			Id de la remise fixe
1897
	 *	@return    int          			>0 si ok, <0 si ko
1898
	 */
1899
	public function insert_discount($idremise)
1900
	{
1901
        // phpcs:enable
1902
		global $langs;
1903
1904
		include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
1905
		include_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
1906
1907
		$this->db->begin();
1908
1909
		$remise = new DiscountAbsolute($this->db);
1910
		$result = $remise->fetch($idremise);
1911
1912
		if ($result > 0)
1913
		{
1914
			if ($remise->fk_facture)	// Protection against multiple submission
1915
			{
1916
				$this->error = $langs->trans("ErrorDiscountAlreadyUsed");
1917
				$this->db->rollback();
1918
				return -5;
1919
			}
1920
1921
			$line = new OrderLine($this->db);
1922
1923
			$line->fk_commande = $this->id;
1924
			$line->fk_remise_except = $remise->id;
1925
			$line->desc = $remise->description; // Description ligne
1926
			$line->vat_src_code = $remise->vat_src_code;
1927
			$line->tva_tx = $remise->tva_tx;
1928
			$line->subprice = -$remise->amount_ht;
1929
			$line->price = -$remise->amount_ht;
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

1929
			/** @scrutinizer ignore-deprecated */ $line->price = -$remise->amount_ht;
Loading history...
1930
			$line->fk_product = 0; // Id produit predefini
1931
			$line->qty = 1;
1932
			$line->remise = 0;
1 ignored issue
show
Deprecated Code introduced by
The property OrderLine::$remise 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

1932
			/** @scrutinizer ignore-deprecated */ $line->remise = 0;
Loading history...
1933
			$line->remise_percent = 0;
1934
			$line->rang = -1;
1935
			$line->info_bits = 2;
1936
1937
			$line->total_ht  = -$remise->amount_ht;
1938
			$line->total_tva = -$remise->amount_tva;
1939
			$line->total_ttc = -$remise->amount_ttc;
1940
1941
			$result = $line->insert();
1942
			if ($result > 0)
1943
			{
1944
				$result = $this->update_price(1);
1945
				if ($result > 0)
1946
				{
1947
					$this->db->commit();
1948
					return 1;
1949
				} else {
1950
					$this->db->rollback();
1951
					return -1;
1952
				}
1953
			} else {
1954
				$this->error = $line->error;
1955
				$this->db->rollback();
1956
				return -2;
1957
			}
1958
		} else {
1959
			$this->db->rollback();
1960
			return -2;
1961
		}
1962
	}
1963
1964
1965
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1966
	/**
1967
	 *	Load array lines
1968
	 *
1969
	 *	@param		int		$only_product	Return only physical products, not services
1970
	 *	@param		int		$loadalsotranslation	Return translation for products
1971
	 *	@return		int						<0 if KO, >0 if OK
1972
	 */
1973
	public function fetch_lines($only_product = 0, $loadalsotranslation = 0)
1974
	{
1975
		global $langs, $conf;
1976
        // phpcs:enable
1977
		$this->lines = array();
1978
1979
		$sql = 'SELECT l.rowid, l.fk_product, l.fk_parent_line, l.product_type, l.fk_commande, l.label as custom_label, l.description, l.price, l.qty, l.vat_src_code, l.tva_tx,';
1980
		$sql .= ' l.localtax1_tx, l.localtax2_tx, l.localtax1_type, l.localtax2_type, l.fk_remise_except, l.remise_percent, l.subprice, l.fk_product_fournisseur_price as fk_fournprice, l.buy_price_ht as pa_ht, l.rang, l.info_bits, l.special_code,';
1981
		$sql .= ' l.total_ht, l.total_ttc, l.total_tva, l.total_localtax1, l.total_localtax2, l.date_start, l.date_end,';
1982
		$sql .= ' l.fk_unit,';
1983
		$sql .= ' l.fk_multicurrency, l.multicurrency_code, l.multicurrency_subprice, l.multicurrency_total_ht, l.multicurrency_total_tva, l.multicurrency_total_ttc,';
1984
		$sql .= ' p.ref as product_ref, p.description as product_desc, p.fk_product_type, p.label as product_label, p.tobatch as product_tobatch,';
1985
		$sql .= ' p.weight, p.weight_units, p.volume, p.volume_units';
1986
		$sql .= ' FROM '.MAIN_DB_PREFIX.'commandedet as l';
1987
		$sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON (p.rowid = l.fk_product)';
1988
		$sql .= ' WHERE l.fk_commande = '.$this->id;
1989
		if ($only_product) $sql .= ' AND p.fk_product_type = 0';
1990
		$sql .= ' ORDER BY l.rang, l.rowid';
1991
1992
		dol_syslog(get_class($this)."::fetch_lines", LOG_DEBUG);
1993
		$result = $this->db->query($sql);
1994
		if ($result)
1995
		{
1996
			$num = $this->db->num_rows($result);
1997
1998
			$i = 0;
1999
			while ($i < $num)
2000
			{
2001
				$objp = $this->db->fetch_object($result);
2002
2003
				$line = new OrderLine($this->db);
2004
2005
				$line->rowid            = $objp->rowid;
2006
				$line->id               = $objp->rowid;
2007
				$line->fk_commande      = $objp->fk_commande;
2008
				$line->commande_id      = $objp->fk_commande;
2009
				$line->label            = $objp->custom_label;
2010
				$line->desc             = $objp->description;
2011
				$line->description      = $objp->description; // Description line
2012
				$line->product_type     = $objp->product_type;
2013
				$line->qty              = $objp->qty;
2014
2015
				$line->vat_src_code     = $objp->vat_src_code;
2016
				$line->tva_tx           = $objp->tva_tx;
2017
				$line->localtax1_tx     = $objp->localtax1_tx;
2018
				$line->localtax2_tx     = $objp->localtax2_tx;
2019
				$line->localtax1_type	= $objp->localtax1_type;
2020
				$line->localtax2_type	= $objp->localtax2_type;
2021
				$line->total_ht         = $objp->total_ht;
2022
				$line->total_ttc        = $objp->total_ttc;
2023
				$line->total_tva        = $objp->total_tva;
2024
				$line->total_localtax1  = $objp->total_localtax1;
2025
				$line->total_localtax2  = $objp->total_localtax2;
2026
				$line->subprice         = $objp->subprice;
2027
				$line->fk_remise_except = $objp->fk_remise_except;
2028
				$line->remise_percent   = $objp->remise_percent;
2029
				$line->price            = $objp->price;
2030
				$line->fk_product       = $objp->fk_product;
2031
				$line->fk_fournprice = $objp->fk_fournprice;
2032
				$marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $line->fk_fournprice, $objp->pa_ht);
2033
				$line->pa_ht = $marginInfos[0];
2034
				$line->marge_tx			= $marginInfos[1];
2035
				$line->marque_tx		= $marginInfos[2];
2036
				$line->rang             = $objp->rang;
2037
				$line->info_bits        = $objp->info_bits;
2038
				$line->special_code = $objp->special_code;
2039
				$line->fk_parent_line = $objp->fk_parent_line;
2040
2041
				$line->ref = $objp->product_ref;
2042
				$line->product_ref = $objp->product_ref;
2043
				$line->libelle = $objp->product_label;
2044
				$line->product_label = $objp->product_label;
2045
				$line->product_desc     = $objp->product_desc;
2046
				$line->product_tobatch  = $objp->product_tobatch;
2047
				$line->fk_product_type  = $objp->fk_product_type; // Produit ou service
2048
				$line->fk_unit          = $objp->fk_unit;
2049
2050
				$line->weight           = $objp->weight;
2051
				$line->weight_units     = $objp->weight_units;
2052
				$line->volume           = $objp->volume;
2053
				$line->volume_units     = $objp->volume_units;
2054
2055
				$line->date_start       = $this->db->jdate($objp->date_start);
2056
				$line->date_end         = $this->db->jdate($objp->date_end);
2057
2058
				// Multicurrency
2059
				$line->fk_multicurrency = $objp->fk_multicurrency;
2060
				$line->multicurrency_code = $objp->multicurrency_code;
2061
				$line->multicurrency_subprice 	= $objp->multicurrency_subprice;
2062
				$line->multicurrency_total_ht 	= $objp->multicurrency_total_ht;
2063
				$line->multicurrency_total_tva 	= $objp->multicurrency_total_tva;
2064
				$line->multicurrency_total_ttc 	= $objp->multicurrency_total_ttc;
2065
2066
				$line->fetch_optionals();
2067
2068
				// multilangs
2069
        		if (!empty($conf->global->MAIN_MULTILANGS) && !empty($objp->fk_product) && !empty($loadalsotranslation)) {
2070
            		$line = new Product($this->db);
2071
            		$line->fetch($objp->fk_product);
2072
            		$line->getMultiLangs();
2073
        		}
2074
2075
                $this->lines[$i] = $line;
2076
2077
				$i++;
2078
			}
2079
2080
			$this->db->free($result);
2081
2082
			return 1;
2083
		} else {
2084
			$this->error = $this->db->error();
2085
			return -3;
2086
		}
2087
	}
2088
2089
2090
	/**
2091
	 *	Return number of line with type product.
2092
	 *
2093
	 *	@return		int		<0 if KO, Nbr of product lines if OK
2094
	 */
2095
	public function getNbOfProductsLines()
2096
	{
2097
		$nb = 0;
2098
		foreach ($this->lines as $line)
2099
		{
2100
			if ($line->product_type == 0) $nb++;
2101
		}
2102
		return $nb;
2103
	}
2104
2105
	/**
2106
	 *	Return number of line with type service.
2107
	 *
2108
	 *	@return		int		<0 if KO, Nbr of service lines if OK
2109
	 */
2110
	public function getNbOfServicesLines()
2111
	{
2112
		$nb = 0;
2113
		foreach ($this->lines as $line)
2114
		{
2115
			if ($line->product_type == 1) $nb++;
2116
		}
2117
		return $nb;
2118
	}
2119
2120
	/**
2121
	 *	Count number of shipments for this order
2122
	 *
2123
	 * 	@return     int                			<0 if KO, Nb of shipment found if OK
2124
	 */
2125
	public function getNbOfShipments()
2126
	{
2127
		$nb = 0;
2128
2129
		$sql = 'SELECT COUNT(DISTINCT ed.fk_expedition) as nb';
2130
		$sql .= ' FROM '.MAIN_DB_PREFIX.'expeditiondet as ed,';
2131
		$sql .= ' '.MAIN_DB_PREFIX.'commandedet as cd';
2132
		$sql .= ' WHERE';
2133
		$sql .= ' ed.fk_origin_line = cd.rowid';
2134
		$sql .= ' AND cd.fk_commande ='.$this->id;
2135
		//print $sql;
2136
2137
		dol_syslog(get_class($this)."::getNbOfShipments", LOG_DEBUG);
2138
		$resql = $this->db->query($sql);
2139
		if ($resql)
2140
		{
2141
			$obj = $this->db->fetch_object($resql);
2142
			if ($obj) $nb = $obj->nb;
2143
2144
			$this->db->free($resql);
2145
			return $nb;
2146
		} else {
2147
			$this->error = $this->db->lasterror();
2148
			return -1;
2149
		}
2150
	}
2151
2152
	/**
2153
	 *	Load array this->expeditions of lines of shipments with nb of products sent for each order line
2154
	 *  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
2155
	 *
2156
	 *	@param      int		$filtre_statut      Filter on shipment status
2157
	 * 	@return     int                			<0 if KO, Nb of lines found if OK
2158
	 */
2159
	public function loadExpeditions($filtre_statut = -1)
2160
	{
2161
		$this->expeditions = array();
2162
2163
		$sql = 'SELECT cd.rowid, cd.fk_product,';
2164
		$sql .= ' sum(ed.qty) as qty';
2165
		$sql .= ' FROM '.MAIN_DB_PREFIX.'expeditiondet as ed,';
2166
		if ($filtre_statut >= 0) $sql .= ' '.MAIN_DB_PREFIX.'expedition as e,';
2167
		$sql .= ' '.MAIN_DB_PREFIX.'commandedet as cd';
2168
		$sql .= ' WHERE';
2169
		if ($filtre_statut >= 0) $sql .= ' ed.fk_expedition = e.rowid AND';
2170
		$sql .= ' ed.fk_origin_line = cd.rowid';
2171
		$sql .= ' AND cd.fk_commande ='.$this->id;
2172
		if ($this->fk_product > 0) $sql .= ' AND cd.fk_product = '.$this->fk_product;
2173
		if ($filtre_statut >= 0) $sql .= ' AND e.fk_statut >= '.$filtre_statut;
2174
		$sql .= ' GROUP BY cd.rowid, cd.fk_product';
2175
		//print $sql;
2176
2177
		dol_syslog(get_class($this)."::loadExpeditions", LOG_DEBUG);
2178
		$resql = $this->db->query($sql);
2179
		if ($resql)
2180
		{
2181
			$num = $this->db->num_rows($resql);
2182
			$i = 0;
2183
			while ($i < $num)
2184
			{
2185
				$obj = $this->db->fetch_object($resql);
2186
				$this->expeditions[$obj->rowid] = $obj->qty;
2187
				$i++;
2188
			}
2189
			$this->db->free($resql);
2190
			return $num;
2191
		} else {
2192
			$this->error = $this->db->lasterror();
2193
			return -1;
2194
		}
2195
	}
2196
2197
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2198
	/**
2199
	 * Returns a array with expeditions lines number
2200
	 *
2201
	 * @return	int		Nb of shipments
2202
	 *
2203
	 * TODO deprecate, move to Shipping class
2204
	 */
2205
	public function nb_expedition()
2206
	{
2207
        // phpcs:enable
2208
		$sql = 'SELECT count(*)';
2209
		$sql .= ' FROM '.MAIN_DB_PREFIX.'expedition as e';
2210
		$sql .= ', '.MAIN_DB_PREFIX.'element_element as el';
2211
		$sql .= ' WHERE el.fk_source = '.$this->id;
2212
		$sql .= " AND el.fk_target = e.rowid";
2213
		$sql .= " AND el.targettype = 'shipping'";
2214
2215
		$resql = $this->db->query($sql);
2216
		if ($resql)
2217
		{
2218
			$row = $this->db->fetch_row($resql);
2219
			return $row[0];
2220
		} else dol_print_error($this->db);
2221
	}
2222
2223
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2224
	/**
2225
	 *	Return a array with the pending stock by product
2226
	 *
2227
	 *	@param      int		$filtre_statut      Filtre sur statut
2228
	 *	@return     int                 		0 si OK, <0 si KO
2229
	 *
2230
	 *	TODO		FONCTION NON FINIE A FINIR
2231
	 */
2232
	public function stock_array($filtre_statut = self::STATUS_CANCELED)
2233
	{
2234
        // phpcs:enable
2235
		$this->stocks = array();
2236
2237
		// Tableau des id de produit de la commande
2238
		$array_of_product = array();
2239
2240
		// Recherche total en stock pour chaque produit
2241
		// TODO $array_of_product est défini vide juste au dessus !!
2242
		if (count($array_of_product))
2243
		{
2244
			$sql = "SELECT fk_product, sum(ps.reel) as total";
2245
			$sql .= " FROM ".MAIN_DB_PREFIX."product_stock as ps";
2246
			$sql .= " WHERE ps.fk_product IN (".join(',', $array_of_product).")";
2247
			$sql .= ' GROUP BY fk_product ';
2248
			$resql = $this->db->query($sql);
2249
			if ($resql)
2250
			{
2251
				$num = $this->db->num_rows($resql);
2252
				$i = 0;
2253
				while ($i < $num)
2254
				{
2255
					$obj = $this->db->fetch_object($resql);
2256
					$this->stocks[$obj->fk_product] = $obj->total;
2257
					$i++;
2258
				}
2259
				$this->db->free($resql);
2260
			}
2261
		}
2262
		return 0;
2263
	}
2264
2265
	/**
2266
	 *  Delete an order line
2267
	 *
2268
	 *	@param      User	$user		User object
2269
	 *  @param      int		$lineid		Id of line to delete
2270
	 *  @return     int        		 	>0 if OK, 0 if nothing to do, <0 if KO
2271
	 */
2272
	public function deleteline($user = null, $lineid = 0)
2273
	{
2274
		if ($this->statut == self::STATUS_DRAFT)
2275
		{
2276
			$this->db->begin();
2277
2278
			$sql = "SELECT fk_product, qty";
2279
			$sql .= " FROM ".MAIN_DB_PREFIX."commandedet";
2280
			$sql .= " WHERE rowid = ".$lineid;
2281
2282
			$result = $this->db->query($sql);
2283
			if ($result)
2284
			{
2285
				$obj = $this->db->fetch_object($result);
2286
2287
				if ($obj)
2288
				{
2289
					$product = new Product($this->db);
2290
					$product->id = $obj->fk_product;
2291
2292
					// Delete line
2293
					$line = new OrderLine($this->db);
2294
2295
					// For triggers
2296
					$line->fetch($lineid);
2297
2298
					if ($line->delete($user) > 0)
2299
					{
2300
						$result = $this->update_price(1);
2301
2302
						if ($result > 0)
2303
						{
2304
							$this->db->commit();
2305
							return 1;
2306
						} else {
2307
							$this->db->rollback();
2308
							$this->error = $this->db->lasterror();
2309
							return -1;
2310
						}
2311
					} else {
2312
						$this->db->rollback();
2313
						$this->error = $line->error;
2314
						return -1;
2315
					}
2316
				} else {
2317
					$this->db->rollback();
2318
					return 0;
2319
				}
2320
			} else {
2321
				$this->db->rollback();
2322
				$this->error = $this->db->lasterror();
2323
				return -1;
2324
			}
2325
		} else {
2326
			$this->error = 'ErrorDeleteLineNotAllowedByObjectStatus';
2327
			return -1;
2328
		}
2329
	}
2330
2331
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2332
	/**
2333
	 * 	Applique une remise relative
2334
	 *
2335
	 * 	@param     	User		$user		User qui positionne la remise
2336
	 * 	@param     	float		$remise		Discount (percent)
2337
	 * 	@param     	int			$notrigger	1=Does not execute triggers, 0= execute triggers
2338
	 *	@return		int 					<0 if KO, >0 if OK
2339
	 */
2340
	public function set_remise($user, $remise, $notrigger = 0)
2341
	{
2342
        // phpcs:enable
2343
		$remise = trim($remise) ?trim($remise) : 0;
2344
2345
		if ($user->rights->commande->creer)
2346
		{
2347
			$error = 0;
2348
2349
			$this->db->begin();
2350
2351
			$remise = price2num($remise);
2352
2353
			$sql = 'UPDATE '.MAIN_DB_PREFIX.'commande';
2354
			$sql .= ' SET remise_percent = '.$remise;
2355
			$sql .= ' WHERE rowid = '.$this->id.' AND fk_statut = '.self::STATUS_DRAFT.' ;';
2356
2357
			dol_syslog(__METHOD__, LOG_DEBUG);
2358
			$resql = $this->db->query($sql);
2359
			if (!$resql)
2360
			{
2361
				$this->errors[] = $this->db->error();
2362
				$error++;
2363
			}
2364
2365
			if (!$error)
2366
			{
2367
				$this->oldcopy = clone $this;
2368
				$this->remise_percent = $remise;
2369
				$this->update_price(1);
2370
			}
2371
2372
			if (!$notrigger && empty($error))
2373
			{
2374
				// Call trigger
2375
				$result = $this->call_trigger('ORDER_MODIFY', $user);
2376
				if ($result < 0) $error++;
2377
				// End call triggers
2378
			}
2379
2380
			if (!$error)
2381
			{
2382
				$this->db->commit();
2383
				return 1;
2384
			} else {
2385
				foreach ($this->errors as $errmsg)
2386
				{
2387
					dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2388
					$this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2389
				}
2390
				$this->db->rollback();
2391
				return -1 * $error;
2392
			}
2393
		}
2394
	}
2395
2396
2397
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2398
	/**
2399
	 * 		Applique une remise absolue
2400
	 *
2401
	 * 		@param     	User		$user 		User qui positionne la remise
2402
	 * 		@param     	float		$remise		Discount
2403
	 * 		@param     	int			$notrigger	1=Does not execute triggers, 0= execute triggers
2404
	 *		@return		int 					<0 if KO, >0 if OK
2405
	 */
2406
	public function set_remise_absolue($user, $remise, $notrigger = 0)
2407
	{
2408
        // phpcs:enable
2409
		$remise = trim($remise) ?trim($remise) : 0;
2410
2411
		if ($user->rights->commande->creer)
2412
		{
2413
			$error = 0;
2414
2415
			$this->db->begin();
2416
2417
			$remise = price2num($remise);
2418
2419
			$sql = 'UPDATE '.MAIN_DB_PREFIX.'commande';
2420
			$sql .= ' SET remise_absolue = '.$remise;
2421
			$sql .= ' WHERE rowid = '.$this->id.' AND fk_statut = '.self::STATUS_DRAFT.' ;';
2422
2423
			dol_syslog(__METHOD__, LOG_DEBUG);
2424
			$resql = $this->db->query($sql);
2425
			if (!$resql)
2426
			{
2427
				$this->errors[] = $this->db->error();
2428
				$error++;
2429
			}
2430
2431
			if (!$error)
2432
			{
2433
				$this->oldcopy = clone $this;
2434
				$this->remise_absolue = $remise;
2435
				$this->update_price(1);
2436
			}
2437
2438
			if (!$notrigger && empty($error))
2439
			{
2440
				// Call trigger
2441
				$result = $this->call_trigger('ORDER_MODIFY', $user);
2442
				if ($result < 0) $error++;
2443
				// End call triggers
2444
			}
2445
2446
			if (!$error)
2447
			{
2448
				$this->db->commit();
2449
				return 1;
2450
			} else {
2451
				foreach ($this->errors as $errmsg)
2452
				{
2453
					dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2454
					$this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2455
				}
2456
				$this->db->rollback();
2457
				return -1 * $error;
2458
			}
2459
		}
2460
	}
2461
2462
2463
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2464
	/**
2465
	 *	Set the order date
2466
	 *
2467
	 *	@param      User	$user       Object user making change
2468
	 *	@param      int		$date		Date
2469
	 * 	@param     	int		$notrigger	1=Does not execute triggers, 0= execute triggers
2470
	 *	@return     int         		<0 if KO, >0 if OK
2471
	 */
2472
	public function set_date($user, $date, $notrigger = 0)
2473
	{
2474
        // phpcs:enable
2475
		if ($user->rights->commande->creer)
2476
		{
2477
			$error = 0;
2478
2479
			$this->db->begin();
2480
2481
			$sql = "UPDATE ".MAIN_DB_PREFIX."commande";
2482
			$sql .= " SET date_commande = ".($date ? "'".$this->db->idate($date)."'" : 'null');
2483
			$sql .= " WHERE rowid = ".$this->id." AND fk_statut = ".self::STATUS_DRAFT;
2484
2485
			dol_syslog(__METHOD__, LOG_DEBUG);
2486
			$resql = $this->db->query($sql);
2487
			if (!$resql)
2488
			{
2489
				$this->errors[] = $this->db->error();
2490
				$error++;
2491
			}
2492
2493
			if (!$error)
2494
			{
2495
				$this->oldcopy = clone $this;
2496
				$this->date = $date;
2497
			}
2498
2499
			if (!$notrigger && empty($error))
2500
			{
2501
				// Call trigger
2502
				$result = $this->call_trigger('ORDER_MODIFY', $user);
2503
				if ($result < 0) $error++;
2504
				// End call triggers
2505
			}
2506
2507
			if (!$error)
2508
			{
2509
				$this->db->commit();
2510
				return 1;
2511
			} else {
2512
				foreach ($this->errors as $errmsg)
2513
				{
2514
					dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2515
					$this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2516
				}
2517
				$this->db->rollback();
2518
				return -1 * $error;
2519
			}
2520
		} else {
2521
			return -2;
2522
		}
2523
	}
2524
2525
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2526
	/**
2527
	 *	Set the planned delivery date
2528
	 *
2529
	 *	@param      User	$user        		Objet utilisateur qui modifie
2530
	 *	@param      int		$date_livraison     Date de livraison
2531
	 *  @param     	int		$notrigger			1=Does not execute triggers, 0= execute triggers
2532
	 *	@return     int         				<0 si ko, >0 si ok
2533
	 */
2534
	public function set_date_livraison($user, $date_livraison, $notrigger = 0)
2535
	{
2536
        // phpcs:enable
2537
		if ($user->rights->commande->creer)
2538
		{
2539
			$error = 0;
2540
2541
			$this->db->begin();
2542
2543
			$sql = "UPDATE ".MAIN_DB_PREFIX."commande";
2544
			$sql .= " SET date_livraison = ".($date_livraison ? "'".$this->db->idate($date_livraison)."'" : 'null');
2545
			$sql .= " WHERE rowid = ".$this->id;
2546
2547
			dol_syslog(__METHOD__, LOG_DEBUG);
2548
			$resql = $this->db->query($sql);
2549
			if (!$resql)
2550
			{
2551
				$this->errors[] = $this->db->error();
2552
				$error++;
2553
			}
2554
2555
			if (!$error)
2556
			{
2557
				$this->oldcopy = clone $this;
2558
				$this->date_livraison = $date_livraison;
2559
			}
2560
2561
			if (!$notrigger && empty($error))
2562
			{
2563
				// Call trigger
2564
				$result = $this->call_trigger('ORDER_MODIFY', $user);
2565
				if ($result < 0) $error++;
2566
				// End call triggers
2567
			}
2568
2569
			if (!$error)
2570
			{
2571
				$this->db->commit();
2572
				return 1;
2573
			} else {
2574
				foreach ($this->errors as $errmsg)
2575
				{
2576
					dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2577
					$this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2578
				}
2579
				$this->db->rollback();
2580
				return -1 * $error;
2581
			}
2582
		} else {
2583
			return -2;
2584
		}
2585
	}
2586
2587
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2588
	/**
2589
	 *  Return list of orders (eventuelly filtered on a user) into an array
2590
	 *
2591
	 *  @param		int		$shortlist		0=Return array[id]=ref, 1=Return array[](id=>id,ref=>ref,name=>name)
2592
	 *  @param      int		$draft      	0=not draft, 1=draft
2593
	 *  @param      User	$excluser      	Objet user to exclude
2594
	 *  @param    	int		$socid			Id third pary
2595
	 *  @param    	int		$limit			For pagination
2596
	 *  @param    	int		$offset			For pagination
2597
	 *  @param    	string	$sortfield		Sort criteria
2598
	 *  @param    	string	$sortorder		Sort order
2599
	 *  @return     int             		-1 if KO, array with result if OK
2600
	 */
2601
	public function liste_array($shortlist = 0, $draft = 0, $excluser = '', $socid = 0, $limit = 0, $offset = 0, $sortfield = 'c.date_commande', $sortorder = 'DESC')
2602
	{
2603
        // phpcs:enable
2604
		global $user;
2605
2606
		$ga = array();
2607
2608
		$sql = "SELECT s.rowid, s.nom as name, s.client,";
2609
		$sql .= " c.rowid as cid, c.ref";
2610
		if (!$user->rights->societe->client->voir && !$socid) $sql .= ", sc.fk_soc, sc.fk_user";
2611
		$sql .= " FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."commande as c";
2612
		if (!$user->rights->societe->client->voir && !$socid) $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc";
2613
		$sql .= " WHERE c.entity IN (".getEntity('commande').")";
2614
		$sql .= " AND c.fk_soc = s.rowid";
2615
		if (!$user->rights->societe->client->voir && !$socid) //restriction
2616
		{
2617
			$sql .= " AND s.rowid = sc.fk_soc AND sc.fk_user = ".$user->id;
2618
		}
2619
		if ($socid) $sql .= " AND s.rowid = ".$socid;
2620
		if ($draft) $sql .= " AND c.fk_statut = ".self::STATUS_DRAFT;
2621
		if (is_object($excluser)) $sql .= " AND c.fk_user_author <> ".$excluser->id;
2622
		$sql .= $this->db->order($sortfield, $sortorder);
2623
		$sql .= $this->db->plimit($limit, $offset);
2624
2625
		$result = $this->db->query($sql);
2626
		if ($result)
2627
		{
2628
			$numc = $this->db->num_rows($result);
2629
			if ($numc)
2630
			{
2631
				$i = 0;
2632
				while ($i < $numc)
2633
				{
2634
					$obj = $this->db->fetch_object($result);
2635
2636
					if ($shortlist == 1)
2637
					{
2638
						$ga[$obj->cid] = $obj->ref;
2639
					} elseif ($shortlist == 2)
2640
					{
2641
						$ga[$obj->cid] = $obj->ref.' ('.$obj->name.')';
2642
					} else {
2643
						$ga[$i]['id'] = $obj->cid;
2644
						$ga[$i]['ref'] 	= $obj->ref;
2645
						$ga[$i]['name'] = $obj->name;
2646
					}
2647
					$i++;
2648
				}
2649
			}
2650
			return $ga;
2651
		} else {
2652
			dol_print_error($this->db);
2653
			return -1;
2654
		}
2655
	}
2656
2657
	/**
2658
	 *	Update delivery delay
2659
	 *
2660
	 *	@param      int		$availability_id	Id du nouveau mode
2661
	 *  @param     	int		$notrigger			1=Does not execute triggers, 0= execute triggers
2662
	 *	@return     int         				>0 if OK, <0 if KO
2663
	 */
2664
	public function availability($availability_id, $notrigger = 0)
2665
	{
2666
		global $user;
2667
2668
		dol_syslog('Commande::availability('.$availability_id.')');
2669
		if ($this->statut >= self::STATUS_DRAFT)
2670
		{
2671
			$error = 0;
2672
2673
			$this->db->begin();
2674
2675
			$sql = 'UPDATE '.MAIN_DB_PREFIX.'commande';
2676
			$sql .= ' SET fk_availability = '.$availability_id;
2677
			$sql .= ' WHERE rowid='.$this->id;
2678
2679
			dol_syslog(__METHOD__, LOG_DEBUG);
2680
			$resql = $this->db->query($sql);
2681
			if (!$resql)
2682
			{
2683
				$this->errors[] = $this->db->error();
2684
				$error++;
2685
			}
2686
2687
			if (!$error)
2688
			{
2689
				$this->oldcopy = clone $this;
2690
				$this->availability_id = $availability_id;
2691
			}
2692
2693
			if (!$notrigger && empty($error))
2694
			{
2695
				// Call trigger
2696
				$result = $this->call_trigger('ORDER_MODIFY', $user);
2697
				if ($result < 0) $error++;
2698
				// End call triggers
2699
			}
2700
2701
			if (!$error)
2702
			{
2703
				$this->db->commit();
2704
				return 1;
2705
			} else {
2706
				foreach ($this->errors as $errmsg)
2707
				{
2708
					dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2709
					$this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2710
				}
2711
				$this->db->rollback();
2712
				return -1 * $error;
2713
			}
2714
		} else {
2715
			$error_str = 'Command status do not meet requirement '.$this->statut;
2716
			dol_syslog(__METHOD__.$error_str, LOG_ERR);
2717
			$this->error = $error_str;
2718
			$this->errors[] = $this->error;
2719
			return -2;
2720
		}
2721
	}
2722
2723
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2724
	/**
2725
	 *	Update order demand_reason
2726
	 *
2727
	 *  @param      int		$demand_reason_id	Id of new demand
2728
	 *  @param     	int		$notrigger			1=Does not execute triggers, 0= execute triggers
2729
	 *  @return     int        			 		>0 if ok, <0 if ko
2730
	 */
2731
	public function demand_reason($demand_reason_id, $notrigger = 0)
2732
	{
2733
        // phpcs:enable
2734
		global $user;
2735
2736
		dol_syslog('Commande::demand_reason('.$demand_reason_id.')');
2737
		if ($this->statut >= self::STATUS_DRAFT)
2738
		{
2739
			$error = 0;
2740
2741
			$this->db->begin();
2742
2743
			$sql = 'UPDATE '.MAIN_DB_PREFIX.'commande';
2744
			$sql .= ' SET fk_input_reason = '.$demand_reason_id;
2745
			$sql .= ' WHERE rowid='.$this->id;
2746
2747
			dol_syslog(__METHOD__, LOG_DEBUG);
2748
			$resql = $this->db->query($sql);
2749
			if (!$resql)
2750
			{
2751
				$this->errors[] = $this->db->error();
2752
				$error++;
2753
			}
2754
2755
			if (!$error)
2756
			{
2757
				$this->oldcopy = clone $this;
2758
				$this->demand_reason_id = $demand_reason_id;
2759
			}
2760
2761
			if (!$notrigger && empty($error))
2762
			{
2763
				// Call trigger
2764
				$result = $this->call_trigger('ORDER_MODIFY', $user);
2765
				if ($result < 0) $error++;
2766
				// End call triggers
2767
			}
2768
2769
			if (!$error)
2770
			{
2771
				$this->db->commit();
2772
				return 1;
2773
			} else {
2774
				foreach ($this->errors as $errmsg)
2775
				{
2776
					dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2777
					$this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2778
				}
2779
				$this->db->rollback();
2780
				return -1 * $error;
2781
			}
2782
		} else {
2783
			$error_str = 'order status do not meet requirement '.$this->statut;
2784
			dol_syslog(__METHOD__.$error_str, LOG_ERR);
2785
			$this->error = $error_str;
2786
			$this->errors[] = $this->error;
2787
			return -2;
2788
		}
2789
	}
2790
2791
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2792
	/**
2793
	 *	Set customer ref
2794
	 *
2795
	 *	@param      User	$user           User that make change
2796
	 *	@param      string	$ref_client     Customer ref
2797
	 *  @param     	int		$notrigger		1=Does not execute triggers, 0= execute triggers
2798
	 *	@return     int             		<0 if KO, >0 if OK
2799
	 */
2800
	public function set_ref_client($user, $ref_client, $notrigger = 0)
2801
	{
2802
        // phpcs:enable
2803
		if ($user->rights->commande->creer)
2804
		{
2805
			$error = 0;
2806
2807
			$this->db->begin();
2808
2809
			$sql = 'UPDATE '.MAIN_DB_PREFIX.'commande SET';
2810
			$sql .= ' ref_client = '.(empty($ref_client) ? 'NULL' : '\''.$this->db->escape($ref_client).'\'');
2811
			$sql .= ' WHERE rowid = '.$this->id;
2812
2813
			dol_syslog(__METHOD__.' this->id='.$this->id.', ref_client='.$ref_client, LOG_DEBUG);
2814
			$resql = $this->db->query($sql);
2815
			if (!$resql)
2816
			{
2817
				$this->errors[] = $this->db->error();
2818
				$error++;
2819
			}
2820
2821
			if (!$error)
2822
			{
2823
				$this->oldcopy = clone $this;
2824
				$this->ref_client = $ref_client;
2825
			}
2826
2827
			if (!$notrigger && empty($error))
2828
			{
2829
				// Call trigger
2830
				$result = $this->call_trigger('ORDER_MODIFY', $user);
2831
				if ($result < 0) $error++;
2832
				// End call triggers
2833
			}
2834
			if (!$error)
2835
			{
2836
				$this->db->commit();
2837
				return 1;
2838
			} else {
2839
				foreach ($this->errors as $errmsg)
2840
				{
2841
					dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
2842
					$this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2843
				}
2844
				$this->db->rollback();
2845
				return -1 * $error;
2846
			}
2847
		} else {
2848
			return -1;
2849
		}
2850
	}
2851
2852
	/**
2853
	 * Classify the order as invoiced
2854
	 *
2855
	 * @param	User    $user       Object user making the change
2856
	 * @param	int		$notrigger	1=Does not execute triggers, 0= execute triggers
2857
	 * @return	int                 <0 if KO, >0 if OK
2858
	 */
2859
	public function classifyBilled(User $user, $notrigger = 0)
2860
	{
2861
		$error = 0;
2862
2863
		$this->db->begin();
2864
2865
		$sql = 'UPDATE '.MAIN_DB_PREFIX.'commande SET facture = 1';
2866
		$sql .= ' WHERE rowid = '.$this->id.' AND fk_statut > '.self::STATUS_DRAFT;
2867
2868
		dol_syslog(get_class($this)."::classifyBilled", LOG_DEBUG);
2869
		if ($this->db->query($sql))
2870
		{
2871
			if (!$error)
2872
			{
2873
				$this->oldcopy = clone $this;
2874
				$this->billed = 1;
2875
			}
2876
2877
			if (!$notrigger && empty($error))
2878
			{
2879
				// Call trigger
2880
				$result = $this->call_trigger('ORDER_CLASSIFY_BILLED', $user);
2881
				if ($result < 0) $error++;
2882
				// End call triggers
2883
			}
2884
2885
			if (!$error)
2886
			{
2887
				$this->db->commit();
2888
				return 1;
2889
			} else {
2890
				foreach ($this->errors as $errmsg)
2891
				{
2892
					dol_syslog(get_class($this)."::classifyBilled ".$errmsg, LOG_ERR);
2893
					$this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2894
				}
2895
				$this->db->rollback();
2896
				return -1 * $error;
2897
			}
2898
		} else {
2899
			$this->error = $this->db->error();
2900
			$this->db->rollback();
2901
			return -1;
2902
		}
2903
	}
2904
2905
	/**
2906
	 * Classify the order as not invoiced
2907
	 *
2908
	 * @return     int     <0 if ko, >0 if ok
2909
	 */
2910
	public function classifyUnBilled()
2911
	{
2912
		global $conf, $user, $langs;
2913
		$error = 0;
2914
2915
		$this->db->begin();
2916
2917
		$sql = 'UPDATE '.MAIN_DB_PREFIX.'commande SET facture = 0';
2918
		$sql .= ' WHERE rowid = '.$this->id.' AND fk_statut > '.self::STATUS_DRAFT;
2919
2920
		dol_syslog(get_class($this)."::classifyUnBilled", LOG_DEBUG);
2921
		if ($this->db->query($sql))
2922
		{
2923
			if (!$error)
2924
			{
2925
				$this->oldcopy = clone $this;
2926
				$this->billed = 1;
2927
			}
2928
2929
			// Call trigger
2930
			$result = $this->call_trigger('ORDER_CLASSIFY_UNBILLED', $user);
2931
			if ($result < 0) $error++;
2932
			// End call triggers
2933
2934
			if (!$error)
2935
			{
2936
				$this->billed = 0;
2937
2938
				$this->db->commit();
2939
				return 1;
2940
			} else {
2941
				foreach ($this->errors as $errmsg)
2942
				{
2943
					dol_syslog(get_class($this)."::classifyUnBilled ".$errmsg, LOG_ERR);
2944
					$this->error .= ($this->error ? ', '.$errmsg : $errmsg);
2945
				}
2946
				$this->db->rollback();
2947
				return -1 * $error;
2948
			}
2949
		} else {
2950
			$this->error = $this->db->error();
2951
			$this->db->rollback();
2952
			return -1;
2953
		}
2954
	}
2955
2956
2957
	/**
2958
	 *  Update a line in database
2959
	 *
2960
	 *  @param    	int				$rowid            	Id of line to update
2961
	 *  @param    	string			$desc             	Description of line
2962
	 *  @param    	float			$pu               	Unit price
2963
	 *  @param    	float			$qty              	Quantity
2964
	 *  @param    	float			$remise_percent   	Percent of discount
2965
	 *  @param    	float			$txtva           	Taux TVA
2966
	 * 	@param		float			$txlocaltax1		Local tax 1 rate
2967
	 *  @param		float			$txlocaltax2		Local tax 2 rate
2968
	 *  @param    	string			$price_base_type	HT or TTC
2969
	 *  @param    	int				$info_bits        	Miscellaneous informations on line
2970
	 *  @param    	int				$date_start        	Start date of the line
2971
	 *  @param    	int				$date_end          	End date of the line
2972
	 * 	@param		int				$type				Type of line (0=product, 1=service)
2973
	 * 	@param		int				$fk_parent_line		Id of parent line (0 in most cases, used by modules adding sublevels into lines).
2974
	 * 	@param		int				$skip_update_total	Keep fields total_xxx to 0 (used for special lines by some modules)
2975
	 *  @param		int				$fk_fournprice		Id of origin supplier price
2976
	 *  @param		int				$pa_ht				Price (without tax) of product when it was bought
2977
	 *  @param		string			$label				Label
2978
	 *  @param		int				$special_code		Special code (also used by externals modules!)
2979
	 *  @param		array			$array_options		extrafields array
2980
	 * 	@param 		string			$fk_unit 			Code of the unit to use. Null to use the default one
2981
	 *  @param		double			$pu_ht_devise		Amount in currency
2982
	 * 	@param		int				$notrigger			disable line update trigger
2983
	 *  @return   	int              					< 0 if KO, > 0 if OK
2984
	 */
2985
	public function updateline($rowid, $desc, $pu, $qty, $remise_percent, $txtva, $txlocaltax1 = 0.0, $txlocaltax2 = 0.0, $price_base_type = 'HT', $info_bits = 0, $date_start = '', $date_end = '', $type = 0, $fk_parent_line = 0, $skip_update_total = 0, $fk_fournprice = null, $pa_ht = 0, $label = '', $special_code = 0, $array_options = 0, $fk_unit = null, $pu_ht_devise = 0, $notrigger = 0)
2986
	{
2987
		global $conf, $mysoc, $langs, $user;
2988
2989
		dol_syslog(get_class($this)."::updateline id=$rowid, desc=$desc, pu=$pu, qty=$qty, remise_percent=$remise_percent, txtva=$txtva, txlocaltax1=$txlocaltax1, txlocaltax2=$txlocaltax2, price_base_type=$price_base_type, info_bits=$info_bits, date_start=$date_start, date_end=$date_end, type=$type, fk_parent_line=$fk_parent_line, pa_ht=$pa_ht, special_code=$special_code");
2990
		include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2991
2992
		if ($this->statut == Commande::STATUS_DRAFT)
2993
		{
2994
			// Clean parameters
2995
			if (empty($qty)) $qty = 0;
2996
			if (empty($info_bits)) $info_bits = 0;
2997
			if (empty($txtva)) $txtva = 0;
2998
			if (empty($txlocaltax1)) $txlocaltax1 = 0;
2999
			if (empty($txlocaltax2)) $txlocaltax2 = 0;
3000
			if (empty($remise_percent)) $remise_percent = 0;
3001
			if (empty($special_code) || $special_code == 3) $special_code = 0;
3002
3003
			if ($date_start && $date_end && $date_start > $date_end) {
3004
				$langs->load("errors");
3005
				$this->error = $langs->trans('ErrorStartDateGreaterEnd');
3006
				return -1;
3007
			}
3008
3009
			$remise_percent = price2num($remise_percent);
3010
			$qty = price2num($qty);
3011
			$pu = price2num($pu);
3012
			$pa_ht = price2num($pa_ht);
3013
			$pu_ht_devise = price2num($pu_ht_devise);
3014
			$txtva = price2num($txtva);
3015
			$txlocaltax1 = price2num($txlocaltax1);
3016
			$txlocaltax2 = price2num($txlocaltax2);
3017
3018
			$this->db->begin();
3019
3020
			// Calcul du total TTC et de la TVA pour la ligne a partir de
3021
			// qty, pu, remise_percent et txtva
3022
			// TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
3023
			// la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
3024
3025
			$localtaxes_type = getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc);
3026
3027
			// Clean vat code
3028
			$vat_src_code = '';
3029
			if (preg_match('/\((.*)\)/', $txtva, $reg))
3030
			{
3031
				$vat_src_code = $reg[1];
3032
				$txtva = preg_replace('/\s*\(.*\)/', '', $txtva); // Remove code into vatrate.
3033
			}
3034
3035
			$tabprice = calcul_price_total($qty, $pu, $remise_percent, $txtva, $txlocaltax1, $txlocaltax2, 0, $price_base_type, $info_bits, $type, $mysoc, $localtaxes_type, 100, $this->multicurrency_tx, $pu_ht_devise);
3036
3037
			$total_ht  = $tabprice[0];
3038
			$total_tva = $tabprice[1];
3039
			$total_ttc = $tabprice[2];
3040
			$total_localtax1 = $tabprice[9];
3041
			$total_localtax2 = $tabprice[10];
3042
			$pu_ht  = $tabprice[3];
3043
			$pu_tva = $tabprice[4];
3044
			$pu_ttc = $tabprice[5];
3045
3046
			// MultiCurrency
3047
			$multicurrency_total_ht  = $tabprice[16];
3048
			$multicurrency_total_tva = $tabprice[17];
3049
			$multicurrency_total_ttc = $tabprice[18];
3050
			$pu_ht_devise = $tabprice[19];
3051
3052
			// Anciens indicateurs: $price, $subprice (a ne plus utiliser)
3053
			$price = $pu_ht;
3054
			if ($price_base_type == 'TTC')
3055
			{
3056
				$subprice = $pu_ttc;
3057
			} else {
3058
				$subprice = $pu_ht;
3059
			}
3060
			$remise = 0;
3061
			if ($remise_percent > 0)
3062
			{
3063
				$remise = round(($pu * $remise_percent / 100), 2);
3064
				$price = ($pu - $remise);
3065
			}
3066
3067
			//Fetch current line from the database and then clone the object and set it in $oldline property
3068
			$line = new OrderLine($this->db);
3069
			$line->fetch($rowid);
3070
			$line->fetch_optionals();
3071
3072
			if (!empty($line->fk_product))
3073
			{
3074
				$product = new Product($this->db);
3075
				$result = $product->fetch($line->fk_product);
3076
				$product_type = $product->type;
3077
3078
				if (!empty($conf->global->STOCK_MUST_BE_ENOUGH_FOR_ORDER) && $product_type == 0 && $product->stock_reel < $qty)
3079
				{
3080
					$langs->load("errors");
3081
					$this->error = $langs->trans('ErrorStockIsNotEnoughToAddProductOnOrder', $product->ref);
3082
					$this->errors[] = $this->error;
3083
					dol_syslog(get_class($this)."::addline error=Product ".$product->ref.": ".$this->error, LOG_ERR);
3084
					$this->db->rollback();
3085
					return self::STOCK_NOT_ENOUGH_FOR_ORDER;
3086
				}
3087
			}
3088
3089
			$staticline = clone $line;
3090
3091
			$line->oldline = $staticline;
3092
			$this->line = $line;
3093
			$this->line->context = $this->context;
3094
3095
			// Reorder if fk_parent_line change
3096
			if (!empty($fk_parent_line) && !empty($staticline->fk_parent_line) && $fk_parent_line != $staticline->fk_parent_line)
3097
			{
3098
				$rangmax = $this->line_max($fk_parent_line);
3099
				$this->line->rang = $rangmax + 1;
3100
			}
3101
3102
			$this->line->id = $rowid;
3103
			$this->line->label = $label;
3104
			$this->line->desc = $desc;
3105
			$this->line->qty = $qty;
3106
3107
			$this->line->vat_src_code = $vat_src_code;
3108
			$this->line->tva_tx         = $txtva;
3109
			$this->line->localtax1_tx   = $txlocaltax1;
3110
			$this->line->localtax2_tx   = $txlocaltax2;
3111
			$this->line->localtax1_type = $localtaxes_type[0];
3112
			$this->line->localtax2_type = $localtaxes_type[2];
3113
			$this->line->remise_percent = $remise_percent;
3114
			$this->line->subprice       = $subprice;
3115
			$this->line->info_bits      = $info_bits;
3116
			$this->line->special_code   = $special_code;
3117
			$this->line->total_ht       = $total_ht;
3118
			$this->line->total_tva      = $total_tva;
3119
			$this->line->total_localtax1 = $total_localtax1;
3120
			$this->line->total_localtax2 = $total_localtax2;
3121
			$this->line->total_ttc      = $total_ttc;
3122
			$this->line->date_start     = $date_start;
3123
			$this->line->date_end       = $date_end;
3124
			$this->line->product_type   = $type;
3125
			$this->line->fk_parent_line = $fk_parent_line;
3126
			$this->line->skip_update_total = $skip_update_total;
3127
			$this->line->fk_unit        = $fk_unit;
3128
3129
			$this->line->fk_fournprice = $fk_fournprice;
3130
			$this->line->pa_ht = $pa_ht;
3131
3132
			// Multicurrency
3133
			$this->line->multicurrency_subprice		= $pu_ht_devise;
3134
			$this->line->multicurrency_total_ht 	= $multicurrency_total_ht;
3135
			$this->line->multicurrency_total_tva 	= $multicurrency_total_tva;
3136
			$this->line->multicurrency_total_ttc 	= $multicurrency_total_ttc;
3137
3138
			// TODO deprecated
3139
			$this->line->price = $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

3139
			/** @scrutinizer ignore-deprecated */ $this->line->price = $price;
Loading history...
3140
			$this->line->remise = $remise;
1 ignored issue
show
Deprecated Code introduced by
The property OrderLine::$remise 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

3140
			/** @scrutinizer ignore-deprecated */ $this->line->remise = $remise;
Loading history...
3141
3142
			if (is_array($array_options) && count($array_options) > 0) {
3143
				// We replace values in this->line->array_options only for entries defined into $array_options
3144
				foreach ($array_options as $key => $value) {
3145
					$this->line->array_options[$key] = $array_options[$key];
3146
				}
3147
			}
3148
3149
			$result = $this->line->update($user, $notrigger);
3150
			if ($result > 0)
3151
			{
3152
				// Reorder if child line
3153
				if (!empty($fk_parent_line)) $this->line_order(true, 'DESC');
3154
3155
				// Mise a jour info denormalisees
3156
				$this->update_price(1);
3157
3158
				$this->db->commit();
3159
				return $result;
3160
			} else {
3161
				$this->error = $this->line->error;
3162
3163
				$this->db->rollback();
3164
				return -1;
3165
			}
3166
		} else {
3167
			$this->error = get_class($this)."::updateline Order status makes operation forbidden";
3168
			$this->errors = array('OrderStatusMakeOperationForbidden');
3169
			return -2;
3170
		}
3171
	}
3172
3173
	/**
3174
	 *      Update database
3175
	 *
3176
	 *      @param      User	$user        	User that modify
3177
	 *      @param      int		$notrigger	    0=launch triggers after, 1=disable triggers
3178
	 *      @return     int      			   	<0 if KO, >0 if OK
3179
	 */
3180
	public function update(User $user, $notrigger = 0)
3181
	{
3182
		global $conf;
3183
3184
		$error = 0;
3185
3186
		// Clean parameters
3187
		if (isset($this->ref)) $this->ref = trim($this->ref);
3188
		if (isset($this->ref_client)) $this->ref_client = trim($this->ref_client);
3189
		if (isset($this->note) || isset($this->note_private)) $this->note_private = (isset($this->note_private) ? trim($this->note_private) : trim($this->note));
3190
		if (isset($this->note_public)) $this->note_public = trim($this->note_public);
3191
		if (isset($this->modelpdf)) $this->modelpdf = trim($this->modelpdf);
3192
		if (isset($this->import_key)) $this->import_key = trim($this->import_key);
3193
3194
		// Check parameters
3195
		// Put here code to add control on parameters values
3196
3197
		// Update request
3198
		$sql = "UPDATE ".MAIN_DB_PREFIX."commande SET";
3199
3200
		$sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "null").",";
3201
		$sql .= " ref_client=".(isset($this->ref_client) ? "'".$this->db->escape($this->ref_client)."'" : "null").",";
3202
		$sql .= " ref_ext=".(isset($this->ref_ext) ? "'".$this->db->escape($this->ref_ext)."'" : "null").",";
3203
		$sql .= " fk_soc=".(isset($this->socid) ? $this->socid : "null").",";
3204
		$sql .= " date_commande=".(strval($this->date_commande) != '' ? "'".$this->db->idate($this->date_commande)."'" : 'null').",";
3205
		$sql .= " date_valid=".(strval($this->date_validation) != '' ? "'".$this->db->idate($this->date_validation)."'" : 'null').",";
3206
		$sql .= " tva=".(isset($this->total_tva) ? $this->total_tva : "null").",";
3207
		$sql .= " localtax1=".(isset($this->total_localtax1) ? $this->total_localtax1 : "null").",";
3208
		$sql .= " localtax2=".(isset($this->total_localtax2) ? $this->total_localtax2 : "null").",";
3209
		$sql .= " total_ht=".(isset($this->total_ht) ? $this->total_ht : "null").",";
3210
		$sql .= " total_ttc=".(isset($this->total_ttc) ? $this->total_ttc : "null").",";
3211
		$sql .= " fk_statut=".(isset($this->statut) ? $this->statut : "null").",";
3212
		$sql .= " fk_user_author=".(isset($this->user_author_id) ? $this->user_author_id : "null").",";
3213
		$sql .= " fk_user_valid=".(isset($this->user_valid) ? $this->user_valid : "null").",";
3214
		$sql .= " fk_projet=".(isset($this->fk_project) ? $this->fk_project : "null").",";
3215
		$sql .= " fk_cond_reglement=".(isset($this->cond_reglement_id) ? $this->cond_reglement_id : "null").",";
3216
		$sql .= " fk_mode_reglement=".(isset($this->mode_reglement_id) ? $this->mode_reglement_id : "null").",";
3217
		$sql .= " fk_account=".($this->fk_account > 0 ? $this->fk_account : "null").",";
3218
		$sql .= " note_private=".(isset($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null").",";
3219
		$sql .= " note_public=".(isset($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null").",";
3220
		$sql .= " model_pdf=".(isset($this->modelpdf) ? "'".$this->db->escape($this->modelpdf)."'" : "null").",";
3221
		$sql .= " import_key=".(isset($this->import_key) ? "'".$this->db->escape($this->import_key)."'" : "null")."";
3222
3223
		$sql .= " WHERE rowid=".$this->id;
3224
3225
		$this->db->begin();
3226
3227
		dol_syslog(get_class($this)."::update", LOG_DEBUG);
3228
		$resql = $this->db->query($sql);
3229
		if (!$resql) {
3230
			$error++; $this->errors[] = "Error ".$this->db->lasterror();
3231
		}
3232
3233
		if (!$error)
3234
		{
3235
			$result = $this->insertExtraFields();
3236
			if ($result < 0)
3237
			{
3238
				$error++;
3239
			}
3240
		}
3241
3242
		if (!$error && !$notrigger)
3243
		{
3244
			// Call trigger
3245
			$result = $this->call_trigger('ORDER_MODIFY', $user);
3246
			if ($result < 0) $error++;
3247
			// End call triggers
3248
		}
3249
3250
		// Commit or rollback
3251
		if ($error)
3252
		{
3253
			foreach ($this->errors as $errmsg)
3254
			{
3255
				dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
3256
				$this->error .= ($this->error ? ', '.$errmsg : $errmsg);
3257
			}
3258
			$this->db->rollback();
3259
			return -1 * $error;
3260
		} else {
3261
			$this->db->commit();
3262
			return 1;
3263
		}
3264
	}
3265
3266
	/**
3267
	 *	Delete the customer order
3268
	 *
3269
	 *	@param	User	$user		User object
3270
	 *	@param	int		$notrigger	1=Does not execute triggers, 0= execute triggers
3271
	 * 	@return	int					<=0 if KO, >0 if OK
3272
	 */
3273
	public function delete($user, $notrigger = 0)
3274
	{
3275
		global $conf, $langs;
3276
		require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
3277
3278
		$error = 0;
3279
3280
		dol_syslog(get_class($this)."::delete ".$this->id, LOG_DEBUG);
3281
3282
		$this->db->begin();
3283
3284
		if (!$error && !$notrigger)
3285
		{
3286
			// Call trigger
3287
			$result = $this->call_trigger('ORDER_DELETE', $user);
3288
			if ($result < 0) $error++;
3289
			// End call triggers
3290
		}
3291
3292
		if ($this->nb_expedition() != 0)
3293
		{
3294
			$this->errors[] = $langs->trans('SomeShipmentExists');
3295
			$error++;
3296
		}
3297
3298
		if (!$error)
3299
		{
3300
			// Delete extrafields of order details
3301
                        $main = MAIN_DB_PREFIX.'commandedet';
3302
                        $ef = $main."_extrafields";
3303
                        $sql = "DELETE FROM $ef WHERE fk_object IN (SELECT rowid FROM $main WHERE fk_commande = ".$this->id.")";
3304
			if (!$this->db->query($sql))
3305
			{
3306
				$error++;
3307
				$this->errors[] = $this->db->lasterror();
3308
			}
3309
		}
3310
3311
		if (!$error)
3312
		{
3313
			// Delete order details
3314
			$sql = 'DELETE FROM '.MAIN_DB_PREFIX."commandedet WHERE fk_commande = ".$this->id;
3315
			if (!$this->db->query($sql))
3316
			{
3317
				$error++;
3318
				$this->errors[] = $this->db->lasterror();
3319
			}
3320
		}
3321
3322
		if (!$error)
3323
		{
3324
			// Delete linked object
3325
			$res = $this->deleteObjectLinked();
3326
			if ($res < 0) $error++;
3327
		}
3328
3329
		if (!$error)
3330
		{
3331
			// Delete linked contacts
3332
			$res = $this->delete_linked_contact();
3333
			if ($res < 0) $error++;
3334
		}
3335
3336
		if (!$error)
3337
		{
3338
			// Remove extrafields
3339
			$result = $this->deleteExtraFields();
3340
			if ($result < 0)
3341
			{
3342
				$error++;
3343
				dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
3344
			}
3345
		}
3346
3347
		if (!$error)
3348
		{
3349
			// Delete object
3350
			$sql = 'DELETE FROM '.MAIN_DB_PREFIX."commande WHERE rowid = ".$this->id;
3351
			if (!$this->db->query($sql))
3352
			{
3353
				$error++;
3354
				$this->errors[] = $this->db->lasterror();
3355
			}
3356
		}
3357
3358
		if (!$error)
3359
		{
3360
			// Remove directory with files
3361
			$comref = dol_sanitizeFileName($this->ref);
3362
			if ($conf->commande->multidir_output[$this->entity] && !empty($this->ref))
3363
			{
3364
				$dir = $conf->commande->multidir_output[$this->entity]."/".$comref;
3365
				$file = $conf->commande->multidir_output[$this->entity]."/".$comref."/".$comref.".pdf";
3366
				if (file_exists($file))	// We must delete all files before deleting directory
3367
				{
3368
					dol_delete_preview($this);
3369
3370
					if (!dol_delete_file($file, 0, 0, 0, $this)) // For triggers
3371
					{
3372
						$this->db->rollback();
3373
						return 0;
3374
					}
3375
				}
3376
				if (file_exists($dir))
3377
				{
3378
					if (!dol_delete_dir_recursive($dir))
3379
					{
3380
						$this->error = $langs->trans("ErrorCanNotDeleteDir", $dir);
3381
						$this->db->rollback();
3382
						return 0;
3383
					}
3384
				}
3385
			}
3386
		}
3387
3388
		if (!$error)
3389
		{
3390
			$this->db->commit();
3391
			return 1;
3392
		} else {
3393
			foreach ($this->errors as $errmsg)
3394
			{
3395
				$this->error .= ($this->error ? ', '.$errmsg : $errmsg);
3396
			}
3397
			$this->db->rollback();
3398
			return -1 * $error;
3399
		}
3400
	}
3401
3402
3403
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3404
	/**
3405
	 *	Load indicators for dashboard (this->nbtodo and this->nbtodolate)
3406
	 *
3407
	 *	@param		User	$user   Object user
3408
	 *	@return WorkboardResponse|int <0 if KO, WorkboardResponse if OK
3409
	 */
3410
	public function load_board($user)
3411
	{
3412
        // phpcs:enable
3413
		global $conf, $langs;
3414
3415
		$clause = " WHERE";
3416
3417
		$sql = "SELECT c.rowid, c.date_creation as datec, c.date_commande, c.date_livraison as delivery_date, c.fk_statut, c.total_ht";
3418
		$sql .= " FROM ".MAIN_DB_PREFIX."commande as c";
3419
		if (!$user->rights->societe->client->voir && !$user->socid)
3420
		{
3421
			$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON c.fk_soc = sc.fk_soc";
3422
			$sql .= " WHERE sc.fk_user = ".$user->id;
3423
			$clause = " AND";
3424
		}
3425
		$sql .= $clause." c.entity IN (".getEntity('commande').")";
3426
		//$sql.= " AND c.fk_statut IN (1,2,3) AND c.facture = 0";
3427
		$sql .= " AND ((c.fk_statut IN (".self::STATUS_VALIDATED.",".self::STATUS_SHIPMENTONPROCESS.")) OR (c.fk_statut = ".self::STATUS_CLOSED." AND c.facture = 0))"; // If status is 2 and facture=1, it must be selected
3428
		if ($user->socid) $sql .= " AND c.fk_soc = ".$user->socid;
3429
3430
		$resql = $this->db->query($sql);
3431
		if ($resql)
3432
		{
3433
			$response = new WorkboardResponse();
3434
			$response->warning_delay = $conf->commande->client->warning_delay / 60 / 60 / 24;
3435
			$response->label = $langs->trans("OrdersToProcess");
3436
			$response->labelShort = $langs->trans("Opened");
3437
			$response->url = DOL_URL_ROOT.'/commande/list.php?search_status=-3&mainmenu=commercial&leftmenu=orders';
3438
			$response->img = img_object('', "order");
3439
3440
			$generic_commande = new Commande($this->db);
3441
3442
			while ($obj = $this->db->fetch_object($resql))
3443
			{
3444
				$response->nbtodo++;
3445
				$response->total += $obj->total_ht;
3446
3447
				$generic_commande->statut = $obj->fk_statut;
3448
				$generic_commande->date_commande = $this->db->jdate($obj->date_commande);
3449
				$generic_commande->date_livraison = $this->db->jdate($obj->delivery_date);
3450
3451
				if ($generic_commande->hasDelay()) {
3452
					$response->nbtodolate++;
3453
				}
3454
			}
3455
3456
			return $response;
3457
		} else {
3458
			$this->error = $this->db->error();
3459
			return -1;
3460
		}
3461
	}
3462
3463
	/**
3464
	 *	Return source label of order
3465
	 *
3466
	 *	@return     string      Label
3467
	 */
3468
	public function getLabelSource()
3469
	{
3470
		global $langs;
3471
3472
		$label = $langs->trans('OrderSource'.$this->source);
3473
3474
		if ($label == 'OrderSource') return '';
3475
		return $label;
3476
	}
3477
3478
	/**
3479
	 *	Return status label of Order
3480
	 *
3481
	 *	@param      int		$mode       0=Long label, 1=Short label, 2=Picto + Short label, 3=Picto, 4=Picto + Long label, 5=Short label + Picto, 6=Long label + Picto
3482
	 *	@return     string      		Label of status
3483
	 */
3484
	public function getLibStatut($mode)
3485
	{
3486
		return $this->LibStatut($this->statut, $this->billed, $mode);
3487
	}
3488
3489
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3490
	/**
3491
	 *	Return label of status
3492
	 *
3493
	 *	@param		int		$status      	  Id status
3494
	 *  @param      int		$billed    		  If invoiced
3495
	 *	@param      int		$mode        	  0=Long label, 1=Short label, 2=Picto + Short label, 3=Picto, 4=Picto + Long label, 5=Short label + Picto, 6=Long label + Picto
3496
	 *  @param      int     $donotshowbilled  Do not show billed status after order status
3497
	 *  @return     string					  Label of status
3498
	 */
3499
	public function LibStatut($status, $billed, $mode, $donotshowbilled = 0)
3500
	{
3501
        // phpcs:enable
3502
		global $langs, $conf;
3503
3504
		$billedtext = '';
3505
		if (empty($donotshowbilled)) $billedtext .= ($billed ? ' - '.$langs->trans("Billed") : '');
3506
3507
		if ($status == self::STATUS_CANCELED) {
3508
		    $labelStatus = $langs->trans('StatusOrderCanceled');
3509
		    $labelStatusShort = $langs->trans('StatusOrderCanceledShort');
3510
		    $statusType = 'status9';
3511
		} elseif ($status == self::STATUS_DRAFT) {
3512
		    $labelStatus = $langs->trans('StatusOrderDraft');
3513
		    $labelStatusShort = $langs->trans('StatusOrderDraftShort');
3514
		    $statusType = 'status0';
3515
		} elseif ($status == self::STATUS_VALIDATED) {
3516
		    $labelStatus = $langs->trans('StatusOrderValidated').$billedtext;
3517
		    $labelStatusShort = $langs->trans('StatusOrderValidatedShort').$billedtext;
3518
		    $statusType = 'status1';
3519
		} elseif ($status == self::STATUS_SHIPMENTONPROCESS) {
3520
		    $labelStatus = $langs->trans('StatusOrderSentShort').$billedtext;
3521
		    $labelStatusShort = $langs->trans('StatusOrderSentShort').$billedtext;
3522
		    $statusType = 'status4';
3523
		} elseif ($status == self::STATUS_CLOSED && (!$billed && empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT))) {
3524
		    $labelStatus = $langs->trans('StatusOrderToBill');
3525
		    $labelStatusShort = $langs->trans('StatusOrderToBillShort');
3526
		    $statusType = 'status4';
3527
		} elseif ($status == self::STATUS_CLOSED && ($billed && empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT))) {
3528
		    $labelStatus = $langs->trans('StatusOrderProcessed').$billedtext;
3529
		    $labelStatusShort = $langs->trans('StatusOrderProcessed').$billedtext;
3530
		    $statusType = 'status6';
3531
		} elseif ($status == self::STATUS_CLOSED && (!empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT))) {
3532
		    $labelStatus = $langs->trans('StatusOrderDelivered');
3533
		    $labelStatusShort = $langs->trans('StatusOrderDelivered');
3534
		    $statusType = 'status6';
3535
		} else {
3536
		    $labelStatus = $langs->trans('Unknown');
3537
		    $labelStatusShort = '';
3538
		    $statusType = '';
3539
		    $mode = 0;
3540
		}
3541
3542
		return dolGetStatus($labelStatus, $labelStatusShort, '', $statusType, $mode);
3543
	}
3544
3545
3546
	/**
3547
	 *	Return clicable link of object (with eventually picto)
3548
	 *
3549
	 *	@param      int			$withpicto                Add picto into link
3550
	 *	@param      string	    $option                   Where point the link (0=> main card, 1,2 => shipment, 'nolink'=>No link)
3551
	 *	@param      int			$max          	          Max length to show
3552
	 *	@param      int			$short			          ???
3553
	 *  @param	    int   	    $notooltip		          1=Disable tooltip
3554
	 *  @param      int         $save_lastsearch_value    -1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking
3555
	 *  @param		int			$addlinktonotes			  Add link to notes
3556
	 *	@return     string          			          String with URL
3557
	 */
3558
	public function getNomUrl($withpicto = 0, $option = '', $max = 0, $short = 0, $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = 0)
3559
	{
3560
		global $conf, $langs, $user;
3561
3562
		if (!empty($conf->dol_no_mouse_hover)) $notooltip = 1; // Force disable tooltips
3563
3564
		$result = '';
3565
3566
		if (!empty($conf->expedition->enabled) && ($option == '1' || $option == '2')) $url = DOL_URL_ROOT.'/expedition/shipment.php?id='.$this->id;
3567
		else $url = DOL_URL_ROOT.'/commande/card.php?id='.$this->id;
3568
3569
		if (!$user->rights->commande->lire)
3570
			$option = 'nolink';
3571
3572
		if ($option !== 'nolink')
3573
		{
3574
			// Add param to save lastsearch_values or not
3575
			$add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
3576
			if ($save_lastsearch_value == -1 && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) $add_save_lastsearch_values = 1;
3577
			if ($add_save_lastsearch_values) $url .= '&save_lastsearch_values=1';
3578
		}
3579
3580
		if ($short) return $url;
3581
3582
		$label = '';
3583
3584
		if ($user->rights->commande->lire) {
3585
			$label = '<u>'.$langs->trans("Order").'</u>';
3586
			$label .= '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
3587
			$label .= '<br><b>'.$langs->trans('RefCustomer').':</b> '.($this->ref_customer ? $this->ref_customer : $this->ref_client);
3588
			if (!empty($this->total_ht)) {
3589
				$label .= '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
3590
			}
3591
			if (!empty($this->total_tva)) {
3592
				$label .= '<br><b>'.$langs->trans('VAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
3593
			}
3594
			if (!empty($this->total_ttc)) {
3595
				$label .= '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
3596
			}
3597
			if (isset($this->statut)) {
3598
				$label .= '<br><b>'.$langs->trans("Status").":</b> ".$this->getLibStatut(5);
3599
			}
3600
		}
3601
3602
		$linkclose = '';
3603
		if (empty($notooltip) && $user->rights->commande->lire)
3604
		{
3605
			if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER))
3606
			{
3607
				$label = $langs->trans("Order");
3608
				$linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
3609
			}
3610
			$linkclose .= ' title="'.dol_escape_htmltag($label, 1).'"';
3611
			$linkclose .= ' class="classfortooltip"';
3612
		}
3613
3614
		$linkstart = '<a href="'.$url.'"';
3615
		$linkstart .= $linkclose.'>';
3616
		$linkend = '</a>';
3617
3618
		if ($option === 'nolink') {
3619
			$linkstart = '';
3620
			$linkend = '';
3621
		}
3622
3623
		$result .= $linkstart;
3624
		if ($withpicto) $result .= img_object(($notooltip ? '' : $label), $this->picto, ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip ? 0 : 1);
3625
		if ($withpicto != 2) $result .= $this->ref;
3626
		$result .= $linkend;
3627
3628
		if ($addlinktonotes)
3629
		{
3630
			$txttoshow = ($user->socid > 0 ? $this->note_public : $this->note_private);
3631
			if ($txttoshow)
3632
			{
3633
				$notetoshow = $langs->trans("ViewPrivateNote").':<br>'.dol_string_nohtmltag($txttoshow, 1);
3634
				$result .= ' <span class="note inline-block">';
3635
				$result .= '<a href="'.DOL_URL_ROOT.'/commande/note.php?id='.$this->id.'" class="classfortooltip" title="'.dol_escape_htmltag($notetoshow).'">';
3636
				$result .= img_picto('', 'note');
3637
				$result .= '</a>';
3638
				//$result.=img_picto($langs->trans("ViewNote"),'object_generic');
3639
				//$result.='</a>';
3640
				$result .= '</span>';
3641
			}
3642
		}
3643
3644
		return $result;
3645
	}
3646
3647
3648
	/**
3649
	 *	Charge les informations d'ordre info dans l'objet commande
3650
	 *
3651
	 *	@param  int		$id       Id of order
3652
	 *	@return	void
3653
	 */
3654
	public function info($id)
3655
	{
3656
		$sql = 'SELECT c.rowid, date_creation as datec, tms as datem,';
3657
		$sql .= ' date_valid as datev,';
3658
		$sql .= ' date_cloture as datecloture,';
3659
		$sql .= ' fk_user_author, fk_user_valid, fk_user_cloture';
3660
		$sql .= ' FROM '.MAIN_DB_PREFIX.'commande as c';
3661
		$sql .= ' WHERE c.rowid = '.$id;
3662
		$result = $this->db->query($sql);
3663
		if ($result)
3664
		{
3665
			if ($this->db->num_rows($result))
3666
			{
3667
				$obj = $this->db->fetch_object($result);
3668
				$this->id = $obj->rowid;
3669
				if ($obj->fk_user_author)
3670
				{
3671
					$cuser = new User($this->db);
3672
					$cuser->fetch($obj->fk_user_author);
3673
					$this->user_creation = $cuser;
3674
				}
3675
3676
				if ($obj->fk_user_valid)
3677
				{
3678
					$vuser = new User($this->db);
3679
					$vuser->fetch($obj->fk_user_valid);
3680
					$this->user_validation = $vuser;
3681
				}
3682
3683
				if ($obj->fk_user_cloture)
3684
				{
3685
					$cluser = new User($this->db);
3686
					$cluser->fetch($obj->fk_user_cloture);
3687
					$this->user_cloture = $cluser;
3688
				}
3689
3690
				$this->date_creation     = $this->db->jdate($obj->datec);
3691
				$this->date_modification = $this->db->jdate($obj->datem);
3692
				$this->date_validation   = $this->db->jdate($obj->datev);
3693
				$this->date_cloture      = $this->db->jdate($obj->datecloture);
3694
			}
3695
3696
			$this->db->free($result);
3697
		} else {
3698
			dol_print_error($this->db);
3699
		}
3700
	}
3701
3702
3703
	/**
3704
	 *  Initialise an instance with random values.
3705
	 *  Used to build previews or test instances.
3706
	 *	id must be 0 if object instance is a specimen.
3707
	 *
3708
	 *  @return	void
3709
	 */
3710
	public function initAsSpecimen()
3711
	{
3712
		global $conf, $langs;
3713
3714
		dol_syslog(get_class($this)."::initAsSpecimen");
3715
3716
		// Load array of products prodids
3717
		$num_prods = 0;
3718
		$prodids = array();
3719
		$sql = "SELECT rowid";
3720
		$sql .= " FROM ".MAIN_DB_PREFIX."product";
3721
		$sql .= " WHERE entity IN (".getEntity('product').")";
3722
		$resql = $this->db->query($sql);
3723
		if ($resql)
3724
		{
3725
			$num_prods = $this->db->num_rows($resql);
3726
			$i = 0;
3727
			while ($i < $num_prods)
3728
			{
3729
				$i++;
3730
				$row = $this->db->fetch_row($resql);
3731
				$prodids[$i] = $row[0];
3732
			}
3733
		}
3734
3735
		// Initialise parametres
3736
		$this->id = 0;
3737
		$this->ref = 'SPECIMEN';
3738
		$this->specimen = 1;
3739
		$this->socid = 1;
3740
		$this->date = time();
3741
		$this->date_lim_reglement = $this->date + 3600 * 24 * 30;
3742
		$this->cond_reglement_code = 'RECEP';
3743
		$this->mode_reglement_code = 'CHQ';
3744
		$this->availability_code   = 'DSP';
3745
		$this->demand_reason_code  = 'SRC_00';
3746
3747
		$this->note_public = 'This is a comment (public)';
3748
		$this->note_private = 'This is a comment (private)';
3749
3750
		$this->multicurrency_tx = 1;
3751
		$this->multicurrency_code = $conf->currency;
3752
3753
		// Lines
3754
		$nbp = 5;
3755
		$xnbp = 0;
3756
		while ($xnbp < $nbp)
3757
		{
3758
			$line = new OrderLine($this->db);
3759
3760
			$line->desc = $langs->trans("Description")." ".$xnbp;
3761
			$line->qty = 1;
3762
			$line->subprice = 100;
3763
			$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

3763
			/** @scrutinizer ignore-deprecated */ $line->price = 100;
Loading history...
3764
			$line->tva_tx = 20;
3765
			if ($xnbp == 2)
3766
			{
3767
				$line->total_ht = 50;
3768
				$line->total_ttc = 60;
3769
				$line->total_tva = 10;
3770
				$line->remise_percent = 50;
3771
			} else {
3772
				$line->total_ht = 100;
3773
				$line->total_ttc = 120;
3774
				$line->total_tva = 20;
3775
				$line->remise_percent = 0;
3776
			}
3777
			if ($num_prods > 0)
3778
			{
3779
				$prodid = mt_rand(1, $num_prods);
3780
				$line->fk_product = $prodids[$prodid];
3781
				$line->product_ref = 'SPECIMEN';
3782
			}
3783
3784
			$this->lines[$xnbp] = $line;
3785
3786
			$this->total_ht       += $line->total_ht;
3787
			$this->total_tva      += $line->total_tva;
3788
			$this->total_ttc      += $line->total_ttc;
3789
3790
			$xnbp++;
3791
		}
3792
	}
3793
3794
3795
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3796
	/**
3797
	 *	Charge indicateurs this->nb de tableau de bord
3798
	 *
3799
	 *	@return     int         <0 si ko, >0 si ok
3800
	 */
3801
	public function load_state_board()
3802
	{
3803
        // phpcs:enable
3804
		global $user;
3805
3806
		$this->nb = array();
3807
		$clause = "WHERE";
3808
3809
		$sql = "SELECT count(co.rowid) as nb";
3810
		$sql .= " FROM ".MAIN_DB_PREFIX."commande as co";
3811
		$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON co.fk_soc = s.rowid";
3812
		if (!$user->rights->societe->client->voir && !$user->socid)
3813
		{
3814
			$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
3815
			$sql .= " WHERE sc.fk_user = ".$user->id;
3816
			$clause = "AND";
3817
		}
3818
		$sql .= " ".$clause." co.entity IN (".getEntity('commande').")";
3819
3820
		$resql = $this->db->query($sql);
3821
		if ($resql)
3822
		{
3823
			while ($obj = $this->db->fetch_object($resql))
3824
			{
3825
				$this->nb["orders"] = $obj->nb;
3826
			}
3827
			$this->db->free($resql);
3828
			return 1;
3829
		} else {
3830
			dol_print_error($this->db);
3831
			$this->error = $this->db->error();
3832
			return -1;
3833
		}
3834
	}
3835
3836
	/**
3837
	 * 	Create an array of order lines
3838
	 *
3839
	 * 	@return int		>0 if OK, <0 if KO
3840
	 */
3841
	public function getLinesArray()
3842
	{
3843
		return $this->fetch_lines();
3844
	}
3845
3846
	/**
3847
	 *  Create a document onto disk according to template module.
3848
	 *
3849
	 *  @param	    string		$modele			Force template to use ('' to not force)
3850
	 *  @param		Translate	$outputlangs	objet lang a utiliser pour traduction
3851
	 *  @param      int			$hidedetails    Hide details of lines
3852
	 *  @param      int			$hidedesc       Hide description
3853
	 *  @param      int			$hideref        Hide ref
3854
	 *  @param      null|array  $moreparams     Array to provide more information
3855
	 *  @return     int         				0 if KO, 1 if OK
3856
	 */
3857
	public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
3858
	{
3859
		global $conf, $langs;
3860
3861
		$langs->load("orders");
3862
		$outputlangs->load("products");
3863
3864
		if (!dol_strlen($modele)) {
3865
			$modele = 'einstein';
3866
3867
			if ($this->modelpdf) {
3868
				$modele = $this->modelpdf;
3869
			} elseif (!empty($conf->global->COMMANDE_ADDON_PDF)) {
3870
				$modele = $conf->global->COMMANDE_ADDON_PDF;
3871
			}
3872
		}
3873
3874
		$modelpath = "core/modules/commande/doc/";
3875
3876
		return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
3877
	}
3878
3879
3880
	/**
3881
	 * Function used to replace a thirdparty id with another one.
3882
	 *
3883
	 * @param DoliDB $db Database handler
3884
	 * @param int $origin_id Old thirdparty id
3885
	 * @param int $dest_id New thirdparty id
3886
	 * @return bool
3887
	 */
3888
	public static function replaceThirdparty(DoliDB $db, $origin_id, $dest_id)
3889
	{
3890
		$tables = array(
3891
		'commande'
3892
		);
3893
3894
		return CommonObject::commonReplaceThirdparty($db, $origin_id, $dest_id, $tables);
3895
	}
3896
3897
	/**
3898
	 * Is the customer order delayed?
3899
	 *
3900
	 * @return bool     true if late, false if not
3901
	 */
3902
	public function hasDelay()
3903
	{
3904
		global $conf;
3905
3906
		if (!($this->statut > Commande::STATUS_DRAFT && $this->statut < Commande::STATUS_CLOSED)) {
3907
			return false; // Never late if not inside this status range
3908
		}
3909
3910
		$now = dol_now();
3911
3912
		return max($this->date_commande, $this->date_livraison) < ($now - $conf->commande->client->warning_delay);
1 ignored issue
show
Deprecated Code introduced by
The property Commande::$date_commande 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

3912
		return max(/** @scrutinizer ignore-deprecated */ $this->date_commande, $this->date_livraison) < ($now - $conf->commande->client->warning_delay);
Loading history...
3913
	}
3914
3915
	/**
3916
	 * Show the customer delayed info
3917
	 *
3918
	 * @return string       Show delayed information
3919
	 */
3920
	public function showDelay()
3921
	{
3922
		global $conf, $langs;
3923
3924
		if (empty($this->date_livraison)) $text = $langs->trans("OrderDate").' '.dol_print_date($this->date_commande, 'day');
1 ignored issue
show
Deprecated Code introduced by
The property Commande::$date_commande 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

3924
		if (empty($this->date_livraison)) $text = $langs->trans("OrderDate").' '.dol_print_date(/** @scrutinizer ignore-deprecated */ $this->date_commande, 'day');
Loading history...
3925
		else $text = $text = $langs->trans("DeliveryDate").' '.dol_print_date($this->date_livraison, 'day');
3926
		$text .= ' '.($conf->commande->client->warning_delay > 0 ? '+' : '-').' '.round(abs($conf->commande->client->warning_delay) / 3600 / 24, 1).' '.$langs->trans("days").' < '.$langs->trans("Today");
3927
3928
		return $text;
3929
	}
3930
}
3931
3932
3933
/**
3934
 *  Class to manage order lines
3935
 */
3936
class OrderLine extends CommonOrderLine
3937
{
3938
	/**
3939
	 * @var string ID to identify managed object
3940
	 */
3941
	public $element = 'commandedet';
3942
3943
	public $table_element = 'commandedet';
3944
3945
	public $oldline;
3946
3947
	/**
3948
	 * Id of parent order
3949
	 * @var int
3950
	 */
3951
	public $fk_commande;
3952
3953
	/**
3954
	 * Id of parent order
3955
	 * @var int
3956
	 * @deprecated Use fk_commande
3957
	 * @see $fk_commande
3958
	 */
3959
	public $commande_id;
3960
3961
	// From llx_commandedet
3962
	public $fk_parent_line;
3963
	public $fk_facture;
3964
3965
	/**
3966
	 * @var string Order lines label
3967
	 */
3968
	public $label;
3969
3970
	public $fk_remise_except;
3971
	public $rang = 0;
3972
	public $fk_fournprice;
3973
3974
	/**
3975
	 * Buy price without taxes
3976
	 * @var float
3977
	 */
3978
	public $pa_ht;
3979
	public $marge_tx;
3980
	public $marque_tx;
3981
3982
	/**
3983
	 * @deprecated
3984
	 * @see $remise_percent, $fk_remise_except
3985
	 */
3986
	public $remise;
3987
3988
	// Start and end date of the line
3989
	public $date_start;
3990
	public $date_end;
3991
3992
	public $skip_update_total; // Skip update price total for special lines
3993
3994
3995
	/**
3996
	 *      Constructor
3997
	 *
3998
	 *      @param     DoliDB	$db      handler d'acces base de donnee
3999
	 */
4000
    public function __construct($db)
4001
    {
4002
        $this->db = $db;
4003
    }
4004
4005
	/**
4006
	 *  Load line order
4007
	 *
4008
	 *  @param  int		$rowid          Id line order
4009
	 *  @return	int						<0 if KO, >0 if OK
4010
	 */
4011
	public function fetch($rowid)
4012
	{
4013
		$sql = 'SELECT cd.rowid, cd.fk_commande, cd.fk_parent_line, cd.fk_product, cd.product_type, cd.label as custom_label, cd.description, cd.price, cd.qty, cd.tva_tx, cd.localtax1_tx, cd.localtax2_tx,';
4014
		$sql .= ' cd.remise, cd.remise_percent, cd.fk_remise_except, cd.subprice,';
4015
		$sql .= ' cd.info_bits, cd.total_ht, cd.total_tva, cd.total_localtax1, cd.total_localtax2, cd.total_ttc, cd.fk_product_fournisseur_price as fk_fournprice, cd.buy_price_ht as pa_ht, cd.rang, cd.special_code,';
4016
		$sql .= ' cd.fk_unit,';
4017
		$sql .= ' cd.fk_multicurrency, cd.multicurrency_code, cd.multicurrency_subprice, cd.multicurrency_total_ht, cd.multicurrency_total_tva, cd.multicurrency_total_ttc,';
4018
		$sql .= ' p.ref as product_ref, p.label as product_libelle, p.description as product_desc, p.tobatch as product_tobatch,';
4019
		$sql .= ' cd.date_start, cd.date_end';
4020
		$sql .= ' FROM '.MAIN_DB_PREFIX.'commandedet as cd';
4021
		$sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON cd.fk_product = p.rowid';
4022
		$sql .= ' WHERE cd.rowid = '.$rowid;
4023
		$result = $this->db->query($sql);
4024
		if ($result)
4025
		{
4026
			$objp = $this->db->fetch_object($result);
4027
			$this->rowid            = $objp->rowid;
4028
			$this->id = $objp->rowid;
4029
			$this->fk_commande      = $objp->fk_commande;
4030
			$this->fk_parent_line   = $objp->fk_parent_line;
4031
			$this->label            = $objp->custom_label;
4032
			$this->desc             = $objp->description;
4033
			$this->qty              = $objp->qty;
4034
			$this->price            = $objp->price;
4035
			$this->subprice         = $objp->subprice;
4036
			$this->vat_src_code     = $objp->vat_src_code;
4037
			$this->tva_tx           = $objp->tva_tx;
4038
			$this->localtax1_tx		= $objp->localtax1_tx;
4039
			$this->localtax2_tx		= $objp->localtax2_tx;
4040
			$this->remise           = $objp->remise;
4041
			$this->remise_percent   = $objp->remise_percent;
4042
			$this->fk_remise_except = $objp->fk_remise_except;
4043
			$this->fk_product       = $objp->fk_product;
4044
			$this->product_type     = $objp->product_type;
4045
			$this->info_bits        = $objp->info_bits;
4046
			$this->special_code = $objp->special_code;
4047
			$this->total_ht         = $objp->total_ht;
4048
			$this->total_tva        = $objp->total_tva;
4049
			$this->total_localtax1  = $objp->total_localtax1;
4050
			$this->total_localtax2  = $objp->total_localtax2;
4051
			$this->total_ttc        = $objp->total_ttc;
4052
			$this->fk_fournprice = $objp->fk_fournprice;
4053
			$marginInfos			= getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $this->fk_fournprice, $objp->pa_ht);
4054
			$this->pa_ht			= $marginInfos[0];
4055
			$this->marge_tx			= $marginInfos[1];
4056
			$this->marque_tx		= $marginInfos[2];
4057
			$this->special_code = $objp->special_code;
4058
			$this->rang = $objp->rang;
4059
4060
			$this->ref = $objp->product_ref; // deprecated
4061
			$this->product_ref = $objp->product_ref;
4062
			$this->libelle = $objp->product_libelle; // deprecated
4063
			$this->product_label = $objp->product_libelle;
4064
			$this->product_desc     = $objp->product_desc;
4065
			$this->product_tobatch  = $objp->product_tobatch;
4066
			$this->fk_unit          = $objp->fk_unit;
4067
4068
			$this->date_start       = $this->db->jdate($objp->date_start);
4069
			$this->date_end         = $this->db->jdate($objp->date_end);
4070
4071
			$this->fk_multicurrency = $objp->fk_multicurrency;
4072
			$this->multicurrency_code = $objp->multicurrency_code;
4073
			$this->multicurrency_subprice	= $objp->multicurrency_subprice;
4074
			$this->multicurrency_total_ht	= $objp->multicurrency_total_ht;
4075
			$this->multicurrency_total_tva	= $objp->multicurrency_total_tva;
4076
			$this->multicurrency_total_ttc	= $objp->multicurrency_total_ttc;
4077
4078
			$this->db->free($result);
4079
4080
			return 1;
4081
		} else {
4082
			$this->error = $this->db->lasterror();
4083
			return -1;
4084
		}
4085
	}
4086
4087
	/**
4088
	 * 	Delete line in database
4089
	 *
4090
	 *	@param      User	$user        	User that modify
4091
	 *  @param      int		$notrigger	    0=launch triggers after, 1=disable triggers
4092
	 *	@return	 int  <0 si ko, >0 si ok
4093
	 */
4094
	public function delete(User $user, $notrigger = 0)
4095
	{
4096
		global $conf, $langs;
4097
4098
		$error = 0;
4099
4100
        // check if order line is not in a shipment line before deleting
4101
        $sqlCheckShipmentLine  = "SELECT";
4102
        $sqlCheckShipmentLine .= " ed.rowid";
4103
        $sqlCheckShipmentLine .= " FROM ".MAIN_DB_PREFIX."expeditiondet ed";
4104
        $sqlCheckShipmentLine .= " WHERE ed.fk_origin_line = ".$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

4104
        $sqlCheckShipmentLine .= " WHERE ed.fk_origin_line = "./** @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...
4105
4106
        $resqlCheckShipmentLine = $this->db->query($sqlCheckShipmentLine);
4107
        if (!$resqlCheckShipmentLine) {
4108
            $error++;
4109
            $this->error    = $this->db->lasterror();
4110
            $this->errors[] = $this->error;
4111
        } else {
4112
            $langs->load('errors');
4113
            $num = $this->db->num_rows($resqlCheckShipmentLine);
4114
            if ($num > 0) {
4115
                $error++;
4116
                $objCheckShipmentLine = $this->db->fetch_object($resqlCheckShipmentLine);
4117
                $this->error = $langs->trans('ErrorRecordAlreadyExists').' : '.$langs->trans('ShipmentLine').' '.$objCheckShipmentLine->rowid;
4118
                $this->errors[] = $this->error;
4119
            }
4120
            $this->db->free($resqlCheckShipmentLine);
4121
        }
4122
        if ($error) {
4123
            dol_syslog(__METHOD__.'Error ; '.$this->error, LOG_ERR);
4124
            return -1;
4125
        }
4126
4127
		$this->db->begin();
4128
4129
		$sql = 'DELETE FROM '.MAIN_DB_PREFIX."commandedet 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

4129
		$sql = 'DELETE FROM '.MAIN_DB_PREFIX."commandedet 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...
4130
4131
		dol_syslog("OrderLine::delete", LOG_DEBUG);
4132
		$resql = $this->db->query($sql);
4133
		if ($resql)
4134
		{
4135
			// Remove extrafields
4136
			if (!$error)
4137
			{
4138
				$this->id = $this->rowid;
4139
				$result = $this->deleteExtraFields();
4140
				if ($result < 0)
4141
				{
4142
					$error++;
4143
					dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
4144
				}
4145
			}
4146
4147
			if (!$error && !$notrigger)
4148
			{
4149
				// Call trigger
4150
				$result = $this->call_trigger('LINEORDER_DELETE', $user);
4151
				if ($result < 0) $error++;
4152
				// End call triggers
4153
			}
4154
4155
			if (!$error) {
4156
				$this->db->commit();
4157
				return 1;
4158
			}
4159
4160
			foreach ($this->errors as $errmsg)
4161
			{
4162
				dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
4163
				$this->error .= ($this->error ? ', '.$errmsg : $errmsg);
4164
			}
4165
			$this->db->rollback();
4166
			return -1 * $error;
4167
		} else {
4168
			$this->error = $this->db->lasterror();
4169
			return -1;
4170
		}
4171
	}
4172
4173
	/**
4174
	 *	Insert line into database
4175
	 *
4176
	 *	@param      User	$user        	User that modify
4177
	 *	@param      int		$notrigger		1 = disable triggers
4178
	 *	@return		int						<0 if KO, >0 if OK
4179
	 */
4180
	public function insert($user = null, $notrigger = 0)
4181
	{
4182
		global $langs, $conf;
4183
4184
		$error = 0;
4185
4186
		$pa_ht_isemptystring = (empty($this->pa_ht) && $this->pa_ht == ''); // If true, we can use a default value. If this->pa_ht = '0', we must use '0'.
4187
4188
		dol_syslog(get_class($this)."::insert rang=".$this->rang);
4189
4190
		// Clean parameters
4191
		if (empty($this->tva_tx)) $this->tva_tx = 0;
4192
		if (empty($this->localtax1_tx)) $this->localtax1_tx = 0;
4193
		if (empty($this->localtax2_tx)) $this->localtax2_tx = 0;
4194
		if (empty($this->localtax1_type)) $this->localtax1_type = 0;
4195
		if (empty($this->localtax2_type)) $this->localtax2_type = 0;
4196
		if (empty($this->total_localtax1)) $this->total_localtax1 = 0;
4197
		if (empty($this->total_localtax2)) $this->total_localtax2 = 0;
4198
		if (empty($this->rang)) $this->rang = 0;
4199
		if (empty($this->remise)) $this->remise = 0;
1 ignored issue
show
Deprecated Code introduced by
The property OrderLine::$remise 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

4199
		if (empty(/** @scrutinizer ignore-deprecated */ $this->remise)) $this->remise = 0;
Loading history...
4200
		if (empty($this->remise_percent)) $this->remise_percent = 0;
4201
		if (empty($this->info_bits)) $this->info_bits = 0;
4202
		if (empty($this->special_code)) $this->special_code = 0;
4203
		if (empty($this->fk_parent_line)) $this->fk_parent_line = 0;
4204
		if (empty($this->pa_ht)) $this->pa_ht = 0;
4205
4206
		// if buy price not defined, define buyprice as configured in margin admin
4207
		if ($this->pa_ht == 0 && $pa_ht_isemptystring)
4208
		{
4209
			if (($result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product)) < 0)
4210
			{
4211
				return $result;
4212
			} else {
4213
				$this->pa_ht = $result;
4214
			}
4215
		}
4216
4217
		// Check parameters
4218
		if ($this->product_type < 0) return -1;
4219
4220
		$this->db->begin();
4221
4222
		// Insertion dans base de la ligne
4223
		$sql = 'INSERT INTO '.MAIN_DB_PREFIX.'commandedet';
4224
		$sql .= ' (fk_commande, fk_parent_line, label, description, qty, ';
4225
		$sql .= ' vat_src_code, tva_tx, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type,';
4226
		$sql .= ' fk_product, product_type, remise_percent, subprice, price, remise, fk_remise_except,';
4227
		$sql .= ' special_code, rang, fk_product_fournisseur_price, buy_price_ht,';
4228
		$sql .= ' info_bits, total_ht, total_tva, total_localtax1, total_localtax2, total_ttc, date_start, date_end,';
4229
		$sql .= ' fk_unit';
4230
		$sql .= ', fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc';
4231
		$sql .= ')';
4232
		$sql .= " VALUES (".$this->fk_commande.",";
4233
		$sql .= " ".($this->fk_parent_line > 0 ? "'".$this->db->escape($this->fk_parent_line)."'" : "null").",";
4234
		$sql .= " ".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null").",";
4235
		$sql .= " '".$this->db->escape($this->desc)."',";
4236
		$sql .= " '".price2num($this->qty)."',";
4237
		$sql .= " ".(empty($this->vat_src_code) ? "''" : "'".$this->db->escape($this->vat_src_code)."'").",";
4238
		$sql .= " '".price2num($this->tva_tx)."',";
4239
		$sql .= " '".price2num($this->localtax1_tx)."',";
4240
		$sql .= " '".price2num($this->localtax2_tx)."',";
4241
		$sql .= " '".$this->db->escape($this->localtax1_type)."',";
4242
		$sql .= " '".$this->db->escape($this->localtax2_type)."',";
4243
		$sql .= ' '.(!empty($this->fk_product) ? $this->fk_product : "null").',';
4244
		$sql .= " '".$this->db->escape($this->product_type)."',";
4245
		$sql .= " '".price2num($this->remise_percent)."',";
4246
		$sql .= " ".(price2num($this->subprice) !== '' ?price2num($this->subprice) : "null").",";
4247
		$sql .= " ".($this->price != '' ? "'".price2num($this->price)."'" : "null").",";
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

4247
		$sql .= " ".($this->price != '' ? "'".price2num(/** @scrutinizer ignore-deprecated */ $this->price)."'" : "null").",";
Loading history...
4248
		$sql .= " '".price2num($this->remise)."',";
1 ignored issue
show
Deprecated Code introduced by
The property OrderLine::$remise 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

4248
		$sql .= " '".price2num(/** @scrutinizer ignore-deprecated */ $this->remise)."',";
Loading history...
4249
		$sql .= ' '.(!empty($this->fk_remise_except) ? $this->fk_remise_except : "null").',';
4250
		$sql .= ' '.$this->special_code.',';
4251
		$sql .= ' '.$this->rang.',';
4252
		$sql .= ' '.(!empty($this->fk_fournprice) ? $this->fk_fournprice : "null").',';
4253
		$sql .= ' '.price2num($this->pa_ht).',';
4254
		$sql .= " '".$this->db->escape($this->info_bits)."',";
4255
		$sql .= " ".price2num($this->total_ht).",";
4256
		$sql .= " ".price2num($this->total_tva).",";
4257
		$sql .= " ".price2num($this->total_localtax1).",";
4258
		$sql .= " ".price2num($this->total_localtax2).",";
4259
		$sql .= " ".price2num($this->total_ttc).",";
4260
		$sql .= " ".(!empty($this->date_start) ? "'".$this->db->idate($this->date_start)."'" : "null").',';
4261
		$sql .= " ".(!empty($this->date_end) ? "'".$this->db->idate($this->date_end)."'" : "null").',';
4262
		$sql .= ' '.(!$this->fk_unit ? 'NULL' : $this->fk_unit);
4263
		$sql .= ", ".(!empty($this->fk_multicurrency) ? $this->fk_multicurrency : 'NULL');
4264
		$sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
4265
		$sql .= ", ".$this->multicurrency_subprice;
4266
		$sql .= ", ".$this->multicurrency_total_ht;
4267
		$sql .= ", ".$this->multicurrency_total_tva;
4268
		$sql .= ", ".$this->multicurrency_total_ttc;
4269
		$sql .= ')';
4270
4271
		dol_syslog(get_class($this)."::insert", LOG_DEBUG);
4272
		$resql = $this->db->query($sql);
4273
		if ($resql)
4274
		{
4275
			$this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'commandedet');
4276
			$this->rowid = $this->id;
4277
4278
			if (!$error)
4279
			{
4280
				$result = $this->insertExtraFields();
4281
				if ($result < 0)
4282
				{
4283
					$error++;
4284
				}
4285
			}
4286
4287
			if (!$error && !$notrigger)
4288
			{
4289
				// Call trigger
4290
				$result = $this->call_trigger('LINEORDER_INSERT', $user);
4291
				if ($result < 0) $error++;
4292
				// End call triggers
4293
			}
4294
4295
			if (!$error) {
4296
				$this->db->commit();
4297
				return 1;
4298
			}
4299
4300
			foreach ($this->errors as $errmsg)
4301
			{
4302
				dol_syslog(get_class($this)."::insert ".$errmsg, LOG_ERR);
4303
				$this->error .= ($this->error ? ', '.$errmsg : $errmsg);
4304
			}
4305
			$this->db->rollback();
4306
			return -1 * $error;
4307
		} else {
4308
			$this->error = $this->db->error();
4309
			$this->db->rollback();
4310
			return -2;
4311
		}
4312
	}
4313
4314
	/**
4315
	 *	Update the line object into db
4316
	 *
4317
	 *	@param      User	$user        	User that modify
4318
	 *	@param      int		$notrigger		1 = disable triggers
4319
	 *	@return		int		<0 si ko, >0 si ok
4320
	 */
4321
	public function update(User $user, $notrigger = 0)
4322
	{
4323
		global $conf, $langs;
4324
4325
		$error = 0;
4326
4327
		$pa_ht_isemptystring = (empty($this->pa_ht) && $this->pa_ht == ''); // If true, we can use a default value. If this->pa_ht = '0', we must use '0'.
4328
4329
		// Clean parameters
4330
		if (empty($this->tva_tx)) $this->tva_tx = 0;
4331
		if (empty($this->localtax1_tx)) $this->localtax1_tx = 0;
4332
		if (empty($this->localtax2_tx)) $this->localtax2_tx = 0;
4333
		if (empty($this->localtax1_type)) $this->localtax1_type = 0;
4334
		if (empty($this->localtax2_type)) $this->localtax2_type = 0;
4335
		if (empty($this->qty)) $this->qty = 0;
4336
		if (empty($this->total_localtax1)) $this->total_localtax1 = 0;
4337
		if (empty($this->total_localtax2)) $this->total_localtax2 = 0;
4338
		if (empty($this->marque_tx)) $this->marque_tx = 0;
4339
		if (empty($this->marge_tx)) $this->marge_tx = 0;
4340
		if (empty($this->remise)) $this->remise = 0;
1 ignored issue
show
Deprecated Code introduced by
The property OrderLine::$remise 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

4340
		if (empty($this->remise)) /** @scrutinizer ignore-deprecated */ $this->remise = 0;
Loading history...
4341
		if (empty($this->remise_percent)) $this->remise_percent = 0;
4342
		if (empty($this->info_bits)) $this->info_bits = 0;
4343
		if (empty($this->special_code)) $this->special_code = 0;
4344
		if (empty($this->product_type)) $this->product_type = 0;
4345
		if (empty($this->fk_parent_line)) $this->fk_parent_line = 0;
4346
		if (empty($this->pa_ht)) $this->pa_ht = 0;
4347
4348
		// if buy price not defined, define buyprice as configured in margin admin
4349
		if ($this->pa_ht == 0 && $pa_ht_isemptystring)
4350
		{
4351
			if (($result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product)) < 0)
4352
			{
4353
				return $result;
4354
			} else {
4355
				$this->pa_ht = $result;
4356
			}
4357
		}
4358
4359
		$this->db->begin();
4360
4361
		// Mise a jour ligne en base
4362
		$sql = "UPDATE ".MAIN_DB_PREFIX."commandedet SET";
4363
		$sql .= " description='".$this->db->escape($this->desc)."'";
4364
		$sql .= " , label=".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null");
4365
		$sql .= " , vat_src_code=".(!empty($this->vat_src_code) ? "'".$this->db->escape($this->vat_src_code)."'" : "''");
4366
		$sql .= " , tva_tx=".price2num($this->tva_tx);
4367
		$sql .= " , localtax1_tx=".price2num($this->localtax1_tx);
4368
		$sql .= " , localtax2_tx=".price2num($this->localtax2_tx);
4369
		$sql .= " , localtax1_type='".$this->db->escape($this->localtax1_type)."'";
4370
		$sql .= " , localtax2_type='".$this->db->escape($this->localtax2_type)."'";
4371
		$sql .= " , qty=".price2num($this->qty);
4372
		$sql .= " , subprice=".price2num($this->subprice)."";
4373
		$sql .= " , remise_percent=".price2num($this->remise_percent)."";
4374
		$sql .= " , price=".price2num($this->price).""; // TODO A virer
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

4374
		$sql .= " , price=".price2num(/** @scrutinizer ignore-deprecated */ $this->price).""; // TODO A virer
Loading history...
4375
		$sql .= " , remise=".price2num($this->remise).""; // TODO A virer
1 ignored issue
show
Deprecated Code introduced by
The property OrderLine::$remise 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

4375
		$sql .= " , remise=".price2num(/** @scrutinizer ignore-deprecated */ $this->remise).""; // TODO A virer
Loading history...
4376
		if (empty($this->skip_update_total))
4377
		{
4378
			$sql .= " , total_ht=".price2num($this->total_ht)."";
4379
			$sql .= " , total_tva=".price2num($this->total_tva)."";
4380
			$sql .= " , total_ttc=".price2num($this->total_ttc)."";
4381
			$sql .= " , total_localtax1=".price2num($this->total_localtax1);
4382
			$sql .= " , total_localtax2=".price2num($this->total_localtax2);
4383
		}
4384
		$sql .= " , fk_product_fournisseur_price=".(!empty($this->fk_fournprice) ? $this->fk_fournprice : "null");
4385
		$sql .= " , buy_price_ht='".price2num($this->pa_ht)."'";
4386
		$sql .= " , info_bits=".$this->info_bits;
4387
		$sql .= " , special_code=".$this->special_code;
4388
		$sql .= " , date_start=".(!empty($this->date_start) ? "'".$this->db->idate($this->date_start)."'" : "null");
4389
		$sql .= " , date_end=".(!empty($this->date_end) ? "'".$this->db->idate($this->date_end)."'" : "null");
4390
		$sql .= " , product_type=".$this->product_type;
4391
		$sql .= " , fk_parent_line=".(!empty($this->fk_parent_line) ? $this->fk_parent_line : "null");
4392
		if (!empty($this->rang)) $sql .= ", rang=".$this->rang;
4393
		$sql .= " , fk_unit=".(!$this->fk_unit ? 'NULL' : $this->fk_unit);
4394
4395
		// Multicurrency
4396
		$sql .= " , multicurrency_subprice=".price2num($this->multicurrency_subprice)."";
4397
		$sql .= " , multicurrency_total_ht=".price2num($this->multicurrency_total_ht)."";
4398
		$sql .= " , multicurrency_total_tva=".price2num($this->multicurrency_total_tva)."";
4399
		$sql .= " , multicurrency_total_ttc=".price2num($this->multicurrency_total_ttc)."";
4400
4401
		$sql .= " 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

4401
		$sql .= " 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...
4402
4403
		dol_syslog(get_class($this)."::update", LOG_DEBUG);
4404
		$resql = $this->db->query($sql);
4405
		if ($resql)
4406
		{
4407
			if (!$error)
4408
			{
4409
				$this->id = $this->rowid;
4410
				$result = $this->insertExtraFields();
4411
				if ($result < 0)
4412
				{
4413
					$error++;
4414
				}
4415
			}
4416
4417
			if (!$error && !$notrigger)
4418
			{
4419
				// Call trigger
4420
				$result = $this->call_trigger('LINEORDER_UPDATE', $user);
4421
				if ($result < 0) $error++;
4422
				// End call triggers
4423
			}
4424
4425
			if (!$error) {
4426
				$this->db->commit();
4427
				return 1;
4428
			}
4429
4430
			foreach ($this->errors as $errmsg)
4431
			{
4432
				dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
4433
				$this->error .= ($this->error ? ', '.$errmsg : $errmsg);
4434
			}
4435
			$this->db->rollback();
4436
			return -1 * $error;
4437
		} else {
4438
			$this->error = $this->db->error();
4439
			$this->db->rollback();
4440
			return -2;
4441
		}
4442
	}
4443
4444
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4445
	/**
4446
	 *	Update DB line fields total_xxx
4447
	 *	Used by migration
4448
	 *
4449
	 *	@return		int		<0 if KO, >0 if OK
4450
	 */
4451
	public function update_total()
4452
	{
4453
        // phpcs:enable
4454
		$this->db->begin();
4455
4456
		// Clean parameters
4457
		if (empty($this->total_localtax1)) $this->total_localtax1 = 0;
4458
		if (empty($this->total_localtax2)) $this->total_localtax2 = 0;
4459
4460
		// Mise a jour ligne en base
4461
		$sql = "UPDATE ".MAIN_DB_PREFIX."commandedet SET";
4462
		$sql .= " total_ht='".price2num($this->total_ht)."'";
4463
		$sql .= ",total_tva='".price2num($this->total_tva)."'";
4464
		$sql .= ",total_localtax1='".price2num($this->total_localtax1)."'";
4465
		$sql .= ",total_localtax2='".price2num($this->total_localtax2)."'";
4466
		$sql .= ",total_ttc='".price2num($this->total_ttc)."'";
4467
		$sql .= " 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

4467
		$sql .= " 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...
4468
4469
		dol_syslog("OrderLine::update_total", LOG_DEBUG);
4470
4471
		$resql = $this->db->query($sql);
4472
		if ($resql)
4473
		{
4474
			$this->db->commit();
4475
			return 1;
4476
		} else {
4477
			$this->error = $this->db->error();
4478
			$this->db->rollback();
4479
			return -2;
4480
		}
4481
	}
4482
}
4483