Passed
Branch develop (f95612)
by
unknown
98:25
created

BOM::updateLine()   F

Complexity

Conditions 16
Paths 513

Size

Total Lines 91
Code Lines 61

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 16
eloc 61
c 2
b 0
f 0
nc 513
nop 8
dl 0
loc 91
rs 2.0762

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) 2019  Laurent Destailleur <[email protected]>
3
 *
4
 * This program is free software; you can redistribute it and/or modify
5
 * it under the terms of the GNU General Public License as published by
6
 * the Free Software Foundation; either version 3 of the License, or
7
 * (at your option) any later version.
8
 *
9
 * This program is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 * GNU General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU General Public License
15
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16
 */
17
18
/**
19
 * \file        bom/class/bom.class.php
20
 * \ingroup     bom
21
 * \brief       This file is a CRUD class file for BOM (Create/Read/Update/Delete)
22
 */
23
24
// Put here all includes required by your class file
25
require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
26
//require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php';
27
//require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
28
29
30
/**
31
 * Class for BOM
32
 */
33
class BOM extends CommonObject
34
{
35
	/**
36
	 * @var string ID to identify managed object
37
	 */
38
	public $element = 'bom';
39
40
	/**
41
	 * @var string Name of table without prefix where object is stored
42
	 */
43
	public $table_element = 'bom_bom';
44
45
	/**
46
	 * @var int  Does bom support multicompany module ? 0=No test on entity, 1=Test with field entity, 2=Test with link by societe
47
	 */
48
	public $ismultientitymanaged = 1;
49
50
	/**
51
	 * @var int  Does object support extrafields ? 0=No, 1=Yes
52
	 */
53
	public $isextrafieldmanaged = 1;
54
55
	/**
56
	 * @var string String with name of icon for bom. Must be the part after the 'object_' into object_bom.png
57
	 */
58
	public $picto = 'bom';
59
60
61
	const STATUS_DRAFT = 0;
62
	const STATUS_VALIDATED = 1;
63
	const STATUS_CANCELED = 9;
64
65
66
	/**
67
	 *  'type' field format ('integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter]]', 'sellist:TableName:LabelFieldName[:KeyFieldName[:KeyFieldParent[:Filter]]]', 'varchar(x)', 'double(24,8)', 'real', 'price', 'text', 'text:none', 'html', 'date', 'datetime', 'timestamp', 'duration', 'mail', 'phone', 'url', 'password')
68
	 *         Note: Filter can be a string like "(t.ref:like:'SO-%') or (t.date_creation:<:'20160101') or (t.nature:is:NULL)"
69
	 *  'label' the translation key.
70
	 *  'picto' is code of a picto to show before value in forms
71
	 *  'enabled' is a condition when the field must be managed (Example: 1 or '$conf->global->MY_SETUP_PARAM)
72
	 *  'position' is the sort order of field.
73
	 *  'notnull' is set to 1 if not null in database. Set to -1 if we must set data to null if empty ('' or 0).
74
	 *  '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)
75
	 *  'noteditable' says if field is not editable (1 or 0)
76
	 *  '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.
77
	 *  'index' if we want an index in database.
78
	 *  'foreignkey'=>'tablename.field' if the field is a foreign key (it is recommanded to name the field fk_...).
79
	 *  'searchall' is 1 if we want to search in this field when making a search from the quick search button.
80
	 *  '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).
81
	 *  'css' and 'cssview' and 'csslist' is the CSS style to use on field. 'css' is used in creation and update. 'cssview' is used in view mode. 'csslist' is used for columns in lists. For example: 'css'=>'minwidth300 maxwidth500 widthcentpercentminusx', 'cssview'=>'wordbreak', 'csslist'=>'tdoverflowmax200'
82
	 *  'help' is a 'TranslationString' to use to show a tooltip on field. You can also use 'TranslationString:keyfortooltiponlick' for a tooltip on click.
83
	 *  'showoncombobox' if value of the field must be visible into the label of the combobox that list record
84
	 *  '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.
85
	 *  'arrayofkeyval' to set list of value if type is a list of predefined values. For example: array("0"=>"Draft","1"=>"Active","-1"=>"Cancel")
86
	 *  'autofocusoncreate' to have field having the focus on a create form. Only 1 field should have this property set to 1.
87
	 *  'comment' is not used. You can store here any text of your choice. It is not used by application.
88
	 *
89
	 *  Note: To have value dynamic, you can set value to 0 in definition and edit the value on the fly into the constructor.
90
	 */
91
92
	// BEGIN MODULEBUILDER PROPERTIES
93
	/**
94
	 * @var array  Array with all fields and their property. Do not use it as a static var. It may be modified by constructor.
95
	 */
96
	public $fields = array(
97
		'rowid' => array('type'=>'integer', 'label'=>'TechnicalID', 'enabled'=>1, 'visible'=>-2, 'position'=>1, 'notnull'=>1, 'index'=>1, 'comment'=>"Id",),
98
		'entity' => array('type'=>'integer', 'label'=>'Entity', 'enabled'=>1, 'visible'=>0, 'notnull'=> 1, 'default'=>1, 'index'=>1, 'position'=>5),
99
		'ref' => array('type'=>'varchar(128)', 'label'=>'Ref', 'enabled'=>1, 'noteditable'=>1, 'visible'=>4, 'position'=>10, 'notnull'=>1, 'default'=>'(PROV)', 'index'=>1, 'searchall'=>1, 'comment'=>"Reference of BOM", 'showoncombobox'=>'1', 'csslist'=>'nowraponall'),
100
		'label' => array('type'=>'varchar(255)', 'label'=>'Label', 'enabled'=>1, 'visible'=>1, 'position'=>30, 'notnull'=>1, 'searchall'=>1, 'showoncombobox'=>'2', 'autofocusoncreate'=>1, 'css'=>'minwidth300 maxwidth400', 'csslist'=>'tdoverflowmax200'),
101
		'bomtype' => array('type'=>'integer', 'label'=>'Type', 'enabled'=>1, 'visible'=>1, 'position'=>33, 'notnull'=>1, 'default'=>'0', 'arrayofkeyval'=>array(0=>'Manufacturing', 1=>'Disassemble'), 'css'=>'minwidth175', 'csslist'=>'minwidth175 center'),
102
		//'bomtype' => array('type'=>'integer', 'label'=>'Type', 'enabled'=>1, 'visible'=>-1, 'position'=>32, 'notnull'=>1, 'default'=>'0', 'arrayofkeyval'=>array(0=>'Manufacturing')),
103
		'fk_product' => array('type'=>'integer:Product:product/class/product.class.php:1:(finished IS NULL or finished <> 0)', 'label'=>'Product', 'picto'=>'product', 'enabled'=>1, 'visible'=>1, 'position'=>35, 'notnull'=>1, 'index'=>1, 'help'=>'ProductBOMHelp', 'css'=>'maxwidth500', 'csslist'=>'tdoverflowmax100'),
104
		'description' => array('type'=>'text', 'label'=>'Description', 'enabled'=>1, 'visible'=>-1, 'position'=>60, 'notnull'=>-1,),
105
		'qty' => array('type'=>'real', 'label'=>'Quantity', 'enabled'=>1, 'visible'=>1, 'default'=>1, 'position'=>55, 'notnull'=>1, 'isameasure'=>'1', 'css'=>'maxwidth75imp'),
106
		//'efficiency' => array('type'=>'real', 'label'=>'ManufacturingEfficiency', 'enabled'=>1, 'visible'=>-1, 'default'=>1, 'position'=>100, 'notnull'=>0, 'css'=>'maxwidth50imp', 'help'=>'ValueOfMeansLossForProductProduced'),
107
		'duration' => array('type'=>'duration', 'label'=>'EstimatedDuration', 'enabled'=>1, 'visible'=>-1, 'position'=>101, 'notnull'=>-1, 'css'=>'maxwidth50imp', 'help'=>'EstimatedDurationDesc'),
108
		'fk_warehouse' => array('type'=>'integer:Entrepot:product/stock/class/entrepot.class.php:0', 'label'=>'WarehouseForProduction', 'picto'=>'stock', 'enabled'=>1, 'visible'=>-1, 'position'=>102, 'css'=>'maxwidth500', 'csslist'=>'tdoverflowmax100'),
109
		'note_public' => array('type'=>'html', 'label'=>'NotePublic', 'enabled'=>1, 'visible'=>-2, 'position'=>161, 'notnull'=>-1,),
110
		'note_private' => array('type'=>'html', 'label'=>'NotePrivate', 'enabled'=>1, 'visible'=>-2, 'position'=>162, 'notnull'=>-1,),
111
		'date_creation' => array('type'=>'datetime', 'label'=>'DateCreation', 'enabled'=>1, 'visible'=>-2, 'position'=>300, 'notnull'=>1,),
112
		'tms' => array('type'=>'timestamp', 'label'=>'DateModification', 'enabled'=>1, 'visible'=>-2, 'position'=>501, 'notnull'=>1,),
113
		'date_valid' => array('type'=>'datetime', 'label'=>'DateValidation', 'enabled'=>1, 'visible'=>-2, 'position'=>502, 'notnull'=>0,),
114
		'fk_user_creat' => array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserCreation', 'picto'=>'user', 'enabled'=>1, 'visible'=>-2, 'position'=>510, 'notnull'=>1, 'foreignkey'=>'user.rowid', 'csslist'=>'tdoverflowmax100'),
115
		'fk_user_modif' => array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserModif', 'picto'=>'user', 'enabled'=>1, 'visible'=>-2, 'position'=>511, 'notnull'=>-1, 'csslist'=>'tdoverflowmax100'),
116
		'fk_user_valid' => array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserValidation', 'picto'=>'user', 'enabled'=>1, 'visible'=>-2, 'position'=>512, 'notnull'=>0, 'csslist'=>'tdoverflowmax100'),
117
		'import_key' => array('type'=>'varchar(14)', 'label'=>'ImportId', 'enabled'=>1, 'visible'=>-2, 'position'=>1000, 'notnull'=>-1,),
118
		'model_pdf' =>array('type'=>'varchar(255)', 'label'=>'Model pdf', 'enabled'=>1, 'visible'=>0, 'position'=>1010),
119
		'status' => array('type'=>'integer', 'label'=>'Status', 'enabled'=>1, 'visible'=>2, 'position'=>1000, 'notnull'=>1, 'default'=>0, 'index'=>1, 'arrayofkeyval'=>array(0=>'Draft', 1=>'Enabled', 9=>'Disabled')),
120
	);
121
122
	/**
123
	 * @var int rowid
124
	 */
125
	public $rowid;
126
127
	/**
128
	 * @var string ref
129
	 */
130
	public $ref;
131
132
	/**
133
	 * @var string label
134
	 */
135
	public $label;
136
137
	/**
138
	 * @var int bomtype
139
	 */
140
	public $bomtype;
141
142
	/**
143
	 * @var string description
144
	 */
145
	public $description;
146
147
	/**
148
	 * @var integer|string date_creation
149
	 */
150
	public $date_creation;
151
152
153
	public $tms;
154
155
	/**
156
	 * @var int Id User creator
157
	 */
158
	public $fk_user_creat;
159
160
	/**
161
	 * @var int Id User modifying
162
	 */
163
	public $fk_user_modif;
164
165
	/**
166
	 * @var string import key
167
	 */
168
	public $import_key;
169
170
	/**
171
	 * @var int status
172
	 */
173
	public $status;
174
175
	/**
176
	 * @var int product Id
177
	 */
178
	public $fk_product;
179
	public $qty;
180
	public $efficiency;
181
	// END MODULEBUILDER PROPERTIES
182
183
184
	// If this object has a subtable with lines
185
186
	/**
187
	 * @var int    Name of subtable line
188
	 */
189
	public $table_element_line = 'bom_bomline';
190
191
	/**
192
	 * @var string    Fieldname with ID of parent key if this field has a parent
193
	 */
194
	public $fk_element = 'fk_bom';
195
196
	/**
197
	 * @var string    Name of subtable class that manage subtable lines
198
	 */
199
	public $class_element_line = 'BOMLine';
200
201
	// /**
202
	//  * @var array	List of child tables. To test if we can delete object.
203
	//  */
204
	// protected $childtables=array();
205
206
	/**
207
	 * @var array	List of child tables. To know object to delete on cascade.
208
	 */
209
	protected $childtablesoncascade = array('bom_bomline');
210
211
	/**
212
	 * @var BOMLine[]     Array of subtable lines
213
	 */
214
	public $lines = array();
215
216
	/**
217
	 * @var int		Calculated cost for the BOM
218
	 */
219
	public $total_cost = 0;
220
221
	/**
222
	 * @var int		Calculated cost for 1 unit of the product in BOM
223
	 */
224
	public $unit_cost = 0;
225
226
227
228
	/**
229
	 * Constructor
230
	 *
231
	 * @param DoliDb $db Database handler
232
	 */
233
	public function __construct(DoliDB $db)
234
	{
235
		global $conf, $langs;
236
237
		$this->db = $db;
238
239
		if (empty($conf->global->MAIN_SHOW_TECHNICAL_ID) && isset($this->fields['rowid'])) {
240
			$this->fields['rowid']['visible'] = 0;
241
		}
242
		if (empty($conf->multicompany->enabled) && isset($this->fields['entity'])) {
243
			$this->fields['entity']['enabled'] = 0;
244
		}
245
246
		// Unset fields that are disabled
247
		foreach ($this->fields as $key => $val) {
248
			if (isset($val['enabled']) && empty($val['enabled'])) {
249
				unset($this->fields[$key]);
250
			}
251
		}
252
253
		// Translate some data of arrayofkeyval
254
		foreach ($this->fields as $key => $val) {
255
			if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
256
				foreach ($val['arrayofkeyval'] as $key2 => $val2) {
257
					$this->fields[$key]['arrayofkeyval'][$key2] = $langs->trans($val2);
258
				}
259
			}
260
		}
261
	}
