dolibarr  18.0.6
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  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program. If not, see <https://www.gnu.org/licenses/>.
18  */
19 
26 // Put here all includes required by your class file
27 require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
28 require_once DOL_DOCUMENT_ROOT."/core/class/commonobjectline.class.php";
29 require_once DOL_DOCUMENT_ROOT.'/workstation/class/workstation.class.php';
30 require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
31 //require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php';
32 //require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
33 
34 
38 class BOM extends CommonObject
39 {
40 
44  public $module = 'bom';
45 
49  public $element = 'bom';
50 
54  public $table_element = 'bom_bom';
55 
59  public $ismultientitymanaged = 1;
60 
64  public $isextrafieldmanaged = 1;
65 
69  public $picto = 'bom';
70 
71 
72  const STATUS_DRAFT = 0;
73  const STATUS_VALIDATED = 1;
74  const STATUS_CANCELED = 9;
75 
76 
103  // BEGIN MODULEBUILDER PROPERTIES
107  public $fields = array(
108  'rowid' => array('type'=>'integer', 'label'=>'TechnicalID', 'enabled'=>1, 'visible'=>-2, 'position'=>1, 'notnull'=>1, 'index'=>1, 'comment'=>"Id",),
109  'entity' => array('type'=>'integer', 'label'=>'Entity', 'enabled'=>1, 'visible'=>0, 'notnull'=> 1, 'default'=>1, 'index'=>1, 'position'=>5),
110  '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'),
111  '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'),
112  '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'),
113  //'bomtype' => array('type'=>'integer', 'label'=>'Type', 'enabled'=>1, 'visible'=>-1, 'position'=>32, 'notnull'=>1, 'default'=>'0', 'arrayofkeyval'=>array(0=>'Manufacturing')),
114  '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'),
115  'description' => array('type'=>'text', 'label'=>'Description', 'enabled'=>1, 'visible'=>-1, 'position'=>60, 'notnull'=>-1,),
116  'qty' => array('type'=>'real', 'label'=>'Quantity', 'enabled'=>1, 'visible'=>1, 'default'=>1, 'position'=>55, 'notnull'=>1, 'isameasure'=>'1', 'css'=>'maxwidth50imp right'),
117  //'efficiency' => array('type'=>'real', 'label'=>'ManufacturingEfficiency', 'enabled'=>1, 'visible'=>-1, 'default'=>1, 'position'=>100, 'notnull'=>0, 'css'=>'maxwidth50imp', 'help'=>'ValueOfMeansLossForProductProduced'),
118  'duration' => array('type'=>'duration', 'label'=>'EstimatedDuration', 'enabled'=>1, 'visible'=>-1, 'position'=>101, 'notnull'=>-1, 'css'=>'maxwidth50imp', 'help'=>'EstimatedDurationDesc'),
119  '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'),
120  'note_public' => array('type'=>'html', 'label'=>'NotePublic', 'enabled'=>1, 'visible'=>-2, 'position'=>161, 'notnull'=>-1,),
121  'note_private' => array('type'=>'html', 'label'=>'NotePrivate', 'enabled'=>1, 'visible'=>-2, 'position'=>162, 'notnull'=>-1,),
122  'date_creation' => array('type'=>'datetime', 'label'=>'DateCreation', 'enabled'=>1, 'visible'=>-2, 'position'=>300, 'notnull'=>1,),
123  'tms' => array('type'=>'timestamp', 'label'=>'DateModification', 'enabled'=>1, 'visible'=>-2, 'position'=>501, 'notnull'=>1,),
124  'date_valid' => array('type'=>'datetime', 'label'=>'DateValidation', 'enabled'=>1, 'visible'=>-2, 'position'=>502, 'notnull'=>0,),
125  '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'),
126  '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'),
127  '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'),
128  'import_key' => array('type'=>'varchar(14)', 'label'=>'ImportId', 'enabled'=>1, 'visible'=>-2, 'position'=>1000, 'notnull'=>-1,),
129  'model_pdf' =>array('type'=>'varchar(255)', 'label'=>'Model pdf', 'enabled'=>1, 'visible'=>0, 'position'=>1010),
130  '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')),
131  );
132 
136  public $rowid;
137 
141  public $ref;
142 
146  public $label;
147 
151  public $bomtype;
152 
156  public $description;
157 
161  public $date_creation;
162 
166  public $date_valid;
167 
168  public $tms;
169 
173  public $fk_user_creat;
174 
178  public $fk_user_modif;
179 
183  public $fk_user_valid;
184 
188  public $fk_warehouse;
189 
193  public $import_key;
194 
198  public $status;
199 
203  public $fk_product;
204  public $qty;
205  public $duration;
206  public $efficiency;
207  // END MODULEBUILDER PROPERTIES
208 
209 
210  // If this object has a subtable with lines
211 
215  public $table_element_line = 'bom_bomline';
216 
220  public $fk_element = 'fk_bom';
221 
225  public $class_element_line = 'BOMLine';
226 
227  // /**
228  // * @var array List of child tables. To test if we can delete object.
229  // */
230  // protected $childtables=array();
231 
235  protected $childtablesoncascade = array('bom_bomline');
236 
240  public $lines = array();
241 
245  public $total_cost = 0;
246 
250  public $unit_cost = 0;
251 
252 
258  public function __construct(DoliDB $db)
259  {
260  global $conf, $langs;
261 
262  $this->db = $db;
263 
264  if (empty($conf->global->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 = false)
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, array $filter = array(), $filtermode = 'AND')
491  {
492  global $conf;
493 
494  dol_syslog(__METHOD__, LOG_DEBUG);
495 
496  $records = array();
497 
498  $sql = 'SELECT ';
499  $sql .= $this->getFieldList();
500  $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as t';
501  if ($this->ismultientitymanaged) {
502  $sql .= ' WHERE t.entity IN ('.getEntity($this->element).')';
503  } else {
504  $sql .= ' WHERE 1 = 1';
505  }
506  // Manage filter
507  $sqlwhere = array();
508  if (count($filter) > 0) {
509  foreach ($filter as $key => $value) {
510  if ($key == 't.rowid') {
511  $sqlwhere[] = $key." = ".((int) $value);
512  } elseif (strpos($key, 'date') !== false) {
513  $sqlwhere[] = $key." = '".$this->db->idate($value)."'";
514  } elseif ($key == 'customsql') {
515  $sqlwhere[] = $value;
516  } else {
517  $sqlwhere[] = $key." LIKE '%".$this->db->escape($value)."%'";
518  }
519  }
520  }
521  if (count($sqlwhere) > 0) {
522  $sql .= " AND (".implode(" ".$filtermode." ", $sqlwhere).")";
523  }
524 
525  if (!empty($sortfield)) {
526  $sql .= $this->db->order($sortfield, $sortorder);
527  }
528  if (!empty($limit)) {
529  $sql .= $this->db->plimit($limit, $offset);
530  }
531 
532  $resql = $this->db->query($sql);
533  if ($resql) {
534  $num = $this->db->num_rows($resql);
535 
536  while ($obj = $this->db->fetch_object($resql)) {
537  $record = new self($this->db);
538  $record->setVarsFromFetchObj($obj);
539 
540  $records[$record->id] = $record;
541  }
542  $this->db->free($resql);
543 
544  return $records;
545  } else {
546  $this->errors[] = 'Error '.$this->db->lasterror();
547  dol_syslog(__METHOD__.' '.join(',', $this->errors), LOG_ERR);
548 
549  return -1;
550  }
551  }
552 
560  public function update(User $user, $notrigger = false)
561  {
562  if ($this->efficiency <= 0 || $this->efficiency > 1) {
563  $this->efficiency = 1;
564  }
565 
566  return $this->updateCommon($user, $notrigger);
567  }
568 
576  public function delete(User $user, $notrigger = false)
577  {
578  return $this->deleteCommon($user, $notrigger);
579  //return $this->deleteCommon($user, $notrigger, 1);
580  }
581 
598  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 = '', $array_options = 0, $fk_default_workstation = null)
599  {
600  global $mysoc, $conf, $langs, $user;
601 
602  $logtext = "::addLine bomid=$this->id, qty=$qty, fk_product=$fk_product, qty_frozen=$qty_frozen, disable_stock_change=$disable_stock_change, efficiency=$efficiency";
603  $logtext .= ", fk_bom_child=$fk_bom_child, import_key=$import_key";
604  dol_syslog(get_class($this).$logtext, LOG_DEBUG);
605 
606  if ($this->statut == self::STATUS_DRAFT) {
607  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
608 
609  // Clean parameters
610  if (empty($qty)) {
611  $qty = 0;
612  }
613  if (empty($qty_frozen)) {
614  $qty_frozen = 0;
615  }
616  if (empty($disable_stock_change)) {
617  $disable_stock_change = 0;
618  }
619  if (empty($efficiency)) {
620  $efficiency = 1.0;
621  }
622  if (empty($fk_bom_child)) {
623  $fk_bom_child = null;
624  }
625  if (empty($import_key)) {
626  $import_key = null;
627  }
628  if (empty($position)) {
629  $position = -1;
630  }
631 
632  $qty = price2num($qty);
633  $efficiency = price2num($efficiency);
634  $position = price2num($position);
635 
636  $this->db->begin();
637 
638  // Rank to use
639  $rangMax = $this->line_max();
640  $rankToUse = $position;
641  if ($rankToUse <= 0 or $rankToUse > $rangMax) { // New line after existing lines
642  $rankToUse = $rangMax + 1;
643  } else { // New line between the existing lines
644  foreach ($this->lines as $bl) {
645  if ($bl->position >= $rankToUse) {
646  $bl->position++;
647  $bl->update($user);
648  }
649  }
650  }
651 
652  // Insert line
653  $line = new BOMLine($this->db);
654 
655  $line->context = $this->context;
656 
657  $line->fk_bom = $this->id;
658  $line->fk_product = $fk_product;
659  $line->qty = $qty;
660  $line->qty_frozen = $qty_frozen;
661  $line->disable_stock_change = $disable_stock_change;
662  $line->efficiency = $efficiency;
663  $line->fk_bom_child = $fk_bom_child;
664  $line->import_key = $import_key;
665  $line->position = $rankToUse;
666  $line->fk_unit = $fk_unit;
667  $line->fk_default_workstation = $fk_default_workstation;
668 
669  if (is_array($array_options) && count($array_options) > 0) {
670  $line->array_options = $array_options;
671  }
672 
673  $result = $line->create($user);
674 
675  if ($result > 0) {
676  $this->calculateCosts();
677  $this->db->commit();
678  return $result;
679  } else {
680  $this->setErrorsFromObject($line);
681  dol_syslog(get_class($this)."::addLine error=".$this->error, LOG_ERR);
682  $this->db->rollback();
683  return -2;
684  }
685  } else {
686  dol_syslog(get_class($this)."::addLine status of BOM must be Draft to allow use of ->addLine()", LOG_ERR);
687  return -3;
688  }
689  }
690 
705  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 = 0)
706  {
707  global $mysoc, $conf, $langs, $user;
708 
709  $logtext = "::updateLine bomid=$this->id, qty=$qty, qty_frozen=$qty_frozen, disable_stock_change=$disable_stock_change, efficiency=$efficiency";
710  $logtext .= ", import_key=$import_key";
711  dol_syslog(get_class($this).$logtext, LOG_DEBUG);
712 
713  if ($this->statut == self::STATUS_DRAFT) {
714  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
715 
716  // Clean parameters
717  if (empty($qty)) {
718  $qty = 0;
719  }
720  if (empty($qty_frozen)) {
721  $qty_frozen = 0;
722  }
723  if (empty($disable_stock_change)) {
724  $disable_stock_change = 0;
725  }
726  if (empty($efficiency)) {
727  $efficiency = 1.0;
728  }
729  if (empty($import_key)) {
730  $import_key = null;
731  }
732  if (empty($position)) {
733  $position = -1;
734  }
735 
736  $qty = price2num($qty);
737  $efficiency = price2num($efficiency);
738  $position = price2num($position);
739 
740  $this->db->begin();
741 
742  //Fetch current line from the database and then clone the object and set it in $oldline property
743  $line = new BOMLine($this->db);
744  $line->fetch($rowid);
745  $line->fetch_optionals();
746 
747  $staticLine = clone $line;
748  $line->oldcopy = $staticLine;
749  $line->context = $this->context;
750 
751  // Rank to use
752  $rankToUse = (int) $position;
753  if ($rankToUse != $line->oldcopy->position) { // check if position have a new value
754  foreach ($this->lines as $bl) {
755  if ($bl->position >= $rankToUse AND $bl->position < ($line->oldcopy->position + 1)) { // move rank up
756  $bl->position++;
757  $bl->update($user);
758  }
759  if ($bl->position <= $rankToUse AND $bl->position > ($line->oldcopy->position)) { // move rank down
760  $bl->position--;
761  $bl->update($user);
762  }
763  }
764  }
765 
766 
767  $line->fk_bom = $this->id;
768  $line->qty = $qty;
769  $line->qty_frozen = $qty_frozen;
770  $line->disable_stock_change = $disable_stock_change;
771  $line->efficiency = $efficiency;
772  $line->import_key = $import_key;
773  $line->position = $rankToUse;
774  if (!empty($fk_unit)) {
775  $line->fk_unit = $fk_unit;
776  }
777 
778  if (is_array($array_options) && count($array_options) > 0) {
779  // We replace values in this->line->array_options only for entries defined into $array_options
780  foreach ($array_options as $key => $value) {
781  $line->array_options[$key] = $array_options[$key];
782  }
783  }
784 
785  $result = $line->update($user);
786 
787  if ($result > 0) {
788  $this->calculateCosts();
789  $this->db->commit();
790  return $result;
791  } else {
792  $this->setErrorsFromObject($line);
793  dol_syslog(get_class($this)."::addLine error=".$this->error, LOG_ERR);
794  $this->db->rollback();
795  return -2;
796  }
797  } else {
798  dol_syslog(get_class($this)."::addLine status of BOM must be Draft to allow use of ->addLine()", LOG_ERR);
799  return -3;
800  }
801  }
802 
811  public function deleteLine(User $user, $idline, $notrigger = false)
812  {
813  if ($this->status < 0) {
814  $this->error = 'ErrorDeleteLineNotAllowedByObjectStatus';
815  return -2;
816  }
817 
818  $this->db->begin();
819 
820  //Fetch current line from the database and then clone the object and set it in $oldline property
821  $line = new BOMLine($this->db);
822  $line->fetch($idline);
823  $line->fetch_optionals();
824 
825  $staticLine = clone $line;
826  $line->oldcopy = $staticLine;
827  $line->context = $this->context;
828 
829  $result = $line->delete($user, $notrigger);
830 
831  //Positions (rank) reordering
832  foreach ($this->lines as $bl) {
833  if ($bl->position > ($line->oldcopy->position)) { // move rank down
834  $bl->position--;
835  $bl->update($user);
836  }
837  }
838 
839  if ($result > 0) {
840  $this->calculateCosts();
841  $this->db->commit();
842  return $result;
843  } else {
844  $this->setErrorsFromObject($line);
845  dol_syslog(get_class($this)."::addLine error=".$this->error, LOG_ERR);
846  $this->db->rollback();
847  return -2;
848  }
849  }
850 
858  public function getNextNumRef($prod)
859  {
860  global $langs, $conf;
861  $langs->load("mrp");
862 
863  if (!empty($conf->global->BOM_ADDON)) {
864  $mybool = false;
865 
866  $file = $conf->global->BOM_ADDON.".php";
867  $classname = $conf->global->BOM_ADDON;
868 
869  // Include file with class
870  $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
871  foreach ($dirmodels as $reldir) {
872  $dir = dol_buildpath($reldir."core/modules/bom/");
873 
874  // Load file with numbering class (if found)
875  $mybool |= @include_once $dir.$file;
876  }
877 
878  if ($mybool === false) {
879  dol_print_error('', "Failed to include file ".$file);
880  return '';
881  }
882 
883  $obj = new $classname();
884  $numref = $obj->getNextValue($prod, $this);
885 
886  if ($numref != "") {
887  return $numref;
888  } else {
889  $this->error = $obj->error;
890  //dol_print_error($this->db,get_class($this)."::getNextNumRef ".$obj->error);
891  return "";
892  }
893  } else {
894  print $langs->trans("Error")." ".$langs->trans("Error_BOM_ADDON_NotDefined");
895  return "";
896  }
897  }
898 
906  public function validate($user, $notrigger = 0)
907  {
908  global $conf, $langs;
909 
910  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
911 
912  $error = 0;
913 
914  // Protection
915  if ($this->status == self::STATUS_VALIDATED) {
916  dol_syslog(get_class($this)."::validate action abandonned: already validated", LOG_WARNING);
917  return 0;
918  }
919 
920  /*if (! ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->bom->create))
921  || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->bom->bom_advance->validate))))
922  {
923  $this->error='NotEnoughPermissions';
924  dol_syslog(get_class($this)."::valid ".$this->error, LOG_ERR);
925  return -1;
926  }*/
927 
928  $now = dol_now();
929 
930  $this->db->begin();
931 
932  // Define new ref
933  if (!$error && (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref))) { // empty should not happened, but when it occurs, the test save life
934  $this->fetch_product();
935  $num = $this->getNextNumRef($this->product);
936  } else {
937  $num = $this->ref;
938  }
939  $this->newref = dol_sanitizeFileName($num);
940 
941  // Validate
942  $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
943  $sql .= " SET ref = '".$this->db->escape($num)."',";
944  $sql .= " status = ".self::STATUS_VALIDATED.",";
945  $sql .= " date_valid='".$this->db->idate($now)."',";
946  $sql .= " fk_user_valid = ".((int) $user->id);
947  $sql .= " WHERE rowid = ".((int) $this->id);
948 
949  dol_syslog(get_class($this)."::validate()", LOG_DEBUG);
950  $resql = $this->db->query($sql);
951  if (!$resql) {
952  dol_print_error($this->db);
953  $this->error = $this->db->lasterror();
954  $error++;
955  }
956 
957  if (!$error && !$notrigger) {
958  // Call trigger
959  $result = $this->call_trigger('BOM_VALIDATE', $user);
960  if ($result < 0) {
961  $error++;
962  }
963  // End call triggers
964  }
965 
966  if (!$error) {
967  $this->oldref = $this->ref;
968 
969  // Rename directory if dir was a temporary ref
970  if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
971  // Now we rename also files into index
972  $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)."'";
973  $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'bom/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
974  $resql = $this->db->query($sql);
975  if (!$resql) {
976  $error++; $this->error = $this->db->lasterror();
977  }
978  $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filepath = 'bom/".$this->db->escape($this->newref)."'";
979  $sql .= " WHERE filepath = 'bom/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
980  $resql = $this->db->query($sql);
981  if (!$resql) {
982  $error++; $this->error = $this->db->lasterror();
983  }
984 
985  // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
986  $oldref = dol_sanitizeFileName($this->ref);
987  $newref = dol_sanitizeFileName($num);
988  $dirsource = $conf->bom->dir_output.'/'.$oldref;
989  $dirdest = $conf->bom->dir_output.'/'.$newref;
990  if (!$error && file_exists($dirsource)) {
991  dol_syslog(get_class($this)."::validate() rename dir ".$dirsource." into ".$dirdest);
992 
993  if (@rename($dirsource, $dirdest)) {
994  dol_syslog("Rename ok");
995  // Rename docs starting with $oldref with $newref
996  $listoffiles = dol_dir_list($conf->bom->dir_output.'/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
997  foreach ($listoffiles as $fileentry) {
998  $dirsource = $fileentry['name'];
999  $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
1000  $dirsource = $fileentry['path'].'/'.$dirsource;
1001  $dirdest = $fileentry['path'].'/'.$dirdest;
1002  @rename($dirsource, $dirdest);
1003  }
1004  }
1005  }
1006  }
1007  }
1008 
1009  // Set new ref and current status
1010  if (!$error) {
1011  $this->ref = $num;
1012  $this->status = self::STATUS_VALIDATED;
1013  }
1014 
1015  if (!$error) {
1016  $this->db->commit();
1017  return 1;
1018  } else {
1019  $this->db->rollback();
1020  return -1;
1021  }
1022  }
1023 
1031  public function setDraft($user, $notrigger = 0)
1032  {
1033  // Protection
1034  if ($this->status <= self::STATUS_DRAFT) {
1035  return 0;
1036  }
1037 
1038  /*if (! ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->bom->write))
1039  || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->bom->bom_advance->validate))))
1040  {
1041  $this->error='Permission denied';
1042  return -1;
1043  }*/
1044 
1045  return $this->setStatusCommon($user, self::STATUS_DRAFT, $notrigger, 'BOM_UNVALIDATE');
1046  }
1047 
1055  public function cancel($user, $notrigger = 0)
1056  {
1057  // Protection
1058  if ($this->status != self::STATUS_VALIDATED) {
1059  return 0;
1060  }
1061 
1062  /*if (! ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->bom->write))
1063  || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->bom->bom_advance->validate))))
1064  {
1065  $this->error='Permission denied';
1066  return -1;
1067  }*/
1068 
1069  return $this->setStatusCommon($user, self::STATUS_CANCELED, $notrigger, 'BOM_CLOSE');
1070  }
1071 
1079  public function reopen($user, $notrigger = 0)
1080  {
1081  // Protection
1082  if ($this->status != self::STATUS_CANCELED) {
1083  return 0;
1084  }
1085 
1086  /*if (! ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->bom->write))
1087  || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->bom->bom_advance->validate))))
1088  {
1089  $this->error='Permission denied';
1090  return -1;
1091  }*/
1092 
1093  return $this->setStatusCommon($user, self::STATUS_VALIDATED, $notrigger, 'BOM_REOPEN');
1094  }
1095 
1102  public function getTooltipContentArray($params)
1103  {
1104  global $conf, $langs, $user;
1105 
1106  $langs->loadLangs(['product', 'mrp']);
1107 
1108  $datas = [];
1109 
1110  if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
1111  return ['optimize' => $langs->trans("ShowBillOfMaterials")];
1112  }
1113  $picto = img_picto('', $this->picto).' <u class="paddingrightonly">'.$langs->trans("BillOfMaterials").'</u>';
1114  if (isset($this->status)) {
1115  $picto .= ' '.$this->getLibStatut(5);
1116  }
1117  $datas['picto'] = $picto;
1118  $datas['ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
1119  if (isset($this->label)) {
1120  $datas['label'] = '<br><b>'.$langs->trans('Label').':</b> '.$this->label;
1121  }
1122  if (!empty($this->fk_product) && $this->fk_product > 0) {
1123  include_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
1124  $product = new Product($this->db);
1125  $resultFetch = $product->fetch($this->fk_product);
1126  if ($resultFetch > 0) {
1127  $datas['product'] = "<br><b>".$langs->trans("Product").'</b>: '.$product->ref.' - '.$product->label;
1128  }
1129  }
1130 
1131  return $datas;
1132  }
1133 
1144  public function getNomUrl($withpicto = 0, $option = '', $notooltip = 0, $morecss = '', $save_lastsearch_value = -1)
1145  {
1146  global $db, $conf, $langs, $hookmanager;
1147 
1148  if (!empty($conf->dol_no_mouse_hover)) {
1149  $notooltip = 1; // Force disable tooltips
1150  }
1151 
1152  $result = '';
1153  $params = [
1154  'id' => $this->id,
1155  'objecttype' => $this->element,
1156  'option' => $option,
1157  ];
1158  $classfortooltip = 'classfortooltip';
1159  $dataparams = '';
1160  if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
1161  $classfortooltip = 'classforajaxtooltip';
1162  $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
1163  $label = '';
1164  } else {
1165  $label = implode($this->getTooltipContentArray($params));
1166  }
1167 
1168  $url = DOL_URL_ROOT.'/bom/bom_card.php?id='.$this->id;
1169 
1170  if ($option != 'nolink') {
1171  // Add param to save lastsearch_values or not
1172  $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
1173  if ($save_lastsearch_value == -1 && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
1174  $add_save_lastsearch_values = 1;
1175  }
1176  if ($add_save_lastsearch_values) {
1177  $url .= '&save_lastsearch_values=1';
1178  }
1179  }
1180 
1181  $linkclose = '';
1182  if (empty($notooltip)) {
1183  if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
1184  $label = $langs->trans("ShowBillOfMaterials");
1185  $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
1186  }
1187  $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
1188  $linkclose .= $dataparams.' class="'.$classfortooltip.($morecss ? ' '.$morecss : '').'"';
1189  } else {
1190  $linkclose = ($morecss ? ' class="'.$morecss.'"' : '');
1191  }
1192 
1193  $linkstart = '<a href="'.$url.'"';
1194  $linkstart .= $linkclose.'>';
1195  $linkend = '</a>';
1196 
1197  $result .= $linkstart;
1198  if ($withpicto) {
1199  $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), (($withpicto != 2) ? 'class="paddingright"' : ''), 0, 0, $notooltip ? 0 : 1);
1200  }
1201  if ($withpicto != 2) {
1202  $result .= $this->ref;
1203  }
1204  $result .= $linkend;
1205  //if ($withpicto != 2) $result.=(($addlabel && $this->label) ? $sep . dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : '');
1206 
1207  global $action, $hookmanager;
1208  $hookmanager->initHooks(array('bomdao'));
1209  $parameters = array('id'=>$this->id, 'getnomurl' => &$result);
1210  $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1211  if ($reshook > 0) {
1212  $result = $hookmanager->resPrint;
1213  } else {
1214  $result .= $hookmanager->resPrint;
1215  }
1216 
1217  return $result;
1218  }
1219 
1226  public function getLibStatut($mode = 0)
1227  {
1228  return $this->LibStatut($this->status, $mode);
1229  }
1230 
1231  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1239  public function LibStatut($status, $mode = 0)
1240  {
1241  // phpcs:enable
1242  if (empty($this->labelStatus)) {
1243  global $langs;
1244  //$langs->load("mrp");
1245  $this->labelStatus[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv('Draft');
1246  $this->labelStatus[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv('Enabled');
1247  $this->labelStatus[self::STATUS_CANCELED] = $langs->transnoentitiesnoconv('Disabled');
1248  }
1249 
1250  $statusType = 'status'.$status;
1251  if ($status == self::STATUS_VALIDATED) {
1252  $statusType = 'status4';
1253  }
1254  if ($status == self::STATUS_CANCELED) {
1255  $statusType = 'status6';
1256  }
1257 
1258  return dolGetStatus($this->labelStatus[$status], $this->labelStatus[$status], '', $statusType, $mode);
1259  }
1260 
1267  public function info($id)
1268  {
1269  $sql = 'SELECT rowid, date_creation as datec, tms as datem,';
1270  $sql .= ' fk_user_creat, fk_user_modif';
1271  $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as t';
1272  $sql .= ' WHERE t.rowid = '.((int) $id);
1273  $result = $this->db->query($sql);
1274  if ($result) {
1275  if ($this->db->num_rows($result)) {
1276  $obj = $this->db->fetch_object($result);
1277  $this->id = $obj->rowid;
1278 
1279  $this->user_creation_id = $obj->fk_user_creat;
1280  $this->user_modification_id = $obj->fk_user_modif;
1281  $this->date_creation = $this->db->jdate($obj->datec);
1282  $this->date_modification = empty($obj->datem) ? '' : $this->db->jdate($obj->datem);
1283  }
1284 
1285  $this->db->free($result);
1286  } else {
1287  dol_print_error($this->db);
1288  }
1289  }
1290 
1296  public function getLinesArray()
1297  {
1298  $this->lines = array();
1299 
1300  $objectline = new BOMLine($this->db);
1301  $result = $objectline->fetchAll('ASC', 'position', 0, 0, array('customsql'=>'fk_bom = '.((int) $this->id)));
1302 
1303  if (is_numeric($result)) {
1304  $this->error = $objectline->error;
1305  $this->errors = $objectline->errors;
1306  return $result;
1307  } else {
1308  $this->lines = $result;
1309  return $this->lines;
1310  }
1311  }
1312 
1324  public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
1325  {
1326  global $conf, $langs;
1327 
1328  $langs->load("mrp");
1329  $outputlangs->load("products");
1330 
1331  if (!dol_strlen($modele)) {
1332  $modele = '';
1333 
1334  if ($this->model_pdf) {
1335  $modele = $this->model_pdf;
1336  } elseif (!empty($conf->global->BOM_ADDON_PDF)) {
1337  $modele = $conf->global->BOM_ADDON_PDF;
1338  }
1339  }
1340 
1341  $modelpath = "core/modules/bom/doc/";
1342  if (!empty($modele)) {
1343  return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
1344  } else {
1345  return 0;
1346  }
1347  }
1348 
1355  public function initAsSpecimen()
1356  {
1357  $this->initAsSpecimenCommon();
1358  $this->ref = 'BOM-123';
1359  $this->date = $this->date_creation;
1360  }
1361 
1362 
1369  public function doScheduledJob()
1370  {
1371  global $conf, $langs;
1372 
1373  //$conf->global->SYSLOG_FILE = 'DOL_DATA_ROOT/dolibarr_mydedicatedlofile.log';
1374 
1375  $error = 0;
1376  $this->output = '';
1377  $this->error = '';
1378 
1379  dol_syslog(__METHOD__, LOG_DEBUG);
1380 
1381  $now = dol_now();
1382 
1383  $this->db->begin();
1384 
1385  // ...
1386 
1387  $this->db->commit();
1388 
1389  return $error;
1390  }
1391 
1398  public function calculateCosts()
1399  {
1400  global $conf, $hookmanager;
1401 
1402  include_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
1403  $this->unit_cost = 0;
1404  $this->total_cost = 0;
1405 
1406  $parameters=array();
1407  $reshook = $hookmanager->executeHooks('calculateCostsBom', $parameters, $this); // Note that $action and $object may have been modified by hook
1408 
1409  if ($reshook > 0) {
1410  return $hookmanager->resPrint;
1411  }
1412 
1413  if (is_array($this->lines) && count($this->lines)) {
1414  require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
1415  $productFournisseur = new ProductFournisseur($this->db);
1416  $tmpproduct = new Product($this->db);
1417 
1418  foreach ($this->lines as &$line) {
1419  $tmpproduct->cost_price = 0;
1420  $tmpproduct->pmp = 0;
1421  $result = $tmpproduct->fetch($line->fk_product, '', '', '', 0, 1, 1); // We discard selling price and language loading
1422 
1423  if ($tmpproduct->type == $tmpproduct::TYPE_PRODUCT) {
1424  if (empty($line->fk_bom_child)) {
1425  if ($result < 0) {
1426  $this->error = $tmpproduct->error;
1427  return -1;
1428  }
1429  $line->unit_cost = price2num((!empty($tmpproduct->cost_price)) ? $tmpproduct->cost_price : $tmpproduct->pmp);
1430  if (empty($line->unit_cost)) {
1431  if ($productFournisseur->find_min_price_product_fournisseur($line->fk_product) > 0) {
1432  if ($productFournisseur->fourn_remise_percent != "0") {
1433  $line->unit_cost = $productFournisseur->fourn_unitprice_with_discount;
1434  } else {
1435  $line->unit_cost = $productFournisseur->fourn_unitprice;
1436  }
1437  }
1438  }
1439 
1440  $line->total_cost = price2num($line->qty * $line->unit_cost, 'MT');
1441 
1442  $this->total_cost += $line->total_cost;
1443  } else {
1444  $bom_child = new BOM($this->db);
1445  $res = $bom_child->fetch($line->fk_bom_child);
1446  if ($res > 0) {
1447  $bom_child->calculateCosts();
1448  $line->childBom[] = $bom_child;
1449  $this->total_cost += price2num($bom_child->total_cost * $line->qty, 'MT');
1450  $this->total_cost += $line->total_cost;
1451  } else {
1452  $this->error = $bom_child->error;
1453  return -2;
1454  }
1455  }
1456  } else {
1457  // Convert qty of line into hours
1458  $unitforline = measuringUnitString($line->fk_unit, '', '', 1);
1459  $qtyhourforline = convertDurationtoHour($line->qty, $unitforline);
1460 
1461  if (isModEnabled('workstation') && !empty($tmpproduct->fk_default_workstation)) {
1462  $workstation = new Workstation($this->db);
1463  $res = $workstation->fetch($tmpproduct->fk_default_workstation);
1464 
1465  if ($res > 0) $line->total_cost = price2num($qtyhourforline * ($workstation->thm_operator_estimated + $workstation->thm_machine_estimated), 'MT');
1466  else {
1467  $this->error = $workstation->error;
1468  return -3;
1469  }
1470  } else {
1471  $defaultdurationofservice = $tmpproduct->duration;
1472  $reg = array();
1473  $qtyhourservice = 0;
1474  if (preg_match('/^(\d+)([a-z]+)$/', $defaultdurationofservice, $reg)) {
1475  $qtyhourservice = convertDurationtoHour($reg[1], $reg[2]);
1476  }
1477 
1478  if ($qtyhourservice) {
1479  $line->total_cost = price2num($qtyhourforline / $qtyhourservice * $tmpproduct->cost_price, 'MT');
1480  } else {
1481  $line->total_cost = price2num($line->qty * $tmpproduct->cost_price, 'MT');
1482  }
1483  }
1484 
1485  $this->total_cost += $line->total_cost;
1486  }
1487  }
1488 
1489  $this->total_cost = price2num($this->total_cost, 'MT');
1490 
1491  if ($this->qty > 0) {
1492  $this->unit_cost = price2num($this->total_cost / $this->qty, 'MU');
1493  } elseif ($this->qty < 0) {
1494  $this->unit_cost = price2num($this->total_cost * $this->qty, 'MU');
1495  }
1496  }
1497 
1498  return 1;
1499  }
1500 
1509  public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
1510  {
1511  $tables = array(
1512  'bom_bomline'
1513  );
1514 
1515  return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
1516  }
1517 
1525  public function getNetNeeds(&$TNetNeeds = array(), $qty = 0)
1526  {
1527  if (!empty($this->lines)) {
1528  foreach ($this->lines as $line) {
1529  if (!empty($line->childBom)) {
1530  foreach ($line->childBom as $childBom) $childBom->getNetNeeds($TNetNeeds, $line->qty*$qty);
1531  } else {
1532  if (empty($TNetNeeds[$line->fk_product])) {
1533  $TNetNeeds[$line->fk_product] = 0;
1534  }
1535  $TNetNeeds[$line->fk_product] += $line->qty*$qty;
1536  }
1537  }
1538  }
1539  }
1540 
1549  public function getNetNeedsTree(&$TNetNeeds = array(), $qty = 0, $level = 0)
1550  {
1551  if (!empty($this->lines)) {
1552  foreach ($this->lines as $line) {
1553  if (!empty($line->childBom)) {
1554  foreach ($line->childBom as $childBom) {
1555  $TNetNeeds[$childBom->id]['bom'] = $childBom;
1556  $TNetNeeds[$childBom->id]['parentid'] = $this->id;
1557  $TNetNeeds[$childBom->id]['qty'] = $line->qty*$qty;
1558  $TNetNeeds[$childBom->id]['level'] = $level;
1559  $childBom->getNetNeedsTree($TNetNeeds, $line->qty*$qty, $level+1);
1560  }
1561  } else {
1562  $TNetNeeds[$this->id]['product'][$line->fk_product]['qty'] += $line->qty * $qty;
1563  $TNetNeeds[$this->id]['product'][$line->fk_product]['level'] = $level;
1564  }
1565  }
1566  }
1567  }
1568 
1577  public function getParentBomTreeRecursive(&$TParentBom, $bom_id = '', $level = 1)
1578  {
1579 
1580  // Protection against infinite loop
1581  if ($level > 1000) {
1582  return;
1583  }
1584 
1585  if (empty($bom_id)) $bom_id=$this->id;
1586 
1587  $sql = 'SELECT l.fk_bom, b.label
1588  FROM '.MAIN_DB_PREFIX.'bom_bomline l
1589  INNER JOIN '.MAIN_DB_PREFIX.$this->table_element.' b ON b.rowid = l.fk_bom
1590  WHERE fk_bom_child = '.((int) $bom_id);
1591 
1592  $resql = $this->db->query($sql);
1593  if (!empty($resql)) {
1594  while ($res = $this->db->fetch_object($resql)) {
1595  $TParentBom[$res->fk_bom] = $res->fk_bom;
1596  $this->getParentBomTreeRecursive($TParentBom, $res->fk_bom, $level+1);
1597  }
1598  }
1599  }
1600 
1608  public function getKanbanView($option = '', $arraydata = null)
1609  {
1610  global $db,$langs;
1611 
1612  $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
1613 
1614  $prod = new Product($db);
1615  $prod->fetch($this->fk_product);
1616 
1617  $return = '<div class="box-flex-item box-flex-grow-zero">';
1618  $return .= '<div class="info-box info-box-sm">';
1619  $return .= '<span class="info-box-icon bg-infobox-action">';
1620  $return .= img_picto('', $this->picto);
1621  $return .= '</span>';
1622  $return .= '<div class="info-box-content">';
1623  $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl() : '').'</span>';
1624  $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
1625  if (property_exists($this, 'fields') && !empty($this->fields['bomtype']['arrayofkeyval'])) {
1626  $return .= '<br><span class="info-box-label opacitymedium">'.$langs->trans("Type").' : </span>';
1627  if ($this->bomtype == 0) {
1628  $return .= '<span class="info-box-label">'.$this->fields['bomtype']['arrayofkeyval'][0].'</span>';
1629  } else {
1630  $return .= '<span class="info-box-label">'.$this->fields['bomtype']['arrayofkeyval'][1].'</span>';
1631  }
1632  }
1633  if (property_exists($this, 'fk_product') && !is_null($this->fk_product)) {
1634  $return .= '<br><span class="info-box-label">'.$prod->getNomUrl(1).'</span>';
1635  }
1636  if (method_exists($this, 'getLibStatut')) {
1637  $return .= '<br><div class="info-box-status margintoponly">'.$this->getLibStatut(3).'</div>';
1638  }
1639 
1640  $return .= '</div>';
1641  $return .= '</div>';
1642  $return .= '</div>';
1643  return $return;
1644  }
1645 }
1646 
1647 
1652 {
1656  public $element = 'bomline';
1657 
1661  public $table_element = 'bom_bomline';
1662 
1666  public $ismultientitymanaged = 0;
1667 
1671  public $isextrafieldmanaged = 1;
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 
1792  public $fk_unit;
1793 
1797  public $fk_default_workstation;
1798 
1799 
1800 
1806  public function __construct(DoliDB $db)
1807  {
1808  global $conf, $langs;
1809 
1810  $this->db = $db;
1811 
1812  if (empty($conf->global->MAIN_SHOW_TECHNICAL_ID) && isset($this->fields['rowid'])) {
1813  $this->fields['rowid']['visible'] = 0;
1814  }
1815  if (!isModEnabled('multicompany') && isset($this->fields['entity'])) {
1816  $this->fields['entity']['enabled'] = 0;
1817  }
1818 
1819  // Unset fields that are disabled
1820  foreach ($this->fields as $key => $val) {
1821  if (isset($val['enabled']) && empty($val['enabled'])) {
1822  unset($this->fields[$key]);
1823  }
1824  }
1825 
1826  // Translate some data of arrayofkeyval
1827  foreach ($this->fields as $key => $val) {
1828  if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
1829  foreach ($val['arrayofkeyval'] as $key2 => $val2) {
1830  $this->fields[$key]['arrayofkeyval'][$key2] = $langs->trans($val2);
1831  }
1832  }
1833  }
1834  }
1835 
1843  public function create(User $user, $notrigger = false)
1844  {
1845  if ($this->efficiency < 0 || $this->efficiency > 1) {
1846  $this->efficiency = 1;
1847  }
1848 
1849  return $this->createCommon($user, $notrigger);
1850  }
1851 
1859  public function fetch($id, $ref = null)
1860  {
1861  $result = $this->fetchCommon($id, $ref);
1862  //if ($result > 0 && !empty($this->table_element_line)) $this->fetchLines();
1863  return $result;
1864  }
1865 
1877  public function fetchAll($sortorder = '', $sortfield = '', $limit = 0, $offset = 0, array $filter = array(), $filtermode = 'AND')
1878  {
1879  global $conf;
1880 
1881  dol_syslog(__METHOD__, LOG_DEBUG);
1882 
1883  $records = array();
1884 
1885  $sql = 'SELECT ';
1886  $sql .= $this->getFieldList();
1887  $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as t';
1888  if ($this->ismultientitymanaged) {
1889  $sql .= ' WHERE t.entity IN ('.getEntity($this->element).')';
1890  } else {
1891  $sql .= ' WHERE 1 = 1';
1892  }
1893  // Manage filter
1894  $sqlwhere = array();
1895  if (count($filter) > 0) {
1896  foreach ($filter as $key => $value) {
1897  if ($key == 't.rowid') {
1898  $sqlwhere[] = $key." = ".((int) $value);
1899  } elseif (strpos($key, 'date') !== false) {
1900  $sqlwhere[] = $key." = '".$this->db->idate($value)."'";
1901  } elseif ($key == 'customsql') {
1902  $sqlwhere[] = $value;
1903  } else {
1904  $sqlwhere[] = $key." LIKE '%".$this->db->escape($value)."%'";
1905  }
1906  }
1907  }
1908  if (count($sqlwhere) > 0) {
1909  $sql .= ' AND ('.implode(' '.$this->db->escape($filtermode).' ', $sqlwhere).')';
1910  }
1911 
1912  if (!empty($sortfield)) {
1913  $sql .= $this->db->order($sortfield, $sortorder);
1914  }
1915  if (!empty($limit)) {
1916  $sql .= $this->db->plimit($limit, $offset);
1917  }
1918 
1919  $resql = $this->db->query($sql);
1920  if ($resql) {
1921  $num = $this->db->num_rows($resql);
1922 
1923  while ($obj = $this->db->fetch_object($resql)) {
1924  $record = new self($this->db);
1925  $record->setVarsFromFetchObj($obj);
1926 
1927  $records[$record->id] = $record;
1928  }
1929  $this->db->free($resql);
1930 
1931  return $records;
1932  } else {
1933  $this->errors[] = 'Error '.$this->db->lasterror();
1934  dol_syslog(__METHOD__.' '.join(',', $this->errors), LOG_ERR);
1935 
1936  return -1;
1937  }
1938  }
1939 
1947  public function update(User $user, $notrigger = false)
1948  {
1949  if ($this->efficiency < 0 || $this->efficiency > 1) {
1950  $this->efficiency = 1;
1951  }
1952 
1953  return $this->updateCommon($user, $notrigger);
1954  }
1955 
1963  public function delete(User $user, $notrigger = false)
1964  {
1965  return $this->deleteCommon($user, $notrigger);
1966  //return $this->deleteCommon($user, $notrigger, 1);
1967  }
1968 
1979  public function getNomUrl($withpicto = 0, $option = '', $notooltip = 0, $morecss = '', $save_lastsearch_value = -1)
1980  {
1981  global $db, $conf, $langs, $hookmanager;
1982 
1983  if (!empty($conf->dol_no_mouse_hover)) {
1984  $notooltip = 1; // Force disable tooltips
1985  }
1986 
1987  $result = '';
1988 
1989  $label = '<u>'.$langs->trans("BillOfMaterialsLine").'</u>';
1990  $label .= '<br>';
1991  $label .= '<b>'.$langs->trans('Ref').':</b> '.$this->ref;
1992 
1993  $url = DOL_URL_ROOT.'/bom/bomline_card.php?id='.$this->id;
1994 
1995  if ($option != 'nolink') {
1996  // Add param to save lastsearch_values or not
1997  $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
1998  if ($save_lastsearch_value == -1 && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
1999  $add_save_lastsearch_values = 1;
2000  }
2001  if ($add_save_lastsearch_values) {
2002  $url .= '&save_lastsearch_values=1';
2003  }
2004  }
2005 
2006  $linkclose = '';
2007  if (empty($notooltip)) {
2008  if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
2009  $label = $langs->trans("ShowBillOfMaterialsLine");
2010  $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
2011  }
2012  $linkclose .= ' title="'.dol_escape_htmltag($label, 1).'"';
2013  $linkclose .= ' class="classfortooltip'.($morecss ? ' '.$morecss : '').'"';
2014  } else {
2015  $linkclose = ($morecss ? ' class="'.$morecss.'"' : '');
2016  }
2017 
2018  $linkstart = '<a href="'.$url.'"';
2019  $linkstart .= $linkclose.'>';
2020  $linkend = '</a>';
2021 
2022  $result .= $linkstart;
2023  if ($withpicto) {
2024  $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);
2025  }
2026  if ($withpicto != 2) {
2027  $result .= $this->ref;
2028  }
2029  $result .= $linkend;
2030  //if ($withpicto != 2) $result.=(($addlabel && $this->label) ? $sep . dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : '');
2031 
2032  global $action, $hookmanager;
2033  $hookmanager->initHooks(array('bomlinedao'));
2034  $parameters = array('id'=>$this->id, 'getnomurl' => &$result);
2035  $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
2036  if ($reshook > 0) {
2037  $result = $hookmanager->resPrint;
2038  } else {
2039  $result .= $hookmanager->resPrint;
2040  }
2041 
2042  return $result;
2043  }
2044 
2051  public function getLibStatut($mode = 0)
2052  {
2053  return $this->LibStatut($this->status, $mode);
2054  }
2055 
2056  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2064  public function LibStatut($status, $mode = 0)
2065  {
2066  // phpcs:enable
2067  return '';
2068  }
2069 
2076  public function info($id)
2077  {
2078  $sql = 'SELECT rowid, date_creation as datec, tms as datem,';
2079  $sql .= ' fk_user_creat, fk_user_modif';
2080  $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as t';
2081  $sql .= ' WHERE t.rowid = '.((int) $id);
2082  $result = $this->db->query($sql);
2083  if ($result) {
2084  if ($this->db->num_rows($result)) {
2085  $obj = $this->db->fetch_object($result);
2086  $this->id = $obj->rowid;
2087  $this->user_creation_id = $obj->fk_user_creat;
2088  $this->user_modification_id = $obj->fk_user_modif;
2089  $this->date_creation = $this->db->jdate($obj->datec);
2090  $this->date_modification = empty($obj->datem) ? '' : $this->db->jdate($obj->datem);
2091  }
2092  $this->db->free($result);
2093  } else {
2094  dol_print_error($this->db);
2095  }
2096  }
2097 
2104  public function initAsSpecimen()
2105  {
2106  $this->initAsSpecimenCommon();
2107  }
2108 }
$object ref
Definition: info.php:78
Class for BOM.
Definition: bom.class.php:39
static replaceProduct(DoliDB $db, $origin_id, $dest_id)
Function used to replace a product id with another one.
Definition: bom.class.php:1509
fetchLines()
Load object lines in memory from the database.
Definition: bom.class.php:419
__construct(DoliDB $db)
Constructor.
Definition: bom.class.php:258
calculateCosts()
BOM costs calculation based on cost_price or pmp of each BOM line.
Definition: bom.class.php:1398
info($id)
Load the info information in the object.
Definition: bom.class.php:1267
getLibStatut($mode=0)
Return label of the status.
Definition: bom.class.php:1226
initAsSpecimen()
Initialise object with example values Id must be 0 if object instance is a specimen.
Definition: bom.class.php:1355
validate($user, $notrigger=0)
Validate bom.
Definition: bom.class.php:906
reopen($user, $notrigger=0)
Set cancel status.
Definition: bom.class.php:1079
fetchAll($sortorder='', $sortfield='', $limit=0, $offset=0, array $filter=array(), $filtermode='AND')
Load list of objects in memory from the database.
Definition: bom.class.php:490
cancel($user, $notrigger=0)
Set cancel status.
Definition: bom.class.php:1055
create(User $user, $notrigger=false)
Create object into database.
Definition: bom.class.php:295
LibStatut($status, $mode=0)
Return the status.
Definition: bom.class.php:1239
doScheduledJob()
Action executed by scheduler CAN BE A CRON TASK.
Definition: bom.class.php:1369
getTooltipContentArray($params)
getTooltipContentArray
Definition: bom.class.php:1102
fetchLinesbytypeproduct($typeproduct=0)
Load object lines in memory from the database by type of product.
Definition: bom.class.php:434
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 optionaly the picto)
Definition: bom.class.php:1144
getNetNeeds(&$TNetNeeds=array(), $qty=0)
Get Net needs by product.
Definition: bom.class.php:1525
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='', $array_options=0, $fk_default_workstation=null)
Add an BOM line into database (linked to BOM)
Definition: bom.class.php:598
getNextNumRef($prod)
Returns the reference to the following non used BOM depending on the active numbering module defined ...
Definition: bom.class.php:858
generateDocument($modele, $outputlangs, $hidedetails=0, $hidedesc=0, $hideref=0, $moreparams=null)
Create a document onto disk according to template module.
Definition: bom.class.php:1324
getLinesArray()
Create an array of lines.
Definition: bom.class.php:1296
setDraft($user, $notrigger=0)
Set draft status.
Definition: bom.class.php:1031
updateLine($rowid, $qty, $qty_frozen=0, $disable_stock_change=0, $efficiency=1.0, $position=-1, $import_key=null, $fk_unit=0, $array_options=0)
Update an BOM line into database.
Definition: bom.class.php:705
update(User $user, $notrigger=false)
Update object into database.
Definition: bom.class.php:560
getKanbanView($option='', $arraydata=null)
Return clicable link of object (with eventually picto)
Definition: bom.class.php:1608
deleteLine(User $user, $idline, $notrigger=false)
Delete a line of object in database.
Definition: bom.class.php:811
getParentBomTreeRecursive(&$TParentBom, $bom_id='', $level=1)
Recursively retrieves all parent bom in the tree that leads to the $bom_id bom.
Definition: bom.class.php:1577
getNetNeedsTree(&$TNetNeeds=array(), $qty=0, $level=0)
Get Net needs Tree by product or bom.
Definition: bom.class.php:1549
Class for BOMLine.
Definition: bom.class.php:1652
create(User $user, $notrigger=false)
Create object into database.
Definition: bom.class.php:1843
fetchAll($sortorder='', $sortfield='', $limit=0, $offset=0, array $filter=array(), $filtermode='AND')
Load list of objects in memory from the database.
Definition: bom.class.php:1877
fetch($id, $ref=null)
Load object in memory from the database.
Definition: bom.class.php:1859
getLibStatut($mode=0)
Return label of the status.
Definition: bom.class.php:2051
update(User $user, $notrigger=false)
Update object into database.
Definition: bom.class.php:1947
__construct(DoliDB $db)
Constructor.
Definition: bom.class.php:1806
getNomUrl($withpicto=0, $option='', $notooltip=0, $morecss='', $save_lastsearch_value=-1)
Return a link to the object card (with optionaly the picto)
Definition: bom.class.php:1979
initAsSpecimen()
Initialise object with example values Id must be 0 if object instance is a specimen.
Definition: bom.class.php:2104
info($id)
Load the info information in the object.
Definition: bom.class.php:2076
LibStatut($status, $mode=0)
Return the status.
Definition: bom.class.php:2064
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
fetchCommon($id, $ref=null, $morewhere='')
Load object in memory from the database.
createCommon(User $user, $notrigger=false)
Create object into database.
deleteCommon(User $user, $notrigger=false, $forcechilddeletion=0)
Delete object in database.
getFieldList($alias='', $excludefields=array())
Function to concat keys of fields.
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.
updateCommon(User $user, $notrigger=false)
Update object into 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)
fetchLinesCommon($morewhere='')
Load object in memory from the 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:48
Class for Workstation.
if(isModEnabled('facture') && $user->hasRight('facture', 'lire')) if((isModEnabled('fournisseur') &&empty($conf->global->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') &&!empty($user->rights->tax->charges->lire)) if(isModEnabled('facture') &&isModEnabled('commande') && $user->hasRight("commande", "lire") &&empty($conf->global->WORKFLOW_DISABLE_CREATE_INVOICE_FROM_ORDER)) $sql
Social contributions to pay.
Definition: index.php:746
convertDurationtoHour($duration_value, $duration_unit)
Convert duration to hour.
Definition: date.lib.php:332
dol_dir_list($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:62
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
dol_print_error($db='', $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=false, $srconly=0, $notitle=0)
Show a picto called object_picto (generic function)
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
dol_now($mode='auto')
Return date for now.
getDolGlobalInt($key, $default=0)
Return dolibarr global constant int value.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=false, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (generic function)
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.
isModEnabled($module)
Is Dolibarr module enabled.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
measuringUnitString($unit, $measuring_style='', $scale='', $use_short_label=0, $outputlangs=null)
Return translation label of a unit key.