dolibarr  20.0.0-alpha
bom.class.php
Go to the documentation of this file.
1 <?php
2 /* Copyright (C) 2019 Laurent Destailleur <eldy@users.sourceforge.net>
3  * Copyright (C) 2023 Benjamin Falière <benjamin.faliere@altairis.fr>
4  * Copyright (C) 2023 Charlene Benke <charlene@patas-monkey.com>
5  * Copyright (C) 2024 Frédéric France <frederic.france@free.fr>
6  * Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program. If not, see <https://www.gnu.org/licenses/>.
20  */
21 
28 // Put here all includes required by your class file
29 require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
30 require_once DOL_DOCUMENT_ROOT.'/core/class/commonobjectline.class.php';
31 require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
32 
33 if (isModEnabled('workstation')) {
34  require_once DOL_DOCUMENT_ROOT.'/workstation/class/workstation.class.php';
35 }
36 
37 //require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php';
38 //require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
39 
40 
44 class BOM extends CommonObject
45 {
49  public $module = 'bom';
50 
54  public $element = 'bom';
55 
59  public $table_element = 'bom_bom';
60 
65  public $ismultientitymanaged = 1;
66 
70  public $isextrafieldmanaged = 1;
71 
75  public $picto = 'bom';
76 
80  public $product;
81 
82  const STATUS_DRAFT = 0;
83  const STATUS_VALIDATED = 1;
84  const STATUS_CANCELED = 9;
85 
86 
113  // BEGIN MODULEBUILDER PROPERTIES
117  public $fields = array(
118  'rowid' => array('type' => 'integer', 'label' => 'TechnicalID', 'enabled' => 1, 'visible' => -2, 'position' => 1, 'notnull' => 1, 'index' => 1, 'comment' => "Id",),
119  'entity' => array('type' => 'integer', 'label' => 'Entity', 'enabled' => 1, 'visible' => 0, 'notnull' => 1, 'default' => 1, 'index' => 1, 'position' => 5),
120  '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'),
121  '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'),
122  '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'),
123  //'bomtype' => array('type'=>'integer', 'label'=>'Type', 'enabled'=>1, 'visible'=>-1, 'position'=>32, 'notnull'=>1, 'default'=>'0', 'arrayofkeyval'=>array(0=>'Manufacturing')),
124  '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'),
125  'description' => array('type' => 'text', 'label' => 'Description', 'enabled' => 1, 'visible' => -1, 'position' => 60, 'notnull' => -1,),
126  'qty' => array('type' => 'real', 'label' => 'Quantity', 'enabled' => 1, 'visible' => 1, 'default' => 1, 'position' => 55, 'notnull' => 1, 'isameasure' => 1, 'css' => 'maxwidth50imp right'),
127  //'efficiency' => array('type'=>'real', 'label'=>'ManufacturingEfficiency', 'enabled'=>1, 'visible'=>-1, 'default'=>1, 'position'=>100, 'notnull'=>0, 'css'=>'maxwidth50imp', 'help'=>'ValueOfMeansLossForProductProduced'),
128  'duration' => array('type' => 'duration', 'label' => 'EstimatedDuration', 'enabled' => 1, 'visible' => -1, 'position' => 101, 'notnull' => -1, 'css' => 'maxwidth50imp', 'help' => 'EstimatedDurationDesc'),
129  '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'),
130  'note_public' => array('type' => 'html', 'label' => 'NotePublic', 'enabled' => 1, 'visible' => -2, 'position' => 161, 'notnull' => -1,),
131  'note_private' => array('type' => 'html', 'label' => 'NotePrivate', 'enabled' => 1, 'visible' => -2, 'position' => 162, 'notnull' => -1,),
132  'date_creation' => array('type' => 'datetime', 'label' => 'DateCreation', 'enabled' => 1, 'visible' => -2, 'position' => 300, 'notnull' => 1,),
133  'tms' => array('type' => 'timestamp', 'label' => 'DateModification', 'enabled' => 1, 'visible' => -2, 'position' => 501, 'notnull' => 1,),
134  'date_valid' => array('type' => 'datetime', 'label' => 'DateValidation', 'enabled' => 1, 'visible' => -2, 'position' => 502, 'notnull' => 0,),
135  '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'),
136  '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'),
137  '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'),
138  'import_key' => array('type' => 'varchar(14)', 'label' => 'ImportId', 'enabled' => 1, 'visible' => -2, 'position' => 1000, 'notnull' => -1,),
139  'model_pdf' => array('type' => 'varchar(255)', 'label' => 'Model pdf', 'enabled' => 1, 'visible' => 0, 'position' => 1010),
140  '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')),
141  );
142 
146  public $rowid;
147 
151  public $ref;
152 
156  public $label;
157 
161  public $bomtype;
162 
166  public $description;
167 
171  public $date_creation;
172 
176  public $date_valid;
177 
181  public $fk_user_creat;
182 
186  public $fk_user_modif;
187 
191  public $fk_user_valid;
192 
196  public $fk_warehouse;
197 
201  public $import_key;
202 
206  public $status;
207 
211  public $fk_product;
212  public $qty;
213  public $duration;
214  public $efficiency;
215  // END MODULEBUILDER PROPERTIES
216 
217 
218  // If this object has a subtable with lines
219 
223  public $table_element_line = 'bom_bomline';
224 
228  public $fk_element = 'fk_bom';
229 
233  public $class_element_line = 'BOMLine';
234 
235  // /**
236  // * @var array List of child tables. To test if we can delete object.
237  // */
238  // protected $childtables=array();
239 
243  protected $childtablesoncascade = array('bom_bomline');
244 
248  public $lines = array();
249 
253  public $total_cost = 0;
254 
258  public $unit_cost = 0;
259 
260 
266  public function __construct(DoliDB $db)
267  {
268  global $conf, $langs;
269 
270  $this->db = $db;
271 
272  if (!getDolGlobalString('MAIN_SHOW_TECHNICAL_ID') && isset($this->fields['rowid'])) {
273  $this->fields['rowid']['visible'] = 0;
274  }
275  if (!isModEnabled('multicompany') && isset($this->fields['entity'])) {
276  $this->fields['entity']['enabled'] = 0;
277  }
278 
279  // Unset fields that are disabled
280  foreach ($this->fields as $key => $val) {
281  if (isset($val['enabled']) && empty($val['enabled'])) {
282  unset($this->fields[$key]);
283  }
284  }
285 
286  // Translate some data of arrayofkeyval
287  foreach ($this->fields as $key => $val) {
288  if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
289  foreach ($val['arrayofkeyval'] as $key2 => $val2) {
290  $this->fields[$key]['arrayofkeyval'][$key2] = $langs->trans($val2);
291  }
292  }
293  }
294  }
295 
303  public function create(User $user, $notrigger = 1)
304  {
305  if ($this->efficiency <= 0 || $this->efficiency > 1) {
306  $this->efficiency = 1;
307  }
308 
309  return $this->createCommon($user, $notrigger);
310  }
311 
319  public function createFromClone(User $user, $fromid)
320  {
321  global $langs, $hookmanager, $extrafields;
322  $error = 0;
323 
324  dol_syslog(__METHOD__, LOG_DEBUG);
325 
326  $object = new self($this->db);
327 
328  $this->db->begin();
329 
330  // Load source object
331  $result = $object->fetchCommon($fromid);
332  if ($result > 0 && !empty($object->table_element_line)) {
333  $object->fetchLines();
334  }
335 
336  // Get lines so they will be clone
337  //foreach ($object->lines as $line)
338  // $line->fetch_optionals();
339 
340  // Reset some properties
341  unset($object->id);
342  unset($object->fk_user_creat);
343  unset($object->import_key);
344 
345  // Clear fields
346  $object->ref = empty($this->fields['ref']['default']) ? $langs->trans("copy_of_").$object->ref : $this->fields['ref']['default'];
347  $object->label = empty($this->fields['label']['default']) ? $langs->trans("CopyOf")." ".$object->label : $this->fields['label']['default'];
348  $object->status = self::STATUS_DRAFT;
349  // ...
350  // Clear extrafields that are unique
351  if (is_array($object->array_options) && count($object->array_options) > 0) {
352  $extrafields->fetch_name_optionals_label($object->table_element);
353  foreach ($object->array_options as $key => $option) {
354  $shortkey = preg_replace('/options_/', '', $key);
355  if (!empty($extrafields->attributes[$this->element]['unique'][$shortkey])) {
356  //var_dump($key); var_dump($clonedObj->array_options[$key]); exit;
357  unset($object->array_options[$key]);
358  }
359  }
360  }
361 
362  // Create clone
363  $object->context['createfromclone'] = 'createfromclone';
364  $result = $object->createCommon($user);
365  if ($result < 0) {
366  $error++;
367  $this->error = $object->error;
368  $this->errors = $object->errors;
369  }
370 
371  if (!$error) {
372  // copy internal contacts
373  if ($this->copy_linked_contact($object, 'internal') < 0) {
374  $error++;
375  }
376  }
377 
378  if (!$error) {
379  // copy external contacts if same company
380  if (property_exists($this, 'socid') && $this->socid == $object->socid) {
381  if ($this->copy_linked_contact($object, 'external') < 0) {
382  $error++;
383  }
384  }
385  }
386 
387  // If there is lines, create lines too
388 
389 
390 
391  unset($object->context['createfromclone']);
392 
393  // End
394  if (!$error) {
395  $this->db->commit();
396  return $object;
397  } else {
398  $this->db->rollback();
399  return -1;
400  }
401  }
402 
410  public function fetch($id, $ref = null)
411  {
412  $result = $this->fetchCommon($id, $ref);
413 
414  if ($result > 0 && !empty($this->table_element_line)) {
415  $this->fetchLines();
416  }
417  //$this->calculateCosts(); // This consume a high number of subrequests. Do not call it into fetch but when you need it.
418 
419  return $result;
420  }
421 
427  public function fetchLines()
428  {
429  $this->lines = array();
430 
431  $result = $this->fetchLinesCommon();
432  return $result;
433  }
434 
442  public function fetchLinesbytypeproduct($typeproduct = 0)
443  {
444  $this->lines = array();
445 
446  $objectlineclassname = get_class($this).'Line';
447  if (!class_exists($objectlineclassname)) {
448  $this->error = 'Error, class '.$objectlineclassname.' not found during call of fetchLinesCommon';
449  return -1;
450  }
451 
452  $objectline = new $objectlineclassname($this->db);
453 
454  $sql = "SELECT ".$objectline->getFieldList('l');
455  $sql .= " FROM ".$this->db->prefix().$objectline->table_element." as l";
456  $sql .= " LEFT JOIN ".$this->db->prefix()."product as p ON p.rowid = l.fk_product";
457  $sql .= " WHERE l.fk_".$this->db->escape($this->element)." = ".((int) $this->id);
458  $sql .= " AND p.fk_product_type = ". ((int) $typeproduct);
459  if (isset($objectline->fields['position'])) {
460  $sql .= $this->db->order('position', 'ASC');
461  }
462 
463  $resql = $this->db->query($sql);
464  if ($resql) {
465  $num_rows = $this->db->num_rows($resql);
466  $i = 0;
467  while ($i < $num_rows) {
468  $obj = $this->db->fetch_object($resql);
469  if ($obj) {
470  $newline = new $objectlineclassname($this->db);
471  $newline->setVarsFromFetchObj($obj);
472 
473  $this->lines[] = $newline;
474  }
475  $i++;
476  }
477 
478  return $num_rows;
479  } else {
480  $this->error = $this->db->lasterror();
481  $this->errors[] = $this->error;
482  return -1;
483  }
484  }
485 
486 
498  public function fetchAll($sortorder = '', $sortfield = '', $limit = 0, $offset = 0, $filter = '', $filtermode = 'AND')
499  {
500  dol_syslog(__METHOD__, LOG_DEBUG);
501 
502  $records = array();
503 
504  $sql = 'SELECT ';
505  $sql .= $this->getFieldList();
506  $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as t';
507  if ($this->ismultientitymanaged) {
508  $sql .= ' WHERE t.entity IN ('.getEntity($this->element).')';
509  } else {
510  $sql .= ' WHERE 1 = 1';
511  }
512 
513  // Manage filter
514  $errormessage = '';
515  $sql .= forgeSQLFromUniversalSearchCriteria($filter, $errormessage);
516  if ($errormessage) {
517  $this->errors[] = $errormessage;
518  dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
519  return -1;
520  }
521 
522  if (!empty($sortfield)) {
523  $sql .= $this->db->order($sortfield, $sortorder);
524  }
525  if (!empty($limit)) {
526  $sql .= $this->db->plimit($limit, $offset);
527  }
528 
529  $resql = $this->db->query($sql);
530  if ($resql) {
531  $num = $this->db->num_rows($resql);
532 
533  while ($obj = $this->db->fetch_object($resql)) {
534  $record = new self($this->db);
535  $record->setVarsFromFetchObj($obj);
536 
537  $records[$record->id] = $record;
538  }
539  $this->db->free($resql);
540 
541  return $records;
542  } else {
543  $this->errors[] = 'Error '.$this->db->lasterror();
544  dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
545 
546  return -1;
547  }
548  }
549 
557  public function update(User $user, $notrigger = 1)
558  {
559  if ($this->efficiency <= 0 || $this->efficiency > 1) {
560  $this->efficiency = 1;
561  }
562 
563  return $this->updateCommon($user, $notrigger);
564  }
565 
573  public function delete(User $user, $notrigger = 1)
574  {
575  return $this->deleteCommon($user, $notrigger);
576  //return $this->deleteCommon($user, $notrigger, 1);
577  }
578 
595  public function addLine($fk_product, $qty, $qty_frozen = 0, $disable_stock_change = 0, $efficiency = 1.0, $position = -1, $fk_bom_child = null, $import_key = null, $fk_unit = 0, $array_options = array(), $fk_default_workstation = null)
596  {
597  global $mysoc, $conf, $langs, $user;
598 
599  $logtext = "::addLine bomid=$this->id, qty=$qty, fk_product=$fk_product, qty_frozen=$qty_frozen, disable_stock_change=$disable_stock_change, efficiency=$efficiency";
600  $logtext .= ", fk_bom_child=$fk_bom_child, import_key=$import_key";
601  dol_syslog(get_class($this).$logtext, LOG_DEBUG);
602 
603  if ($this->statut == self::STATUS_DRAFT) {
604  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
605 
606  // Clean parameters
607  if (empty($qty)) {
608  $qty = 0;
609  }
610  if (empty($qty_frozen)) {
611  $qty_frozen = 0;
612  }
613  if (empty($disable_stock_change)) {
614  $disable_stock_change = 0;
615  }
616  if (empty($efficiency)) {
617  $efficiency = 1.0;
618  }
619  if (empty($fk_bom_child)) {
620  $fk_bom_child = null;
621  }
622  if (empty($import_key)) {
623  $import_key = null;
624  }
625  if (empty($position)) {
626  $position = -1;
627  }
628 
629  $qty = (float) price2num($qty);
630  $efficiency = (float) price2num($efficiency);
631  $position = (float) price2num($position);
632 
633  $this->db->begin();
634 
635  // Rank to use
636  $rangMax = $this->line_max();
637  $rankToUse = $position;
638  if ($rankToUse <= 0 or $rankToUse > $rangMax) { // New line after existing lines
639  $rankToUse = $rangMax + 1;
640  } else { // New line between the existing lines
641  foreach ($this->lines as $bl) {
642  if ($bl->position >= $rankToUse) {
643  $bl->position++;
644  $bl->update($user);
645  }
646  }
647  }
648 
649  // Insert line
650  $line = new BOMLine($this->db);
651 
652  $line->context = $this->context;
653 
654  $line->fk_bom = $this->id;
655  $line->fk_product = $fk_product;
656  $line->qty = $qty;
657  $line->qty_frozen = $qty_frozen;
658  $line->disable_stock_change = $disable_stock_change;
659  $line->efficiency = $efficiency;
660  $line->fk_bom_child = $fk_bom_child;
661  $line->import_key = $import_key;
662  $line->position = $rankToUse;
663  $line->fk_unit = $fk_unit;
664  $line->fk_default_workstation = $fk_default_workstation;
665 
666  if (is_array($array_options) && count($array_options) > 0) {
667  $line->array_options = $array_options;
668  }
669 
670  $result = $line->create($user);
671 
672  if ($result > 0) {
673  $this->calculateCosts();
674  $this->db->commit();
675  return $result;
676  } else {
677  $this->setErrorsFromObject($line);
678  dol_syslog(get_class($this)."::addLine error=".$this->error, LOG_ERR);
679  $this->db->rollback();
680  return -2;
681  }
682  } else {
683  dol_syslog(get_class($this)."::addLine status of BOM must be Draft to allow use of ->addLine()", LOG_ERR);
684  return -3;
685  }
686  }
687 
703  public function updateLine($rowid, $qty, $qty_frozen = 0, $disable_stock_change = 0, $efficiency = 1.0, $position = -1, $import_key = null, $fk_unit = 0, $array_options = array(), $fk_default_workstation = null)
704  {
705  global $user;
706 
707  $logtext = "::updateLine bomid=$this->id, qty=$qty, qty_frozen=$qty_frozen, disable_stock_change=$disable_stock_change, efficiency=$efficiency";
708  $logtext .= ", import_key=$import_key";
709  dol_syslog(get_class($this).$logtext, LOG_DEBUG);
710 
711  if ($this->statut == self::STATUS_DRAFT) {
712  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
713 
714  // Clean parameters
715  if (empty($qty)) {
716  $qty = 0;
717  }
718  if (empty($qty_frozen)) {
719  $qty_frozen = 0;
720  }
721  if (empty($disable_stock_change)) {
722  $disable_stock_change = 0;
723  }
724  if (empty($efficiency)) {
725  $efficiency = 1.0;
726  }
727  if (empty($import_key)) {
728  $import_key = null;
729  }
730  if (empty($position)) {
731  $position = -1;
732  }
733 
734  $qty = (float) price2num($qty);
735  $efficiency = (float) price2num($efficiency);
736  $position = (float) price2num($position);
737 
738  $this->db->begin();
739 
740  //Fetch current line from the database and then clone the object and set it in $oldline property
741  $line = new BOMLine($this->db);
742  $line->fetch($rowid);
743  $line->fetch_optionals();
744 
745  $staticLine = clone $line;
746  $line->oldcopy = $staticLine;
747  $line->context = $this->context;
748 
749  // Rank to use
750  $rankToUse = (int) $position;
751  if ($rankToUse != $line->oldcopy->position) { // check if position have a new value
752  foreach ($this->lines as $bl) {
753  if ($bl->position >= $rankToUse and $bl->position < ($line->oldcopy->position + 1)) { // move rank up
754  $bl->position++;
755  $bl->update($user);
756  }
757  if ($bl->position <= $rankToUse and $bl->position > ($line->oldcopy->position)) { // move rank down
758  $bl->position--;
759  $bl->update($user);
760  }
761  }
762  }
763 
764 
765  $line->fk_bom = $this->id;
766  $line->qty = $qty;
767  $line->qty_frozen = $qty_frozen;
768  $line->disable_stock_change = $disable_stock_change;
769  $line->efficiency = $efficiency;
770  $line->import_key = $import_key;
771  $line->position = $rankToUse;
772 
773 
774  if (!empty($fk_unit)) {
775  $line->fk_unit = $fk_unit;
776  }
777 
778 
779  if (is_array($array_options) && count($array_options) > 0) {
780  // We replace values in this->line->array_options only for entries defined into $array_options
781  foreach ($array_options as $key => $value) {
782  $line->array_options[$key] = $array_options[$key];
783  }
784  }
785  if ($line->fk_default_workstation != $fk_default_workstation) {
786  $line->fk_default_workstation = ($fk_default_workstation > 0 ? $fk_default_workstation : 0);
787  }
788 
789  $result = $line->update($user);
790 
791  if ($result > 0) {
792  $this->calculateCosts();
793  $this->db->commit();
794  return $result;
795  } else {
796  $this->setErrorsFromObject($line);
797  dol_syslog(get_class($this)."::addLine error=".$this->error, LOG_ERR);
798  $this->db->rollback();
799  return -2;
800  }
801  } else {
802  dol_syslog(get_class($this)."::addLine status of BOM must be Draft to allow use of ->addLine()", LOG_ERR);
803  return -3;
804  }
805  }
806 
815  public function deleteLine(User $user, $idline, $notrigger = 0)
816  {
817  if ($this->status < 0) {
818  $this->error = 'ErrorDeleteLineNotAllowedByObjectStatus';
819  return -2;
820  }
821 
822  $this->db->begin();
823 
824  //Fetch current line from the database and then clone the object and set it in $oldline property
825  $line = new BOMLine($this->db);
826  $line->fetch($idline);
827  $line->fetch_optionals();
828 
829  $staticLine = clone $line;
830  $line->oldcopy = $staticLine;
831  $line->context = $this->context;
832 
833  $result = $line->delete($user, $notrigger);
834 
835  //Positions (rank) reordering
836  foreach ($this->lines as $bl) {
837  if ($bl->position > ($line->oldcopy->position)) { // move rank down
838  $bl->position--;
839  $bl->update($user);
840  }
841  }
842 
843  if ($result > 0) {
844  $this->calculateCosts();
845  $this->db->commit();
846  return $result;
847  } else {
848  $this->setErrorsFromObject($line);
849  dol_syslog(get_class($this)."::addLine error=".$this->error, LOG_ERR);
850  $this->db->rollback();
851  return -2;
852  }
853  }
854 
862  public function getNextNumRef($prod)
863  {
864  global $langs, $conf;
865  $langs->load("mrp");
866 
867  if (getDolGlobalString('BOM_ADDON')) {
868  $mybool = false;
869 
870  $file = getDolGlobalString('BOM_ADDON') . ".php";
871  $classname = getDolGlobalString('BOM_ADDON');
872 
873  // Include file with class
874  $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
875  foreach ($dirmodels as $reldir) {
876  $dir = dol_buildpath($reldir."core/modules/bom/");
877 
878  // Load file with numbering class (if found)
879  $mybool = ((bool) @include_once $dir.$file) || $mybool;
880  }
881 
882  if ($mybool === false) {
883  dol_print_error(null, "Failed to include file ".$file);
884  return '';
885  }
886 
887  $obj = new $classname();
888  $numref = $obj->getNextValue($prod, $this);
889 
890  if ($numref != "") {
891  return $numref;
892  } else {
893  $this->error = $obj->error;
894  //dol_print_error($this->db,get_class($this)."::getNextNumRef ".$obj->error);
895  return "";
896  }
897  } else {
898  print $langs->trans("Error")." ".$langs->trans("Error_BOM_ADDON_NotDefined");
899  return "";
900  }
901  }
902 
910  public function validate($user, $notrigger = 0)
911  {
912  global $conf;
913 
914  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
915 
916  $error = 0;
917 
918  // Protection
919  if ($this->status == self::STATUS_VALIDATED) {
920  dol_syslog(get_class($this)."::validate action abandoned: already validated", LOG_WARNING);
921  return 0;
922  }
923 
924  $now = dol_now();
925 
926  $this->db->begin();
927 
928  // Define new ref
929  if (!$error && (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref))) { // empty should not happened, but when it occurs, the test save life
930  $this->fetch_product();
931  $num = $this->getNextNumRef($this->product);
932  } else {
933  $num = $this->ref;
934  }
935  $this->newref = dol_sanitizeFileName($num);
936 
937  // Validate
938  $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
939  $sql .= " SET ref = '".$this->db->escape($num)."',";
940  $sql .= " status = ".self::STATUS_VALIDATED.",";
941  $sql .= " date_valid='".$this->db->idate($now)."',";
942  $sql .= " fk_user_valid = ".((int) $user->id);
943  $sql .= " WHERE rowid = ".((int) $this->id);
944 
945  dol_syslog(get_class($this)."::validate()", LOG_DEBUG);
946  $resql = $this->db->query($sql);
947  if (!$resql) {
948  dol_print_error($this->db);
949  $this->error = $this->db->lasterror();
950  $error++;
951  }
952 
953  if (!$error && !$notrigger) {
954  // Call trigger
955  $result = $this->call_trigger('BOM_VALIDATE', $user);
956  if ($result < 0) {
957  $error++;
958  }
959  // End call triggers
960  }
961 
962  if (!$error) {
963  $this->oldref = $this->ref;
964 
965  // Rename directory if dir was a temporary ref
966  if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
967  // Now we rename also files into index
968  $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)."'";
969  $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'bom/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
970  $resql = $this->db->query($sql);
971  if (!$resql) {
972  $error++;
973  $this->error = $this->db->lasterror();
974  }
975  $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filepath = 'bom/".$this->db->escape($this->newref)."'";
976  $sql .= " WHERE filepath = 'bom/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
977  $resql = $this->db->query($sql);
978  if (!$resql) {
979  $error++;
980  $this->error = $this->db->lasterror();
981  }
982 
983  // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
984  $oldref = dol_sanitizeFileName($this->ref);
985  $newref = dol_sanitizeFileName($num);
986  $dirsource = $conf->bom->dir_output.'/'.$oldref;
987  $dirdest = $conf->bom->dir_output.'/'.$newref;
988  if (!$error && file_exists($dirsource)) {
989  dol_syslog(get_class($this)."::validate() rename dir ".$dirsource." into ".$dirdest);
990 
991  if (@rename($dirsource, $dirdest)) {
992  dol_syslog("Rename ok");
993  // Rename docs starting with $oldref with $newref
994  $listoffiles = dol_dir_list($conf->bom->dir_output.'/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
995  foreach ($listoffiles as $fileentry) {
996  $dirsource = $fileentry['name'];
997  $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
998  $dirsource = $fileentry['path'].'/'.$dirsource;
999  $dirdest = $fileentry['path'].'/'.$dirdest;
1000  @rename($dirsource, $dirdest);
1001  }
1002  }
1003  }
1004  }
1005  }
1006 
1007  // Set new ref and current status
1008  if (!$error) {
1009  $this->ref = $num;
1010  $this->status = self::STATUS_VALIDATED;
1011  }
1012 
1013  if (!$error) {
1014  $this->db->commit();
1015  return 1;
1016  } else {
1017  $this->db->rollback();
1018  return -1;
1019  }
1020  }
1021 
1029  public function setDraft($user, $notrigger = 0)
1030  {
1031  // Protection
1032  if ($this->status <= self::STATUS_DRAFT) {
1033  return 0;
1034  }
1035 
1036  return $this->setStatusCommon($user, self::STATUS_DRAFT, $notrigger, 'BOM_UNVALIDATE');
1037  }
1038 
1046  public function cancel($user, $notrigger = 0)
1047  {
1048  // Protection
1049  if ($this->status != self::STATUS_VALIDATED) {
1050  return 0;
1051  }
1052 
1053  return $this->setStatusCommon($user, self::STATUS_CANCELED, $notrigger, 'BOM_CLOSE');
1054  }
1055 
1063  public function reopen($user, $notrigger = 0)
1064  {
1065  // Protection
1066  if ($this->status != self::STATUS_CANCELED) {
1067  return 0;
1068  }
1069 
1070  return $this->setStatusCommon($user, self::STATUS_VALIDATED, $notrigger, 'BOM_REOPEN');
1071  }
1072 
1079  public function getTooltipContentArray($params)
1080  {
1081  global $conf, $langs, $user;
1082 
1083  $langs->loadLangs(['product', 'mrp']);
1084 
1085  $datas = [];
1086 
1087  if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
1088  return ['optimize' => $langs->trans("ShowBillOfMaterials")];
1089  }
1090  $picto = img_picto('', $this->picto).' <u class="paddingrightonly">'.$langs->trans("BillOfMaterials").'</u>';
1091  if (isset($this->status)) {
1092  $picto .= ' '.$this->getLibStatut(5);
1093  }
1094  $datas['picto'] = $picto;
1095  $datas['ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
1096  if (isset($this->label)) {
1097  $datas['label'] = '<br><b>'.$langs->trans('Label').':</b> '.$this->label;
1098  }
1099  if (!empty($this->fk_product) && $this->fk_product > 0) {
1100  include_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
1101  $product = new Product($this->db);
1102  $resultFetch = $product->fetch($this->fk_product);
1103  if ($resultFetch > 0) {
1104  $datas['product'] = "<br><b>".$langs->trans("Product").'</b>: '.$product->ref.' - '.$product->label;
1105  }
1106  }
1107 
1108  return $datas;
1109  }
1110 
1121  public function getNomUrl($withpicto = 0, $option = '', $notooltip = 0, $morecss = '', $save_lastsearch_value = -1)
1122  {
1123  global $db, $conf, $langs, $hookmanager;
1124 
1125  if (!empty($conf->dol_no_mouse_hover)) {
1126  $notooltip = 1; // Force disable tooltips
1127  }
1128 
1129  $result = '';
1130  $params = [
1131  'id' => $this->id,
1132  'objecttype' => $this->element,
1133  'option' => $option,
1134  ];
1135  $classfortooltip = 'classfortooltip';
1136  $dataparams = '';
1137  if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
1138  $classfortooltip = 'classforajaxtooltip';
1139  $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
1140  $label = '';
1141  } else {
1142  $label = implode($this->getTooltipContentArray($params));
1143  }
1144 
1145  $url = DOL_URL_ROOT.'/bom/bom_card.php?id='.$this->id;
1146 
1147  if ($option != 'nolink') {
1148  // Add param to save lastsearch_values or not
1149  $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
1150  if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
1151  $add_save_lastsearch_values = 1;
1152  }
1153  if ($add_save_lastsearch_values) {
1154  $url .= '&save_lastsearch_values=1';
1155  }
1156  }
1157 
1158  $linkclose = '';
1159  if (empty($notooltip)) {
1160  if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
1161  $label = $langs->trans("ShowBillOfMaterials");
1162  $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
1163  }
1164  $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
1165  $linkclose .= $dataparams.' class="'.$classfortooltip.($morecss ? ' '.$morecss : '').'"';
1166  } else {
1167  $linkclose = ($morecss ? ' class="'.$morecss.'"' : '');
1168  }
1169 
1170  $linkstart = '<a href="'.$url.'"';
1171  $linkstart .= $linkclose.'>';
1172  $linkend = '</a>';
1173 
1174  $result .= $linkstart;
1175  if ($withpicto) {
1176  $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), (($withpicto != 2) ? 'class="paddingright"' : ''), 0, 0, $notooltip ? 0 : 1);
1177  }
1178  if ($withpicto != 2) {
1179  $result .= $this->ref;
1180  }
1181  $result .= $linkend;
1182  //if ($withpicto != 2) $result.=(($addlabel && $this->label) ? $sep . dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : '');
1183 
1184  global $action, $hookmanager;
1185  $hookmanager->initHooks(array('bomdao'));
1186  $parameters = array('id' => $this->id, 'getnomurl' => &$result);
1187  $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1188  if ($reshook > 0) {
1189  $result = $hookmanager->resPrint;
1190  } else {
1191  $result .= $hookmanager->resPrint;
1192  }
1193 
1194  return $result;
1195  }
1196 
1203  public function getLibStatut($mode = 0)
1204  {
1205  return $this->LibStatut($this->status, $mode);
1206  }
1207 
1208  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1216  public function LibStatut($status, $mode = 0)
1217  {
1218  // phpcs:enable
1219  if (empty($this->labelStatus)) {
1220  global $langs;
1221  //$langs->load("mrp");
1222  $this->labelStatus[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv('Draft');
1223  $this->labelStatus[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv('Enabled');
1224  $this->labelStatus[self::STATUS_CANCELED] = $langs->transnoentitiesnoconv('Disabled');
1225  }
1226 
1227  $statusType = 'status'.$status;
1228  if ($status == self::STATUS_VALIDATED) {
1229  $statusType = 'status4';
1230  }
1231  if ($status == self::STATUS_CANCELED) {
1232  $statusType = 'status6';
1233  }
1234 
1235  return dolGetStatus($this->labelStatus[$status], $this->labelStatus[$status], '', $statusType, $mode);
1236  }
1237 
1244  public function info($id)
1245  {
1246  $sql = 'SELECT rowid, date_creation as datec, tms as datem,';
1247  $sql .= ' fk_user_creat, fk_user_modif';
1248  $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as t';
1249  $sql .= ' WHERE t.rowid = '.((int) $id);
1250  $result = $this->db->query($sql);
1251  if ($result) {
1252  if ($this->db->num_rows($result)) {
1253  $obj = $this->db->fetch_object($result);
1254 
1255  $this->id = $obj->rowid;
1256 
1257  $this->user_creation_id = $obj->fk_user_creat;
1258  $this->user_modification_id = $obj->fk_user_modif;
1259  $this->date_creation = $this->db->jdate($obj->datec);
1260  $this->date_modification = empty($obj->datem) ? '' : $this->db->jdate($obj->datem);
1261  }
1262 
1263  $this->db->free($result);
1264  } else {
1265  dol_print_error($this->db);
1266  }
1267  }
1268 
1274  public function getLinesArray()
1275  {
1276  $this->lines = array();
1277 
1278  $objectline = new BOMLine($this->db);
1279  $result = $objectline->fetchAll('ASC', 'position', 0, 0, '(fk_bom:=:'.((int) $this->id).')');
1280 
1281  if (is_numeric($result)) {
1282  $this->error = $objectline->error;
1283  $this->errors = $objectline->errors;
1284  return $result;
1285  } else {
1286  $this->lines = $result;
1287  return $this->lines;
1288  }
1289  }
1290 
1302  public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
1303  {
1304  global $conf, $langs;
1305 
1306  $langs->load("mrp");
1307  $outputlangs->load("products");
1308 
1309  if (!dol_strlen($modele)) {
1310  $modele = '';
1311 
1312  if ($this->model_pdf) {
1313  $modele = $this->model_pdf;
1314  } elseif (getDolGlobalString('BOM_ADDON_PDF')) {
1315  $modele = getDolGlobalString('BOM_ADDON_PDF');
1316  }
1317  }
1318 
1319  $modelpath = "core/modules/bom/doc/";
1320  if (!empty($modele)) {
1321  return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
1322  } else {
1323  return 0;
1324  }
1325  }
1326 
1327  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1334  public function is_photo_available($sdir)
1335  {
1336  // phpcs:enable
1337  include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1338  include_once DOL_DOCUMENT_ROOT.'/core/lib/images.lib.php';
1339 
1340  $sdir .= '/'.get_exdir(0, 0, 0, 0, $this, 'bom');
1341 
1342  $dir_osencoded = dol_osencode($sdir);
1343  if (file_exists($dir_osencoded)) {
1344  $handle = opendir($dir_osencoded);
1345  if (is_resource($handle)) {
1346  while (($file = readdir($handle)) !== false) {
1347  if (!utf8_check($file)) {
1348  $file = mb_convert_encoding($file, 'UTF-8', 'ISO-8859-1'); // To be sure data is stored in UTF8 in memory
1349  }
1350  if (dol_is_file($sdir.$file) && image_format_supported($file) >= 0) {
1351  return true;
1352  }
1353  }
1354  }
1355  }
1356  return false;
1357  }
1358 
1365  public function initAsSpecimen()
1366  {
1367  $this->initAsSpecimenCommon();
1368  $this->ref = 'BOM-123';
1369  $this->date_creation = dol_now() - 20000;
1370 
1371  return 1;
1372  }
1373 
1374 
1381  public function doScheduledJob()
1382  {
1383  global $conf, $langs;
1384 
1385  //$conf->global->SYSLOG_FILE = 'DOL_DATA_ROOT/dolibarr_mydedicatedlofile.log';
1386 
1387  $error = 0;
1388  $this->output = '';
1389  $this->error = '';
1390 
1391  dol_syslog(__METHOD__, LOG_DEBUG);
1392 
1393  $now = dol_now();
1394 
1395  $this->db->begin();
1396 
1397  // ...
1398 
1399  $this->db->commit();
1400 
1401  return $error;
1402  }
1403 
1410  public function calculateCosts()
1411  {
1412  global $conf, $hookmanager;
1413 
1414  include_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
1415  $this->unit_cost = 0;
1416  $this->total_cost = 0;
1417 
1418  $parameters = array();
1419  $reshook = $hookmanager->executeHooks('calculateCostsBom', $parameters, $this); // Note that $action and $object may have been modified by hook
1420 
1421  if ($reshook > 0) {
1422  return $hookmanager->resPrint;
1423  }
1424 
1425  if (is_array($this->lines) && count($this->lines)) {
1426  require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
1427  $productFournisseur = new ProductFournisseur($this->db);
1428  $tmpproduct = new Product($this->db);
1429 
1430  foreach ($this->lines as &$line) {
1431  $tmpproduct->cost_price = 0;
1432  $tmpproduct->pmp = 0;
1433  $result = $tmpproduct->fetch($line->fk_product, '', '', '', 0, 1, 1); // We discard selling price and language loading
1434 
1435  if ($tmpproduct->type == $tmpproduct::TYPE_PRODUCT) {
1436  if (empty($line->fk_bom_child)) {
1437  if ($result < 0) {
1438  $this->error = $tmpproduct->error;
1439  return -1;
1440  }
1441  $unit_cost = (!empty($tmpproduct->cost_price)) ? $tmpproduct->cost_price : $tmpproduct->pmp;
1442  $line->unit_cost = (float) price2num($unit_cost);
1443  if (empty($line->unit_cost)) {
1444  if ($productFournisseur->find_min_price_product_fournisseur($line->fk_product) > 0) {
1445  if ($productFournisseur->fourn_remise_percent != "0") {
1446  $line->unit_cost = $productFournisseur->fourn_unitprice_with_discount;
1447  } else {
1448  $line->unit_cost = $productFournisseur->fourn_unitprice;
1449  }
1450  }
1451  }
1452 
1453  $line->total_cost = (float) price2num($line->qty * $line->unit_cost, 'MT');
1454 
1455  $this->total_cost += $line->total_cost;
1456  } else {
1457  $bom_child = new BOM($this->db);
1458  $res = $bom_child->fetch($line->fk_bom_child);
1459  if ($res > 0) {
1460  $bom_child->calculateCosts();
1461  $line->childBom[] = $bom_child;
1462  $this->total_cost += (float) price2num($bom_child->total_cost * $line->qty, 'MT');
1463  $this->total_cost += $line->total_cost;
1464  } else {
1465  $this->error = $bom_child->error;
1466  return -2;
1467  }
1468  }
1469  } else {
1470  // Convert qty of line into hours
1471  $unitforline = measuringUnitString($line->fk_unit, '', '', 1);
1472  $qtyhourforline = convertDurationtoHour($line->qty, $unitforline);
1473 
1474  if (isModEnabled('workstation') && !empty($line->fk_default_workstation)) {
1475  $workstation = new Workstation($this->db);
1476  $res = $workstation->fetch($line->fk_default_workstation);
1477 
1478  if ($res > 0) {
1479  $line->total_cost = (float) price2num($qtyhourforline * ($workstation->thm_operator_estimated + $workstation->thm_machine_estimated), 'MT');
1480  } else {
1481  $this->error = $workstation->error;
1482  return -3;
1483  }
1484  } else {
1485  $defaultdurationofservice = $tmpproduct->duration;
1486  $reg = array();
1487  $qtyhourservice = 0;
1488  if (preg_match('/^(\d+)([a-z]+)$/', $defaultdurationofservice, $reg)) {
1489  $qtyhourservice = convertDurationtoHour($reg[1], $reg[2]);
1490  }
1491 
1492  if ($qtyhourservice) {
1493  $line->total_cost = (float) price2num($qtyhourforline / $qtyhourservice * $tmpproduct->cost_price, 'MT');
1494  } else {
1495  $line->total_cost = (float) price2num($line->qty * $tmpproduct->cost_price, 'MT');
1496  }
1497  }
1498 
1499  $this->total_cost += $line->total_cost;
1500  }
1501  }
1502 
1503  $this->total_cost = (float) price2num($this->total_cost, 'MT');
1504 
1505  if ($this->qty > 0) {
1506  $this->unit_cost = (float) price2num($this->total_cost / $this->qty, 'MU');
1507  } elseif ($this->qty < 0) {
1508  $this->unit_cost = (float) price2num($this->total_cost * $this->qty, 'MU');
1509  }
1510  }
1511 
1512  return 1;
1513  }
1514 
1523  public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
1524  {
1525  $tables = array(
1526  'bom_bomline'
1527  );
1528 
1529  return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
1530  }
1531 
1539  public function getNetNeeds(&$TNetNeeds = array(), $qty = 0)
1540  {
1541  if (!empty($this->lines)) {
1542  foreach ($this->lines as $line) {
1543  if (!empty($line->childBom)) {
1544  foreach ($line->childBom as $childBom) {
1545  $childBom->getNetNeeds($TNetNeeds, $line->qty * $qty);
1546  }
1547  } else {
1548  if (empty($TNetNeeds[$line->fk_product])) {
1549  $TNetNeeds[$line->fk_product] = 0;
1550  }
1551  $TNetNeeds[$line->fk_product] += $line->qty * $qty;
1552  }
1553  }
1554  }
1555  }
1556 
1565  public function getNetNeedsTree(&$TNetNeeds = array(), $qty = 0, $level = 0)
1566  {
1567  if (!empty($this->lines)) {
1568  foreach ($this->lines as $line) {
1569  if (!empty($line->childBom)) {
1570  foreach ($line->childBom as $childBom) {
1571  $TNetNeeds[$childBom->id]['bom'] = $childBom;
1572  $TNetNeeds[$childBom->id]['parentid'] = $this->id;
1573  $TNetNeeds[$childBom->id]['qty'] = $line->qty * $qty;
1574  $TNetNeeds[$childBom->id]['level'] = $level;
1575  $childBom->getNetNeedsTree($TNetNeeds, $line->qty * $qty, $level + 1);
1576  }
1577  } else {
1578  $TNetNeeds[$this->id]['product'][$line->fk_product]['qty'] += $line->qty * $qty;
1579  $TNetNeeds[$this->id]['product'][$line->fk_product]['level'] = $level;
1580  }
1581  }
1582  }
1583  }
1584 
1593  public function getParentBomTreeRecursive(&$TParentBom, $bom_id = 0, $level = 1)
1594  {
1595 
1596  // Protection against infinite loop
1597  if ($level > 1000) {
1598  return;
1599  }
1600 
1601  if (empty($bom_id)) {
1602  $bom_id = $this->id;
1603  }
1604 
1605  $sql = 'SELECT l.fk_bom, b.label
1606  FROM '.MAIN_DB_PREFIX.'bom_bomline l
1607  INNER JOIN '.MAIN_DB_PREFIX.$this->table_element.' b ON b.rowid = l.fk_bom
1608  WHERE fk_bom_child = '.((int) $bom_id);
1609 
1610  $resql = $this->db->query($sql);
1611  if (!empty($resql)) {
1612  while ($res = $this->db->fetch_object($resql)) {
1613  $TParentBom[$res->fk_bom] = $res->fk_bom;
1614  $this->getParentBomTreeRecursive($TParentBom, $res->fk_bom, $level + 1);
1615  }
1616  }
1617  }
1618 
1626  public function getKanbanView($option = '', $arraydata = null)
1627  {
1628  global $db,$langs;
1629 
1630  $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
1631 
1632  $return = '<div class="box-flex-item box-flex-grow-zero">';
1633  $return .= '<div class="info-box info-box-sm">';
1634  $return .= '<span class="info-box-icon bg-infobox-action">';
1635  $return .= img_picto('', $this->picto);
1636  $return .= '</span>';
1637  $return .= '<div class="info-box-content">';
1638  $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl() : '').'</span>';
1639  if ($selected >= 0) {
1640  $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
1641  }
1642  if (property_exists($this, 'fields') && !empty($this->fields['bomtype']['arrayofkeyval'])) {
1643  $return .= '<br><span class="info-box-label opacitymedium">'.$langs->trans("Type").' : </span>';
1644  if ($this->bomtype == 0) {
1645  $return .= '<span class="info-box-label">'.$this->fields['bomtype']['arrayofkeyval'][0].'</span>';
1646  } else {
1647  $return .= '<span class="info-box-label">'.$this->fields['bomtype']['arrayofkeyval'][1].'</span>';
1648  }
1649  }
1650  if (!empty($arraydata['prod'])) {
1651  $prod = $arraydata['prod'];
1652  $return .= '<br><span class="info-box-label">'.$prod->getNomUrl(1).'</span>';
1653  }
1654  if (method_exists($this, 'getLibStatut')) {
1655  $return .= '<br><div class="info-box-status">'.$this->getLibStatut(3).'</div>';
1656  }
1657 
1658  $return .= '</div>';
1659  $return .= '</div>';
1660  $return .= '</div>';
1661  return $return;
1662  }
1663 }
1664 
1665 
1670 {
1674  public $element = 'bomline';
1675 
1679  public $table_element = 'bom_bomline';
1680 
1685  public $ismultientitymanaged = 0;
1686 
1690  public $isextrafieldmanaged = 1;
1691 
1695  public $picto = 'bomline';
1696 
1697 
1717  // BEGIN MODULEBUILDER PROPERTIES
1721  public $fields = array(
1722  'rowid' => array('type' => 'integer', 'label' => 'LineID', 'enabled' => 1, 'visible' => -1, 'position' => 1, 'notnull' => 1, 'index' => 1, 'comment' => "Id",),
1723  'fk_bom' => array('type' => 'integer:BillOfMaterials:societe/class/bom.class.php', 'label' => 'BillOfMaterials', 'enabled' => 1, 'visible' => 1, 'position' => 10, 'notnull' => 1, 'index' => 1,),
1724  'fk_product' => array('type' => 'integer:Product:product/class/product.class.php', 'label' => 'Product', 'enabled' => 1, 'visible' => 1, 'position' => 20, 'notnull' => 1, 'index' => 1,),
1725  'fk_bom_child' => array('type' => 'integer:BOM:bom/class/bom.class.php', 'label' => 'BillOfMaterials', 'enabled' => 1, 'visible' => -1, 'position' => 40, 'notnull' => -1,),
1726  'description' => array('type' => 'text', 'label' => 'Description', 'enabled' => 1, 'visible' => -1, 'position' => 60, 'notnull' => -1,),
1727  'qty' => array('type' => 'double(24,8)', 'label' => 'Quantity', 'enabled' => 1, 'visible' => 1, 'position' => 100, 'notnull' => 1, 'isameasure' => 1,),
1728  'qty_frozen' => array('type' => 'smallint', 'label' => 'QuantityFrozen', 'enabled' => 1, 'visible' => 1, 'default' => '0', 'position' => 105, 'css' => 'maxwidth50imp', 'help' => 'QuantityConsumedInvariable'),
1729  'disable_stock_change' => array('type' => 'smallint', 'label' => 'DisableStockChange', 'enabled' => 1, 'visible' => 1, 'default' => '0', 'position' => 108, 'css' => 'maxwidth50imp', 'help' => 'DisableStockChangeHelp'),
1730  'efficiency' => array('type' => 'double(24,8)', 'label' => 'ManufacturingEfficiency', 'enabled' => 1, 'visible' => 0, 'default' => '1', 'position' => 110, 'notnull' => 1, 'css' => 'maxwidth50imp', 'help' => 'ValueOfEfficiencyConsumedMeans'),
1731  'fk_unit' => array('type' => 'integer', 'label' => 'Unit', 'enabled' => 1, 'visible' => 1, 'position' => 120, 'notnull' => -1,),
1732  'position' => array('type' => 'integer', 'label' => 'Rank', 'enabled' => 1, 'visible' => 0, 'default' => '0', 'position' => 200, 'notnull' => 1,),
1733  'import_key' => array('type' => 'varchar(14)', 'label' => 'ImportId', 'enabled' => 1, 'visible' => -2, 'position' => 1000, 'notnull' => -1,),
1734  'fk_default_workstation' => array('type' => 'integer', 'label' => 'DefaultWorkstation', 'enabled' => 1, 'visible' => 1, 'notnull' => 0, 'position' => 1050)
1735  );
1736 
1740  public $rowid;
1741 
1745  public $fk_bom;
1746 
1750  public $fk_product;
1751 
1755  public $fk_bom_child;
1756 
1760  public $description;
1761 
1765  public $qty;
1766 
1770  public $qty_frozen;
1771 
1775  public $disable_stock_change;
1776 
1780  public $efficiency;
1781 
1785  public $position;
1786 
1790  public $import_key;
1791  // END MODULEBUILDER PROPERTIES
1792 
1796  public $total_cost = 0;
1797 
1801  public $unit_cost = 0;
1802 
1806  public $childBom = array();
1807 
1813  public $fk_unit;
1814 
1818  public $fk_default_workstation;
1819 
1820 
1821 
1827  public function __construct(DoliDB $db)
1828  {
1829  global $conf, $langs;
1830 
1831  $this->db = $db;
1832 
1833  if (!getDolGlobalString('MAIN_SHOW_TECHNICAL_ID') && isset($this->fields['rowid'])) {
1834  $this->fields['rowid']['visible'] = 0;
1835  }
1836  if (!isModEnabled('multicompany') && isset($this->fields['entity'])) {
1837  $this->fields['entity']['enabled'] = 0;
1838  }
1839 
1840  // Unset fields that are disabled
1841  foreach ($this->fields as $key => $val) {
1842  if (isset($val['enabled']) && empty($val['enabled'])) {
1843  unset($this->fields[$key]);
1844  }
1845  }
1846 
1847  // Translate some data of arrayofkeyval
1848  foreach ($this->fields as $key => $val) {
1849  if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
1850  foreach ($val['arrayofkeyval'] as $key2 => $val2) {
1851  $this->fields[$key]['arrayofkeyval'][$key2] = $langs->trans($val2);
1852  }
1853  }
1854  }
1855  }
1856 
1864  public function create(User $user, $notrigger = 0)
1865  {
1866  if ($this->efficiency < 0 || $this->efficiency > 1) {
1867  $this->efficiency = 1;
1868  }
1869 
1870  return $this->createCommon($user, $notrigger);
1871  }
1872 
1880  public function fetch($id, $ref = null)
1881  {
1882  $result = $this->fetchCommon($id, $ref);
1883  //if ($result > 0 && !empty($this->table_element_line)) $this->fetchLines();
1884  return $result;
1885  }
1886 
1899  public function fetchAll($sortorder = '', $sortfield = '', $limit = 0, $offset = 0, $filter = '', $filtermode = 'AND')
1900  {
1901  dol_syslog(__METHOD__, LOG_DEBUG);
1902 
1903  $records = array();
1904 
1905  $sql = 'SELECT ';
1906  $sql .= $this->getFieldList();
1907  $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as t';
1908  if ($this->ismultientitymanaged) {
1909  $sql .= ' WHERE t.entity IN ('.getEntity($this->element).')';
1910  } else {
1911  $sql .= ' WHERE 1 = 1';
1912  }
1913 
1914  // Manage filter
1915  $errormessage = '';
1916  $sql .= forgeSQLFromUniversalSearchCriteria($filter, $errormessage);
1917  if ($errormessage) {
1918  $this->errors[] = $errormessage;
1919  dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
1920  return -1;
1921  }
1922 
1923  if (!empty($sortfield)) {
1924  $sql .= $this->db->order($sortfield, $sortorder);
1925  }
1926  if (!empty($limit)) {
1927  $sql .= $this->db->plimit($limit, $offset);
1928  }
1929 
1930  $resql = $this->db->query($sql);
1931  if ($resql) {
1932  $num = $this->db->num_rows($resql);
1933 
1934  while ($obj = $this->db->fetch_object($resql)) {
1935  $record = new self($this->db);
1936  $record->setVarsFromFetchObj($obj);
1937  $record->fetch_optionals();
1938 
1939  $records[$record->id] = $record;
1940  }
1941  $this->db->free($resql);
1942 
1943  return $records;
1944  } else {
1945  $this->errors[] = 'Error '.$this->db->lasterror();
1946  dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
1947 
1948  return -1;
1949  }
1950  }
1951 
1959  public function update(User $user, $notrigger = 0)
1960  {
1961  if ($this->efficiency < 0 || $this->efficiency > 1) {
1962  $this->efficiency = 1;
1963  }
1964 
1965  return $this->updateCommon($user, $notrigger);
1966  }
1967 
1975  public function delete(User $user, $notrigger = 0)
1976  {
1977  return $this->deleteCommon($user, $notrigger);
1978  //return $this->deleteCommon($user, $notrigger, 1);
1979  }
1980 
1991  public function getNomUrl($withpicto = 0, $option = '', $notooltip = 0, $morecss = '', $save_lastsearch_value = -1)
1992  {
1993  global $db, $conf, $langs, $hookmanager;
1994 
1995  if (!empty($conf->dol_no_mouse_hover)) {
1996  $notooltip = 1; // Force disable tooltips
1997  }
1998 
1999  $result = '';
2000 
2001  $label = '<u>'.$langs->trans("BillOfMaterialsLine").'</u>';
2002  $label .= '<br>';
2003  $label .= '<b>'.$langs->trans('Ref').':</b> '.$this->ref;
2004 
2005  $url = DOL_URL_ROOT.'/bom/bomline_card.php?id='.$this->id;
2006 
2007  if ($option != 'nolink') {
2008  // Add param to save lastsearch_values or not
2009  $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
2010  if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
2011  $add_save_lastsearch_values = 1;
2012  }
2013  if ($add_save_lastsearch_values) {
2014  $url .= '&save_lastsearch_values=1';
2015  }
2016  }
2017 
2018  $linkclose = '';
2019  if (empty($notooltip)) {
2020  if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2021  $label = $langs->trans("ShowBillOfMaterialsLine");
2022  $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
2023  }
2024  $linkclose .= ' title="'.dol_escape_htmltag($label, 1).'"';
2025  $linkclose .= ' class="classfortooltip'.($morecss ? ' '.$morecss : '').'"';
2026  } else {
2027  $linkclose = ($morecss ? ' class="'.$morecss.'"' : '');
2028  }
2029 
2030  $linkstart = '<a href="'.$url.'"';
2031  $linkstart .= $linkclose.'>';
2032  $linkend = '</a>';
2033 
2034  $result .= $linkstart;
2035  if ($withpicto) {
2036  $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);
2037  }
2038  if ($withpicto != 2) {
2039  $result .= $this->ref;
2040  }
2041  $result .= $linkend;
2042  //if ($withpicto != 2) $result.=(($addlabel && $this->label) ? $sep . dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : '');
2043 
2044  global $action, $hookmanager;
2045  $hookmanager->initHooks(array('bomlinedao'));
2046  $parameters = array('id' => $this->id, 'getnomurl' => &$result);
2047  $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
2048  if ($reshook > 0) {
2049  $result = $hookmanager->resPrint;
2050  } else {
2051  $result .= $hookmanager->resPrint;
2052  }
2053 
2054  return $result;
2055  }
2056 
2063  public function getLibStatut($mode = 0)
2064  {
2065  return $this->LibStatut($this->status, $mode);
2066  }
2067 
2068  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2076  public function LibStatut($status, $mode = 0)
2077  {
2078  // phpcs:enable
2079  return '';
2080  }
2081 
2088  public function info($id)
2089  {
2090  $sql = 'SELECT rowid, date_creation as datec, tms as datem,';
2091  $sql .= ' fk_user_creat, fk_user_modif';
2092  $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as t';
2093  $sql .= ' WHERE t.rowid = '.((int) $id);
2094  $result = $this->db->query($sql);
2095  if ($result) {
2096  if ($this->db->num_rows($result)) {
2097  $obj = $this->db->fetch_object($result);
2098 
2099  $this->id = $obj->rowid;
2100 
2101  $this->user_creation_id = $obj->fk_user_creat;
2102  $this->user_modification_id = $obj->fk_user_modif;
2103  $this->date_creation = $this->db->jdate($obj->datec);
2104  $this->date_modification = empty($obj->datem) ? '' : $this->db->jdate($obj->datem);
2105  }
2106  $this->db->free($result);
2107  } else {
2108  dol_print_error($this->db);
2109  }
2110  }
2111 
2118  public function initAsSpecimen()
2119  {
2120  return $this->initAsSpecimenCommon();
2121  }
2122 }
if($user->socid > 0) if(! $user->hasRight('accounting', 'chartofaccount')) $object
Definition: card.php:58
print $langs trans("AuditedSecurityEvents").'</strong >< span class="opacitymedium"></span >< br > status
Or an array listing all the potential status of the object: array: int of the status => translated la...
Definition: security.php:604
$object ref
Definition: info.php:79
Class for BOM.
Definition: bom.class.php:45
static replaceProduct(DoliDB $db, $origin_id, $dest_id)
Function used to replace a product id with another one.
Definition: bom.class.php:1523
fetchLines()
Load object lines in memory from the database.
Definition: bom.class.php:427
is_photo_available($sdir)
Return if at least one photo is available.
Definition: bom.class.php:1334
__construct(DoliDB $db)
Constructor.
Definition: bom.class.php:266
calculateCosts()
BOM costs calculation based on cost_price or pmp of each BOM line.
Definition: bom.class.php:1410
info($id)
Load the info information in the object.
Definition: bom.class.php:1244
getLibStatut($mode=0)
Return label of the status.
Definition: bom.class.php:1203
initAsSpecimen()
Initialise object with example values Id must be 0 if object instance is a specimen.
Definition: bom.class.php:1365
validate($user, $notrigger=0)
Validate bom.
Definition: bom.class.php:910
reopen($user, $notrigger=0)
Set cancel status.
Definition: bom.class.php:1063
cancel($user, $notrigger=0)
Set cancel status.
Definition: bom.class.php:1046
LibStatut($status, $mode=0)
Return the status.
Definition: bom.class.php:1216
doScheduledJob()
Action executed by scheduler CAN BE A CRON TASK.
Definition: bom.class.php:1381
getParentBomTreeRecursive(&$TParentBom, $bom_id=0, $level=1)
Recursively retrieves all parent bom in the tree that leads to the $bom_id bom.
Definition: bom.class.php:1593
getTooltipContentArray($params)
getTooltipContentArray
Definition: bom.class.php:1079
fetchLinesbytypeproduct($typeproduct=0)
Load object lines in memory from the database by type of product.
Definition: bom.class.php:442
deleteLine(User $user, $idline, $notrigger=0)
Delete a line of object in database.
Definition: bom.class.php:815
createFromClone(User $user, $fromid)
Clone an object into another one.
Definition: bom.class.php:319
fetch($id, $ref=null)
Load object in memory from the database.
Definition: bom.class.php:410
getNomUrl($withpicto=0, $option='', $notooltip=0, $morecss='', $save_lastsearch_value=-1)
Return a link to the object card (with optionally the picto)
Definition: bom.class.php:1121
updateLine($rowid, $qty, $qty_frozen=0, $disable_stock_change=0, $efficiency=1.0, $position=-1, $import_key=null, $fk_unit=0, $array_options=array(), $fk_default_workstation=null)
Update an BOM line into database.
Definition: bom.class.php:703
getNetNeeds(&$TNetNeeds=array(), $qty=0)
Get Net needs by product.
Definition: bom.class.php:1539
create(User $user, $notrigger=1)
Create object into database.
Definition: bom.class.php:303
getNextNumRef($prod)
Returns the reference to the following non used BOM depending on the active numbering module defined ...
Definition: bom.class.php:862
generateDocument($modele, $outputlangs, $hidedetails=0, $hidedesc=0, $hideref=0, $moreparams=null)
Create a document onto disk according to template module.
Definition: bom.class.php:1302
getLinesArray()
Create an array of lines.
Definition: bom.class.php:1274
setDraft($user, $notrigger=0)
Set draft status.
Definition: bom.class.php:1029
getKanbanView($option='', $arraydata=null)
Return clicable link of object (with eventually picto)
Definition: bom.class.php:1626
update(User $user, $notrigger=1)
Update object into database.
Definition: bom.class.php:557
fetchAll($sortorder='', $sortfield='', $limit=0, $offset=0, $filter='', $filtermode='AND')
Load list of objects in memory from the database.
Definition: bom.class.php:498
addLine($fk_product, $qty, $qty_frozen=0, $disable_stock_change=0, $efficiency=1.0, $position=-1, $fk_bom_child=null, $import_key=null, $fk_unit=0, $array_options=array(), $fk_default_workstation=null)
Add an BOM line into database (linked to BOM)
Definition: bom.class.php:595
getNetNeedsTree(&$TNetNeeds=array(), $qty=0, $level=0)
Get Net needs Tree by product or bom.
Definition: bom.class.php:1565
Class for BOMLine.
Definition: bom.class.php:1670
fetch($id, $ref=null)
Load object in memory from the database.
Definition: bom.class.php:1880
getLibStatut($mode=0)
Return label of the status.
Definition: bom.class.php:2063
__construct(DoliDB $db)
Constructor.
Definition: bom.class.php:1827
update(User $user, $notrigger=0)
Update object into database.
Definition: bom.class.php:1959
fetchAll($sortorder='', $sortfield='', $limit=0, $offset=0, $filter='', $filtermode='AND')
Load list of objects in memory from the database.
Definition: bom.class.php:1899
getNomUrl($withpicto=0, $option='', $notooltip=0, $morecss='', $save_lastsearch_value=-1)
Return a link to the object card (with optionally the picto)
Definition: bom.class.php:1991
initAsSpecimen()
Initialise object with example values Id must be 0 if object instance is a specimen.
Definition: bom.class.php:2118
info($id)
Load the info information in the object.
Definition: bom.class.php:2088
LibStatut($status, $mode=0)
Return the status.
Definition: bom.class.php:2076
create(User $user, $notrigger=0)
Create object into database.
Definition: bom.class.php:1864
Parent class of all other business classes (invoices, contracts, proposals, orders,...
commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams=null)
Common function for all objects extending CommonObject for generating documents.
setErrorsFromObject($object)
setErrorsFromObject
createCommon(User $user, $notrigger=0)
Create object into database.
getFieldList($alias='', $excludefields=array())
Function to concat keys of fields.
updateCommon(User $user, $notrigger=0)
Update object into database.
setStatusCommon($user, $status, $notrigger=0, $triggercode='')
Set to a status.
initAsSpecimenCommon()
Initialise object with example values Id must be 0 if object instance is a specimen.
copy_linked_contact($objFrom, $source='internal')
Copy contact from one element to current.
fetch_product()
Load the product with id $this->fk_product into this->product.
fetchLinesCommon($morewhere='', $noextrafields=0)
Load object in memory from the database.
static commonReplaceProduct(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a product id with another one.
line_max($fk_parent_line=0)
Get max value used for position of line (rang)
fetchCommon($id, $ref=null, $morewhere='', $noextrafields=0)
Load object in memory from the database.
deleteCommon(User $user, $notrigger=0, $forcechilddeletion=0)
Delete object in database.
call_trigger($triggerName, $user)
Call trigger based on this instance.
Parent class for class inheritance lines of business objects This class is useless for the moment so ...
Class to manage Dolibarr database access.
Class to manage predefined suppliers products.
Class to manage products or services.
Class to manage Dolibarr users.
Definition: user.class.php:50
Class for Workstation.
if(isModEnabled('invoice') && $user->hasRight('facture', 'lire')) if((isModEnabled('fournisseur') &&!getDolGlobalString('MAIN_USE_NEW_SUPPLIERMOD') && $user->hasRight("fournisseur", "facture", "lire"))||(isModEnabled('supplier_invoice') && $user->hasRight("supplier_invoice", "lire"))) if(isModEnabled('don') && $user->hasRight('don', 'lire')) if(isModEnabled('tax') && $user->hasRight('tax', 'charges', 'lire')) if(isModEnabled('invoice') &&isModEnabled('order') && $user->hasRight("commande", "lire") &&!getDolGlobalString('WORKFLOW_DISABLE_CREATE_INVOICE_FROM_ORDER')) $sql
Social contributions to pay.
Definition: index.php:744
convertDurationtoHour($duration_value, $duration_unit)
Convert duration to hour.
Definition: date.lib.php:333
dol_is_file($pathoffile)
Return if path is a file.
Definition: files.lib.php:519
dol_dir_list($utf8_path, $types="all", $recursive=0, $filter="", $excludefilter=null, $sortcriteria="name", $sortorder=SORT_ASC, $mode=0, $nohook=0, $relativename="", $donotfollowsymlinks=0, $nbsecondsold=0)
Scan a directory and return a list of files/directories.
Definition: files.lib.php:63
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0)
Show a picto called object_picto (generic function)
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (generic function)
dol_osencode($str)
Return a string encoded into OS filesystem encoding.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
forgeSQLFromUniversalSearchCriteria($filter, &$errorstr='', $noand=0, $nopar=0, $noerror=0)
forgeSQLFromUniversalSearchCriteria
dol_now($mode='auto')
Return date for now.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
dolGetStatus($statusLabel='', $statusLabelShort='', $html='', $statusType='status0', $displayMode=0, $url='', $params=array())
Output the badge of a status.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
dol_sanitizeFileName($str, $newstr='_', $unaccent=1)
Clean a string to use it as a file name.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
getDolGlobalString($key, $default='')
Return dolibarr global constant string value.
isModEnabled($module)
Is Dolibarr module enabled.
utf8_check($str)
Check if a string is in UTF8.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
image_format_supported($file, $acceptsvg=0)
Return if a filename is file name of a supported image format.
Definition: images.lib.php:84
div float
Buy price without taxes.
Definition: style.css.php:959
measuringUnitString($unit, $measuring_style='', $scale='', $use_short_label=0, $outputlangs=null)
Return translation label of a unit key.