262
263
	/**
264
	 * Create object into database
265
	 *
266
	 * @param  User $user      User that creates
267
	 * @param  bool $notrigger false=launch triggers after, true=disable triggers
268
	 * @return int             <0 if KO, Id of created object if OK
269
	 */
270
	public function create(User $user, $notrigger = false)
271
	{
272
		if ($this->efficiency <= 0 || $this->efficiency > 1) {
273
			$this->efficiency = 1;
274
		}
275
276
		return $this->createCommon($user, $notrigger);
277
	}
278
279
	/**
280
	 * Clone an object into another one
281
	 *
282
	 * @param  	User 	$user      	User that creates
283
	 * @param  	int 	$fromid     Id of object to clone
284
	 * @return 	mixed 				New object created, <0 if KO
285
	 */
286
	public function createFromClone(User $user, $fromid)
287
	{
288
		global $langs, $hookmanager, $extrafields;
289
		$error = 0;
290
291
		dol_syslog(__METHOD__, LOG_DEBUG);
292
293
		$object = new self($this->db);
294
295
		$this->db->begin();
296
297
		// Load source object
298
		$result = $object->fetchCommon($fromid);
299
		if ($result > 0 && !empty($object->table_element_line)) {
300
			$object->fetchLines();
301
		}
302
303
		// Get lines so they will be clone
304
		//foreach ($object->lines as $line)
305
		//	$line->fetch_optionals();
306
307
		// Reset some properties
308
		unset($object->id);
309
		unset($object->fk_user_creat);
310
		unset($object->import_key);
311
312
		// Clear fields
313
		$object->ref = empty($this->fields['ref']['default']) ? $langs->trans("copy_of_").$object->ref : $this->fields['ref']['default'];
314
		$object->label = empty($this->fields['label']['default']) ? $langs->trans("CopyOf")." ".$object->label : $this->fields['label']['default'];
315
		$object->status = self::STATUS_DRAFT;
316
		// ...
317
		// Clear extrafields that are unique
318
		if (is_array($object->array_options) && count($object->array_options) > 0) {
319
			$extrafields->fetch_name_optionals_label($object->table_element);
320
			foreach ($object->array_options as $key => $option) {
321
				$shortkey = preg_replace('/options_/', '', $key);
322
				if (!empty($extrafields->attributes[$this->element]['unique'][$shortkey])) {
323
					//var_dump($key); var_dump($clonedObj->array_options[$key]); exit;
324
					unset($object->array_options[$key]);
325
				}
326
			}
327
		}
328
329
		// Create clone
330
		$object->context['createfromclone'] = 'createfromclone';
331
		$result = $object->createCommon($user);
332
		if ($result < 0) {
333
			$error++;
334
			$this->error = $object->error;
335
			$this->errors = $object->errors;
336
		}
337
338
		if (!$error) {
339
			// copy internal contacts
340
			if ($this->copy_linked_contact($object, 'internal') < 0) {
341
				$error++;
342
			}
343
		}
344
345
		if (!$error) {
346
			// copy external contacts if same company
347
			if (property_exists($this, 'socid') && $this->socid == $object->socid) {
348
				if ($this->copy_linked_contact($object, 'external') < 0) {
349
					$error++;
350
				}
351
			}
352
		}
353
354
		// If there is lines, create lines too
355
356
357
358
		unset($object->context['createfromclone']);
359
360
		// End
361
		if (!$error) {
362
			$this->db->commit();
363
			return $object;
364
		} else {
365
			$this->db->rollback();
366
			return -1;
367
		}
368
	}
369
370
	/**
371
	 * Load object in memory from the database
372
	 *
373
	 * @param int    $id   Id object
374
	 * @param string $ref  Ref
375
	 * @return int         <0 if KO, 0 if not found, >0 if OK
376
	 */
377
	public function fetch($id, $ref = null)
378
	{
379
		$result = $this->fetchCommon($id, $ref);
380
381
		if ($result > 0 && !empty($this->table_element_line)) {
382
			$this->fetchLines();
383
		}
384
		//$this->calculateCosts();		// This consume a high number of subrequests. Do not call it into fetch but when you need it.
385
386
		return $result;
387
	}
388
389
	/**
390
	 * Load object lines in memory from the database
391
	 *
392
	 * @return int         <0 if KO, 0 if not found, >0 if OK
393
	 */
394
	public function fetchLines()
395
	{
396
		$this->lines = array();
397
398
		$result = $this->fetchLinesCommon();
399
		return $result;
400
	}
