dolibarr  20.0.0-beta
bom.class.php
Go to the documentation of this file.
1 <?php
2 /* Copyright (C) 2019 Laurent Destailleur <eldy@users.sourceforge.net>
3  * Copyright (C) 2023 Benjamin Falière <benjamin.faliere@altairis.fr>
4  * Copyright (C) 2023 Charlene Benke <charlene@patas-monkey.com>
5  * Copyright (C) 2024 Frédéric France <frederic.france@free.fr>
6  * Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program. If not, see <https://www.gnu.org/licenses/>.
20  */
21 
28 // Put here all includes required by your class file
29 require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
30 require_once DOL_DOCUMENT_ROOT.'/core/class/commonobjectline.class.php';
31 require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
32 
33 if (isModEnabled('workstation')) {
34  require_once DOL_DOCUMENT_ROOT.'/workstation/class/workstation.class.php';
35 }
36 
37 //require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php';
38 //require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
39 
40 
44 class BOM extends CommonObject
45 {
49  public $module = 'bom';
50 
54  public $element = 'bom';
55 
59  public $table_element = 'bom_bom';
60 
64  public $picto = 'bom';
65 
69  public $product;
70 
71  const STATUS_DRAFT = 0;
72  const STATUS_VALIDATED = 1;
73  const STATUS_CANCELED = 9;
74 
75 
102  // BEGIN MODULEBUILDER PROPERTIES
106  public $fields = array(
107  'rowid' => array('type' => 'integer', 'label' => 'TechnicalID', 'enabled' => 1, 'visible' => -2, 'position' => 1, 'notnull' => 1, 'index' => 1, 'comment' => "Id",),
108  'entity' => array('type' => 'integer', 'label' => 'Entity', 'enabled' => 1, 'visible' => 0, 'notnull' => 1, 'default' => 1, 'index' => 1, 'position' => 5),
109  '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'),
110  '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'),
111  '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'),
112  //'bomtype' => array('type'=>'integer', 'label'=>'Type', 'enabled'=>1, 'visible'=>-1, 'position'=>32, 'notnull'=>1, 'default'=>'0', 'arrayofkeyval'=>array(0=>'Manufacturing')),
113  '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'),
114  'description' => array('type' => 'text', 'label' => 'Description', 'enabled' => 1, 'visible' => -1, 'position' => 60, 'notnull' => -1,),
115  'qty' => array('type' => 'real', 'label' => 'Quantity', 'enabled' => 1, 'visible' => 1, 'default' => 1, 'position' => 55, 'notnull' => 1, 'isameasure' => 1, 'css' => 'maxwidth50imp right'),
116  //'efficiency' => array('type'=>'real', 'label'=>'ManufacturingEfficiency', 'enabled'=>1, 'visible'=>-1, 'default'=>1, 'position'=>100, 'notnull'=>0, 'css'=>'maxwidth50imp', 'help'=>'ValueOfMeansLossForProductProduced'),
117  'duration' => array('type' => 'duration', 'label' => 'EstimatedDuration', 'enabled' => 1, 'visible' => -1, 'position' => 101, 'notnull' => -1, 'css' => 'maxwidth50imp', 'help' => 'EstimatedDurationDesc'),
118  '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'),
119  'note_public' => array('type' => 'html', 'label' => 'NotePublic', 'enabled' => 1, 'visible' => -2, 'position' => 161, 'notnull' => -1,),
120  'note_private' => array('type' => 'html', 'label' => 'NotePrivate', 'enabled' => 1, 'visible' => -2, 'position' => 162, 'notnull' => -1,),
121  'date_creation' => array('type' => 'datetime', 'label' => 'DateCreation', 'enabled' => 1, 'visible' => -2, 'position' => 300, 'notnull' => 1,),
122  'tms' => array('type' => 'timestamp', 'label' => 'DateModification', 'enabled' => 1, 'visible' => -2, 'position' => 501, 'notnull' => 1,),
123  'date_valid' => array('type' => 'datetime', 'label' => 'DateValidation', 'enabled' => 1, 'visible' => -2, 'position' => 502, 'notnull' => 0,),
124  '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'),
125  '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'),
126  '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'),
127  'import_key' => array('type' => 'varchar(14)', 'label' => 'ImportId', 'enabled' => 1, 'visible' => -2, 'position' => 1000, 'notnull' => -1,),
128  'model_pdf' => array('type' => 'varchar(255)', 'label' => 'Model pdf', 'enabled' => 1, 'visible' => 0, 'position' => 1010),
129  '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')),
130  );
131 
135  public $rowid;
136 
140  public $ref;
141 
145  public $label;
146 
150  public $bomtype;
151 
155  public $description;
156 
160  public $date_creation;
161 
165  public $date_valid;
166 
170  public $fk_user_creat;
171 
175  public $fk_user_modif;
176 
180  public $fk_user_valid;
181 
185  public $fk_warehouse;
186 
190  public $import_key;
191 
195  public $status;
196 
200  public $fk_product;
201  public $qty;
202  public $duration;
203  public $efficiency;
204  // END MODULEBUILDER PROPERTIES
205 
206 
207  // If this object has a subtable with lines
208 
212  public $table_element_line = 'bom_bomline';
213 
217  public $fk_element = 'fk_bom';
218 
222  public $class_element_line = 'BOMLine';
223 
224  // /**
225  // * @var array List of child tables. To test if we can delete object.
226  // */
227  // protected $childtables=array();
228 
232  protected $childtablesoncascade = array('bom_bomline');
233 
237  public $lines = array();
238 
242  public $total_cost = 0;
243 
247  public $unit_cost = 0;
248 
249 
255  public function __construct(DoliDB $db)
256  {
257  global $conf, $langs;
258 
259  $this->db = $db;
260 
261  $this->ismultientitymanaged = 1;
262  $this->isextrafieldmanaged = 1;
263 
264  if (!getDolGlobalString('MAIN_SHOW_TECHNICAL_ID') && isset($this->fields['rowid'])) {
265  $this->fields['rowid']['visible'] = 0;
266  }
267  if (!isModEnabled('multicompany') && isset($this->fields['entity'])) {
268  $this->fields['entity']['enabled'] = 0;
269  }
270 
271  // Unset fields that are disabled
272  foreach ($this->fields as $key => $val) {
273  if (isset($val['enabled']) && empty($val['enabled'])) {
274  unset($this->fields[$key]);
275  }
276  }
277 
278  // Translate some data of arrayofkeyval
279  foreach ($this->fields as $key => $val) {
280  if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
281  foreach ($val['arrayofkeyval'] as $key2 => $val2) {
282  $this->fields[$key]['arrayofkeyval'][$key2] = $langs->trans($val2);
283  }
284  }
285  }
286  }
287 
295  public function create(User $user, $notrigger = 1)
296  {
297  if ($this->efficiency <= 0 || $this->efficiency > 1) {
298  $this->efficiency = 1;
299  }
300 
301  return $this->createCommon($user, $notrigger);
302  }
303 
311  public function createFromClone(User $user, $fromid)
312  {
313  global $langs, $hookmanager, $extrafields;
314  $error = 0;
315 
316  dol_syslog(__METHOD__, LOG_DEBUG);
317 
318  $object = new self($this->db);
319 
320  $this->db->begin();
321 
322  // Load source object
323  $result = $object->fetchCommon($fromid);
324  if ($result > 0 && !empty($object->table_element_line)) {
325  $object->fetchLines();
326  }
327 
328  // Get lines so they will be clone
329  //foreach ($object->lines as $line)
330  // $line->fetch_optionals();
331 
332  // Reset some properties
333  unset($object->id);
334  unset($object->fk_user_creat);
335  unset($object->import_key);
336 
337  // Clear fields
338  $object->ref = empty($this->fields['ref']['default']) ? $langs->trans("copy_of_").$object->ref : $this->fields['ref']['default'];
339  $object->label = empty($this->fields['label']['default']) ? $langs->trans("CopyOf")." ".$object->label : $this->fields['label']['default'];
340  $object->status = self::STATUS_DRAFT;
341  // ...
342  // Clear extrafields that are unique
343  if (is_array($object->array_options) && count($object->array_options) > 0) {
344  $extrafields->fetch_name_optionals_label($object->table_element);
345  foreach ($object->array_options as $key => $option) {
346  $shortkey = preg_replace('/options_/', '', $key);
347  if (!empty($extrafields->attributes[$this->element]['unique'][$shortkey])) {
348  //var_dump($key); var_dump($clonedObj->array_options[$key]); exit;
349  unset($object->array_options[$key]);
350  }
351  }
352  }
353 
354  // Create clone
355  $object->context['createfromclone'] = 'createfromclone';
356  $result = $object->createCommon($user);
357  if ($result < 0) {
358  $error++;
359  $this->error = $object->error;
360  $this->errors = $object->errors;
361  }
362 
363  if (!$error) {
364  // copy internal contacts
365  if ($this->copy_linked_contact($object, 'internal') < 0) {
366  $error++;
367  }
368  }
369 
370  if (!$error) {
371  // copy external contacts if same company
372  if (property_exists($this, 'socid') && $this->socid == $object->socid) {
373  if ($this->copy_linked_contact($object, 'external') < 0) {
374  $error++;
375  }
376  }
377  }
378 
379  // If there is lines, create lines too
380 
381 
382 
383  unset($object->context['createfromclone']);
384 
385  // End
386  if (!$error) {
387  $this->db->commit();
388  return $object;
389  } else {
390  $this->db->rollback();
391  return -1;
392  }
393  }
394 
402  public function fetch($id, $ref = null)
403  {
404  $result = $this->fetchCommon($id, $ref);
405 
406  if ($result > 0 && !empty($this->table_element_line)) {
407  $this->fetchLines();
408  }
409  //$this->calculateCosts(); // This consume a high number of subrequests. Do not call it into fetch but when you need it.
410 
411  return $result;
412  }
413 
419  public function fetchLines()
420  {
421  $this->lines = array();
422 
423  $result = $this->fetchLinesCommon();
424  return $result;
425  }
426 
434  public function fetchLinesbytypeproduct($typeproduct = 0)
435  {
436  $this->lines = array();
437 
438  $objectlineclassname = get_class($this).'Line';
439  if (!class_exists($objectlineclassname)) {
440  $this->error = 'Error, class '.$objectlineclassname.' not found during call of fetchLinesCommon';
441  return -1;
442  }
443 
444  $objectline = new $objectlineclassname($this->db);
445 
446  $sql = "SELECT ".$objectline->getFieldList('l');
447  $sql .= " FROM ".$this->db->prefix().$objectline->table_element." as l";
448  $sql .= " LEFT JOIN ".$this->db->prefix()."product as p ON p.rowid = l.fk_product";
449  $sql .= " WHERE l.fk_".$this->db->escape($this->element)." = ".((int) $this->id);
450  $sql .= " AND p.fk_product_type = ". ((int) $typeproduct);
451  if (isset($objectline->fields['position'])) {
452  $sql .= $this->db->order('position', 'ASC');
453  }
454 
455  $resql = $this->db->query($sql);
456  if ($resql) {
457  $num_rows = $this->db->num_rows($resql);
458  $i = 0;
459  while ($i < $num_rows) {
460  $obj = $this->db->fetch_object($resql);
461  if ($obj) {
462  $newline = new $objectlineclassname($this->db);
463  $newline->setVarsFromFetchObj($obj);
464 
465  $this->lines[] = $newline;
466  }
467  $i++;
468  }
469 
470  return $num_rows;
471  } else {
472  $this->error = $this->db->lasterror();
473  $this->errors[] = $this->error;
474  return -1;
475  }
476  }
477 
478 
490  public function fetchAll($sortorder = '', $sortfield = '', $limit = 0, $offset = 0, $filter = '', $filtermode = 'AND')
491  {
492  dol_syslog(__METHOD__, LOG_DEBUG);
493 
494  $records = array();
495 
496  $sql = 'SELECT ';
497  $sql .= $this->getFieldList();
498  $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as t';
499  if ($this->ismultientitymanaged) {
500  $sql .= ' WHERE t.entity IN ('.getEntity($this->element).')';
501  } else {
502  $sql .= ' WHERE 1 = 1';
503  }
504 
505  // Manage filter
506  $errormessage = '';
507  $sql .= forgeSQLFromUniversalSearchCriteria($filter, $errormessage);
508  if ($errormessage) {
509  $this->errors[] = $errormessage;
510  dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
511  return -1;
512  }
513 
514  if (!empty($sortfield)) {
515  $sql .= $this->db->order($sortfield, $sortorder);
516  }
517  if (!empty($limit)) {
518  $sql .= $this->db->plimit($limit, $offset);
519  }
520 
521  $resql = $this->db->query($sql);
522  if ($resql) {
523  $num = $this->db->num_rows($resql);
524 
525  while ($obj = $this->db->fetch_object($resql)) {
526  $record = new self($this->db);
527  $record->setVarsFromFetchObj($obj);
528 
529  $records[$record->id] = $record;
530  }
531  $this->db->free($resql);
532 
533  return $records;
534  } else {
535  $this->errors[] = 'Error '.$this->db->lasterror();
536  dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
537 
538  return -1;
539  }
540  }
541 
549  public function update(User $user, $notrigger = 1)
550  {
551  if ($this->efficiency <= 0 || $this->efficiency > 1) {
552  $this->efficiency = 1;
553  }
554 
555  return $this->updateCommon($user, $notrigger);
556  }
557 
565  public function delete(User $user, $notrigger = 1)
566  {
567  return $this->deleteCommon($user, $notrigger);
568  //return $this->deleteCommon($user, $notrigger, 1);
569  }
570 
587  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, $fk_unit = 0, $array_options = array(), $fk_default_workstation = null)
588  {
589  global $mysoc, $conf, $langs, $user;
590 
591  $logtext = "::addLine bomid=$this->id, qty=$qty, fk_product=$fk_product, qty_frozen=$qty_frozen, disable_stock_change=$disable_stock_change, efficiency=$efficiency";
592  $logtext .= ", fk_bom_child=$fk_bom_child, import_key=$import_key";
593  dol_syslog(get_class($this).$logtext, LOG_DEBUG);
594 
595  if ($this->statut == self::STATUS_DRAFT) {
596  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
597 
598  // Clean parameters
599  if (empty($qty)) {
600  $qty = 0;
601  }
602  if (empty($qty_frozen)) {
603  $qty_frozen = 0;
604  }
605  if (empty($disable_stock_change)) {
606  $disable_stock_change = 0;
607  }
608  if (empty($efficiency)) {
609  $efficiency = 1.0;
610  }
611  if (empty($fk_bom_child)) {
612  $fk_bom_child = null;
613  }
614  if (empty($import_key)) {
615  $import_key = null;
616  }
617  if (empty($position)) {
618  $position = -1;
619  }
620 
621  $qty = (float) price2num($qty);
622  $efficiency = (float) price2num($efficiency);
623  $position = (float) price2num($position);
624 
625  $this->db->begin();
626 
627  // Rank to use
628  $rangMax = $this->line_max();
629  $rankToUse = $position;
630  if ($rankToUse <= 0 or $rankToUse > $rangMax) { // New line after existing lines
631  $rankToUse = $rangMax + 1;
632  } else { // New line between the existing lines
633  foreach ($this->lines as $bl) {
634  if ($bl->position >= $rankToUse) {
635  $bl->position++;
636  $bl->update($user);
637  }
638  }
639  }
640 
641  // Insert line
642  $line = new BOMLine($this->db);
643 
644  $line->context = $this->context;
645 
646  $line->fk_bom = $this->id;
647  $line->fk_product = $fk_product;
648  $line->qty = $qty;
649  $line->qty_frozen = $qty_frozen;
650  $line->disable_stock_change = $disable_stock_change;
651  $line->efficiency = $efficiency;
652  $line->fk_bom_child = $fk_bom_child;
653  $line->import_key = $import_key;
654  $line->position = $rankToUse;
655  $line->fk_unit = $fk_unit;
656  $line->fk_default_workstation = $fk_default_workstation;
657 
658  if (is_array($array_options) && count($array_options) > 0) {
659  $line->array_options = $array_options;
660  }
661 
662  $result = $line->create($user);
663 
664  if ($result > 0) {
665  $this->calculateCosts();
666  $this->db->commit();
667  return $result;
668  } else {
669  $this->setErrorsFromObject($line);
670  dol_syslog(get_class($this)."::addLine error=".$this->error, LOG_ERR);
671  $this->db->rollback();
672  return -2;
673  }
674  } else {
675  dol_syslog(get_class($this)."::addLine status of BOM must be Draft to allow use of ->addLine()", LOG_ERR);
676  return -3;
677  }
678  }
679 
695  public function updateLine($rowid, $qty, $qty_frozen = 0, $disable_stock_change = 0, $efficiency = 1.0, $position = -1, $import_key = null, $fk_unit = 0, $array_options = array(), $fk_default_workstation = null)
696  {
697  global $user;
698 
699  $logtext = "::updateLine bomid=$this->id, qty=$qty, qty_frozen=$qty_frozen, disable_stock_change=$disable_stock_change, efficiency=$efficiency";
700  $logtext .= ", import_key=$import_key";
701  dol_syslog(get_class($this).$logtext, LOG_DEBUG);
702 
703  if ($this->statut == self::STATUS_DRAFT) {
704  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
705 
706  // Clean parameters
707  if (empty($qty)) {
708  $qty = 0;
709  }
710  if (empty($qty_frozen)) {
711  $qty_frozen = 0;
712  }
713  if (empty($disable_stock_change)) {
714  $disable_stock_change = 0;
715  }
716  if (empty($efficiency)) {
717  $efficiency = 1.0;
718  }
719  if (empty($import_key)) {
720  $import_key = null;
721  }
722  if (empty($position)) {
723  $position = -1;
724  }
725 
726  $qty = (float) price2num($qty);
727  $efficiency = (float) price2num($efficiency);
728  $position = (float) price2num($position);
729 
730  $this->db->begin();
731 
732  //Fetch current line from the database and then clone the object and set it in $oldline property
733  $line = new BOMLine($this->db);
734  $line->fetch($rowid);
735  $line->fetch_optionals();
736 
737  $staticLine = clone $line;
738  $line->oldcopy = $staticLine;
739  $line->context = $this->context;
740 
741  // Rank to use
742  $rankToUse = (int) $position;
743  if ($rankToUse != $line->oldcopy->position) { // check if position have a new value
744  foreach ($this->lines as $bl) {
745  if ($bl->position >= $rankToUse and $bl->position < ($line->oldcopy->position + 1)) { // move rank up
746  $bl->position++;
747  $bl->update($user);
748  }
749  if ($bl->position <= $rankToUse and $bl->position > ($line->oldcopy->position)) { // move rank down
750  $bl->position--;
751  $bl->update($user);
752  }
753  }
754  }
755 
756 
757  $line->fk_bom = $this->id;
758  $line->qty = $qty;
759  $line->qty_frozen = $qty_frozen;
760  $line->disable_stock_change = $disable_stock_change;
761  $line->efficiency = $efficiency;
762  $line->import_key = $import_key;
763  $line->position = $rankToUse;
764 
765 
766  if (!empty($fk_unit)) {
767  $line->fk_unit = $fk_unit;
768  }
769 
770 
771  if (is_array($array_options) && count($array_options) > 0) {
772  // We replace values in this->line->array_options only for entries defined into $array_options
773  foreach ($array_options as $key => $value) {
774  $line->array_options[$key] = $array_options[$key];
775  }
776  }
777  if ($line->fk_default_workstation != $fk_default_workstation) {
778  $line->fk_default_workstation = ($fk_default_workstation > 0 ? $fk_default_workstation : 0);
779  }
780 
781  $result = $line->update($user);
782 
783  if ($result > 0) {
784  $this->calculateCosts();
785  $this->db->commit();
786  return $result;
787  } else {
788  $this->setErrorsFromObject($line);
789  dol_syslog(get_class($this)."::addLine error=".$this->error, LOG_ERR);
790  $this->db->rollback();
791  return -2;
792  }
793  } else {
794  dol_syslog(get_class($this)."::addLine status of BOM must be Draft to allow use of ->addLine()", LOG_ERR);
795  return -3;
796  }
797  }
798 
807  public function deleteLine(User $user, $idline, $notrigger = 0)
808  {
809  if ($this->status < 0) {
810  $this->error = 'ErrorDeleteLineNotAllowedByObjectStatus';
811  return -2;
812  }
813 
814  $this->db->begin();
815 
816  //Fetch current line from the database and then clone the object and set it in $oldline property
817  $line = new BOMLine($this->db);
818  $line->fetch($idline);
819  $line->fetch_optionals();
820 
821  $staticLine = clone $line;
822  $line->oldcopy = $staticLine;
823  $line->context = $this->context;
824 
825  $result = $line->delete($user, $notrigger);
826 
827  //Positions (rank) reordering
828  foreach ($this->lines as $bl) {
829  if ($bl->position > ($line->oldcopy->position)) { // move rank down
830  $bl->position--;
831  $bl->update($user);
832  }
833  }
834 
835  if ($result > 0) {
836  $this->calculateCosts();
837  $this->db->commit();
838  return $result;
839  } else {
840  $this->setErrorsFromObject($line);
841  dol_syslog(get_class($this)."::addLine error=".$this->error, LOG_ERR);
842  $this->db->rollback();
843  return -2;
844  }
845  }
846 
854  public function getNextNumRef($prod)
855  {
856  global $langs, $conf;
857  $langs->load("mrp");
858 
859  if (getDolGlobalString('BOM_ADDON')) {
860  $mybool = false;
861 
862  $file = getDolGlobalString('BOM_ADDON') . ".php";
863  $classname = getDolGlobalString('BOM_ADDON');
864 
865  // Include file with class
866  $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
867  foreach ($dirmodels as $reldir) {
868  $dir = dol_buildpath($reldir."core/modules/bom/");
869 
870  // Load file with numbering class (if found)
871  $mybool = ((bool) @include_once $dir.$file) || $mybool;
872  }
873 
874  if ($mybool === false) {
875  dol_print_error(null, "Failed to include file ".$file);
876  return '';
877  }
878 
879  $obj = new $classname();
880  $numref = $obj->getNextValue($prod, $this);
881 
882  if ($numref != "") {
883  return $numref;
884  } else {
885  $this->error = $obj->error;
886  //dol_print_error($this->db,get_class($this)."::getNextNumRef ".$obj->error);
887  return "";
888  }
889  } else {
890  print $langs->trans("Error")." ".$langs->trans("Error_BOM_ADDON_NotDefined");
891  return "";
892  }
893  }
894 
902  public function validate($user, $notrigger = 0)
903  {
904  global $conf;
905 
906  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
907 
908  $error = 0;
909 
910  // Protection
911  if ($this->status == self::STATUS_VALIDATED) {
912  dol_syslog(get_class($this)."::validate action abandoned: already validated", LOG_WARNING);
913  return 0;
914  }
915 
916  $now = dol_now();
917 
918  $this->db->begin();
919 
920  // Define new ref
921  if (!$error && (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref))) { // empty should not happened, but when it occurs, the test save life
922  $this->fetch_product();
923  $num = $this->getNextNumRef($this->product);
924  } else {
925  $num = $this->ref;
926  }
927  $this->newref = dol_sanitizeFileName($num);
928 
929  // Validate
930  $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
931  $sql .= " SET ref = '".$this->db->escape($num)."',";
932  $sql .= " status = ".self::STATUS_VALIDATED.",";
933  $sql .= " date_valid='".$this->db->idate($now)."',";
934  $sql .= " fk_user_valid = ".((int) $user->id);
935  $sql .= " WHERE rowid = ".((int) $this->id);
936 
937  dol_syslog(get_class($this)."::validate()", LOG_DEBUG);
938  $resql = $this->db->query($sql);
939  if (!$resql) {
940  dol_print_error($this->db);
941  $this->error = $this->db->lasterror();
942  $error++;
943  }
944 
945  if (!$error && !$notrigger) {
946  // Call trigger
947  $result = $this->call_trigger('BOM_VALIDATE', $user);
948  if ($result < 0) {
949  $error++;
950  }
951  // End call triggers
952  }
953 
954  if (!$error) {
955  $this->oldref = $this->ref;
956 
957  // Rename directory if dir was a temporary ref
958  if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
959  // Now we rename also files into index
960  $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)."'";
961  $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'bom/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
962  $resql = $this->db->query($sql);
963  if (!$resql) {
964  $error++;
965  $this->error = $this->db->lasterror();
966  }
967  $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filepath = 'bom/".$this->db->escape($this->newref)."'";
968  $sql .= " WHERE filepath = 'bom/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
969  $resql = $this->db->query($sql);
970  if (!$resql) {
971  $error++;
972  $this->error = $this->db->lasterror();
973  }
974 
975  // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
976  $oldref = dol_sanitizeFileName($this->ref);
977  $newref = dol_sanitizeFileName($num);
978  $dirsource = $conf->bom->dir_output.'/'.$oldref;
979  $dirdest = $conf->bom->dir_output.'/'.$newref;
980  if (!$error && file_exists($dirsource)) {
981  dol_syslog(get_class($this)."::validate() rename dir ".$dirsource." into ".$dirdest);
982 
983  if (@rename($dirsource, $dirdest)) {
984  dol_syslog("Rename ok");
985  // Rename docs starting with $oldref with $newref
986  $listoffiles = dol_dir_list($conf->bom->dir_output.'/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
987  foreach ($listoffiles as $fileentry) {
988  $dirsource = $fileentry['name'];
989  $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
990  $dirsource = $fileentry['path'].'/'.$dirsource;
991  $dirdest = $fileentry['path'].'/'.$dirdest;
992  @rename($dirsource, $dirdest);
993  }
994  }
995  }
996  }
997  }
998 
999  // Set new ref and current status
1000  if (!$error) {
1001  $this->ref = $num;
1002  $this->status = self::STATUS_VALIDATED;
1003  }
1004 
1005  if (!$error) {
1006  $this->db->commit();
1007  return 1;
1008  } else {
1009  $this->db->rollback();
1010  return -1;
1011  }
1012  }
1013 
1021  public function setDraft($user, $notrigger = 0)
1022  {
1023  // Protection
1024  if ($this->status <= self::STATUS_DRAFT) {
1025  return 0;
1026  }
1027 
1028  return $this->setStatusCommon($user, self::STATUS_DRAFT, $notrigger, 'BOM_UNVALIDATE');
1029  }
1030 
1038  public function cancel($user, $notrigger = 0)
1039  {
1040  // Protection
1041  if ($this->status != self::STATUS_VALIDATED) {
1042  return 0;
1043  }
1044 
1045  return $this->setStatusCommon($user, self::STATUS_CANCELED, $notrigger, 'BOM_CLOSE');
1046  }
1047 
1055  public function reopen($user, $notrigger = 0)
1056  {
1057  // Protection
1058  if ($this->status != self::STATUS_CANCELED) {
1059  return 0;
1060  }
1061 
1062  return $this->setStatusCommon($user, self::STATUS_VALIDATED, $notrigger, 'BOM_REOPEN');
1063  }
1064 
1071  public function getTooltipContentArray($params)
1072  {
1073  global $conf, $langs, $user;
1074 
1075  $langs->loadLangs(['product', 'mrp']);
1076 
1077  $datas = [];
1078 
1079  if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
1080  return ['optimize' => $langs->trans("ShowBillOfMaterials")];
1081  }
1082  $picto = img_picto('', $this->picto).' <u class="paddingrightonly">'.$langs->trans("BillOfMaterials").'</u>';
1083  if (isset($this->status)) {
1084  $picto .= ' '.$this->getLibStatut(5);
1085  }
1086  $datas['picto'] = $picto;
1087  $datas['ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
1088  if (isset($this->label)) {
1089  $datas['label'] = '<br><b>'.$langs->trans('Label').':</b> '.$this->label;
1090  }
1091  if (!empty($this->fk_product) && $this->fk_product > 0) {
1092  include_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
1093  $product = new Product($this->db);
1094  $resultFetch = $product->fetch($this->fk_product);
1095  if ($resultFetch > 0) {
1096  $datas['product'] = "<br><b>".$langs->trans("Product").'</b>: '.$product->ref.' - '.$product->label;
1097  }
1098  }
1099 
1100  return $datas;
1101  }
1102 
1113  public function getNomUrl($withpicto = 0, $option = '', $notooltip = 0, $morecss = '', $save_lastsearch_value = -1)
1114  {
1115  global $db, $conf, $langs, $hookmanager;
1116 
1117  if (!empty($conf->dol_no_mouse_hover)) {
1118  $notooltip = 1; // Force disable tooltips
1119  }
1120 
1121  $result = '';
1122  $params = [
1123  'id' => $this->id,
1124  'objecttype' => $this->element,
1125  'option' => $option,
1126  ];
1127  $classfortooltip = 'classfortooltip';
1128  $dataparams = '';
1129  if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
1130  $classfortooltip = 'classforajaxtooltip';
1131  $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
1132  $label = '';
1133  } else {
1134  $label = implode($this->getTooltipContentArray($params));
1135  }
1136 
1137  $url = DOL_URL_ROOT.'/bom/bom_card.php?id='.$this->id;
1138 
1139  if ($option != 'nolink') {
1140  // Add param to save lastsearch_values or not
1141  $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
1142  if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
1143  $add_save_lastsearch_values = 1;
1144  }
1145  if ($add_save_lastsearch_values) {
1146  $url .= '&save_lastsearch_values=1';
1147  }
1148  }
1149 
1150  $linkclose = '';
1151  if (empty($notooltip)) {
1152  if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
1153  $label = $langs->trans("ShowBillOfMaterials");
1154  $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
1155  }
1156  $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
1157  $linkclose .= $dataparams.' class="'.$classfortooltip.($morecss ? ' '.$morecss : '').'"';
1158  } else {
1159  $linkclose = ($morecss ? ' class="'.$morecss.'"' : '');
1160  }
1161 
1162  $linkstart = '<a href="'.$url.'"';
1163  $linkstart .= $linkclose.'>';
1164  $linkend = '</a>';
1165 
1166  $result .= $linkstart;
1167  if ($withpicto) {
1168  $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), (($withpicto != 2) ? 'class="paddingright"' : ''), 0, 0, $notooltip ? 0 : 1);
1169  }
1170  if ($withpicto != 2) {
1171  $result .= $this->ref;
1172  }
1173  $result .= $linkend;
1174  //if ($withpicto != 2) $result.=(($addlabel && $this->label) ? $sep . dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : '');
1175 
1176  global $action, $hookmanager;
1177  $hookmanager->initHooks(array('bomdao'));
1178  $parameters = array('id' => $this->id, 'getnomurl' => &$result);
1179  $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1180  if ($reshook > 0) {
1181  $result = $hookmanager->resPrint;
1182  } else {
1183  $result .= $hookmanager->resPrint;
1184  }
1185 
1186  return $result;
1187  }
1188 
1195  public function getLibStatut($mode = 0)
1196  {
1197  return $this->LibStatut($this->status, $mode);
1198  }
1199 
1200  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1208  public function LibStatut($status, $mode = 0)
1209  {
1210  // phpcs:enable
1211  if (empty($this->labelStatus)) {
1212  global $langs;
1213  //$langs->load("mrp");
1214  $this->labelStatus[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv('Draft');
1215  $this->labelStatus[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv('Enabled');
1216  $this->labelStatus[self::STATUS_CANCELED] = $langs->transnoentitiesnoconv('Disabled');
1217  }
1218 
1219  $statusType = 'status'.$status;
1220  if ($status == self::STATUS_VALIDATED) {
1221  $statusType = 'status4';
1222  }
1223  if ($status == self::STATUS_CANCELED) {
1224  $statusType = 'status6';
1225  }
1226 
1227  return dolGetStatus($this->labelStatus[$status], $this->labelStatus[$status], '', $statusType, $mode);
1228  }
1229 
1236  public function info($id)
1237  {
1238  $sql = 'SELECT rowid, date_creation as datec, tms as datem,';
1239  $sql .= ' fk_user_creat, fk_user_modif';
1240  $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as t';
1241  $sql .= ' WHERE t.rowid = '.((int) $id);
1242  $result = $this->db->query($sql);
1243  if ($result) {
1244  if ($this->db->num_rows($result)) {
1245  $obj = $this->db->fetch_object($result);
1246 
1247  $this->id = $obj->rowid;
1248 
1249  $this->user_creation_id = $obj->fk_user_creat;
1250  $this->user_modification_id = $obj->fk_user_modif;
1251  $this->date_creation = $this->db->jdate($obj->datec);
1252  $this->date_modification = empty($obj->datem) ? '' : $this->db->jdate($obj->datem);
1253  }
1254 
1255  $this->db->free($result);
1256  } else {
1257  dol_print_error($this->db);
1258  }
1259  }
1260 
1266  public function getLinesArray()
1267  {
1268  $this->lines = array();
1269 
1270  $objectline = new BOMLine($this->db);
1271  $result = $objectline->fetchAll('ASC', 'position', 0, 0, '(fk_bom:=:'.((int) $this->id).')');
1272 
1273  if (is_numeric($result)) {
1274  $this->error = $objectline->error;
1275  $this->errors = $objectline->errors;
1276  return $result;
1277  } else {
1278  $this->lines = $result;
1279  return $this->lines;
1280  }
1281  }
1282 
1294  public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
1295  {
1296  global $conf, $langs;
1297 
1298  $langs->load("mrp");
1299  $outputlangs->load("products");
1300 
1301  if (!dol_strlen($modele)) {
1302  $modele = '';
1303 
1304  if ($this->model_pdf) {
1305  $modele = $this->model_pdf;
1306  } elseif (getDolGlobalString('BOM_ADDON_PDF')) {
1307  $modele = getDolGlobalString('BOM_ADDON_PDF');
1308  }
1309  }
1310 
1311  $modelpath = "core/modules/bom/doc/";
1312  if (!empty($modele)) {
1313  return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
1314  } else {
1315  return 0;
1316  }
1317  }
1318 
1319  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1326  public function is_photo_available($sdir)
1327  {
1328  // phpcs:enable
1329  include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1330  include_once DOL_DOCUMENT_ROOT.'/core/lib/images.lib.php';
1331 
1332  $sdir .= '/'.get_exdir(0, 0, 0, 0, $this, 'bom');
1333 
1334  $dir_osencoded = dol_osencode($sdir);
1335  if (file_exists($dir_osencoded)) {
1336  $handle = opendir($dir_osencoded);
1337  if (is_resource($handle)) {
1338  while (($file = readdir($handle)) !== false) {
1339  if (!utf8_check($file)) {
1340  $file = mb_convert_encoding($file, 'UTF-8', 'ISO-8859-1'); // To be sure data is stored in UTF8 in memory
1341  }
1342  if (dol_is_file($sdir.$file) && image_format_supported($file) >= 0) {
1343  return true;
1344  }
1345  }
1346  }
1347  }
1348  return false;
1349  }
1350 
1357  public function initAsSpecimen()
1358  {
1359  $this->initAsSpecimenCommon();
1360  $this->ref = 'BOM-123';
1361  $this->date_creation = dol_now() - 20000;
1362 
1363  return 1;
1364  }
1365 
1366 
1373  public function doScheduledJob()
1374  {
1375  global $conf, $langs;
1376 
1377  //$conf->global->SYSLOG_FILE = 'DOL_DATA_ROOT/dolibarr_mydedicatedlofile.log';
1378 
1379  $error = 0;
1380  $this->output = '';
1381  $this->error = '';
1382 
1383  dol_syslog(__METHOD__, LOG_DEBUG);
1384 
1385  $now = dol_now();
1386 
1387  $this->db->begin();
1388 
1389  // ...
1390 
1391  $this->db->commit();
1392 
1393  return $error;
1394  }
1395 
1402  public function calculateCosts()
1403  {
1404  global $conf, $hookmanager;
1405 
1406  include_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
1407  $this->unit_cost = 0;
1408  $this->total_cost = 0;
1409 
1410  $parameters = array();
1411  $reshook = $hookmanager->executeHooks('calculateCostsBom', $parameters, $this); // Note that $action and $object may have been modified by hook
1412 
1413  if ($reshook > 0) {
1414  return $hookmanager->resPrint;
1415  }
1416 
1417  if (is_array($this->lines) && count($this->lines)) {
1418  require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
1419  $productFournisseur = new ProductFournisseur($this->db);
1420  $tmpproduct = new Product($this->db);
1421 
1422  foreach ($this->lines as &$line) {
1423  $tmpproduct->cost_price = 0;
1424  $tmpproduct->pmp = 0;
1425  $result = $tmpproduct->fetch($line->fk_product, '', '', '', 0, 1, 1); // We discard selling price and language loading
1426 
1427  if ($tmpproduct->type == $tmpproduct::TYPE_PRODUCT) {
1428  if (empty($line->fk_bom_child)) {
1429  if ($result < 0) {
1430  $this->error = $tmpproduct->error;
1431  return -1;
1432  }
1433  $unit_cost = (!empty($tmpproduct->cost_price)) ? $tmpproduct->cost_price : $tmpproduct->pmp;
1434  $line->unit_cost = (float) price2num($unit_cost);
1435  if (empty($line->unit_cost)) {
1436  if ($productFournisseur->find_min_price_product_fournisseur($line->fk_product) > 0) {
1437  if ($productFournisseur->fourn_remise_percent != "0") {
1438  $line->unit_cost = $productFournisseur->fourn_unitprice_with_discount;
1439  } else {
1440  $line->unit_cost = $productFournisseur->fourn_unitprice;
1441  }
1442  }
1443  }
1444 
1445  $line->total_cost = (float) price2num($line->qty * $line->unit_cost, 'MT');
1446 
1447  $this->total_cost += $line->total_cost;
1448  } else {
1449  $bom_child = new BOM($this->db);
1450  $res = $bom_child->fetch($line->fk_bom_child);
1451  if ($res > 0) {
1452  $bom_child->calculateCosts();
1453  $line->childBom[] = $bom_child;
1454  $this->total_cost += (float) price2num($bom_child->total_cost * $line->qty, 'MT');
1455  $this->total_cost += $line->total_cost;
1456  } else {
1457  $this->error = $bom_child->error;
1458  return -2;
1459  }
1460  }
1461  } else {
1462  // Convert qty of line into hours
1463  $unitforline = measuringUnitString($line->fk_unit, '', '', 1);
1464  $qtyhourforline = convertDurationtoHour($line->qty, $unitforline);
1465 
1466  if (isModEnabled('workstation') && !empty($line->fk_default_workstation)) {
1467  $workstation = new Workstation($this->db);
1468  $res = $workstation->fetch($line->fk_default_workstation);
1469 
1470  if ($res > 0) {
1471  $line->total_cost = (float) price2num($qtyhourforline * ($workstation->thm_operator_estimated + $workstation->thm_machine_estimated), 'MT');
1472  } else {
1473  $this->error = $workstation->error;
1474  return -3;
1475  }
1476  } else {
1477  $defaultdurationofservice = $tmpproduct->duration;
1478  $reg = array();
1479  $qtyhourservice = 0;
1480  if (preg_match('/^(\d+)([a-z]+)$/', $defaultdurationofservice, $reg)) {
1481  $qtyhourservice = convertDurationtoHour($reg[1], $reg[2]);
1482  }
1483 
1484  if ($qtyhourservice) {
1485  $line->total_cost = (float) price2num($qtyhourforline / $qtyhourservice * $tmpproduct->cost_price, 'MT');
1486  } else {
1487  $line->total_cost = (float) price2num($line->qty * $tmpproduct->cost_price, 'MT');
1488  }
1489  }
1490 
1491  $this->total_cost += $line->total_cost;
1492  }
1493  }
1494 
1495  $this->total_cost = (float) price2num($this->total_cost, 'MT');
1496 
1497  if ($this->qty > 0) {
1498  $this->unit_cost = (float) price2num($this->total_cost / $this->qty, 'MU');
1499  } elseif ($this->qty < 0) {
1500  $this->unit_cost = (float) price2num($this->total_cost * $this->qty, 'MU');
1501  }
1502  }
1503 
1504  return 1;
1505  }
1506 
1515  public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
1516  {
1517  $tables = array(
1518  'bom_bomline'
1519  );
1520 
1521  return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
1522  }
1523 
1531  public function getNetNeeds(&$TNetNeeds = array(), $qty = 0)
1532  {
1533  if (!empty($this->lines)) {
1534  foreach ($this->lines as $line) {
1535  if (!empty($line->childBom)) {
1536  foreach ($line->childBom as $childBom) {
1537  $childBom->getNetNeeds($TNetNeeds, $line->qty * $qty);
1538  }
1539  } else {
1540  if (empty($TNetNeeds[$line->fk_product])) {
1541  $TNetNeeds[$line->fk_product] = 0;
1542  }
1543  $TNetNeeds[$line->fk_product] += $line->qty * $qty;
1544  }
1545  }
1546  }
1547  }
1548 
1557  public function getNetNeedsTree(&$TNetNeeds = array(), $qty = 0, $level = 0)
1558  {
1559  if (!empty($this->lines)) {
1560  foreach ($this->lines as $line) {
1561  if (!empty($line->childBom)) {
1562  foreach ($line->childBom as $childBom) {
1563  $TNetNeeds[$childBom->id]['bom'] = $childBom;
1564  $TNetNeeds[$childBom->id]['parentid'] = $this->id;
1565  $TNetNeeds[$childBom->id]['qty'] = $line->qty * $qty;
1566  $TNetNeeds[$childBom->id]['level'] = $level;
1567  $childBom->getNetNeedsTree($TNetNeeds, $line->qty * $qty, $level + 1);
1568  }
1569  } else {
1570  $TNetNeeds[$this->id]['product'][$line->fk_product]['qty'] += $line->qty * $qty;
1571  $TNetNeeds[$this->id]['product'][$line->fk_product]['level'] = $level;
1572  }
1573  }
1574  }
1575  }
1576 
1585  public function getParentBomTreeRecursive(&$TParentBom, $bom_id = 0, $level = 1)
1586  {
1587 
1588  // Protection against infinite loop
1589  if ($level > 1000) {
1590  return;
1591  }
1592 
1593  if (empty($bom_id)) {
1594  $bom_id = $this->id;
1595  }
1596 
1597  $sql = 'SELECT l.fk_bom, b.label
1598  FROM '.MAIN_DB_PREFIX.'bom_bomline l
1599  INNER JOIN '.MAIN_DB_PREFIX.$this->table_element.' b ON b.rowid = l.fk_bom
1600  WHERE fk_bom_child = '.((int) $bom_id);
1601 
1602  $resql = $this->db->query($sql);
1603  if (!empty($resql)) {
1604  while ($res = $this->db->fetch_object($resql)) {
1605  $TParentBom[$res->fk_bom] = $res->fk_bom;
1606  $this->getParentBomTreeRecursive($TParentBom, $res->fk_bom, $level + 1);
1607  }
1608  }
1609  }
1610 
1618  public function getKanbanView($option = '', $arraydata = null)
1619  {
1620  global $db,$langs;
1621 
1622  $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
1623 
1624  $return = '<div class="box-flex-item box-flex-grow-zero">';
1625  $return .= '<div class="info-box info-box-sm">';
1626  $return .= '<span class="info-box-icon bg-infobox-action">';
1627  $return .= img_picto('', $this->picto);
1628  $return .= '</span>';
1629  $return .= '<div class="info-box-content">';
1630  $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl() : '').'</span>';
1631  if ($selected >= 0) {
1632  $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
1633  }
1634  if (property_exists($this, 'fields') && !empty($this->fields['bomtype']['arrayofkeyval'])) {
1635  $return .= '<br><span class="info-box-label opacitymedium">'.$langs->trans("Type").' : </span>';
1636  if ($this->bomtype == 0) {
1637  $return .= '<span class="info-box-label">'.$this->fields['bomtype']['arrayofkeyval'][0].'</span>';
1638  } else {
1639  $return .= '<span class="info-box-label">'.$this->fields['bomtype']['arrayofkeyval'][1].'</span>';
1640  }
1641  }
1642  if (!empty($arraydata['prod'])) {
1643  $prod = $arraydata['prod'];
1644  $return .= '<br><span class="info-box-label">'.$prod->getNomUrl(1).'</span>';
1645  }
1646  if (method_exists($this, 'getLibStatut')) {
1647  $return .= '<br><div class="info-box-status">'.$this->getLibStatut(3).'</div>';
1648  }
1649 
1650  $return .= '</div>';
1651  $return .= '</div>';
1652  $return .= '</div>';
1653  return $return;
1654  }
1655 }
1656 
1657 
1662 {
1666  public $element = 'bomline';
1667 
1671  public $table_element = 'bom_bomline';
1672 
1676  public $picto = 'bomline';
1677 
1678 
1698  // BEGIN MODULEBUILDER PROPERTIES
1702  public $fields = array(
1703  'rowid' => array('type' => 'integer', 'label' => 'LineID', 'enabled' => 1, 'visible' => -1, 'position' => 1, 'notnull' => 1, 'index' => 1, 'comment' => "Id",),
1704  'fk_bom' => array('type' => 'integer:BillOfMaterials:societe/class/bom.class.php', 'label' => 'BillOfMaterials', 'enabled' => 1, 'visible' => 1, 'position' => 10, 'notnull' => 1, 'index' => 1,),
1705  'fk_product' => array('type' => 'integer:Product:product/class/product.class.php', 'label' => 'Product', 'enabled' => 1, 'visible' => 1, 'position' => 20, 'notnull' => 1, 'index' => 1,),
1706  'fk_bom_child' => array('type' => 'integer:BOM:bom/class/bom.class.php', 'label' => 'BillOfMaterials', 'enabled' => 1, 'visible' => -1, 'position' => 40, 'notnull' => -1,),
1707  'description' => array('type' => 'text', 'label' => 'Description', 'enabled' => 1, 'visible' => -1, 'position' => 60, 'notnull' => -1,),
1708  'qty' => array('type' => 'double(24,8)', 'label' => 'Quantity', 'enabled' => 1, 'visible' => 1, 'position' => 100, 'notnull' => 1, 'isameasure' => 1,),
1709  'qty_frozen' => array('type' => 'smallint', 'label' => 'QuantityFrozen', 'enabled' => 1, 'visible' => 1, 'default' => '0', 'position' => 105, 'css' => 'maxwidth50imp', 'help' => 'QuantityConsumedInvariable'),
1710  'disable_stock_change' => array('type' => 'smallint', 'label' => 'DisableStockChange', 'enabled' => 1, 'visible' => 1, 'default' => '0', 'position' => 108, 'css' => 'maxwidth50imp', 'help' => 'DisableStockChangeHelp'),
1711  'efficiency' => array('type' => 'double(24,8)', 'label' => 'ManufacturingEfficiency', 'enabled' => 1, 'visible' => 0, 'default' => '1', 'position' => 110, 'notnull' => 1, 'css' => 'maxwidth50imp', 'help' => 'ValueOfEfficiencyConsumedMeans'),
1712  'fk_unit' => array('type' => 'integer', 'label' => 'Unit', 'enabled' => 1, 'visible' => 1, 'position' => 120, 'notnull' => -1,),
1713  'position' => array('type' => 'integer', 'label' => 'Rank', 'enabled' => 1, 'visible' => 0, 'default' => '0', 'position' => 200, 'notnull' => 1,),
1714  'import_key' => array('type' => 'varchar(14)', 'label' => 'ImportId', 'enabled' => 1, 'visible' => -2, 'position' => 1000, 'notnull' => -1,),
1715  'fk_default_workstation' => array('type' => 'integer', 'label' => 'DefaultWorkstation', 'enabled' => 1, 'visible' => 1, 'notnull' => 0, 'position' => 1050)
1716  );
1717 
1721  public $rowid;
1722 
1726  public $fk_bom;
1727 
1731  public $fk_product;
1732 
1736  public $fk_bom_child;
1737 
1741  public $description;
1742 
1746  public $qty;
1747 
1751  public $qty_frozen;
1752 
1756  public $disable_stock_change;
1757 
1761  public $efficiency;
1762 
1766  public $position;
1767 
1771  public $import_key;
1772  // END MODULEBUILDER PROPERTIES
1773 
1777  public $total_cost = 0;
1778 
1782  public $unit_cost = 0;
1783 
1787  public $childBom = array();
1788 
1794  public $fk_unit;
1795 
1799  public $fk_default_workstation;
1800 
1801 
1802 
1808  public function __construct(DoliDB $db)
1809  {
1810  global $conf, $langs;
1811 
1812  $this->db = $db;
1813 
1814  $this->ismultientitymanaged = 0;
1815 
1816  $this->isextrafieldmanaged = 1;
1817 
1818  if (!getDolGlobalString('MAIN_SHOW_TECHNICAL_ID') && isset($this->fields['rowid'])) {
1819  $this->fields['rowid']['visible'] = 0;
1820  }
1821  if (!isModEnabled('multicompany') && isset($this->fields['entity'])) {
1822  $this->fields['entity']['enabled'] = 0;
1823  }
1824 
1825  // Unset fields that are disabled
1826  foreach ($this->fields as $key => $val) {
1827  if (isset($val['enabled']) && empty($val['enabled'])) {
1828  unset($this->fields[$key]);
1829  }
1830  }
1831 
1832  // Translate some data of arrayofkeyval
1833  foreach ($this->fields as $key => $val) {
1834  if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
1835  foreach ($val['arrayofkeyval'] as $key2 => $val2) {
1836  $this->fields[$key]['arrayofkeyval'][$key2] = $langs->trans($val2);
1837  }
1838  }
1839  }
1840  }
1841 
1849  public function create(User $user, $notrigger = 0)
1850  {
1851  if ($this->efficiency < 0 || $this->efficiency > 1) {
1852  $this->efficiency = 1;
1853  }
1854 
1855  return $this->createCommon($user, $notrigger);
1856  }
1857 
1865  public function fetch($id, $ref = null)
1866  {
1867  $result = $this->fetchCommon($id, $ref);
1868  //if ($result > 0 && !empty($this->table_element_line)) $this->fetchLines();
1869  return $result;
1870  }
1871 
1884  public function fetchAll($sortorder = '', $sortfield = '', $limit = 0, $offset = 0, $filter = '', $filtermode = 'AND')
1885  {
1886  dol_syslog(__METHOD__, LOG_DEBUG);
1887 
1888  $records = array();
1889 
1890  $sql = 'SELECT ';
1891  $sql .= $this->getFieldList();
1892  $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as t';
1893  if ($this->ismultientitymanaged) {
1894  $sql .= ' WHERE t.entity IN ('.getEntity($this->element).')';
1895  } else {
1896  $sql .= ' WHERE 1 = 1';
1897  }
1898 
1899  // Manage filter
1900  $errormessage = '';
1901  $sql .= forgeSQLFromUniversalSearchCriteria($filter, $errormessage);
1902  if ($errormessage) {
1903  $this->errors[] = $errormessage;
1904  dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
1905  return -1;
1906  }
1907 
1908  if (!empty($sortfield)) {
1909  $sql .= $this->db->order($sortfield, $sortorder);
1910  }
1911  if (!empty($limit)) {
1912  $sql .= $this->db->plimit($limit, $offset);
1913  }
1914 
1915  $resql = $this->db->query($sql);
1916  if ($resql) {
1917  $num = $this->db->num_rows($resql);
1918 
1919  while ($obj = $this->db->fetch_object($resql)) {
1920  $record = new self($this->db);
1921  $record->setVarsFromFetchObj($obj);
1922  $record->fetch_optionals();
1923 
1924  $records[$record->id] = $record;
1925  }
1926  $this->db->free($resql);
1927 
1928  return $records;
1929  } else {
1930  $this->errors[] = 'Error '.$this->db->lasterror();
1931  dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
1932 
1933  return -1;
1934  }
1935  }
1936 
1944  public function update(User $user, $notrigger = 0)
1945  {
1946  if ($this->efficiency < 0 || $this->efficiency > 1) {
1947  $this->efficiency = 1;
1948  }
1949 
1950  return $this->updateCommon($user, $notrigger);
1951  }
1952 
1960  public function delete(User $user, $notrigger = 0)
1961  {
1962  return $this->deleteCommon($user, $notrigger);
1963  //return $this->deleteCommon($user, $notrigger, 1);
1964  }
1965 
1976  public function getNomUrl($withpicto = 0, $option = '', $notooltip = 0, $morecss = '', $save_lastsearch_value = -1)
1977  {
1978  global $db, $conf, $langs, $hookmanager;
1979 
1980  if (!empty($conf->dol_no_mouse_hover)) {
1981  $notooltip = 1; // Force disable tooltips
1982  }
1983 
1984  $result = '';
1985 
1986  $label = '<u>'.$langs->trans("BillOfMaterialsLine").'</u>';
1987  $label .= '<br>';
1988  $label .= '<b>'.$langs->trans('Ref').':</b> '.$this->ref;
1989 
1990  $url = DOL_URL_ROOT.'/bom/bomline_card.php?id='.$this->id;
1991 
1992  if ($option != 'nolink') {
1993  // Add param to save lastsearch_values or not
1994  $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
1995  if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
1996  $add_save_lastsearch_values = 1;
1997  }
1998  if ($add_save_lastsearch_values) {
1999  $url .= '&save_lastsearch_values=1';
2000  }
2001  }
2002 
2003  $linkclose = '';
2004  if (empty($notooltip)) {
2005  if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2006  $label = $langs->trans("ShowBillOfMaterialsLine");
2007  $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
2008  }
2009  $linkclose .= ' title="'.dol_escape_htmltag($label, 1).'"';
2010  $linkclose .= ' class="classfortooltip'.($morecss ? ' '.$morecss : '').'"';
2011  } else {
2012  $linkclose = ($morecss ? ' class="'.$morecss.'"' : '');
2013  }
2014 
2015  $linkstart = '<a href="'.$url.'"';
2016  $linkstart .= $linkclose.'>';
2017  $linkend = '</a>';
2018 
2019  $result .= $linkstart;
2020  if ($withpicto) {
2021  $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);
2022  }
2023  if ($withpicto != 2) {
2024  $result .= $this->ref;
2025  }
2026  $result .= $linkend;
2027  //if ($withpicto != 2) $result.=(($addlabel && $this->label) ? $sep . dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : '');
2028 
2029  global $action, $hookmanager;
2030  $hookmanager->initHooks(array('bomlinedao'));
2031  $parameters = array('id' => $this->id, 'getnomurl' => &$result);
2032  $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
2033  if ($reshook > 0) {
2034  $result = $hookmanager->resPrint;
2035  } else {
2036  $result .= $hookmanager->resPrint;
2037  }
2038 
2039  return $result;
2040  }
2041 
2048  public function getLibStatut($mode = 0)
2049  {
2050  return $this->LibStatut($this->status, $mode);
2051  }
2052 
2053  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2061  public function LibStatut($status, $mode = 0)
2062  {
2063  // phpcs:enable
2064  return '';
2065  }
2066 
2073  public function info($id)
2074  {
2075  $sql = 'SELECT rowid, date_creation as datec, tms as datem,';
2076  $sql .= ' fk_user_creat, fk_user_modif';
2077  $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as t';
2078  $sql .= ' WHERE t.rowid = '.((int) $id);
2079  $result = $this->db->query($sql);
2080  if ($result) {
2081  if ($this->db->num_rows($result)) {
2082  $obj = $this->db->fetch_object($result);
2083 
2084  $this->id = $obj->rowid;
2085 
2086  $this->user_creation_id = $obj->fk_user_creat;
2087  $this->user_modification_id = $obj->fk_user_modif;
2088  $this->date_creation = $this->db->jdate($obj->datec);
2089  $this->date_modification = empty($obj->datem) ? '' : $this->db->jdate($obj->datem);
2090  }
2091  $this->db->free($result);
2092  } else {
2093  dol_print_error($this->db);
2094  }
2095  }
2096 
2103  public function initAsSpecimen()
2104  {
2105  return $this->initAsSpecimenCommon();
2106  }
2107 }
if($user->socid > 0) if(! $user->hasRight('accounting', 'chartofaccount')) $object
Definition: card.php:58
print $langs trans("AuditedSecurityEvents").'</strong >< span class="opacitymedium"></span >< br > status
Or an array listing all the potential status of the object: array: int of the status => translated la...
Definition: security.php:607
$object ref
Definition: info.php:79
Class for BOM.
Definition: bom.class.php:45
static replaceProduct(DoliDB $db, $origin_id, $dest_id)
Function used to replace a product id with another one.
Definition: bom.class.php:1515
fetchLines()
Load object lines in memory from the database.
Definition: bom.class.php:419
is_photo_available($sdir)
Return if at least one photo is available.
Definition: bom.class.php:1326
__construct(DoliDB $db)
Constructor.
Definition: bom.class.php:255
calculateCosts()
BOM costs calculation based on cost_price or pmp of each BOM line.
Definition: bom.class.php:1402
info($id)
Load the info information in the object.
Definition: bom.class.php:1236
getLibStatut($mode=0)
Return label of the status.
Definition: bom.class.php:1195
initAsSpecimen()
Initialise object with example values Id must be 0 if object instance is a specimen.
Definition: bom.class.php:1357
validate($user, $notrigger=0)
Validate bom.
Definition: bom.class.php:902
reopen($user, $notrigger=0)
Set cancel status.
Definition: bom.class.php:1055
cancel($user, $notrigger=0)
Set cancel status.
Definition: bom.class.php:1038
LibStatut($status, $mode=0)
Return the status.
Definition: bom.class.php:1208
doScheduledJob()
Action executed by scheduler CAN BE A CRON TASK.
Definition: bom.class.php:1373
getParentBomTreeRecursive(&$TParentBom, $bom_id=0, $level=1)
Recursively retrieves all parent bom in the tree that leads to the $bom_id bom.
Definition: bom.class.php:1585
getTooltipContentArray($params)
getTooltipContentArray
Definition: bom.class.php:1071
fetchLinesbytypeproduct($typeproduct=0)
Load object lines in memory from the database by type of product.
Definition: bom.class.php:434
deleteLine(User $user, $idline, $notrigger=0)
Delete a line of object in database.
Definition: bom.class.php:807
createFromClone(User $user, $fromid)
Clone an object into another one.
Definition: bom.class.php:311
fetch($id, $ref=null)
Load object in memory from the database.
Definition: bom.class.php:402
getNomUrl($withpicto=0, $option='', $notooltip=0, $morecss='', $save_lastsearch_value=-1)
Return a link to the object card (with optionally the picto)
Definition: bom.class.php:1113
updateLine($rowid, $qty, $qty_frozen=0, $disable_stock_change=0, $efficiency=1.0, $position=-1, $import_key=null, $fk_unit=0, $array_options=array(), $fk_default_workstation=null)
Update an BOM line into database.
Definition: bom.class.php:695
getNetNeeds(&$TNetNeeds=array(), $qty=0)
Get Net needs by product.
Definition: bom.class.php:1531
create(User $user, $notrigger=1)
Create object into database.
Definition: bom.class.php:295
getNextNumRef($prod)
Returns the reference to the following non used BOM depending on the active numbering module defined ...
Definition: bom.class.php:854
generateDocument($modele, $outputlangs, $hidedetails=0, $hidedesc=0, $hideref=0, $moreparams=null)
Create a document onto disk according to template module.
Definition: bom.class.php:1294
getLinesArray()
Create an array of lines.
Definition: bom.class.php:1266
setDraft($user, $notrigger=0)
Set draft status.
Definition: bom.class.php:1021
getKanbanView($option='', $arraydata=null)
Return clicable link of object (with eventually picto)
Definition: bom.class.php:1618
update(User $user, $notrigger=1)
Update object into database.
Definition: bom.class.php:549
fetchAll($sortorder='', $sortfield='', $limit=0, $offset=0, $filter='', $filtermode='AND')
Load list of objects in memory from the database.
Definition: bom.class.php:490
addLine($fk_product, $qty, $qty_frozen=0, $disable_stock_change=0, $efficiency=1.0, $position=-1, $fk_bom_child=null, $import_key=null, $fk_unit=0, $array_options=array(), $fk_default_workstation=null)
Add an BOM line into database (linked to BOM)
Definition: bom.class.php:587
getNetNeedsTree(&$TNetNeeds=array(), $qty=0, $level=0)
Get Net needs Tree by product or bom.
Definition: bom.class.php:1557
Class for BOMLine.
Definition: bom.class.php:1662
fetch($id, $ref=null)
Load object in memory from the database.
Definition: bom.class.php:1865
getLibStatut($mode=0)
Return label of the status.
Definition: bom.class.php:2048
__construct(DoliDB $db)
Constructor.
Definition: bom.class.php:1808
update(User $user, $notrigger=0)
Update object into database.
Definition: bom.class.php:1944
fetchAll($sortorder='', $sortfield='', $limit=0, $offset=0, $filter='', $filtermode='AND')
Load list of objects in memory from the database.
Definition: bom.class.php:1884
getNomUrl($withpicto=0, $option='', $notooltip=0, $morecss='', $save_lastsearch_value=-1)
Return a link to the object card (with optionally the picto)
Definition: bom.class.php:1976
initAsSpecimen()
Initialise object with example values Id must be 0 if object instance is a specimen.
Definition: bom.class.php:2103
info($id)
Load the info information in the object.
Definition: bom.class.php:2073
LibStatut($status, $mode=0)
Return the status.
Definition: bom.class.php:2061
create(User $user, $notrigger=0)
Create object into database.
Definition: bom.class.php:1849
Parent class of all other business classes (invoices, contracts, proposals, orders,...
commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams=null)
Common function for all objects extending CommonObject for generating documents.
setErrorsFromObject($object)
setErrorsFromObject
createCommon(User $user, $notrigger=0)
Create object in the database.
getFieldList($alias='', $excludefields=array())
Function to concat keys of fields.
updateCommon(User $user, $notrigger=0)
Update object into database.
setStatusCommon($user, $status, $notrigger=0, $triggercode='')
Set to a status.
initAsSpecimenCommon()
Initialise object with example values Id must be 0 if object instance is a specimen.
copy_linked_contact($objFrom, $source='internal')
Copy contact from one element to current.
fetch_product()
Load the product with id $this->fk_product into this->product.
fetchLinesCommon($morewhere='', $noextrafields=0)
Load object in memory from the database.
static commonReplaceProduct(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a product id with another one.
line_max($fk_parent_line=0)
Get max value used for position of line (rang)
fetchCommon($id, $ref=null, $morewhere='', $noextrafields=0)
Load object in memory from the database.
deleteCommon(User $user, $notrigger=0, $forcechilddeletion=0)
Delete object in database.
call_trigger($triggerName, $user)
Call trigger based on this instance.
Parent class for class inheritance lines of business objects This class is useless for the moment so ...
Class to manage Dolibarr database access.
Class to manage predefined suppliers products.
Class to manage products or services.
Class to manage Dolibarr users.
Definition: user.class.php:50
Class for Workstation.
if(isModEnabled('invoice') && $user->hasRight('facture', 'lire')) if((isModEnabled('fournisseur') &&!getDolGlobalString('MAIN_USE_NEW_SUPPLIERMOD') && $user->hasRight("fournisseur", "facture", "lire"))||(isModEnabled('supplier_invoice') && $user->hasRight("supplier_invoice", "lire"))) if(isModEnabled('don') && $user->hasRight('don', 'lire')) if(isModEnabled('tax') && $user->hasRight('tax', 'charges', 'lire')) if(isModEnabled('invoice') &&isModEnabled('order') && $user->hasRight("commande", "lire") &&!getDolGlobalString('WORKFLOW_DISABLE_CREATE_INVOICE_FROM_ORDER')) $sql
Social contributions to pay.
Definition: index.php:745
convertDurationtoHour($duration_value, $duration_unit)
Convert duration to hour.
Definition: date.lib.php:334
dol_is_file($pathoffile)
Return if path is a file.
Definition: files.lib.php:519
dol_dir_list($utf8_path, $types="all", $recursive=0, $filter="", $excludefilter=null, $sortcriteria="name", $sortorder=SORT_ASC, $mode=0, $nohook=0, $relativename="", $donotfollowsymlinks=0, $nbsecondsold=0)
Scan a directory and return a list of files/directories.
Definition: files.lib.php:63
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0)
Show a picto called object_picto (generic function)
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (generic function)
dol_osencode($str)
Return a string encoded into OS filesystem encoding.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
forgeSQLFromUniversalSearchCriteria($filter, &$errorstr='', $noand=0, $nopar=0, $noerror=0)
forgeSQLFromUniversalSearchCriteria
dol_now($mode='auto')
Return date for now.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
dolGetStatus($statusLabel='', $statusLabelShort='', $html='', $statusType='status0', $displayMode=0, $url='', $params=array())
Output the badge of a status.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
dol_sanitizeFileName($str, $newstr='_', $unaccent=1)
Clean a string to use it as a file name.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
getDolGlobalString($key, $default='')
Return dolibarr global constant string value.
isModEnabled($module)
Is Dolibarr module enabled.
utf8_check($str)
Check if a string is in UTF8.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
image_format_supported($file, $acceptsvg=0)
Return if a filename is file name of a supported image format.
Definition: images.lib.php:84
div float
Buy price without taxes.
Definition: style.css.php:960
measuringUnitString($unit, $measuring_style='', $scale='', $use_short_label=0, $outputlangs=null)
Return translation label of a unit key.