26 require_once DOL_DOCUMENT_ROOT.
'/core/class/commonobject.class.php';
27 require_once DOL_DOCUMENT_ROOT.
'/workstation/class/workstation.class.php';
28 require_once DOL_DOCUMENT_ROOT.
'/core/lib/date.lib.php';
41 public $element =
'bom';
46 public $table_element =
'bom_bom';
51 public $ismultientitymanaged = 1;
56 public $isextrafieldmanaged = 1;
61 public $picto =
'bom';
64 const STATUS_DRAFT = 0;
65 const STATUS_VALIDATED = 1;
66 const STATUS_CANCELED = 9;
99 public $fields = array(
100 'rowid' => array(
'type'=>
'integer',
'label'=>
'TechnicalID',
'enabled'=>1,
'visible'=>-2,
'position'=>1,
'notnull'=>1,
'index'=>1,
'comment'=>
"Id",),
101 'entity' => array(
'type'=>
'integer',
'label'=>
'Entity',
'enabled'=>1,
'visible'=>0,
'notnull'=> 1,
'default'=>1,
'index'=>1,
'position'=>5),
102 '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'),
103 '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'),
104 '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'),
106 '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'),
107 'description' => array(
'type'=>
'text',
'label'=>
'Description',
'enabled'=>1,
'visible'=>-1,
'position'=>60,
'notnull'=>-1,),
108 'qty' => array(
'type'=>
'real',
'label'=>
'Quantity',
'enabled'=>1,
'visible'=>1,
'default'=>1,
'position'=>55,
'notnull'=>1,
'isameasure'=>
'1',
'css'=>
'maxwidth50imp right'),
110 'duration' => array(
'type'=>
'duration',
'label'=>
'EstimatedDuration',
'enabled'=>1,
'visible'=>-1,
'position'=>101,
'notnull'=>-1,
'css'=>
'maxwidth50imp',
'help'=>
'EstimatedDurationDesc'),
111 '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'),
112 'note_public' => array(
'type'=>
'html',
'label'=>
'NotePublic',
'enabled'=>1,
'visible'=>-2,
'position'=>161,
'notnull'=>-1,),
113 'note_private' => array(
'type'=>
'html',
'label'=>
'NotePrivate',
'enabled'=>1,
'visible'=>-2,
'position'=>162,
'notnull'=>-1,),
114 'date_creation' => array(
'type'=>
'datetime',
'label'=>
'DateCreation',
'enabled'=>1,
'visible'=>-2,
'position'=>300,
'notnull'=>1,),
115 'tms' => array(
'type'=>
'timestamp',
'label'=>
'DateModification',
'enabled'=>1,
'visible'=>-2,
'position'=>501,
'notnull'=>1,),
116 'date_valid' => array(
'type'=>
'datetime',
'label'=>
'DateValidation',
'enabled'=>1,
'visible'=>-2,
'position'=>502,
'notnull'=>0,),
117 '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'),
118 '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'),
119 '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'),
120 'import_key' => array(
'type'=>
'varchar(14)',
'label'=>
'ImportId',
'enabled'=>1,
'visible'=>-2,
'position'=>1000,
'notnull'=>-1,),
121 'model_pdf' =>array(
'type'=>
'varchar(255)',
'label'=>
'Model pdf',
'enabled'=>1,
'visible'=>0,
'position'=>1010),
122 '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')),
153 public $date_creation;
161 public $fk_user_creat;
166 public $fk_user_modif;
192 public $table_element_line =
'bom_bomline';
197 public $fk_element =
'fk_bom';
202 public $class_element_line =
'BOMLine';
212 protected $childtablesoncascade = array(
'bom_bomline');
217 public $lines = array();
222 public $total_cost = 0;
227 public $unit_cost = 0;
238 global $conf, $langs;
242 if (empty($conf->global->MAIN_SHOW_TECHNICAL_ID) && isset($this->fields[
'rowid'])) {
243 $this->fields[
'rowid'][
'visible'] = 0;
245 if (!
isModEnabled(
'multicompany') && isset($this->fields[
'entity'])) {
246 $this->fields[
'entity'][
'enabled'] = 0;
250 foreach ($this->fields as $key => $val) {
251 if (isset($val[
'enabled']) && empty($val[
'enabled'])) {
252 unset($this->fields[$key]);
257 foreach ($this->fields as $key => $val) {
258 if (!empty($val[
'arrayofkeyval']) && is_array($val[
'arrayofkeyval'])) {
259 foreach ($val[
'arrayofkeyval'] as $key2 => $val2) {
260 $this->fields[$key][
'arrayofkeyval'][$key2] = $langs->trans($val2);
275 if ($this->efficiency <= 0 || $this->efficiency > 1) {
276 $this->efficiency = 1;
291 global $langs, $hookmanager, $extrafields;
296 $object =
new self($this->db);
301 $result = $object->fetchCommon($fromid);
302 if ($result > 0 && !empty($object->table_element_line)) {
303 $object->fetchLines();
312 unset($object->fk_user_creat);
313 unset($object->import_key);
316 $object->ref = empty($this->fields[
'ref'][
'default']) ? $langs->trans(
"copy_of_").$object->ref : $this->fields[
'ref'][
'default'];
317 $object->label = empty($this->fields[
'label'][
'default']) ? $langs->trans(
"CopyOf").
" ".$object->label : $this->fields[
'label'][
'default'];
318 $object->status = self::STATUS_DRAFT;
321 if (is_array($object->array_options) && count($object->array_options) > 0) {
322 $extrafields->fetch_name_optionals_label($object->table_element);
323 foreach ($object->array_options as $key => $option) {
324 $shortkey = preg_replace(
'/options_/',
'', $key);
325 if (!empty($extrafields->attributes[$this->element][
'unique'][$shortkey])) {
327 unset($object->array_options[$key]);
333 $object->context[
'createfromclone'] =
'createfromclone';
334 $result = $object->createCommon($user);
337 $this->error = $object->error;
338 $this->errors = $object->errors;
350 if (property_exists($this,
'socid') && $this->socid == $object->socid) {
361 unset($object->context[
'createfromclone']);
368 $this->
db->rollback();
380 public function fetch($id, $ref =
null)
384 if ($result > 0 && !empty($this->table_element_line)) {
399 $this->lines = array();
414 $this->lines = array();
416 $objectlineclassname = get_class($this).
'Line';
417 if (!class_exists($objectlineclassname)) {
418 $this->error =
'Error, class '.$objectlineclassname.
' not found during call of fetchLinesCommon';
422 $objectline =
new $objectlineclassname($this->
db);
424 $sql =
"SELECT ".$objectline->getFieldList(
'l');
425 $sql .=
" FROM ".$this->db->prefix().$objectline->table_element.
" as l";
426 $sql .=
" LEFT JOIN ".$this->db->prefix().
"product as p ON p.rowid = l.fk_product";
427 $sql .=
" WHERE l.fk_".$this->db->escape($this->element).
" = ".((int) $this->
id);
428 $sql .=
" AND p.fk_product_type = ". ((int) $typeproduct);
429 if (isset($objectline->fields[
'position'])) {
430 $sql .= $this->
db->order(
'position',
'ASC');
435 $num_rows = $this->
db->num_rows(
$resql);
437 while ($i < $num_rows) {
438 $obj = $this->
db->fetch_object(
$resql);
440 $newline =
new $objectlineclassname($this->
db);
441 $newline->setVarsFromFetchObj($obj);
443 $this->lines[] = $newline;
450 $this->error = $this->
db->lasterror();
451 $this->errors[] = $this->error;
468 public function fetchAll($sortorder =
'', $sortfield =
'', $limit = 0, $offset = 0, array $filter = array(), $filtermode =
'AND')
478 $sql .=
' FROM '.MAIN_DB_PREFIX.$this->table_element.
' as t';
479 if ($this->ismultientitymanaged) {
480 $sql .=
' WHERE t.entity IN ('.getEntity($this->element).
')';
482 $sql .=
' WHERE 1 = 1';
486 if (count($filter) > 0) {
487 foreach ($filter as $key => $value) {
488 if ($key ==
't.rowid') {
489 $sqlwhere[] = $key.
" = ".((int) $value);
490 } elseif (strpos($key,
'date') !==
false) {
491 $sqlwhere[] = $key.
" = '".$this->
db->idate($value).
"'";
492 } elseif ($key ==
'customsql') {
493 $sqlwhere[] = $value;
495 $sqlwhere[] = $key.
" LIKE '%".$this->
db->escape($value).
"%'";
499 if (count($sqlwhere) > 0) {
500 $sql .=
" AND (".implode(
" ".$filtermode.
" ", $sqlwhere).
")";
503 if (!empty($sortfield)) {
504 $sql .= $this->
db->order($sortfield, $sortorder);
506 if (!empty($limit)) {
507 $sql .= $this->
db->plimit($limit, $offset);
514 while ($obj = $this->
db->fetch_object(
$resql)) {
515 $record =
new self($this->db);
516 $record->setVarsFromFetchObj($obj);
518 $records[$record->id] = $record;
524 $this->errors[] =
'Error '.$this->db->lasterror();
525 dol_syslog(__METHOD__.
' '.join(
',', $this->errors), LOG_ERR);
540 if ($this->efficiency <= 0 || $this->efficiency > 1) {
541 $this->efficiency = 1;
554 public function delete(
User $user, $notrigger =
false)
575 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)
577 global $mysoc, $conf, $langs, $user;
579 $logtext =
"::addLine bomid=$this->id, qty=$qty, fk_product=$fk_product, qty_frozen=$qty_frozen, disable_stock_change=$disable_stock_change, efficiency=$efficiency";
580 $logtext .=
", fk_bom_child=$fk_bom_child, import_key=$import_key";
581 dol_syslog(get_class($this).$logtext, LOG_DEBUG);
583 if ($this->statut == self::STATUS_DRAFT) {
584 include_once DOL_DOCUMENT_ROOT.
'/core/lib/price.lib.php';
590 if (empty($qty_frozen)) {
593 if (empty($disable_stock_change)) {
594 $disable_stock_change = 0;
596 if (empty($efficiency)) {
599 if (empty($fk_bom_child)) {
600 $fk_bom_child =
null;
602 if (empty($import_key)) {
605 if (empty($position)) {
617 $rankToUse = $position;
618 if ($rankToUse <= 0 or $rankToUse > $rangMax) {
619 $rankToUse = $rangMax + 1;
621 foreach ($this->lines as $bl) {
622 if ($bl->position >= $rankToUse) {
632 $this->line->context = $this->context;
634 $this->line->fk_bom = $this->id;
635 $this->line->fk_product = $fk_product;
636 $this->line->qty = $qty;
637 $this->line->qty_frozen = $qty_frozen;
638 $this->line->disable_stock_change = $disable_stock_change;
639 $this->line->efficiency = $efficiency;
640 $this->line->fk_bom_child = $fk_bom_child;
641 $this->line->import_key = $import_key;
642 $this->line->position = $rankToUse;
643 $this->line->fk_unit = $fk_unit;
645 if (is_array($array_options) && count($array_options) > 0) {
646 $this->line->array_options = $array_options;
649 $result = $this->line->create($user);
656 $this->error = $this->line->error;
657 dol_syslog(get_class($this).
"::addLine error=".$this->error, LOG_ERR);
658 $this->
db->rollback();
662 dol_syslog(get_class($this).
"::addLine status of BOM must be Draft to allow use of ->addLine()", LOG_ERR);
681 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)
683 global $mysoc, $conf, $langs, $user;
685 $logtext =
"::updateLine bomid=$this->id, qty=$qty, qty_frozen=$qty_frozen, disable_stock_change=$disable_stock_change, efficiency=$efficiency";
686 $logtext .=
", import_key=$import_key";
687 dol_syslog(get_class($this).$logtext, LOG_DEBUG);
689 if ($this->statut == self::STATUS_DRAFT) {
690 include_once DOL_DOCUMENT_ROOT.
'/core/lib/price.lib.php';
696 if (empty($qty_frozen)) {
699 if (empty($disable_stock_change)) {
700 $disable_stock_change = 0;
702 if (empty($efficiency)) {
705 if (empty($import_key)) {
708 if (empty($position)) {
720 $line->fetch($rowid);
721 $line->fetch_optionals();
723 $staticLine = clone $line;
724 $line->oldcopy = $staticLine;
726 $this->line->context = $this->context;
729 $rankToUse = (int) $position;
730 if ($rankToUse != $line->oldcopy->position) {
731 foreach ($this->lines as $bl) {
732 if ($bl->position >= $rankToUse AND $bl->position < ($line->oldcopy->position + 1)) {
736 if ($bl->position <= $rankToUse AND $bl->position > ($line->oldcopy->position)) {
744 $this->line->fk_bom = $this->id;
745 $this->line->qty = $qty;
746 $this->line->qty_frozen = $qty_frozen;
747 $this->line->disable_stock_change = $disable_stock_change;
748 $this->line->efficiency = $efficiency;
749 $this->line->import_key = $import_key;
750 $this->line->position = $rankToUse;
751 if (!empty($fk_unit)) {
752 $this->line->fk_unit = $fk_unit;
755 if (is_array($array_options) && count($array_options) > 0) {
757 foreach ($array_options as $key => $value) {
758 $this->line->array_options[$key] = $array_options[$key];
762 $result = $this->line->update($user);
769 $this->error = $this->line->error;
770 dol_syslog(get_class($this).
"::addLine error=".$this->error, LOG_ERR);
771 $this->
db->rollback();
775 dol_syslog(get_class($this).
"::addLine status of BOM must be Draft to allow use of ->addLine()", LOG_ERR);
790 if ($this->status < 0) {
791 $this->error =
'ErrorDeleteLineNotAllowedByObjectStatus';
799 $line->fetch($idline);
800 $line->fetch_optionals();
802 $staticLine = clone $line;
803 $line->oldcopy = $staticLine;
805 $this->line->context = $this->context;
807 $result = $this->line->delete($user, $notrigger);
810 foreach ($this->lines as $bl) {
811 if ($bl->position > ($line->oldcopy->position)) {
822 $this->error = $this->line->error;
823 dol_syslog(get_class($this).
"::addLine error=".$this->error, LOG_ERR);
824 $this->
db->rollback();
838 global $langs, $conf;
841 if (!empty($conf->global->BOM_ADDON)) {
844 $file = $conf->global->BOM_ADDON.
".php";
845 $classname = $conf->global->BOM_ADDON;
848 $dirmodels = array_merge(array(
'/'), (array) $conf->modules_parts[
'models']);
849 foreach ($dirmodels as $reldir) {
853 $mybool |= @include_once $dir.$file;
856 if ($mybool ===
false) {
861 $obj =
new $classname();
862 $numref = $obj->getNextValue($prod, $this);
867 $this->error = $obj->error;
872 print $langs->trans(
"Error").
" ".$langs->trans(
"Error_BOM_ADDON_NotDefined");
886 global $conf, $langs;
888 require_once DOL_DOCUMENT_ROOT.
'/core/lib/files.lib.php';
893 if ($this->status == self::STATUS_VALIDATED) {
894 dol_syslog(get_class($this).
"::validate action abandonned: already validated", LOG_WARNING);
911 if (!$error && (preg_match(
'/^[\(]?PROV/i', $this->
ref) || empty($this->
ref))) {
920 $sql =
"UPDATE ".MAIN_DB_PREFIX.$this->table_element;
921 $sql .=
" SET ref = '".$this->db->escape($num).
"',";
922 $sql .=
" status = ".self::STATUS_VALIDATED.
",";
923 $sql .=
" date_valid='".$this->db->idate($now).
"',";
924 $sql .=
" fk_user_valid = ".((int) $user->id);
925 $sql .=
" WHERE rowid = ".((int) $this->
id);
927 dol_syslog(get_class($this).
"::validate()", LOG_DEBUG);
931 $this->error = $this->
db->lasterror();
935 if (!$error && !$notrigger) {
945 $this->oldref = $this->ref;
948 if (preg_match(
'/^[\(]?PROV/i', $this->
ref)) {
950 $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).
"'";
951 $sql .=
" WHERE filename LIKE '".$this->db->escape($this->
ref).
"%' AND filepath = 'bom/".$this->
db->escape($this->
ref).
"' and entity = ".$conf->entity;
954 $error++; $this->error = $this->
db->lasterror();
960 $dirsource = $conf->bom->dir_output.
'/'.$oldref;
961 $dirdest = $conf->bom->dir_output.
'/'.$newref;
962 if (!$error && file_exists($dirsource)) {
963 dol_syslog(get_class($this).
"::validate() rename dir ".$dirsource.
" into ".$dirdest);
965 if (@rename($dirsource, $dirdest)) {
968 $listoffiles =
dol_dir_list($conf->bom->dir_output.
'/'.$newref,
'files', 1,
'^'.preg_quote($oldref,
'/'));
969 foreach ($listoffiles as $fileentry) {
970 $dirsource = $fileentry[
'name'];
971 $dirdest = preg_replace(
'/^'.preg_quote($oldref,
'/').
'/', $newref, $dirsource);
972 $dirsource = $fileentry[
'path'].
'/'.$dirsource;
973 $dirdest = $fileentry[
'path'].
'/'.$dirdest;
974 @rename($dirsource, $dirdest);
984 $this->status = self::STATUS_VALIDATED;
991 $this->
db->rollback();
1006 if ($this->status <= self::STATUS_DRAFT) {
1017 return $this->
setStatusCommon($user, self::STATUS_DRAFT, $notrigger,
'BOM_UNVALIDATE');
1027 public function cancel($user, $notrigger = 0)
1030 if ($this->status != self::STATUS_VALIDATED) {
1041 return $this->
setStatusCommon($user, self::STATUS_CANCELED, $notrigger,
'BOM_CLOSE');
1051 public function reopen($user, $notrigger = 0)
1054 if ($this->status != self::STATUS_CANCELED) {
1065 return $this->
setStatusCommon($user, self::STATUS_VALIDATED, $notrigger,
'BOM_REOPEN');
1079 public function getNomUrl($withpicto = 0, $option =
'', $notooltip = 0, $morecss =
'', $save_lastsearch_value = -1)
1081 global $db, $conf, $langs, $hookmanager;
1083 if (!empty($conf->dol_no_mouse_hover)) {
1089 $label =
img_picto(
'', $this->picto).
' <u class="paddingrightonly">'.$langs->trans(
"BillOfMaterials").
'</u>';
1090 if (isset($this->status)) {
1091 $label .=
' '.$this->getLibStatut(5);
1094 $label .=
'<b>'.$langs->trans(
'Ref').
':</b> '.$this->ref;
1095 if (isset($this->label)) {
1096 $label .=
'<br><b>'.$langs->trans(
'Label').
':</b> '.$this->label;
1098 if (!empty($this->fk_product) && $this->fk_product > 0) {
1099 include_once DOL_DOCUMENT_ROOT.
'/product/class/product.class.php';
1101 $resultFetch = $product->fetch($this->fk_product);
1102 if ($resultFetch > 0) {
1103 $label .=
"<br><b>".$langs->trans(
"Product").
'</b>: '.$product->ref.
' - '.$product->label;
1108 $url = DOL_URL_ROOT.
'/bom/bom_card.php?id='.$this->id;
1110 if ($option !=
'nolink') {
1112 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
1113 if ($save_lastsearch_value == -1 && preg_match(
'/list\.php/', $_SERVER[
"PHP_SELF"])) {
1114 $add_save_lastsearch_values = 1;
1116 if ($add_save_lastsearch_values) {
1117 $url .=
'&save_lastsearch_values=1';
1122 if (empty($notooltip)) {
1123 if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
1124 $label = $langs->trans(
"ShowBillOfMaterials");
1125 $linkclose .=
' alt="'.dol_escape_htmltag($label, 1).
'"';
1127 $linkclose .=
' title="'.dol_escape_htmltag($label, 1).
'"';
1128 $linkclose .=
' class="classfortooltip'.($morecss ?
' '.$morecss :
'').
'"';
1130 $linkclose = ($morecss ?
' class="'.$morecss.
'"' :
'');
1133 $linkstart =
'<a href="'.$url.
'"';
1134 $linkstart .= $linkclose.
'>';
1137 $result .= $linkstart;
1139 $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);
1141 if ($withpicto != 2) {
1142 $result .= $this->ref;
1144 $result .= $linkend;
1147 global $action, $hookmanager;
1148 $hookmanager->initHooks(array(
'bomdao'));
1149 $parameters = array(
'id'=>$this->
id,
'getnomurl' => &$result);
1150 $reshook = $hookmanager->executeHooks(
'getNomUrl', $parameters, $this, $action);
1152 $result = $hookmanager->resPrint;
1154 $result .= $hookmanager->resPrint;
1168 return $this->
LibStatut($this->status, $mode);
1182 if (empty($this->labelStatus)) {
1185 $this->labelStatus[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv(
'Draft');
1186 $this->labelStatus[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv(
'Enabled');
1187 $this->labelStatus[self::STATUS_CANCELED] = $langs->transnoentitiesnoconv(
'Disabled');
1190 $statusType =
'status'.$status;
1191 if ($status == self::STATUS_VALIDATED) {
1192 $statusType =
'status4';
1194 if ($status == self::STATUS_CANCELED) {
1195 $statusType =
'status6';
1198 return dolGetStatus($this->labelStatus[$status], $this->labelStatus[$status],
'', $statusType, $mode);
1209 $sql =
'SELECT rowid, date_creation as datec, tms as datem,';
1210 $sql .=
' fk_user_creat, fk_user_modif';
1211 $sql .=
' FROM '.MAIN_DB_PREFIX.$this->table_element.
' as t';
1212 $sql .=
' WHERE t.rowid = '.((int) $id);
1213 $result = $this->
db->query($sql);
1215 if ($this->
db->num_rows($result)) {
1216 $obj = $this->
db->fetch_object($result);
1217 $this->
id = $obj->rowid;
1219 $this->user_creation_id = $obj->fk_user_creat;
1220 $this->user_modification_id = $obj->fk_user_modif;
1221 $this->date_creation = $this->
db->jdate($obj->datec);
1222 $this->date_modification = empty($obj->datem) ?
'' : $this->
db->jdate($obj->datem);
1225 $this->
db->free($result);
1238 $this->lines = array();
1241 $result = $objectline->fetchAll(
'ASC',
'position', 0, 0, array(
'customsql'=>
'fk_bom = '.((
int) $this->
id)));
1243 if (is_numeric($result)) {
1244 $this->error = $objectline->error;
1245 $this->errors = $objectline->errors;
1248 $this->lines = $result;
1249 return $this->lines;
1264 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams =
null)
1266 global $conf, $langs;
1268 $langs->load(
"mrp");
1269 $outputlangs->load(
"products");
1274 if ($this->model_pdf) {
1275 $modele = $this->model_pdf;
1276 } elseif (!empty($conf->global->BOM_ADDON_PDF)) {
1277 $modele = $conf->global->BOM_ADDON_PDF;
1281 $modelpath =
"core/modules/bom/doc/";
1282 if (!empty($modele)) {
1283 return $this->
commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
1298 $this->
ref =
'BOM-123';
1299 $this->date = $this->date_creation;
1311 global $conf, $langs;
1327 $this->
db->commit();
1340 global $conf, $hookmanager;
1342 include_once DOL_DOCUMENT_ROOT.
'/product/class/product.class.php';
1343 $this->unit_cost = 0;
1344 $this->total_cost = 0;
1346 $parameters=array();
1347 $reshook = $hookmanager->executeHooks(
'calculateCostsBom', $parameters, $this);
1350 return $hookmanager->resPrint;
1353 if (is_array($this->lines) && count($this->lines)) {
1354 require_once DOL_DOCUMENT_ROOT.
'/fourn/class/fournisseur.product.class.php';
1358 foreach ($this->lines as &$line) {
1359 $tmpproduct->cost_price = 0;
1360 $tmpproduct->pmp = 0;
1361 $result = $tmpproduct->fetch($line->fk_product,
'',
'',
'', 0, 1, 1);
1363 if ($tmpproduct->type == $tmpproduct::TYPE_PRODUCT) {
1364 if (empty($line->fk_bom_child)) {
1366 $this->error = $tmpproduct->error;
1369 $line->unit_cost =
price2num((!empty($tmpproduct->cost_price)) ? $tmpproduct->cost_price : $tmpproduct->pmp);
1370 if (empty($line->unit_cost)) {
1371 if ($productFournisseur->find_min_price_product_fournisseur($line->fk_product) > 0) {
1372 if ($productFournisseur->fourn_remise_percent !=
"0") {
1373 $line->unit_cost = $productFournisseur->fourn_unitprice_with_discount;
1375 $line->unit_cost = $productFournisseur->fourn_unitprice;
1380 $line->total_cost =
price2num($line->qty * $line->unit_cost,
'MT');
1382 $this->total_cost += $line->total_cost;
1384 $bom_child =
new BOM($this->
db);
1385 $res = $bom_child->fetch($line->fk_bom_child);
1387 $bom_child->calculateCosts();
1388 $line->childBom[] = $bom_child;
1389 $this->total_cost +=
price2num($bom_child->total_cost * $line->qty,
'MT');
1390 $this->total_cost += $line->total_cost;
1392 $this->error = $bom_child->error;
1401 if (
isModEnabled(
'workstation') && !empty($tmpproduct->fk_default_workstation)) {
1403 $res = $workstation->fetch($tmpproduct->fk_default_workstation);
1405 if ($res > 0) $line->total_cost =
price2num($qtyhourforline * ($workstation->thm_operator_estimated + $workstation->thm_machine_estimated),
'MT');
1407 $this->error = $workstation->error;
1411 $defaultdurationofservice = $tmpproduct->duration;
1413 $qtyhourservice = 0;
1414 if (preg_match(
'/^(\d+)([a-z]+)$/', $defaultdurationofservice, $reg)) {
1418 if ($qtyhourservice) {
1419 $line->total_cost =
price2num($qtyhourforline / $qtyhourservice * $tmpproduct->cost_price,
'MT');
1421 $line->total_cost =
price2num($line->qty * $tmpproduct->cost_price,
'MT');
1425 $this->total_cost += $line->total_cost;
1429 $this->total_cost =
price2num($this->total_cost,
'MT');
1431 if ($this->qty > 0) {
1432 $this->unit_cost =
price2num($this->total_cost / $this->qty,
'MU');
1433 } elseif ($this->qty < 0) {
1434 $this->unit_cost =
price2num($this->total_cost * $this->qty,
'MU');
1465 if (!empty($this->lines)) {
1466 foreach ($this->lines as $line) {
1467 if (!empty($line->childBom)) {
1468 foreach ($line->childBom as $childBom) $childBom->getNetNeeds($TNetNeeds, $line->qty*$qty);
1470 if (empty($TNetNeeds[$line->fk_product])) {
1471 $TNetNeeds[$line->fk_product] = 0;
1473 $TNetNeeds[$line->fk_product] += $line->qty*$qty;
1489 if (!empty($this->lines)) {
1490 foreach ($this->lines as $line) {
1491 if (!empty($line->childBom)) {
1492 foreach ($line->childBom as $childBom) {
1493 $TNetNeeds[$childBom->id][
'bom'] = $childBom;
1494 $TNetNeeds[$childBom->id][
'parentid'] = $this->id;
1495 $TNetNeeds[$childBom->id][
'qty'] = $line->qty*$qty;
1496 $TNetNeeds[$childBom->id][
'level'] = $level;
1497 $childBom->getNetNeedsTree($TNetNeeds, $line->qty*$qty, $level+1);
1500 $TNetNeeds[$this->id][
'product'][$line->fk_product][
'qty'] += $line->qty * $qty;
1501 $TNetNeeds[$this->id][
'product'][$line->fk_product][
'level'] = $level;
1519 if ($level > 1000) {
1523 if (empty($bom_id)) $bom_id=$this->id;
1525 $sql =
'SELECT l.fk_bom, b.label
1526 FROM '.MAIN_DB_PREFIX.
'bom_bomline l
1527 INNER JOIN '.MAIN_DB_PREFIX.$this->table_element.
' b ON b.rowid = l.fk_bom
1528 WHERE fk_bom_child = '.((int) $bom_id);
1532 while ($res = $this->
db->fetch_object(
$resql)) {
1533 $TParentBom[$res->fk_bom] = $res->fk_bom;
1549 public $element =
'bomline';
1554 public $table_element =
'bom_bomline';
1559 public $ismultientitymanaged = 0;
1564 public $isextrafieldmanaged = 1;
1569 public $picto =
'bomline';
1595 public $fields = array(
1596 'rowid' => array(
'type'=>
'integer',
'label'=>
'LineID',
'enabled'=>1,
'visible'=>-1,
'position'=>1,
'notnull'=>1,
'index'=>1,
'comment'=>
"Id",),
1597 'fk_bom' => array(
'type'=>
'integer:BillOfMaterials:societe/class/bom.class.php',
'label'=>
'BillOfMaterials',
'enabled'=>1,
'visible'=>1,
'position'=>10,
'notnull'=>1,
'index'=>1,),
1598 'fk_product' => array(
'type'=>
'integer:Product:product/class/product.class.php',
'label'=>
'Product',
'enabled'=>1,
'visible'=>1,
'position'=>20,
'notnull'=>1,
'index'=>1,),
1599 'fk_bom_child' => array(
'type'=>
'integer:BOM:bom/class/bom.class.php',
'label'=>
'BillOfMaterials',
'enabled'=>1,
'visible'=>-1,
'position'=>40,
'notnull'=>-1,),
1600 'description' => array(
'type'=>
'text',
'label'=>
'Description',
'enabled'=>1,
'visible'=>-1,
'position'=>60,
'notnull'=>-1,),
1601 'qty' => array(
'type'=>
'double(24,8)',
'label'=>
'Quantity',
'enabled'=>1,
'visible'=>1,
'position'=>100,
'notnull'=>1,
'isameasure'=>
'1',),
1602 'qty_frozen' => array(
'type'=>
'smallint',
'label'=>
'QuantityFrozen',
'enabled'=>1,
'visible'=>1,
'default'=>0,
'position'=>105,
'css'=>
'maxwidth50imp',
'help'=>
'QuantityConsumedInvariable'),
1603 'disable_stock_change' => array(
'type'=>
'smallint',
'label'=>
'DisableStockChange',
'enabled'=>1,
'visible'=>1,
'default'=>0,
'position'=>108,
'css'=>
'maxwidth50imp',
'help'=>
'DisableStockChangeHelp'),
1604 'efficiency' => array(
'type'=>
'double(24,8)',
'label'=>
'ManufacturingEfficiency',
'enabled'=>1,
'visible'=>0,
'default'=>1,
'position'=>110,
'notnull'=>1,
'css'=>
'maxwidth50imp',
'help'=>
'ValueOfEfficiencyConsumedMeans'),
1605 'fk_unit' => array(
'type'=>
'integer',
'label'=>
'Unit',
'enabled'=>1,
'visible'=>1,
'position'=>120,
'notnull'=>-1,),
1606 'position' => array(
'type'=>
'integer',
'label'=>
'Rank',
'enabled'=>1,
'visible'=>0,
'default'=>0,
'position'=>200,
'notnull'=>1,),
1607 'import_key' => array(
'type'=>
'varchar(14)',
'label'=>
'ImportId',
'enabled'=>1,
'visible'=>-2,
'position'=>1000,
'notnull'=>-1,),
1628 public $fk_bom_child;
1633 public $description;
1640 public $disable_stock_change;
1657 public $total_cost = 0;
1662 public $unit_cost = 0;
1668 public $childBom = array();
1678 global $conf, $langs;
1682 if (empty($conf->global->MAIN_SHOW_TECHNICAL_ID) && isset($this->fields[
'rowid'])) {
1683 $this->fields[
'rowid'][
'visible'] = 0;
1685 if (!
isModEnabled(
'multicompany') && isset($this->fields[
'entity'])) {
1686 $this->fields[
'entity'][
'enabled'] = 0;
1690 foreach ($this->fields as $key => $val) {
1691 if (isset($val[
'enabled']) && empty($val[
'enabled'])) {
1692 unset($this->fields[$key]);
1697 foreach ($this->fields as $key => $val) {
1698 if (!empty($val[
'arrayofkeyval']) && is_array($val[
'arrayofkeyval'])) {
1699 foreach ($val[
'arrayofkeyval'] as $key2 => $val2) {
1700 $this->fields[$key][
'arrayofkeyval'][$key2] = $langs->trans($val2);
1715 if ($this->efficiency < 0 || $this->efficiency > 1) {
1716 $this->efficiency = 1;
1747 public function fetchAll($sortorder =
'', $sortfield =
'', $limit = 0, $offset = 0, array $filter = array(), $filtermode =
'AND')
1757 $sql .=
' FROM '.MAIN_DB_PREFIX.$this->table_element.
' as t';
1758 if ($this->ismultientitymanaged) {
1759 $sql .=
' WHERE t.entity IN ('.getEntity($this->element).
')';
1761 $sql .=
' WHERE 1 = 1';
1764 $sqlwhere = array();
1765 if (count($filter) > 0) {
1766 foreach ($filter as $key => $value) {
1767 if ($key ==
't.rowid') {
1768 $sqlwhere[] = $key.
" = ".((int) $value);
1769 } elseif (strpos($key,
'date') !==
false) {
1770 $sqlwhere[] = $key.
" = '".$this->
db->idate($value).
"'";
1771 } elseif ($key ==
'customsql') {
1772 $sqlwhere[] = $value;
1774 $sqlwhere[] = $key.
" LIKE '%".$this->
db->escape($value).
"%'";
1778 if (count($sqlwhere) > 0) {
1779 $sql .=
' AND ('.implode(
' '.$this->
db->escape($filtermode).
' ', $sqlwhere).
')';
1782 if (!empty($sortfield)) {
1783 $sql .= $this->
db->order($sortfield, $sortorder);
1785 if (!empty($limit)) {
1786 $sql .= $this->
db->plimit($limit, $offset);
1791 $num = $this->
db->num_rows(
$resql);
1793 while ($obj = $this->
db->fetch_object(
$resql)) {
1794 $record =
new self($this->db);
1795 $record->setVarsFromFetchObj($obj);
1797 $records[$record->id] = $record;
1803 $this->errors[] =
'Error '.$this->db->lasterror();
1804 dol_syslog(__METHOD__.
' '.join(
',', $this->errors), LOG_ERR);
1819 if ($this->efficiency < 0 || $this->efficiency > 1) {
1820 $this->efficiency = 1;
1833 public function delete(
User $user, $notrigger =
false)
1849 public function getNomUrl($withpicto = 0, $option =
'', $notooltip = 0, $morecss =
'', $save_lastsearch_value = -1)
1851 global $db, $conf, $langs, $hookmanager;
1853 if (!empty($conf->dol_no_mouse_hover)) {
1859 $label =
'<u>'.$langs->trans(
"BillOfMaterialsLine").
'</u>';
1861 $label .=
'<b>'.$langs->trans(
'Ref').
':</b> '.$this->ref;
1863 $url = DOL_URL_ROOT.
'/bom/bomline_card.php?id='.$this->id;
1865 if ($option !=
'nolink') {
1867 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
1868 if ($save_lastsearch_value == -1 && preg_match(
'/list\.php/', $_SERVER[
"PHP_SELF"])) {
1869 $add_save_lastsearch_values = 1;
1871 if ($add_save_lastsearch_values) {
1872 $url .=
'&save_lastsearch_values=1';
1877 if (empty($notooltip)) {
1878 if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
1879 $label = $langs->trans(
"ShowBillOfMaterialsLine");
1880 $linkclose .=
' alt="'.dol_escape_htmltag($label, 1).
'"';
1882 $linkclose .=
' title="'.dol_escape_htmltag($label, 1).
'"';
1883 $linkclose .=
' class="classfortooltip'.($morecss ?
' '.$morecss :
'').
'"';
1885 $linkclose = ($morecss ?
' class="'.$morecss.
'"' :
'');
1888 $linkstart =
'<a href="'.$url.
'"';
1889 $linkstart .= $linkclose.
'>';
1892 $result .= $linkstart;
1894 $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);
1896 if ($withpicto != 2) {
1897 $result .= $this->ref;
1899 $result .= $linkend;
1902 global $action, $hookmanager;
1903 $hookmanager->initHooks(array(
'bomlinedao'));
1904 $parameters = array(
'id'=>$this->
id,
'getnomurl' => &$result);
1905 $reshook = $hookmanager->executeHooks(
'getNomUrl', $parameters, $this, $action);
1907 $result = $hookmanager->resPrint;
1909 $result .= $hookmanager->resPrint;
1923 return $this->
LibStatut($this->status, $mode);
1948 $sql =
'SELECT rowid, date_creation as datec, tms as datem,';
1949 $sql .=
' fk_user_creat, fk_user_modif';
1950 $sql .=
' FROM '.MAIN_DB_PREFIX.$this->table_element.
' as t';
1951 $sql .=
' WHERE t.rowid = '.((int) $id);
1952 $result = $this->
db->query($sql);
1954 if ($this->
db->num_rows($result)) {
1955 $obj = $this->
db->fetch_object($result);
1956 $this->
id = $obj->rowid;
1957 $this->user_creation_id = $obj->fk_user_creat;
1958 $this->user_modification_id = $obj->fk_user_modif;
1959 $this->date_creation = $this->
db->jdate($obj->datec);
1960 $this->date_modification = empty($obj->datem) ?
'' : $this->
db->jdate($obj->datem);
1962 $this->
db->free($result);
static replaceProduct(DoliDB $db, $origin_id, $dest_id)
Function used to replace a product id with another one.
fetchLines()
Load object lines in memory from the database.
__construct(DoliDB $db)
Constructor.
calculateCosts()
BOM costs calculation based on cost_price or pmp of each BOM line.
info($id)
Load the info information in the object.
getLibStatut($mode=0)
Return label of the status.
initAsSpecimen()
Initialise object with example values Id must be 0 if object instance is a specimen.
validate($user, $notrigger=0)
Validate bom.
reopen($user, $notrigger=0)
Set cancel status.
fetchAll($sortorder='', $sortfield='', $limit=0, $offset=0, array $filter=array(), $filtermode='AND')
Load list of objects in memory from the database.
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)
Add an BOM line into database (linked to BOM)
cancel($user, $notrigger=0)
Set cancel status.
create(User $user, $notrigger=false)
Create object into database.
LibStatut($status, $mode=0)
Return the status.
doScheduledJob()
Action executed by scheduler CAN BE A CRON TASK.
fetchLinesbytypeproduct($typeproduct=0)
Load object lines in memory from the database by type of product.
createFromClone(User $user, $fromid)
Clone an object into another one.
fetch($id, $ref=null)
Load object in memory from the database.
getNomUrl($withpicto=0, $option='', $notooltip=0, $morecss='', $save_lastsearch_value=-1)
Return a link to the object card (with optionaly the picto)
getNetNeeds(&$TNetNeeds=array(), $qty=0)
Get Net needs by product.
getNextNumRef($prod)
Returns the reference to the following non used BOM depending on the active numbering module defined ...
generateDocument($modele, $outputlangs, $hidedetails=0, $hidedesc=0, $hideref=0, $moreparams=null)
Create a document onto disk according to template module.
getLinesArray()
Create an array of lines.
setDraft($user, $notrigger=0)
Set draft status.
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.
update(User $user, $notrigger=false)
Update object into database.
deleteLine(User $user, $idline, $notrigger=false)
Delete a line of object in database.
getParentBomTreeRecursive(&$TParentBom, $bom_id='', $level=1)
Recursively retrieves all parent bom in the tree that leads to the $bom_id bom.
getNetNeedsTree(&$TNetNeeds=array(), $qty=0, $level=0)
Get Net needs Tree by product or bom.
create(User $user, $notrigger=false)
Create object into database.
fetchAll($sortorder='', $sortfield='', $limit=0, $offset=0, array $filter=array(), $filtermode='AND')
Load list of objects in memory from the database.
fetch($id, $ref=null)
Load object in memory from the database.
getLibStatut($mode=0)
Return label of the status.
update(User $user, $notrigger=false)
Update object into database.
__construct(DoliDB $db)
Constructor.
getNomUrl($withpicto=0, $option='', $notooltip=0, $morecss='', $save_lastsearch_value=-1)
Return a link to the object card (with optionaly the picto)
initAsSpecimen()
Initialise object with example values Id must be 0 if object instance is a specimen.
info($id)
Load the info information in the object.
LibStatut($status, $mode=0)
Return the status.
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.
getFieldList($alias='')
Function to concat keys of fields.
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.
setStatusCommon($user, $status, $notrigger=0, $triggercode='')
Set to a status.
static commonReplaceProduct(DoliDB $db, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a product id with another one.
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.
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.
if(isModEnabled('facture') &&!empty($user->rights->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') &&!empty($user->rights->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)) $resql
Social contributions to pay.
convertDurationtoHour($duration_value, $duration_unit)
Convert duration to hour.
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.
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.
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.
$conf db
API class for accounts.