401
402
	/**
403
	 * Load list of objects in memory from the database.
404
	 *
405
	 * @param  string      $sortorder    Sort Order
406
	 * @param  string      $sortfield    Sort field
407
	 * @param  int         $limit        limit
408
	 * @param  int         $offset       Offset
409
	 * @param  array       $filter       Filter array. Example array('field'=>'valueforlike', 'customurl'=>...)
410
	 * @param  string      $filtermode   Filter mode (AND or OR)
411
	 * @return array|int                 int <0 if KO, array of pages if OK
412
	 */
413
	public function fetchAll($sortorder = '', $sortfield = '', $limit = 0, $offset = 0, array $filter = array(), $filtermode = 'AND')
414
	{
415
		global $conf;
416
417
		dol_syslog(__METHOD__, LOG_DEBUG);
418
419
		$records = array();
420
421
		$sql = 'SELECT ';
422
		$sql .= $this->getFieldList();
423
		$sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as t';
424
		if ($this->ismultientitymanaged) {
425
			$sql .= ' WHERE t.entity IN ('.getEntity($this->table_element).')';
426
		} else {
427
			$sql .= ' WHERE 1 = 1';
428
		}
429
		// Manage filter
430
		$sqlwhere = array();
431
		if (count($filter) > 0) {
432
			foreach ($filter as $key => $value) {
433
				if ($key == 't.rowid') {
434
					$sqlwhere[] = $key." = ".((int) $value);
435
				} elseif (strpos($key, 'date') !== false) {
436
					$sqlwhere[] = $key." = '".$this->db->idate($value)."'";
437
				} elseif ($key == 'customsql') {
438
					$sqlwhere[] = $value;
439
				} else {
440
					$sqlwhere[] = $key." LIKE '%".$this->db->escape($value)."%'";
441
				}
442
			}
443
		}
444
		if (count($sqlwhere) > 0) {
445
			$sql .= " AND (".implode(" ".$filtermode." ", $sqlwhere).")";
446
		}
447
448
		if (!empty($sortfield)) {
449
			$sql .= $this->db->order($sortfield, $sortorder);
450
		}
451
		if (!empty($limit)) {
452
			$sql .= $this->db->plimit($limit, $offset);
453
		}
454
455
		$resql = $this->db->query($sql);
456
		if ($resql) {
457
			$num = $this->db->num_rows($resql);
458
459
			while ($obj = $this->db->fetch_object($resql)) {
460
				$record = new self($this->db);
461
				$record->setVarsFromFetchObj($obj);
462
463
				$records[$record->id] = $record;
464
			}
465
			$this->db->free($resql);
466
467
			return $records;
468
		} else {
469
			$this->errors[] = 'Error '.$this->db->lasterror();
470
			dol_syslog(__METHOD__.' '.join(',', $this->errors), LOG_ERR);
471
472
			return -1;
473
		}
474
	}
475
476
	/**
477
	 * Update object into database
478
	 *
479
	 * @param  User $user      User that modifies
480
	 * @param  bool $notrigger false=launch triggers after, true=disable triggers
481
	 * @return int             <0 if KO, >0 if OK
482
	 */
483
	public function update(User $user, $notrigger = false)
484
	{
485
		if ($this->efficiency <= 0 || $this->efficiency > 1) {
486
			$this->efficiency = 1;
487
		}
488
489
		return $this->updateCommon($user, $notrigger);
490
	}
491
492
	/**
493
	 * Delete object in database
494
	 *
495
	 * @param User $user       User that deletes
496
	 * @param bool $notrigger  false=launch triggers after, true=disable triggers
497
	 * @return int             <0 if KO, >0 if OK
498
	 */
499
	public function delete(User $user, $notrigger = false)
500
	{
501
		return $this->deleteCommon($user, $notrigger);
502
		//return $this->deleteCommon($user, $notrigger, 1);
503
	}
504
505
	/**
506
	 * Add an BOM line into database (linked to BOM)
507
	 *
508
	 * @param	int		$fk_product				Id of product
509
	 * @param	float	$qty					Quantity
510
	 * @param	int		$qty_frozen				Frozen quantity
511
	 * @param 	int		$disable_stock_change	Disable stock change on using in MO
512
	 * @param	float	$efficiency				Efficiency in MO
513
	 * @param	int		$position				Position of BOM-Line in BOM-Lines
514
	 * @param	int		$fk_bom_child			Id of BOM Child
515
	 * @param	string	$import_key				Import Key
516
	 * @return	int								<0 if KO, Id of created object if OK
517
	 */
518
	public function addLine($fk_product, $qty, $qty_frozen = 0, $disable_stock_change = 0, $efficiency = 1.0, $position = -1, $fk_bom_child = null, $import_key = null)
519
	{
520
521
		global $mysoc, $conf, $langs, $user;
522
523
		$logtext = "::addLine bomid=$this->id, qty=$qty, fk_product=$fk_product, qty_frozen=$qty_frozen, disable_stock_change=$disable_stock_change, efficiency=$efficiency";
524
		$logtext .= ", fk_bom_child=$fk_bom_child, import_key=$import_key";
525
		dol_syslog(get_class($this).$logtext, LOG_DEBUG);
526
527
		if ($this->statut == self::STATUS_DRAFT) {
528
			include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
529
530
			// Clean parameters
531
			if (empty($qty)) {
532
				$qty = 0;
533
			}
534
			if (empty($qty_frozen)) {
535
				$qty_frozen = 0;
536
			}
537
			if (empty($disable_stock_change)) {
538
				$disable_stock_change = 0;
539
			}
540
			if (empty($efficiency)) {
541
				$efficiency = 1.0;
542
			}
543
			if (empty($fk_bom_child)) {
544
				$fk_bom_child = null;
545
			}
546
			if (empty($import_key)) {
547
				$import_key = null;
548
			}
549
			if (empty($position)) {
550
				$position = -1;
551
			}
552
553
			$qty = price2num($qty);
554
			$efficiency = price2num($efficiency);
555
			$position = price2num($position);
556
557
			$this->db->begin();
558
559
			// Rank to use
560
			$rangMax = $this->line_max();
561
			$rankToUse = $position;
562
			if ($rankToUse <= 0 or $rankToUse > $rangMax) { // New line after existing lines
563
				$rankToUse = $rangMax + 1;
564
			} else { // New line between the existing lines
565
				foreach ($this->lines as $bl) {
566
					if ($bl->position >= $rankToUse) {
567
						$bl->position++;
568
						$bl->update($user);
569
					}
570
				}
571
			}
572
573
			// Insert line
574
			$this->line = new BOMLine($this->db);
575
576
			$this->line->context = $this->context;
577
578
			$this->line->fk_bom = $this->id;
579
			$this->line->fk_product = $fk_product;
580
			$this->line->qty = $qty;
581
			$this->line->qty_frozen = $qty_frozen;
582
			$this->line->disable_stock_change = $disable_stock_change;
583
			$this->line->efficiency = $efficiency;
584
			$this->line->fk_bom_child = $fk_bom_child;
585
			$this->line->import_key = $import_key;
586
			$this->line->position = $rankToUse;
587
588
			$result = $this->line->create($user);
589
590
			if ($result > 0) {
591
				$this->calculateCosts();
592
				$this->db->commit();
593
				return $result;
594
			} else {
595
				$this->error = $this->line->error;
596
				dol_syslog(get_class($this)."::addLine error=".$this->error, LOG_ERR);
597
				$this->db->rollback();
598
				return -2;
599
			}
600
		} else {
601
			dol_syslog(get_class($this)."::addLine status of BOM must be Draft to allow use of ->addLine()", LOG_ERR);
602
			return -3;
603
		}
604
	}
605
606
	/**
607
	 * Update an BOM line into database
608
	 *
609
	 * @param 	int		$rowid					Id of line to update
610
	 * @param	float	$qty					Quantity
611
	 * @param	int		$qty_frozen				Frozen quantity
612
	 * @param 	int		$disable_stock_change	Disable stock change on using in MO
613
	 * @param	float	$efficiency				Efficiency in MO
614
	 * @param	int		$position				Position of BOM-Line in BOM-Lines
615
	 * @param	int		$fk_bom_child			Id of BOM Child
616
	 * @param	string	$import_key				Import Key
617
	 * @return	int								<0 if KO, Id of updated BOM-Line if OK
618
	 */
619
	public function updateLine($rowid, $qty, $qty_frozen = 0, $disable_stock_change = 0, $efficiency = 1.0, $position = -1, $fk_bom_child = null, $import_key = null)
620
	{
621
622
		global $mysoc, $conf, $langs, $user;
623
624
		$logtext = "::updateLine bomid=$this->id, qty=$qty, qty_frozen=$qty_frozen, disable_stock_change=$disable_stock_change, efficiency=$efficiency";
625
		$logtext .= ", fk_bom_child=$fk_bom_child, import_key=$import_key";
626
		dol_syslog(get_class($this).$logtext, LOG_DEBUG);
627
628
		if ($this->statut == self::STATUS_DRAFT) {
629
			include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
630
631
			// Clean parameters
632
			if (empty($qty)) {
633
				$qty = 0;
634
			}
635
			if (empty($qty_frozen)) {
636
				$qty_frozen = 0;
637
			}
638
			if (empty($disable_stock_change)) {
639
				$disable_stock_change = 0;
640
			}
641
			if (empty($efficiency)) {
642
				$efficiency = 1.0;
643
			}
644
			if (empty($fk_bom_child)) {
645
				$fk_bom_child = null;
646
			}
647
			if (empty($import_key)) {
648
				$import_key = null;
649
			}
650
			if (empty($position)) {
651
				$position = -1;
652
			}
653
654
			$qty = price2num($qty);
655
			$efficiency = price2num($efficiency);
656
			$position = price2num($position);
657
658
			$this->db->begin();
659
660
			//Fetch current line from the database and then clone the object and set it in $oldline property
661
			$line = new BOMLine($this->db);
662
			$line->fetch($rowid);
663
			$line->fetch_optionals();
664
665
			$staticLine = clone $line;
666
			$line->oldcopy = $staticLine;
667
			$this->line = $line;
668
			$this->line->context = $this->context;
669
670
			// Rank to use
671
			$rankToUse = (int) $position;
672
			if ($rankToUse != $line->oldcopy->position) { // check if position have a new value
673
				foreach ($this->lines as $bl) {
674
					if ($bl->position >= $rankToUse AND $bl->position < ($line->oldcopy->position + 1)) { // move rank up
675
						$bl->position++;
676
						$bl->update($user);
677
					}
678
					if ($bl->position <= $rankToUse AND $bl->position > ($line->oldcopy->position)) { // move rank down
679
						$bl->position--;
680
						$bl->update($user);
681
					}
682
				}
683
			}
684
685
686
			$this->line->fk_bom = $this->id;
687
			$this->line->qty = $qty;
688
			$this->line->qty_frozen = $qty_frozen;
689
			$this->line->disable_stock_change = $disable_stock_change;
690
			$this->line->efficiency = $efficiency;
691
			$this->line->fk_bom_child = $fk_bom_child;
692
			$this->line->import_key = $import_key;
693
			$this->line->position = $rankToUse;
694
695
			$result = $this->line->update($user);
696
697
			if ($result > 0) {
698
				$this->calculateCosts();
699
				$this->db->commit();
700
				return $result;
701
			} else {
702
				$this->error = $this->line->error;
703
				dol_syslog(get_class($this)."::addLine error=".$this->error, LOG_ERR);
704
				$this->db->rollback();
705
				return -2;
706
			}
707
		} else {
708
			dol_syslog(get_class($this)."::addLine status of BOM must be Draft to allow use of ->addLine()", LOG_ERR);
709
			return -3;
710
		}
711
	}
712
713
	/**
714
	 *  Delete a line of object in database
715
	 *
716
	 *	@param  User	$user       User that delete
717
	 *  @param	int		$idline		Id of line to delete
718
	 *  @param 	bool 	$notrigger  false=launch triggers after, true=disable triggers
719
	 *  @return int         		>0 if OK, <0 if KO
720
	 */
721
	public function deleteLine(User $user, $idline, $notrigger = false)
722
	{
723
		if ($this->status < 0) {
724
			$this->error = 'ErrorDeleteLineNotAllowedByObjectStatus';
725
			return -2;
726
		}
727
728
		$this->db->begin();
729
730
		//Fetch current line from the database and then clone the object and set it in $oldline property
731
		$line = new BOMLine($this->db);
732
		$line->fetch($idline);
733
		$line->fetch_optionals();
734
735
		$staticLine = clone $line;
736
		$line->oldcopy = $staticLine;
737
		$this->line = $line;
738
		$this->line->context = $this->context;
739
740
		$result = $this->line->delete($user, $notrigger);
741
742
		//Positions (rank) reordering
743
		foreach ($this->lines as $bl) {
744
			if ($bl->position > ($line->oldcopy->position)) { // move rank down
745
				$bl->position--;
746
				$bl->update($user);
747
			}
748
		}
749
750
		if ($result > 0) {
751
			$this->calculateCosts();
752
			$this->db->commit();
753
			return $result;
754
		} else {
755
			$this->error = $this->line->error;
756
			dol_syslog(get_class($this)."::addLine error=".$this->error, LOG_ERR);
757
			$this->db->rollback();
758
			return -2;
759
		}
760
	}
761
762
	/**
763
	 *  Returns the reference to the following non used BOM depending on the active numbering module
764
	 *  defined into BOM_ADDON
765
	 *
766
	 *  @param	Product		$prod 	Object product
767
	 *  @return string      		BOM free reference
768
	 */
769
	public function getNextNumRef($prod)
770
	{
771
		global $langs, $conf;
772
		$langs->load("mrp");
773
774
		if (!empty($conf->global->BOM_ADDON)) {
775
			$mybool = false;
776
777
			$file = $conf->global->BOM_ADDON.".php";
778
			$classname = $conf->global->BOM_ADDON;
779
780
			// Include file with class
781
			$dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
782
			foreach ($dirmodels as $reldir) {
783
				$dir = dol_buildpath($reldir."core/modules/bom/");
784
785
				// Load file with numbering class (if found)
786
				$mybool |= @include_once $dir.$file;
787
			}
788
789
			if ($mybool === false) {
790
				dol_print_error('', "Failed to include file ".$file);
791
				return '';
792
			}
793
794
			$obj = new $classname();
795
			$numref = $obj->getNextValue($prod, $this);
796
797
			if ($numref != "") {
798
				return $numref;
799
			} else {
800
				$this->error = $obj->error;
801
				//dol_print_error($this->db,get_class($this)."::getNextNumRef ".$obj->error);
802
				return "";
803
			}
804
		} else {
805
			print $langs->trans("Error")." ".$langs->trans("Error_BOM_ADDON_NotDefined");
806
			return "";
807
		}
808
	}
809
810
	/**
811
	 *	Validate bom
812
	 *
813
	 *	@param		User	$user     		User making status change
814
	 *  @param		int		$notrigger		1=Does not execute triggers, 0= execute triggers
815
	 *	@return  	int						<=0 if OK, 0=Nothing done, >0 if KO
816
	 */
817
	public function validate($user, $notrigger = 0)
818
	{
819
		global $conf, $langs;
820
821
		require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
822
823
		$error = 0;
824
825
		// Protection
826
		if ($this->status == self::STATUS_VALIDATED) {
827
			dol_syslog(get_class($this)."::validate action abandonned: already validated", LOG_WARNING);
828
			return 0;
829
		}
830
831
		/*if (! ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->bom->create))
832
			|| (! empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->bom->bom_advance->validate))))
833
		{
834
			$this->error='NotEnoughPermissions';
835
			dol_syslog(get_class($this)."::valid ".$this->error, LOG_ERR);
836
			return -1;
837
		}*/
838
839
		$now = dol_now();
840
841
		$this->db->begin();
842
843
		// Define new ref
844
		if (!$error && (preg_match('/^[\(]?PROV/i', $this->ref) || empty($this->ref))) { // empty should not happened, but when it occurs, the test save life
845
			$this->fetch_product();
846
			$num = $this->getNextNumRef($this->product);
847
		} else {
848
			$num = $this->ref;
849
		}
850
		$this->newref = dol_sanitizeFileName($num);
851
852
		// Validate
853
		$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
854
		$sql .= " SET ref = '".$this->db->escape($num)."',";
855
		$sql .= " status = ".self::STATUS_VALIDATED.",";
856
		$sql .= " date_valid='".$this->db->idate($now)."',";
857
		$sql .= " fk_user_valid = ".((int) $user->id);
858
		$sql .= " WHERE rowid = ".((int) $this->id);
859
860
		dol_syslog(get_class($this)."::validate()", LOG_DEBUG);
861
		$resql = $this->db->query($sql);
862
		if (!$resql) {
863
			dol_print_error($this->db);
864
			$this->error = $this->db->lasterror();
865
			$error++;
866
		}
867
868
		if (!$error && !$notrigger) {
869
			// Call trigger
870
			$result = $this->call_trigger('BOM_VALIDATE', $user);
871
			if ($result < 0) {
872
				$error++;
873
			}
874
			// End call triggers
875
		}
876
877
		if (!$error) {
878
			$this->oldref = $this->ref;
879
880
			// Rename directory if dir was a temporary ref
881
			if (preg_match('/^[\(]?PROV/i', $this->ref)) {
882
				// Now we rename also files into index
883
				$sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filename = CONCAT('".$this->db->escape($this->newref)."', SUBSTR(filename, ".(strlen($this->ref) + 1).")), filepath = 'bom/".$this->db->escape($this->newref)."'";
884
				$sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'bom/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
885
				$resql = $this->db->query($sql);
886
				if (!$resql) {
887
					$error++; $this->error = $this->db->lasterror();
888
				}
889
890
				// We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
891
				$oldref = dol_sanitizeFileName($this->ref);
892
				$newref = dol_sanitizeFileName($num);
893
				$dirsource = $conf->bom->dir_output.'/'.$oldref;
894
				$dirdest = $conf->bom->dir_output.'/'.$newref;
895
				if (!$error && file_exists($dirsource)) {
896
					dol_syslog(get_class($this)."::validate() rename dir ".$dirsource." into ".$dirdest);
897
898
					if (@rename($dirsource, $dirdest)) {
899
						dol_syslog("Rename ok");
900
						// Rename docs starting with $oldref with $newref
901
						$listoffiles = dol_dir_list($conf->bom->dir_output.'/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
902
						foreach ($listoffiles as $fileentry) {
903
							$dirsource = $fileentry['name'];
904
							$dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
905
							$dirsource = $fileentry['path'].'/'.$dirsource;
906
							$dirdest = $fileentry['path'].'/'.$dirdest;
907
							@rename($dirsource, $dirdest);
908
						}
909
					}
910
				}
911
			}
912
		}
913
914
		// Set new ref and current status
915
		if (!$error) {
916
			$this->ref = $num;
917
			$this->status = self::STATUS_VALIDATED;
918
		}
919
920
		if (!$error) {
921
			$this->db->commit();
922
			return 1;
923
		} else {
924
			$this->db->rollback();
925
			return -1;
926
		}
927
	}
928
929
	/**
930
	 *	Set draft status
931
	 *
932
	 *	@param	User	$user			Object user that modify
933
	 *  @param	int		$notrigger		1=Does not execute triggers, 0=Execute triggers
934
	 *	@return	int						<0 if KO, >0 if OK
935
	 */
936
	public function setDraft($user, $notrigger = 0)
937
	{
938
		// Protection
939
		if ($this->status <= self::STATUS_DRAFT) {
940
			return 0;
941
		}
942
943
		/*if (! ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->bom->write))
944
		 || (! empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->bom->bom_advance->validate))))
945
		 {
946
		 $this->error='Permission denied';
947
		 return -1;
948
		 }*/
949
950
		return $this->setStatusCommon($user, self::STATUS_DRAFT, $notrigger, 'BOM_UNVALIDATE');
951
	}
952
953
	/**
954
	 *	Set cancel status
955
	 *
956
	 *	@param	User	$user			Object user that modify
957
	 *  @param	int		$notrigger		1=Does not execute triggers, 0=Execute triggers
958
	 *	@return	int						<0 if KO, 0=Nothing done, >0 if OK
959
	 */
960
	public function cancel($user, $notrigger = 0)
961
	{
962
		// Protection
963
		if ($this->status != self::STATUS_VALIDATED) {
964
			return 0;
965
		}
966
967
		/*if (! ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->bom->write))
968
		 || (! empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->bom->bom_advance->validate))))
969
		 {
970
		 $this->error='Permission denied';
971
		 return -1;
972
		 }*/
973
974
		return $this->setStatusCommon($user, self::STATUS_CANCELED, $notrigger, 'BOM_CLOSE');
975
	}
976
977
	/**
978
	 *	Set cancel status
979
	 *
980
	 *	@param	User	$user			Object user that modify
981
	 *  @param	int		$notrigger		1=Does not execute triggers, 0=Execute triggers
982
	 *	@return	int						<0 if KO, 0=Nothing done, >0 if OK
983
	 */
984
	public function reopen($user, $notrigger = 0)
985
	{
986
		// Protection
987
		if ($this->status != self::STATUS_CANCELED) {
988
			return 0;
989
		}
990
991
		/*if (! ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->bom->write))
992
		 || (! empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->bom->bom_advance->validate))))
993
		 {
994
		 $this->error='Permission denied';
995
		 return -1;
996
		 }*/
997
998
		return $this->setStatusCommon($user, self::STATUS_VALIDATED, $notrigger, 'BOM_REOPEN');
999
	}
1000
1001
1002
	/**
1003
	 *  Return a link to the object card (with optionaly the picto)
1004
	 *
1005
	 *	@param	int		$withpicto					Include picto in link (0=No picto, 1=Include picto into link, 2=Only picto)
1006
	 *	@param	string	$option						On what the link point to ('nolink', ...)
1007
	 *  @param	int  	$notooltip					1=Disable tooltip
1008
	 *  @param  string  $morecss            		Add more css on link
1009
	 *  @param  int     $save_lastsearch_value    	-1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking
1010
	 *	@return	string								String with URL
1011
	 */
1012
	public function getNomUrl($withpicto = 0, $option = '', $notooltip = 0, $morecss = '', $save_lastsearch_value = -1)
1013
	{
1014
		global $db, $conf, $langs, $hookmanager;
1015
1016
		if (!empty($conf->dol_no_mouse_hover)) {
1017
			$notooltip = 1; // Force disable tooltips
1018
		}
1019
1020
		$result = '';
1021
1022
		$label = img_picto('', $this->picto).' <u class="paddingrightonly">'.$langs->trans("BillOfMaterials").'</u>';
1023
		if (isset($this->status)) {
1024
			$label .= ' '.$this->getLibStatut(5);
1025
		}
1026
		$label .= '<br>';
1027
		$label .= '<b>'.$langs->trans('Ref').':</b> '.$this->ref;
1028
		if (isset($this->label)) {
1029
			$label .= '<br><b>'.$langs->trans('Label').':</b> '.$this->label;
1030
		}
1031
		if (!empty($this->fk_product) && $this->fk_product > 0) {
1032
			include_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
1033
			$product = new Product($db);
1034
			$resultFetch = $product->fetch($this->fk_product);
1035
			if ($resultFetch > 0) {
1036
				$label .= "<br><b>".$langs->trans("Product").'</b>: '.$product->ref.' - '.$product->label;
1037
			}
1038
		}
1039
1040
1041
		$url = DOL_URL_ROOT.'/bom/bom_card.php?id='.$this->id;
1042
1043
		if ($option != 'nolink') {
1044
			// Add param to save lastsearch_values or not
1045
			$add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
1046
			if ($save_lastsearch_value == -1 && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
1047
				$add_save_lastsearch_values = 1;
1048
			}
1049
			if ($add_save_lastsearch_values) {
1050
				$url .= '&save_lastsearch_values=1';
1051
			}
1052
		}
1053
1054
		$linkclose = '';
1055
		if (empty($notooltip)) {
1056
			if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
1057
				$label = $langs->trans("ShowBillOfMaterials");
1058
				$linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
1059
			}
1060
			$linkclose .= ' title="'.dol_escape_htmltag($label, 1).'"';
1061
			$linkclose .= ' class="classfortooltip'.($morecss ? ' '.$morecss : '').'"';
1062
		} else {
1063
			$linkclose = ($morecss ? ' class="'.$morecss.'"' : '');
1064
		}
1065
1066
		$linkstart = '<a href="'.$url.'"';
1067
		$linkstart .= $linkclose.'>';
1068
		$linkend = '</a>';
1069
1070
		$result .= $linkstart;
1071
		if ($withpicto) {
1072
			$result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip ? 0 : 1);
1073
		}
1074
		if ($withpicto != 2) {
1075
			$result .= $this->ref;
1076
		}
1077
		$result .= $linkend;
1078
		//if ($withpicto != 2) $result.=(($addlabel && $this->label) ? $sep . dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : '');
1079
1080
		global $action, $hookmanager;
1081
		$hookmanager->initHooks(array('bomdao'));
1082
		$parameters = array('id'=>$this->id, 'getnomurl' => &$result);
1083
		$reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1084
		if ($reshook > 0) {
1085
			$result = $hookmanager->resPrint;
1086
		} else {
1087
			$result .= $hookmanager->resPrint;
1088
		}
1089
1090
		return $result;
1091
	}
1092
1093
	/**
1094
	 *  Return label of the status
1095
	 *
1096
	 *  @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
1097
	 *  @return	string 			       Label of status
1098
	 */
1099
	public function getLibStatut($mode = 0)
1100
	{
1101
		return $this->LibStatut($this->status, $mode);
1102
	}
1103
1104
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1105
	/**
1106
	 *  Return the status
1107
	 *
1108
	 *  @param	int		$status        Id status
1109
	 *  @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
1110
	 *  @return string 			       Label of status
1111
	 */
1112
	public function LibStatut($status, $mode = 0)
1113
	{
1114
		// phpcs:enable
1115
		if (empty($this->labelStatus)) {
0 ignored issues
show
Bug introduced by
The property labelStatus is declared private in CommonObject and cannot be accessed from this context.
Loading history...
1116
			global $langs;
1117
			//$langs->load("mrp");
1118
			$this->labelStatus[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv('Draft');
1119
			$this->labelStatus[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv('Enabled');
1120
			$this->labelStatus[self::STATUS_CANCELED] = $langs->transnoentitiesnoconv('Disabled');
1121
		}
1122
1123
		$statusType = 'status'.$status;
1124
		if ($status == self::STATUS_VALIDATED) {
1125
			$statusType = 'status4';
1126
		}
1127
		if ($status == self::STATUS_CANCELED) {
1128
			$statusType = 'status6';
1129
		}
1130
1131
		return dolGetStatus($this->labelStatus[$status], $this->labelStatus[$status], '', $statusType, $mode);
1132
	}
1133
1134
	/**
1135
	 *	Load the info information in the object
1136
	 *
1137
	 *	@param  int		$id       Id of object
1138
	 *	@return	void
1139
	 */
1140
	public function info($id)
1141
	{
1142
		$sql = 'SELECT rowid, date_creation as datec, tms as datem,';
1143
		$sql .= ' fk_user_creat, fk_user_modif';
1144
		$sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as t';
1145
		$sql .= ' WHERE t.rowid = '.((int) $id);
1146
		$result = $this->db->query($sql);
1147
		if ($result) {
1148
			if ($this->db->num_rows($result)) {
1149
				$obj = $this->db->fetch_object($result);
1150
				$this->id = $obj->rowid;
1151
1152
				$this->user_creation_id = $obj->fk_user_creat;
1153
				$this->user_modification_id = $obj->fk_user_modif;
1154
				$this->date_creation     = $this->db->jdate($obj->datec);
1155
				$this->date_modification = empty($obj->datem) ? '' : $this->db->jdate($obj->datem);
1156
			}
1157
1158
			$this->db->free($result);
1159
		} else {
1160
			dol_print_error($this->db);
1161
		}
1162
	}
1163
1164
	/**
1165
	 * 	Create an array of lines
1166
	 *
1167
	 * 	@return array|int		array of lines if OK, <0 if KO
1168
	 */
1169
	public function getLinesArray()
1170
	{
1171
		$this->lines = array();
1172
1173
		$objectline = new BOMLine($this->db);
1174
		$result = $objectline->fetchAll('ASC', 'position', 0, 0, array('customsql'=>'fk_bom = '.((int) $this->id)));
1175
1176
		if (is_numeric($result)) {
1177
			$this->error = $objectline->error;
1178
			$this->errors = $objectline->errors;
1179
			return $result;
1180
		} else {
1181
			$this->lines = $result;
1182
			return $this->lines;
1183
		}
1184
	}
1185
1186
	/**
1187
	 *  Create a document onto disk according to template module.
1188
	 *
1189
	 *  @param	    string		$modele			Force template to use ('' to not force)
1190
	 *  @param		Translate	$outputlangs	objet lang a utiliser pour traduction
1191
	 *  @param      int			$hidedetails    Hide details of lines
1192
	 *  @param      int			$hidedesc       Hide description
1193
	 *  @param      int			$hideref        Hide ref
1194
	 *  @param      null|array  $moreparams     Array to provide more information
1195
	 *  @return     int         				0 if KO, 1 if OK
1196
	 */
1197
	public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
1198
	{
1199
		global $conf, $langs;
1200
1201
		$langs->load("mrp");
1202
		$outputlangs->load("products");
1203
1204
		if (!dol_strlen($modele)) {
1205
			$modele = 'standard';
1206
1207
			if ($this->model_pdf) {
1208
				$modele = $this->model_pdf;
1209
			} elseif (!empty($conf->global->BOM_ADDON_PDF)) {
1210
				$modele = $conf->global->BOM_ADDON_PDF;
1211
			}
1212
		}
1213
1214
		$modelpath = "core/modules/bom/doc/";
1215
1216
		return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
1217
	}
1218
1219
	/**
1220
	 * Initialise object with example values
1221
	 * Id must be 0 if object instance is a specimen
1222
	 *
1223
	 * @return void
1224
	 */
1225
	public function initAsSpecimen()
1226
	{
1227
		$this->initAsSpecimenCommon();
1228
		$this->ref = 'BOM-123';
1229
		$this->date = $this->date_creation;
1230
	}
1231
1232
1233
	/**
1234
	 * Action executed by scheduler
1235
	 * CAN BE A CRON TASK. In such a case, parameters come from the schedule job setup field 'Parameters'
1236
	 *
1237
	 * @return	int			0 if OK, <>0 if KO (this function is used also by cron so only 0 is OK)
1238
	 */
1239
	public function doScheduledJob()
1240
	{
1241
		global $conf, $langs;
1242
1243
		//$conf->global->SYSLOG_FILE = 'DOL_DATA_ROOT/dolibarr_mydedicatedlofile.log';
1244
1245
		$error = 0;
1246
		$this->output = '';
1247
		$this->error = '';
1248
1249
		dol_syslog(__METHOD__, LOG_DEBUG);
1250
1251
		$now = dol_now();
1252
1253
		$this->db->begin();
1254
1255
		// ...
1256
1257
		$this->db->commit();
1258
1259
		return $error;
1260
	}
1261
1262
	/**
1263
	 * BOM costs calculation based on cost_price or pmp of each BOM line.
1264
	 * Set the property ->total_cost and ->unit_cost of BOM.
1265
	 *
1266
	 * @return void
1267
	 */
1268
	public function calculateCosts()
1269
	{
1270
		include_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
1271
		$this->unit_cost = 0;
1272
		$this->total_cost = 0;
1273
1274
		if (is_array($this->lines) && count($this->lines)) {
1275
			require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
1276
			$productFournisseur = new ProductFournisseur($this->db);
1277
			$tmpproduct = new Product($this->db);
1278
1279
			foreach ($this->lines as &$line) {
1280
				$tmpproduct->cost_price = 0;
1281
				$tmpproduct->pmp = 0;
1282
1283
				if (empty($line->fk_bom_child)) {
1284
					$result = $tmpproduct->fetch($line->fk_product, '', '', '', 0, 1, 1);	// We discard selling price and language loading
1285
					if ($result < 0) {
1286
						$this->error = $tmpproduct->error;
1287
						return -1;
1288
					}
1289
					$line->unit_cost = price2num((!empty($tmpproduct->cost_price)) ? $tmpproduct->cost_price : $tmpproduct->pmp);
1290
					if (empty($line->unit_cost)) {
1291
						if ($productFournisseur->find_min_price_product_fournisseur($line->fk_product) > 0) {
1292
							$line->unit_cost = $productFournisseur->fourn_unitprice;
1293
						}
1294
					}
1295
1296
					$line->total_cost = price2num($line->qty * $line->unit_cost, 'MT');
1297
1298
					$this->total_cost += $line->total_cost;
1299
				} else {
1300
					$bom_child= new BOM($this->db);
1301
					$res = $bom_child->fetch($line->fk_bom_child);
1302
					if ($res>0) {
1303
						$bom_child->calculateCosts();
1304
						$line->childBom[] = $bom_child;
1305
						$this->total_cost += $bom_child->total_cost  * $line->qty;
1306
					} else {
1307
						$this->error = $bom_child->error;
1308
						return -2;
1309
					}
1310
				}
1311
			}
1312
1313
			$this->total_cost = price2num($this->total_cost, 'MT');
1314
			if ($this->qty > 0) {
1315
				$this->unit_cost = price2num($this->total_cost / $this->qty, 'MU');
1316
			} elseif ($this->qty < 0) {
1317
				$this->unit_cost = price2num($this->total_cost * $this->qty, 'MU');
1318
			}
1319
		}
1320
	}
1321
1322
	/**
1323
	 * Function used to replace a product id with another one.
1324
	 *
1325
	 * @param DoliDB $db Database handler
1326
	 * @param int $origin_id Old product id
1327
	 * @param int $dest_id New product id
1328
	 * @return bool
1329
	 */
1330
	public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
1331
	{
1332
		$tables = array(
1333
			'bom_bomline'
1334
		);
1335
1336
		return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
1337
	}
1338
1339
	/**
1340
	 * Get Net needs by product
1341
	 *
1342
	 * @param array $TNetNeeds Array of ChildBom and infos linked to
1343
	 * @param int   $qty       qty needed
1344
	 * @return void
1345
	 */
1346
	public function getNetNeeds(&$TNetNeeds = array(), $qty = 0)
1347
	{
1348
		if (! empty($this->lines)) {
1349
			foreach ($this->lines as $line) {
1350
				if (! empty($line->childBom)) {
1351
					foreach ($line->childBom as $childBom) $childBom->getNetNeeds($TNetNeeds, $line->qty*$qty);
1352
				} else {
1353
					if (empty($TNetNeeds[$line->fk_product])) {
1354
						$TNetNeeds[$line->fk_product] = 0;
1355
					}
1356
					$TNetNeeds[$line->fk_product] += $line->qty*$qty;
1357
				}
1358
			}
1359
		}
1360
	}
1361
1362
	/**
1363
	 * Get Net needs Tree by product or bom
1364
	 *
1365
	 * @param array $TNetNeeds Array of ChildBom and infos linked to
1366
	 * @param int   $qty       qty needed
1367
	 * @param int   $level     level of recursivity
1368
	 * @return void
1369
	 */
1370
	public function getNetNeedsTree(&$TNetNeeds = array(), $qty = 0, $level = 0)
1371
	{
1372
		if (! empty($this->lines)) {
1373
			foreach ($this->lines as $line) {
1374
				if (! empty($line->childBom)) {
1375
					foreach ($line->childBom as $childBom) {
1376
						$TNetNeeds[$childBom->id]['bom'] = $childBom;
1377
						$TNetNeeds[$childBom->id]['parentid'] = $this->id;
1378
						$TNetNeeds[$childBom->id]['qty'] = $line->qty*$qty;
1379
						$TNetNeeds[$childBom->id]['level'] = $level;
1380
						$childBom->getNetNeedsTree($TNetNeeds, $line->qty*$qty, $level+1);
1381
					}
1382
				} else {
1383
					$TNetNeeds[$this->id]['product'][$line->fk_product]['qty'] += $line->qty * $qty;
1384
					$TNetNeeds[$this->id]['product'][$line->fk_product]['level'] = $level;
1385
				}
1386
			}
1387
		}
1388
	}
1389
}
1390
1391
1392
/**
1393
 * Class for BOMLine
1394
 */
1395
class BOMLine extends CommonObjectLine
1396
{
1397
	/**
1398
	 * @var string ID to identify managed object
1399
	 */
1400
	public $element = 'bomline';
1401
1402
	/**
1403
	 * @var string Name of table without prefix where object is stored
1404
	 */
1405
	public $table_element = 'bom_bomline';
1406
1407
	/**
1408
	 * @var int  Does bomline support multicompany module ? 0=No test on entity, 1=Test with field entity, 2=Test with link by societe
1409
	 */
1410
	public $ismultientitymanaged = 0;
1411
1412
	/**
1413
	 * @var int  Does bomline support extrafields ? 0=No, 1=Yes
1414
	 */
1415
	public $isextrafieldmanaged = 1;
1416
1417
	/**
1418
	 * @var string String with name of icon for bomline. Must be the part after the 'object_' into object_bomline.png
1419
	 */
1420
	public $picto = 'bomline';
1421
1422
1423
	/**
1424
	 *  'type' if the field format.
1425
	 *  'label' the translation key.
1426
	 *  'enabled' is a condition when the field must be managed.
1427
	 *  '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. Using a negative value means field is not shown by default on list but can be selected for viewing)
1428
	 *  'notnull' is set to 1 if not null in database. Set to -1 if we must set data to null if empty ('' or 0).
1429
	 *  'default' is a default value for creation (can still be replaced by the global setup of default values)
1430
	 *  'index' if we want an index in database.
1431
	 *  'foreignkey'=>'tablename.field' if the field is a foreign key (it is recommanded to name the field fk_...).
1432
	 *  'position' is the sort order of field.
1433
	 *  'searchall' is 1 if we want to search in this field when making a search from the quick search button.
1434
	 *  '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).
1435
	 *  'css' is the CSS style to use on field. For example: 'maxwidth200'
1436
	 *  'help' is a string visible as a tooltip on field
1437
	 *  'comment' is not used. You can store here any text of your choice. It is not used by application.
1438
	 *  'showoncombobox' if value of the field must be visible into the label of the combobox that list record
1439
	 *  'arrayofkeyval' to set list of value if type is a list of predefined values. For example: array("0"=>"Draft","1"=>"Active","-1"=>"Cancel")
1440
	 */
1441
1442
	// BEGIN MODULEBUILDER PROPERTIES
1443
	/**
1444
	 * @var array  Array with all fields and their property. Do not use it as a static var. It may be modified by constructor.
1445
	 */
1446
	public $fields = array(
1447
		'rowid' => array('type'=>'integer', 'label'=>'LineID', 'enabled'=>1, 'visible'=>-1, 'position'=>1, 'notnull'=>1, 'index'=>1, 'comment'=>"Id",),
1448
		'fk_bom' => array('type'=>'integer:BillOfMaterials:societe/class/bom.class.php', 'label'=>'BillOfMaterials', 'enabled'=>1, 'visible'=>1, 'position'=>10, 'notnull'=>1, 'index'=>1,),
1449
		'fk_product' => array('type'=>'integer:Product:product/class/product.class.php', 'label'=>'Product', 'enabled'=>1, 'visible'=>1, 'position'=>20, 'notnull'=>1, 'index'=>1,),
1450
		'fk_bom_child' => array('type'=>'integer:BOM:bom/class/bom.class.php', 'label'=>'BillOfMaterials', 'enabled'=>1, 'visible'=>-1, 'position'=>40, 'notnull'=>-1,),
1451
		'description' => array('type'=>'text', 'label'=>'Description', 'enabled'=>1, 'visible'=>-1, 'position'=>60, 'notnull'=>-1,),
1452
		'qty' => array('type'=>'double(24,8)', 'label'=>'Quantity', 'enabled'=>1, 'visible'=>1, 'position'=>100, 'notnull'=>1, 'isameasure'=>'1',),
1453
		'qty_frozen' => array('type'=>'smallint', 'label'=>'QuantityFrozen', 'enabled'=>1, 'visible'=>1, 'default'=>0, 'position'=>105, 'css'=>'maxwidth50imp', 'help'=>'QuantityConsumedInvariable'),
1454
		'disable_stock_change' => array('type'=>'smallint', 'label'=>'DisableStockChange', 'enabled'=>1, 'visible'=>1, 'default'=>0, 'position'=>108, 'css'=>'maxwidth50imp', 'help'=>'DisableStockChangeHelp'),
1455
		'efficiency' => array('type'=>'double(24,8)', 'label'=>'ManufacturingEfficiency', 'enabled'=>1, 'visible'=>0, 'default'=>1, 'position'=>110, 'notnull'=>1, 'css'=>'maxwidth50imp', 'help'=>'ValueOfEfficiencyConsumedMeans'),
1456
		'position' => array('type'=>'integer', 'label'=>'Rank', 'enabled'=>1, 'visible'=>0, 'default'=>0, 'position'=>200, 'notnull'=>1,),
1457
		'import_key' => array('type'=>'varchar(14)', 'label'=>'ImportId', 'enabled'=>1, 'visible'=>-2, 'position'=>1000, 'notnull'=>-1,),
1458
	);
1459
1460
	/**
1461
	 * @var int rowid
1462
	 */
1463
	public $rowid;
1464
1465
	/**
1466
	 * @var int fk_bom
1467
	 */
1468
	public $fk_bom;
1469
1470
	/**
1471
	 * @var int Id of product
1472
	 */
1473
	public $fk_product;
1474
1475
	/**
1476
	 * @var int Id of parent bom
1477
	 */
1478
	public $fk_bom_child;
1479
1480
	/**
1481
	 * @var string description
1482
	 */
1483
	public $description;
1484
	public $qty;
1485
1486
	/**
1487
	 * @var int qty frozen
1488
	 */
1489
	public $qty_frozen;
1490
	public $disable_stock_change;
1491
	public $efficiency;
1492
1493
	/**
1494
	 * @var int position of line
1495
	 */
1496
	public $position;
1497
1498
	/**
1499
	 * @var string import key
1500
	 */
1501
	public $import_key;
1502
	// END MODULEBUILDER PROPERTIES
1503
1504
	/**
1505
	 * @var int		Calculated cost for the BOM line
1506
	 */
1507
	public $total_cost = 0;
1508
1509
	/**
1510
	 * @var int		Line unit cost based on product cost price or pmp
1511
	 */
1512
	public $unit_cost = 0;
1513
1514
1515
	/**
1516
	 * @var Bom     array of Bom in line
1517
	 */
1518
	public $childBom = array();
1519
1520
1521
	/**
1522
	 * Constructor
1523
	 *
1524
	 * @param DoliDb $db Database handler
1525
	 */
1526
	public function __construct(DoliDB $db)
1527
	{
1528
		global $conf, $langs;
1529
1530
		$this->db = $db;
1531
1532
		if (empty($conf->global->MAIN_SHOW_TECHNICAL_ID) && isset($this->fields['rowid'])) {
1533
			$this->fields['rowid']['visible'] = 0;
1534
		}
1535
		if (empty($conf->multicompany->enabled) && isset($this->fields['entity'])) {
1536
			$this->fields['entity']['enabled'] = 0;
1537
		}
1538
1539
		// Unset fields that are disabled
1540
		foreach ($this->fields as $key => $val) {
1541
			if (isset($val['enabled']) && empty($val['enabled'])) {
1542
				unset($this->fields[$key]);
1543
			}
1544
		}
1545
1546
		// Translate some data of arrayofkeyval
1547
		foreach ($this->fields as $key => $val) {
1548
			if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
1549
				foreach ($val['arrayofkeyval'] as $key2 => $val2) {
1550
					$this->fields[$key]['arrayofkeyval'][$key2] = $langs->trans($val2);
1551
				}
1552
			}
1553
		}
1554
	}
1555
1556
	/**
1557
	 * Create object into database
1558
	 *
1559
	 * @param  User $user      User that creates
1560
	 * @param  bool $notrigger false=launch triggers after, true=disable triggers
1561
	 * @return int             <0 if KO, Id of created object if OK
1562
	 */
1563
	public function create(User $user, $notrigger = false)
1564
	{
1565
		if ($this->efficiency < 0 || $this->efficiency > 1) {
1566
			$this->efficiency = 1;
1567
		}
1568
1569
		return $this->createCommon($user, $notrigger);
1570
	}
1571
1572
	/**
1573
	 * Load object in memory from the database
1574
	 *
1575
	 * @param int    $id   Id object
1576
	 * @param string $ref  Ref
1577
	 * @return int         <0 if KO, 0 if not found, >0 if OK
1578
	 */
1579
	public function fetch($id, $ref = null)
1580
	{
1581
		$result = $this->fetchCommon($id, $ref);
1582
		//if ($result > 0 && ! empty($this->table_element_line)) $this->fetchLines();
1583
		return $result;
1584
	}
1585
1586
	/**
1587
	 * Load list of objects in memory from the database.
1588
	 *
1589
	 * @param  string      $sortorder    Sort Order
1590
	 * @param  string      $sortfield    Sort field
1591
	 * @param  int         $limit        limit
1592
	 * @param  int         $offset       Offset
1593
	 * @param  array       $filter       Filter array. Example array('field'=>'valueforlike', 'customurl'=>...)
1594
	 * @param  string      $filtermode   Filter mode (AND or OR)
1595
	 * @return array|int                 int <0 if KO, array of pages if OK
1596
	 */
1597
	public function fetchAll($sortorder = '', $sortfield = '', $limit = 0, $offset = 0, array $filter = array(), $filtermode = 'AND')
1598
	{
1599
		global $conf;
1600
1601
		dol_syslog(__METHOD__, LOG_DEBUG);
1602
1603
		$records = array();
1604
1605
		$sql = 'SELECT ';
1606
		$sql .= $this->getFieldList();
1607
		$sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as t';
1608
		if ($this->ismultientitymanaged) {
1609
			$sql .= ' WHERE t.entity IN ('.getEntity($this->table_element).')';
1610
		} else {
1611
			$sql .= ' WHERE 1 = 1';
1612
		}
1613
		// Manage filter
1614
		$sqlwhere = array();
1615
		if (count($filter) > 0) {
1616
			foreach ($filter as $key => $value) {
1617
				if ($key == 't.rowid') {
1618
					$sqlwhere[] = $key." = ".((int) $value);
1619
				} elseif (strpos($key, 'date') !== false) {
1620
					$sqlwhere[] = $key." = '".$this->db->idate($value)."'";
1621
				} elseif ($key == 'customsql') {
1622
					$sqlwhere[] = $value;
1623
				} else {
1624
					$sqlwhere[] = $key." LIKE '%".$this->db->escape($value)."%'";
1625
				}
1626
			}
1627
		}
1628
		if (count($sqlwhere) > 0) {
1629
			$sql .= ' AND ('.implode(' '.$this->db->escape($filtermode).' ', $sqlwhere).')';
1630
		}
1631
1632
		if (!empty($sortfield)) {
1633
			$sql .= $this->db->order($sortfield, $sortorder);
1634
		}
1635
		if (!empty($limit)) {
1636
			$sql .= $this->db->plimit($limit, $offset);
1637
		}
1638
1639
		$resql = $this->db->query($sql);
1640
		if ($resql) {
1641
			$num = $this->db->num_rows($resql);
1642
1643
			while ($obj = $this->db->fetch_object($resql)) {
1644
				$record = new self($this->db);
1645
				$record->setVarsFromFetchObj($obj);
1646
1647
				$records[$record->id] = $record;
1648
			}
1649
			$this->db->free($resql);
1650
1651
			return $records;
1652
		} else {
1653
			$this->errors[] = 'Error '.$this->db->lasterror();
1654
			dol_syslog(__METHOD__.' '.join(',', $this->errors), LOG_ERR);
1655
1656
			return -1;
1657
		}
1658
	}
1659
1660
	/**
1661
	 * Update object into database
1662
	 *
1663
	 * @param  User $user      User that modifies
1664
	 * @param  bool $notrigger false=launch triggers after, true=disable triggers
1665
	 * @return int             <0 if KO, >0 if OK
1666
	 */
1667
	public function update(User $user, $notrigger = false)
1668
	{
1669
		if ($this->efficiency < 0 || $this->efficiency > 1) {
1670
			$this->efficiency = 1;
1671
		}
1672
1673
		return $this->updateCommon($user, $notrigger);
1674
	}
1675
1676
	/**
1677
	 * Delete object in database
1678
	 *
1679
	 * @param User $user       User that deletes
1680
	 * @param bool $notrigger  false=launch triggers after, true=disable triggers
1681
	 * @return int             <0 if KO, >0 if OK
1682
	 */
1683
	public function delete(User $user, $notrigger = false)
1684
	{
1685
		return $this->deleteCommon($user, $notrigger);
1686
		//return $this->deleteCommon($user, $notrigger, 1);
1687
	}
1688
1689
	/**
1690
	 *  Return a link to the object card (with optionaly the picto)
1691
	 *
1692
	 *	@param	int		$withpicto					Include picto in link (0=No picto, 1=Include picto into link, 2=Only picto)
1693
	 *	@param	string	$option						On what the link point to ('nolink', ...)
1694
	 *  @param	int  	$notooltip					1=Disable tooltip
1695
	 *  @param  string  $morecss            		Add more css on link
1696
	 *  @param  int     $save_lastsearch_value    	-1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking
1697
	 *  @return	string								String with URL
1698
	 */
1699
	public function getNomUrl($withpicto = 0, $option = '', $notooltip = 0, $morecss = '', $save_lastsearch_value = -1)
1700
	{
1701
		global $db, $conf, $langs, $hookmanager;
1702
1703
		if (!empty($conf->dol_no_mouse_hover)) {
1704
			$notooltip = 1; // Force disable tooltips
1705
		}
1706
1707
		$result = '';
1708
1709
		$label = '<u>'.$langs->trans("BillOfMaterialsLine").'</u>';
1710
		$label .= '<br>';
1711
		$label .= '<b>'.$langs->trans('Ref').':</b> '.$this->ref;
1712
1713
		$url = DOL_URL_ROOT.'/bom/bomline_card.php?id='.$this->id;
1714
1715
		if ($option != 'nolink') {
1716
			// Add param to save lastsearch_values or not
1717
			$add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
1718
			if ($save_lastsearch_value == -1 && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
1719
				$add_save_lastsearch_values = 1;
1720
			}
1721
			if ($add_save_lastsearch_values) {
1722
				$url .= '&save_lastsearch_values=1';
1723
			}
1724
		}
1725
1726
		$linkclose = '';
1727
		if (empty($notooltip)) {
1728
			if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
1729
				$label = $langs->trans("ShowBillOfMaterialsLine");
1730
				$linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
1731
			}
1732
			$linkclose .= ' title="'.dol_escape_htmltag($label, 1).'"';
1733
			$linkclose .= ' class="classfortooltip'.($morecss ? ' '.$morecss : '').'"';
1734
		} else {
1735
			$linkclose = ($morecss ? ' class="'.$morecss.'"' : '');
1736
		}
1737
1738
		$linkstart = '<a href="'.$url.'"';
1739
		$linkstart .= $linkclose.'>';
1740
		$linkend = '</a>';
1741
1742
		$result .= $linkstart;
1743
		if ($withpicto) {
1744
			$result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip ? 0 : 1);
1745
		}
1746
		if ($withpicto != 2) {
1747
			$result .= $this->ref;
1748
		}
1749
		$result .= $linkend;
1750
		//if ($withpicto != 2) $result.=(($addlabel && $this->label) ? $sep . dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : '');
1751
1752
		global $action, $hookmanager;
1753
		$hookmanager->initHooks(array('bomlinedao'));
1754
		$parameters = array('id'=>$this->id, 'getnomurl' => &$result);
1755
		$reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1756
		if ($reshook > 0) {
1757
			$result = $hookmanager->resPrint;
1758
		} else {
1759
			$result .= $hookmanager->resPrint;
1760
		}
1761
1762
		return $result;
1763
	}
1764
1765
	/**
1766
	 *  Return label of the status
1767
	 *
1768
	 *  @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
1769
	 *  @return	string 			       Label of status
1770
	 */
1771
	public function getLibStatut($mode = 0)
1772
	{
1773
		return $this->LibStatut($this->status, $mode);
1774
	}
1775
1776
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1777
	/**
1778
	 *  Return the status
1779
	 *
1780
	 *  @param	int		$status        Id status
1781
	 *  @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
1782
	 *  @return string 			       Label of status
1783
	 */
1784
	public function LibStatut($status, $mode = 0)
1785
	{
1786
		// phpcs:enable
1787
		return '';
1788
	}
1789
1790
	/**
1791
	 *	Load the info information in the object
1792
	 *
1793
	 *	@param  int		$id       Id of object
1794
	 *	@return	void
1795
	 */
1796
	public function info($id)
1797
	{
1798
		$sql = 'SELECT rowid, date_creation as datec, tms as datem,';
1799
		$sql .= ' fk_user_creat, fk_user_modif';
1800
		$sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as t';
1801
		$sql .= ' WHERE t.rowid = '.((int) $id);
1802
		$result = $this->db->query($sql);
1803
		if ($result) {
1804
			if ($this->db->num_rows($result)) {
1805
				$obj = $this->db->fetch_object($result);
1806
				$this->id = $obj->rowid;
1807
				$this->user_creation_id = $obj->fk_user_creat;
1808
				$this->user_modification_id = $obj->fk_user_modif;
1809
				$this->date_creation     = $this->db->jdate($obj->datec);
1810
				$this->date_modification = empty($obj->datem) ? '' : $this->db->jdate($obj->datem);
1811
			}
1812
			$this->db->free($result);
1813
		} else {
1814
			dol_print_error($this->db);
1815
		}
1816
	}
1817
1818
	/**
1819
	 * Initialise object with example values
1820
	 * Id must be 0 if object instance is a specimen
1821
	 *
1822
	 * @return void
1823
	 */
1824
	public function initAsSpecimen()
1825
	{
1826
		$this->initAsSpecimenCommon();
1827
	}
1828
}
1829