dolibarr  21.0.0-alpha
mo.class.php
Go to the documentation of this file.
1 <?php
2 /* Copyright (C) 2017 Laurent Destailleur <eldy@users.sourceforge.net>
3  * Copyright (C) 2020 Lenin Rivas <lenin@leninrivas.com>
4  * Copyright (C) 2023-2024 Frédéric France <frederic.france@free.fr>
5  * Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program. If not, see <https://www.gnu.org/licenses/>.
19  */
20 
27 // Put here all includes required by your class file
28 require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
29 require_once DOL_DOCUMENT_ROOT.'/core/class/commonobjectline.class.php';
30 
31 
35 class Mo extends CommonObject
36 {
40  public $element = 'mo';
41 
45  public $table_element = 'mrp_mo';
46 
50  public $picto = 'mrp';
51 
52 
53  const STATUS_DRAFT = 0;
54  const STATUS_VALIDATED = 1; // To produce
55  const STATUS_INPROGRESS = 2;
56  const STATUS_PRODUCED = 3;
57  const STATUS_CANCELED = 9;
58 
59 
89  public $fields = array(
90  'rowid' => array('type' => 'integer', 'label' => 'TechnicalID', 'enabled' => 1, 'visible' => -2, 'position' => 1, 'notnull' => 1, 'index' => 1, 'comment' => "Id",),
91  'entity' => array('type' => 'integer', 'label' => 'Entity', 'enabled' => 1, 'visible' => 0, 'position' => 5, 'notnull' => 1, 'default' => '1', 'index' => 1),
92  'ref' => array('type' => 'varchar(128)', 'label' => 'Ref', 'enabled' => 1, 'visible' => 4, 'position' => 10, 'notnull' => 1, 'default' => '(PROV)', 'index' => 1, 'searchall' => 1, 'comment' => "Reference of object", 'showoncombobox' => 1, 'noteditable' => 1),
93  'fk_bom' => array('type' => 'integer:Bom:bom/class/bom.class.php:0:(t.status:=:1)', 'filter' => 'active=1', 'label' => 'BOM', 'enabled' => '$conf->bom->enabled', 'visible' => 1, 'position' => 33, 'notnull' => -1, 'index' => 1, 'comment' => "Original BOM", 'css' => 'minwidth100 maxwidth500', 'csslist' => 'tdoverflowmax150', 'picto' => 'bom'),
94  'mrptype' => array('type' => 'integer', 'label' => 'Type', 'enabled' => 1, 'visible' => 1, 'position' => 34, 'notnull' => 1, 'default' => '0', 'arrayofkeyval' => array(0 => 'Manufacturing', 1 => 'Disassemble'), 'css' => 'minwidth150', 'csslist' => 'minwidth150 center'),
95  'fk_product' => array('type' => 'integer:Product:product/class/product.class.php:0', 'label' => 'Product', 'enabled' => 'isModEnabled("product")', 'visible' => 1, 'position' => 35, 'notnull' => 1, 'index' => 1, 'comment' => "Product to produce", 'css' => 'maxwidth300', 'csslist' => 'tdoverflowmax100', 'picto' => 'product'),
96  'qty' => array('type' => 'real', 'label' => 'QtyToProduce', 'enabled' => 1, 'visible' => 1, 'position' => 40, 'notnull' => 1, 'comment' => "Qty to produce", 'css' => 'width75', 'default' => '1', 'isameasure' => 1),
97  'label' => array('type' => 'varchar(255)', 'label' => 'Label', 'enabled' => 1, 'visible' => 1, 'position' => 42, 'notnull' => -1, 'searchall' => 1, 'showoncombobox' => 2, 'css' => 'maxwidth300', 'csslist' => 'tdoverflowmax200', 'alwayseditable' => 1),
98  'fk_soc' => array('type' => 'integer:Societe:societe/class/societe.class.php:1', 'label' => 'ThirdParty', 'picto' => 'company', 'enabled' => 'isModEnabled("societe")', 'visible' => -1, 'position' => 50, 'notnull' => -1, 'index' => 1, 'css' => 'maxwidth400', 'csslist' => 'tdoverflowmax150'),
99  'fk_project' => array('type' => 'integer:Project:projet/class/project.class.php:1:(fk_statut:=:1)', 'label' => 'Project', 'picto' => 'project', 'enabled' => '$conf->project->enabled', 'visible' => -1, 'position' => 51, 'notnull' => -1, 'index' => 1, 'css' => 'minwidth200 maxwidth400', 'csslist' => 'tdoverflowmax100'),
100  'fk_warehouse' => array('type' => 'integer:Entrepot:product/stock/class/entrepot.class.php:0', 'label' => 'WarehouseForProduction', 'picto' => 'stock', 'enabled' => 'isModEnabled("stock")', 'visible' => 1, 'position' => 52, 'css' => 'maxwidth400', 'csslist' => 'tdoverflowmax200'),
101  'note_public' => array('type' => 'html', 'label' => 'NotePublic', 'enabled' => 1, 'visible' => 0, 'position' => 61, 'notnull' => -1,),
102  'note_private' => array('type' => 'html', 'label' => 'NotePrivate', 'enabled' => 1, 'visible' => 0, 'position' => 62, 'notnull' => -1,),
103  'date_creation' => array('type' => 'datetime', 'label' => 'DateCreation', 'enabled' => 1, 'visible' => -2, 'position' => 500, 'notnull' => 1,),
104  'tms' => array('type' => 'timestamp', 'label' => 'DateModification', 'enabled' => 1, 'visible' => -2, 'position' => 501, 'notnull' => 1,),
105  'date_valid' => array('type' => 'datetime', 'label' => 'DateValidation', 'enabled' => 1, 'visible' => -2, 'position' => 502,),
106  'fk_user_creat' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserAuthor', 'enabled' => 1, 'visible' => -2, 'position' => 510, 'notnull' => 1, 'foreignkey' => 'user.rowid', 'csslist' => 'tdoverflowmax100'),
107  'fk_user_modif' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserModif', 'enabled' => 1, 'visible' => -2, 'position' => 511, 'notnull' => -1, 'csslist' => 'tdoverflowmax100'),
108  'date_start_planned' => array('type' => 'datetime', 'label' => 'DateStartPlannedMo', 'enabled' => 1, 'visible' => 1, 'position' => 55, 'notnull' => -1, 'index' => 1, 'help' => 'KeepEmptyForAsap', 'alwayseditable' => 1, 'csslist' => 'nowraponall'),
109  'date_end_planned' => array('type' => 'datetime', 'label' => 'DateEndPlannedMo', 'enabled' => 1, 'visible' => 1, 'position' => 56, 'notnull' => -1, 'index' => 1, 'alwayseditable' => 1, 'csslist' => 'nowraponall'),
110  'import_key' => array('type' => 'varchar(14)', 'label' => 'ImportId', 'enabled' => 1, 'visible' => -2, 'position' => 1000, 'notnull' => -1,),
111  'model_pdf' => array('type' => 'varchar(255)', 'label' => 'Model pdf', 'enabled' => 1, 'visible' => 0, 'position' => 1010),
112  'status' => array('type' => 'integer', 'label' => 'Status', 'enabled' => 1, 'visible' => 2, 'position' => 1000, 'default' => '0', 'notnull' => 1, 'index' => 1, 'arrayofkeyval' => array('0' => 'Draft', '1' => 'Validated', '2' => 'InProgress', '3' => 'StatusMOProduced', '9' => 'Canceled')),
113  'fk_parent_line' => array('type' => 'integer:MoLine:mrp/class/mo.class.php', 'label' => 'ParentMo', 'enabled' => 1, 'visible' => 0, 'position' => 1020, 'default' => '0', 'notnull' => 0, 'index' => 1,'showoncombobox' => 0),
114  );
115  public $rowid;
116  public $entity;
117  public $ref;
118 
122  public $mrptype;
123  public $label;
124 
128  public $qty;
129  public $fk_warehouse;
130  public $fk_soc;
131  public $socid;
132 
136  public $note_public;
137 
141  public $note_private;
142 
146  public $date_valid;
147 
148  public $fk_user_creat;
149  public $fk_user_modif;
150  public $import_key;
151  public $status;
152 
156  public $fk_product;
157 
161  public $product;
162 
166  public $date_start_planned;
167 
171  public $date_end_planned;
172 
176  public $fk_bom;
177 
181  public $bom;
182 
186  public $fk_project;
187 
192  public $oldQty;
193 
194 
195  // If this object has a subtable with lines
196 
200  public $table_element_line = 'mrp_production';
201 
205  public $fk_element = 'fk_mo';
206 
210  public $class_element_line = 'MoLine';
211 
215  protected $childtables = array();
216 
220  protected $childtablesoncascade = array('mrp_production');
221 
225  public $lines = array();
226 
230  public $line = null;
231 
235  public $fk_parent_line;
236 
240  public $tpl = array();
241 
242 
248  public function __construct(DoliDB $db)
249  {
250  global $langs;
251 
252  $this->db = $db;
253 
254  $this->ismultientitymanaged = 1;
255  $this->isextrafieldmanaged = 1;
256 
257  if (!getDolGlobalString('MAIN_SHOW_TECHNICAL_ID') && isset($this->fields['rowid'])) {
258  $this->fields['rowid']['visible'] = 0;
259  }
260  if (!isModEnabled('multicompany') && isset($this->fields['entity'])) {
261  $this->fields['entity']['enabled'] = 0;
262  }
263 
264  // Unset fields that are disabled
265  foreach ($this->fields as $key => $val) {
266  if (isset($val['enabled']) && empty($val['enabled'])) {
267  unset($this->fields[$key]);
268  }
269  }
270 
271  // Translate some data of arrayofkeyval
272  foreach ($this->fields as $key => $val) {
273  if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
274  foreach ($val['arrayofkeyval'] as $key2 => $val2) {
275  $this->fields[$key]['arrayofkeyval'][$key2] = $langs->trans($val2);
276  }
277  }
278  }
279  }
280 
288  public function create(User $user, $notrigger = 0)
289  {
290  $error = 0;
291  $idcreated = 0;
292 
293  // If kits feature is enabled and we don't allow kits into BOM and MO, we check that the product is not a kit/virtual product
294  if (getDolGlobalString('PRODUIT_SOUSPRODUITS') && !getDolGlobalString('ALLOW_USE_KITS_INTO_BOM_AND_MO') && $this->fk_product > 0) {
295  include_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
296  $tmpproduct = new Product($this->db);
297  $tmpproduct->fetch($this->fk_product);
298  if ($tmpproduct->hasFatherOrChild(1) > 0) {
299  $this->error = 'ErrorAVirtualProductCantBeUsedIntoABomOrMo';
300  $this->errors[] = $this->error;
301  return -1;
302  }
303  }
304 
305  $this->db->begin();
306 
307  if ($this->fk_bom > 0) {
308  // If there is a known BOM, we force the type of MO to the type of BOM
309  include_once DOL_DOCUMENT_ROOT.'/bom/class/bom.class.php';
310  $tmpbom = new BOM($this->db);
311  $tmpbom->fetch($this->fk_bom);
312 
313  $this->mrptype = $tmpbom->bomtype;
314  }
315 
316  if (!$error) {
317  $idcreated = $this->createCommon($user, $notrigger);
318  if ($idcreated <= 0) {
319  $error++;
320  }
321  }
322 
323  if (!$error) {
324  $result = $this->createProduction($user, $notrigger); // Insert lines from BOM
325  if ($result <= 0) {
326  $error++;
327  }
328  }
329 
330  if (!$error) {
331  $this->db->commit();
332  return $idcreated;
333  } else {
334  $this->db->rollback();
335  return -1;
336  }
337  }
338 
346  public function createFromClone(User $user, $fromid)
347  {
348  global $langs, $extrafields;
349  $error = 0;
350 
351  dol_syslog(__METHOD__, LOG_DEBUG);
352 
353  $object = new self($this->db);
354 
355  $this->db->begin();
356 
357  // Load source object
358  $result = $object->fetchCommon($fromid);
359  if ($result > 0 && !empty($object->table_element_line)) {
360  $object->fetchLines();
361  }
362 
363  // get lines so they will be clone
364  //foreach($this->lines as $line)
365  // $line->fetch_optionals();
366 
367  // Reset some properties
368  unset($object->id);
369  unset($object->fk_user_creat);
370  unset($object->import_key);
371 
372  // We make $object->lines empty to sort it without produced and consumed lines
373  $TLines = $object->lines;
374  $object->lines = array();
375 
376  // Remove produced and consumed lines
377  foreach ($TLines as $key => $line) {
378  if (in_array($line->role, array('consumed', 'produced'))) {
379  unset($object->lines[$key]);
380  } else {
381  $object->lines[] = $line;
382  }
383  }
384 
385 
386  // Clear fields
387  $object->ref = empty($this->fields['ref']['default']) ? "copy_of_".$object->ref : $this->fields['ref']['default'];
388  $object->label = empty($this->fields['label']['default']) ? $langs->trans("CopyOf")." ".$object->label : $this->fields['label']['default'];
389  $object->status = self::STATUS_DRAFT;
390  // ...
391  // Clear extrafields that are unique
392  if (is_array($object->array_options) && count($object->array_options) > 0) {
393  $extrafields->fetch_name_optionals_label($this->table_element);
394  foreach ($object->array_options as $key => $option) {
395  $shortkey = preg_replace('/options_/', '', $key);
396  if (!empty($extrafields->attributes[$this->element]['unique'][$shortkey])) {
397  //var_dump($key);
398  //var_dump($clonedObj->array_options[$key]); exit;
399  unset($object->array_options[$key]);
400  }
401  }
402  }
403 
404  // Create clone
405  $object->context['createfromclone'] = 'createfromclone';
406  $result = $object->createCommon($user);
407  if ($result < 0) {
408  $error++;
409  $this->error = $object->error;
410  $this->errors = $object->errors;
411  }
412 
413  if (!$error) {
414  // copy internal contacts
415  if ($this->copy_linked_contact($object, 'internal') < 0) {
416  $error++;
417  }
418  }
419 
420  if (!$error) {
421  // copy external contacts if same company
422  if (property_exists($this, 'socid') && $this->socid == $object->socid) {
423  if ($this->copy_linked_contact($object, 'external') < 0) {
424  $error++;
425  }
426  }
427  }
428 
429  unset($object->context['createfromclone']);
430 
431  // End
432  if (!$error) {
433  $this->db->commit();
434  return $object;
435  } else {
436  $this->db->rollback();
437  return -1;
438  }
439  }
440 
448  public function fetch($id, $ref = null)
449  {
450  $result = $this->fetchCommon($id, $ref);
451  if ($result > 0 && !empty($this->table_element_line)) {
452  $this->fetchLines();
453  }
454 
455  $this->socid = $this->fk_soc;
456 
457  return $result;
458  }
459 
465  public function fetchLines()
466  {
467  $this->lines = array();
468 
469  $result = $this->fetchLinesCommon();
470  return $result;
471  }
472 
473 
485  public function fetchAll($sortorder = '', $sortfield = '', $limit = 0, $offset = 0, $filter = '', $filtermode = 'AND')
486  {
487  dol_syslog(__METHOD__, LOG_DEBUG);
488 
489  $records = array();
490 
491  $sql = 'SELECT ';
492  $sql .= $this->getFieldList();
493  $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as t';
494  if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) {
495  $sql .= ' WHERE t.entity IN ('.getEntity($this->element).')';
496  } else {
497  $sql .= ' WHERE 1 = 1';
498  }
499 
500  // Manage filter
501  if (is_array($filter)) {
502  $sqlwhere = array();
503  if (count($filter) > 0) {
504  foreach ($filter as $key => $value) {
505  if ($key == 't.rowid') {
506  $sqlwhere[] = $this->db->sanitize($key)." = ".((int) $value);
507  } elseif (strpos($key, 'date') !== false) {
508  $sqlwhere[] = $this->db->sanitize($key)." = '".$this->db->idate($value)."'";
509  } else {
510  $sqlwhere[] = $this->db->sanitize($key)." LIKE '%".$this->db->escape($this->db->escapeforlike($value))."%'";
511  }
512  }
513  }
514  if (count($sqlwhere) > 0) {
515  $sql .= ' AND ('.implode(' '.$this->db->escape($filtermode).' ', $sqlwhere).')';
516  }
517 
518  $filter = '';
519  }
520 
521  // Manage filter
522  $errormessage = '';
523  $sql .= forgeSQLFromUniversalSearchCriteria($filter, $errormessage);
524  if ($errormessage) {
525  $this->errors[] = $errormessage;
526  dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
527  return -1;
528  }
529 
530  if (!empty($sortfield)) {
531  $sql .= $this->db->order($sortfield, $sortorder);
532  }
533  if (!empty($limit)) {
534  $sql .= $this->db->plimit($limit, $offset);
535  }
536 
537  $resql = $this->db->query($sql);
538  if ($resql) {
539  $num = $this->db->num_rows($resql);
540  $i = 0;
541  while ($i < min($limit, $num)) {
542  $obj = $this->db->fetch_object($resql);
543 
544  $record = new self($this->db);
545  $record->setVarsFromFetchObj($obj);
546 
547  $records[$record->id] = $record;
548 
549  $i++;
550  }
551  $this->db->free($resql);
552 
553  return $records;
554  } else {
555  $this->errors[] = 'Error '.$this->db->lasterror();
556  dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
557 
558  return -1;
559  }
560  }
561 
569  public function fetchLinesLinked($role, $lineid = 0)
570  {
571  $resarray = array();
572  $mostatic = new MoLine($this->db);
573 
574  $sql = 'SELECT ';
575  $sql .= $mostatic->getFieldList();
576  $sql .= ' FROM '.MAIN_DB_PREFIX.$mostatic->table_element.' as t';
577  $sql .= " WHERE t.role = '".$this->db->escape($role)."'";
578  if ($lineid > 0) {
579  $sql .= ' AND t.fk_mrp_production = '.((int) $lineid);
580  } else {
581  $sql .= 'AND t.fk_mo = '.((int) $this->id);
582  }
583 
584  $resql = $this->db->query($sql);
585  if ($resql) {
586  $num = $this->db->num_rows($resql);
587 
588  $i = 0;
589  while ($i < $num) {
590  $obj = $this->db->fetch_object($resql);
591  if ($obj) {
592  $resarray[] = array(
593  'rowid' => $obj->rowid,
594  'date' => $this->db->jdate($obj->date_creation),
595  'qty' => $obj->qty,
596  'role' => $obj->role,
597  'fk_product' => $obj->fk_product,
598  'fk_warehouse' => $obj->fk_warehouse,
599  'batch' => $obj->batch,
600  'fk_stock_movement' => $obj->fk_stock_movement,
601  'fk_unit' => $obj->fk_unit
602  );
603  }
604 
605  $i++;
606  }
607 
608  return $resarray;
609  } else {
610  $this->error = $this->db->lasterror();
611  return array();
612  }
613  }
614 
615 
621  public function countMovements()
622  {
623  $result = 0;
624 
625  $sql = 'SELECT COUNT(rowid) as nb FROM '.MAIN_DB_PREFIX.'stock_mouvement as sm';
626  $sql .= " WHERE sm.origintype = 'mo' and sm.fk_origin = ".((int) $this->id);
627 
628  $resql = $this->db->query($sql);
629  if ($resql) {
630  $num = $this->db->num_rows($resql);
631 
632  $i = 0;
633  while ($i < $num) {
634  $obj = $this->db->fetch_object($resql);
635  if ($obj) {
636  $result = $obj->nb;
637  }
638 
639  $i++;
640  }
641  } else {
642  $this->error = $this->db->lasterror();
643  }
644 
645  return $result;
646  }
647 
648 
656  public function update(User $user, $notrigger = 0)
657  {
658  $error = 0;
659 
660  $this->db->begin();
661 
662  $result = $this->updateCommon($user, $notrigger);
663  if ($result <= 0) {
664  $error++;
665  }
666 
667  // Update the lines (the qty) to consume or to produce
668  $result = $this->updateProduction($user, $notrigger);
669  if ($result <= 0) {
670  $error++;
671  }
672 
673  if (!$error) {
674  $this->db->commit();
675  return 1;
676  } else {
677  $this->db->rollback();
678  return -1;
679  }
680  }
681 
682 
690  public function createProduction(User $user, $notrigger = 0)
691  {
692  $error = 0;
693  $role = "";
694 
695  if ($this->status != self::STATUS_DRAFT) {
696  return -1;
697  }
698 
699  $this->db->begin();
700 
701  // Insert lines in mrp_production table from BOM data
702  if (!$error) {
703  $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'mrp_production WHERE fk_mo = '.((int) $this->id);
704  $this->db->query($sql);
705 
706  $moline = new MoLine($this->db);
707 
708  // Line to produce
709  $moline->fk_mo = $this->id;
710  $moline->qty = $this->qty;
711  $moline->fk_product = $this->fk_product;
712  $moline->position = 1;
713  include_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
714  $tmpproduct = new Product($this->db);
715  $tmpproduct->fetch($this->fk_product);
716  $moline->fk_unit = $tmpproduct->fk_unit;
717 
718  if ($this->fk_bom > 0) { // If a BOM is defined, we know what to produce.
719  include_once DOL_DOCUMENT_ROOT.'/bom/class/bom.class.php';
720  $bom = new BOM($this->db);
721  $bom->fetch($this->fk_bom);
722  if ($bom->bomtype == 1) {
723  $role = 'toproduce';
724  $moline->role = 'toconsume';
725  } else {
726  $role = 'toconsume';
727  $moline->role = 'toproduce';
728  }
729  } else {
730  if ($this->mrptype == 1) {
731  $moline->role = 'toconsume';
732  } else {
733  $moline->role = 'toproduce';
734  }
735  }
736 
737  $resultline = $moline->create($user, false); // Never use triggers here
738  if ($resultline <= 0) {
739  $error++;
740  $this->error = $moline->error;
741  $this->errors = $moline->errors;
742  }
743 
744  if ($this->fk_bom > 0) { // If a BOM is defined, we know what to consume.
745  if ($bom->id > 0) {
746  // Lines to consume
747  if (!$error) {
748  foreach ($bom->lines as $line) {
749  $moline = new MoLine($this->db);
750 
751  $moline->fk_mo = $this->id;
752  $moline->origin_id = $line->id;
753  $moline->origin_type = 'bomline';
754  if (!empty($line->fk_unit)) {
755  $moline->fk_unit = $line->fk_unit;
756  }
757  if ($line->qty_frozen) {
758  $moline->qty = $line->qty; // Qty to consume does not depends on quantity to produce
759  } else {
760  $moline->qty = (float) price2num(($line->qty / (!empty($bom->qty) ? $bom->qty : 1)) * $this->qty / (!empty($line->efficiency) ? $line->efficiency : 1), 'MS'); // Calculate with Qty to produce and more presition
761  }
762  if ($moline->qty <= 0) {
763  $error++;
764  $this->error = "BadValueForquantityToConsume";
765  break;
766  } else {
767  $moline->fk_product = $line->fk_product;
768  $moline->role = $role;
769  $moline->position = $line->position;
770  $moline->qty_frozen = $line->qty_frozen;
771  $moline->disable_stock_change = $line->disable_stock_change;
772  if (!empty($line->fk_default_workstation)) {
773  $moline->fk_default_workstation = $line->fk_default_workstation;
774  }
775 
776  $resultline = $moline->create($user, false); // Never use triggers here
777  if ($resultline <= 0) {
778  $error++;
779  $this->error = $moline->error;
780  $this->errors = $moline->errors;
781  dol_print_error($this->db, $moline->error, $moline->errors);
782  break;
783  }
784  }
785  }
786  }
787  }
788  }
789  }
790 
791  if (!$error) {
792  $this->db->commit();
793  return 1;
794  } else {
795  $this->db->rollback();
796  return -1;
797  }
798  }
799 
807  public function updateProduction(User $user, $notrigger = 0)
808  {
809  $error = 0;
810 
811  if ($this->status != self::STATUS_DRAFT) {
812  return 1;
813  }
814 
815  $this->db->begin();
816 
817  $oldQty = $this->oldQty;
818  $newQty = $this->qty;
819  if ($newQty != $oldQty && !empty($this->oldQty)) {
820  $sql = "SELECT rowid FROM " . MAIN_DB_PREFIX . "mrp_production WHERE fk_mo = " . (int) $this->id;
821  $resql = $this->db->query($sql);
822  if ($resql) {
823  while ($obj = $this->db->fetch_object($resql)) {
824  $moLine = new MoLine($this->db);
825  $res = $moLine->fetch($obj->rowid);
826  if (!$res) {
827  $error++;
828  }
829 
830  if ($moLine->role == 'toconsume' || $moLine->role == 'toproduce') {
831  if (empty($moLine->qty_frozen)) {
832  $qty = $newQty * $moLine->qty / $oldQty;
833  $moLine->qty = (float) price2num($qty, 'MS');
834  $res = $moLine->update($user);
835  if (!$res) {
836  $error++;
837  }
838  }
839  }
840  }
841  }
842  }
843  if (!$error) {
844  $this->db->commit();
845  return 1;
846  } else {
847  $this->db->rollback();
848  return -1;
849  }
850  }
851 
852 
861  public function delete(User $user, $notrigger = 0, $also_cancel_consumed_and_produced_lines = false)
862  {
863  $error = 0;
864  $this->db->begin();
865 
866  if ($also_cancel_consumed_and_produced_lines) {
867  $result = $this->cancelConsumedAndProducedLines($user, 0, false, $notrigger);
868  if ($result < 0) {
869  $error++;
870  }
871  }
872 
873  if (!$error) {
874  $result = $this->deleteCommon($user, $notrigger);
875  if ($result < 0) {
876  $error++;
877  }
878  }
879 
880  if ($error) {
881  $this->db->rollback();
882  return -1;
883  } else {
884  $this->db->commit();
885  return 1;
886  }
887  }
888 
898  public function deleteLine(User $user, $idline, $notrigger = 0, $fk_movement = 0)
899  {
900  global $langs;
901  $langs->loadLangs(array('stocks', 'mrp'));
902 
903  if ($this->status < 0) {
904  $this->error = 'ErrorDeleteLineNotAllowedByObjectStatus';
905  return -2;
906  }
907  $productstatic = new Product($this->db);
908 
909  $arrayoflines = $this->fetchLinesLinked('consumed', $idline); // Get lines consumed under the one to delete
910 
911  $result = 0;
912 
913  $this->db->begin();
914 
915  if (!empty($arrayoflines)) {
916  // If there is child lines
917  $stockmove = new MouvementStock($this->db);
918  $stockmove->setOrigin($this->element, $this->id);
919 
920  if (!empty($fk_movement)) {
921  // The fk_movement was not recorded so we try to guess the product and quantity to restore.
922  $moline = new MoLine($this->db);
923  $TArrayMoLine = $moline->fetchAll('', '', 1, 0, '(fk_stock_movement:=:'.((int) $fk_movement).')');
924  $moline = array_shift($TArrayMoLine);
925 
926  $movement = new MouvementStock($this->db);
927  $movement->fetch($fk_movement);
928  $productstatic->fetch($movement->product_id);
929  $qtytoprocess = $movement->qty;
930 
931  // Reverse stock movement
932  $labelmovementCancel = $langs->trans("CancelProductionForRef", $productstatic->ref);
933  $codemovementCancel = $langs->trans("StockIncrease");
934 
935  if (($qtytoprocess >= 0)) {
936  $idstockmove = $stockmove->reception($user, $movement->product_id, $movement->warehouse_id, $qtytoprocess, 0, $labelmovementCancel, '', '', $movement->batch, dol_now(), 0, $codemovementCancel);
937  } else {
938  $idstockmove = $stockmove->livraison($user, $movement->product_id, $movement->warehouse_id, $qtytoprocess, 0, $labelmovementCancel, dol_now(), '', '', $movement->batch, 0, $codemovementCancel);
939  }
940  if ($idstockmove < 0) {
941  $this->error++;
942  setEventMessages($stockmove->error, $stockmove->errors, 'errors');
943  } else {
944  $result = $moline->delete($user, $notrigger);
945  }
946  } else {
947  // Loop on each child lines
948  foreach ($arrayoflines as $key => $arrayofline) {
949  $lineDetails = $arrayoflines[$key];
950  $productstatic->fetch($lineDetails['fk_product']);
951  $qtytoprocess = $lineDetails['qty'];
952 
953  // Reverse stock movement
954  $labelmovementCancel = $langs->trans("CancelProductionForRef", $productstatic->ref);
955  $codemovementCancel = $langs->trans("StockIncrease");
956 
957 
958  if ($qtytoprocess >= 0) {
959  $idstockmove = $stockmove->reception($user, $lineDetails['fk_product'], $lineDetails['fk_warehouse'], $qtytoprocess, 0, $labelmovementCancel, '', '', $lineDetails['batch'], dol_now(), 0, $codemovementCancel);
960  } else {
961  $idstockmove = $stockmove->livraison($user, $lineDetails['fk_product'], $lineDetails['fk_warehouse'], $qtytoprocess, 0, $labelmovementCancel, dol_now(), '', '', $lineDetails['batch'], 0, $codemovementCancel);
962  }
963  if ($idstockmove < 0) {
964  $this->error++;
965  setEventMessages($stockmove->error, $stockmove->errors, 'errors');
966  } else {
967  $moline = new MoLine($this->db);
968  $moline->fetch($lineDetails['rowid']);
969 
970  $resdel = $moline->delete($user, $notrigger);
971  if ($resdel < 0) {
972  $this->error++;
973  setEventMessages($moline->error, $moline->errors, 'errors');
974  }
975  }
976  }
977 
978  if (empty($this->error)) {
979  $result = $this->deleteLineCommon($user, $idline, $notrigger);
980  }
981  }
982  } else {
983  // No child lines
984  $result = $this->deleteLineCommon($user, $idline, $notrigger);
985  }
986 
987  if (!empty($this->error) || $result <= 0) {
988  $this->db->rollback();
989  } else {
990  $this->db->commit();
991  }
992 
993  return $result;
994  }
995 
996 
1004  public function getNextNumRef($prod)
1005  {
1006  global $langs, $conf;
1007  $langs->load("mrp");
1008 
1009  if (getDolGlobalString('MRP_MO_ADDON')) {
1010  $mybool = false;
1011 
1012  $file = getDolGlobalString('MRP_MO_ADDON') . ".php";
1013  $classname = getDolGlobalString('MRP_MO_ADDON');
1014 
1015  // Include file with class
1016  $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
1017  foreach ($dirmodels as $reldir) {
1018  $dir = dol_buildpath($reldir."core/modules/mrp/");
1019 
1020  // Load file with numbering class (if found)
1021  $mybool = ((bool) @include_once $dir.$file) || $mybool;
1022  }
1023 
1024  if ($mybool === false) {
1025  dol_print_error(null, "Failed to include file ".$file);
1026  return '';
1027  }
1028 
1029  $obj = new $classname();
1030  $numref = $obj->getNextValue($prod, $this);
1031 
1032  if ($numref != "") {
1033  return $numref;
1034  } else {
1035  $this->error = $obj->error;
1036  //dol_print_error($this->db,get_class($this)."::getNextNumRef ".$obj->error);
1037  return "";
1038  }
1039  } else {
1040  print $langs->trans("Error")." ".$langs->trans("Error_MRP_MO_ADDON_NotDefined");
1041  return "";
1042  }
1043  }
1044 
1052  public function validate($user, $notrigger = 0)
1053  {
1054  global $conf;
1055 
1056  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1057 
1058  $error = 0;
1059 
1060  // Protection
1061  if ($this->status == self::STATUS_VALIDATED) {
1062  dol_syslog(get_class($this)."::validate action abandoned: already validated", LOG_WARNING);
1063  return 0;
1064  }
1065 
1066  /*if (! ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->mrp->create))
1067  || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->mrp->mrp_advance->validate))))
1068  {
1069  $this->error='NotEnoughPermissions';
1070  dol_syslog(get_class($this)."::valid ".$this->error, LOG_ERR);
1071  return -1;
1072  }*/
1073 
1074  $now = dol_now();
1075 
1076  $this->db->begin();
1077 
1078  // Define new ref
1079  if (!$error && (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref))) { // empty should not happened, but when it occurs, the test save life
1080  $this->fetch_product();
1081  $num = $this->getNextNumRef($this->product);
1082  } else {
1083  $num = $this->ref;
1084  }
1085  $this->newref = $num;
1086 
1087  // Validate
1088  $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
1089  $sql .= " SET ref = '".$this->db->escape($num)."',";
1090  $sql .= " status = ".self::STATUS_VALIDATED.",";
1091  $sql .= " date_valid='".$this->db->idate($now)."',";
1092  $sql .= " fk_user_valid = ".$user->id;
1093  $sql .= " WHERE rowid = ".((int) $this->id);
1094 
1095  dol_syslog(get_class($this)."::validate()", LOG_DEBUG);
1096  $resql = $this->db->query($sql);
1097  if (!$resql) {
1098  dol_print_error($this->db);
1099  $this->error = $this->db->lasterror();
1100  $error++;
1101  }
1102 
1103  if (!$error && !$notrigger) {
1104  // Call trigger
1105  $result = $this->call_trigger('MRP_MO_VALIDATE', $user);
1106  if ($result < 0) {
1107  $error++;
1108  }
1109  // End call triggers
1110  }
1111 
1112  if (!$error) {
1113  $this->oldref = $this->ref;
1114 
1115  // Rename directory if dir was a temporary ref
1116  if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
1117  // Now we rename also files into index
1118  $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filename = CONCAT('".$this->db->escape($this->newref)."', SUBSTR(filename, ".(strlen($this->ref) + 1).")), filepath = 'mrp/".$this->db->escape($this->newref)."'";
1119  $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'mrp/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
1120  $resql = $this->db->query($sql);
1121  if (!$resql) {
1122  $error++;
1123  $this->error = $this->db->lasterror();
1124  }
1125  $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filepath = 'mrp/".$this->db->escape($this->newref)."'";
1126  $sql .= " WHERE filepath = 'mrp/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
1127  $resql = $this->db->query($sql);
1128  if (!$resql) {
1129  $error++;
1130  $this->error = $this->db->lasterror();
1131  }
1132 
1133  // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
1134  $oldref = dol_sanitizeFileName($this->ref);
1135  $newref = dol_sanitizeFileName($num);
1136  $dirsource = $conf->mrp->dir_output.'/'.$oldref;
1137  $dirdest = $conf->mrp->dir_output.'/'.$newref;
1138  if (!$error && file_exists($dirsource)) {
1139  dol_syslog(get_class($this)."::validate() rename dir ".$dirsource." into ".$dirdest);
1140 
1141  if (@rename($dirsource, $dirdest)) {
1142  dol_syslog("Rename ok");
1143  // Rename docs starting with $oldref with $newref
1144  $listoffiles = dol_dir_list($conf->mrp->dir_output.'/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
1145  foreach ($listoffiles as $fileentry) {
1146  $dirsource = $fileentry['name'];
1147  $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
1148  $dirsource = $fileentry['path'].'/'.$dirsource;
1149  $dirdest = $fileentry['path'].'/'.$dirdest;
1150  @rename($dirsource, $dirdest);
1151  }
1152  }
1153  }
1154  }
1155  }
1156 
1157  // Set new ref and current status
1158  if (!$error) {
1159  $this->ref = $num;
1160  $this->status = self::STATUS_VALIDATED;
1161  }
1162 
1163  if (!$error) {
1164  $this->db->commit();
1165  return 1;
1166  } else {
1167  $this->db->rollback();
1168  return -1;
1169  }
1170  }
1171 
1179  public function setDraft($user, $notrigger = 0)
1180  {
1181  // Protection
1182  if ($this->status <= self::STATUS_DRAFT) {
1183  return 0;
1184  }
1185 
1186  /*if (! ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->mymodule->write))
1187  || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->mymodule->mymodule_advance->validate))))
1188  {
1189  $this->error='Permission denied';
1190  return -1;
1191  }*/
1192 
1193  return $this->setStatusCommon($user, self::STATUS_DRAFT, $notrigger, 'MRP_MO_UNVALIDATE');
1194  }
1195 
1204  public function cancel($user, $notrigger = 0, $also_cancel_consumed_and_produced_lines = false)
1205  {
1206  // Protection
1207  if ($this->status != self::STATUS_VALIDATED && $this->status != self::STATUS_INPROGRESS) {
1208  return 0;
1209  }
1210 
1211  /*if (! ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->mymodule->write))
1212  || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->mymodule->mymodule_advance->validate))))
1213  {
1214  $this->error='Permission denied';
1215  return -1;
1216  }*/
1217 
1218  $error = 0;
1219  $this->db->begin();
1220 
1221  if ($also_cancel_consumed_and_produced_lines) {
1222  $result = $this->cancelConsumedAndProducedLines($user, 0, true, $notrigger);
1223  if ($result < 0) {
1224  $error++;
1225  }
1226  }
1227 
1228  if (!$error) {
1229  $result = $this->setStatusCommon($user, self::STATUS_CANCELED, $notrigger, 'MRP_MO_CANCEL');
1230  if ($result < 0) {
1231  $error++;
1232  }
1233  }
1234 
1235  if ($error) {
1236  $this->db->rollback();
1237  return -1;
1238  } else {
1239  $this->db->commit();
1240  return 1;
1241  }
1242  }
1243 
1251  public function reopen($user, $notrigger = 0)
1252  {
1253  // Protection
1254  if ($this->status != self::STATUS_PRODUCED && $this->status != self::STATUS_CANCELED) {
1255  return 0;
1256  }
1257 
1258  /*if (! ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->mymodule->write))
1259  || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->mymodule->mymodule_advance->validate))))
1260  {
1261  $this->error='Permission denied';
1262  return -1;
1263  }*/
1264 
1265  return $this->setStatusCommon($user, self::STATUS_VALIDATED, $notrigger, 'MRP_MO_REOPEN');
1266  }
1267 
1277  public function cancelConsumedAndProducedLines($user, $mode = 0, $also_delete_lines = false, $notrigger = 0)
1278  {
1279  global $langs;
1280 
1281  if (!isModEnabled('stock')) {
1282  return 1;
1283  }
1284 
1285  require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
1286  require_once DOL_DOCUMENT_ROOT . '/product/stock/class/mouvementstock.class.php';
1287  $error = 0;
1288  $langs->load('stocks');
1289 
1290  $this->db->begin();
1291 
1292  // Cancel consumed lines
1293  if (empty($mode) || $mode == 1) {
1294  $arrayoflines = $this->fetchLinesLinked('consumed');
1295  if (!empty($arrayoflines)) {
1296  foreach ($arrayoflines as $key => $lineDetails) {
1297  $productstatic = new Product($this->db);
1298  $productstatic->fetch($lineDetails['fk_product']);
1299  $qtytoprocess = $lineDetails['qty'];
1300 
1301  // Reverse stock movement
1302  $labelmovementCancel = $langs->trans("CancelProductionForRef", $productstatic->ref);
1303  $codemovementCancel = $langs->trans("StockIncrease");
1304 
1305  $stockmove = new MouvementStock($this->db);
1306  $stockmove->setOrigin($this->element, $this->id);
1307  if ($qtytoprocess >= 0) {
1308  $idstockmove = $stockmove->reception($user, $lineDetails['fk_product'], $lineDetails['fk_warehouse'], $qtytoprocess, 0, $labelmovementCancel, '', '', $lineDetails['batch'], dol_now(), 0, $codemovementCancel);
1309  } else {
1310  $idstockmove = $stockmove->livraison($user, $lineDetails['fk_product'], $lineDetails['fk_warehouse'], $qtytoprocess, 0, $labelmovementCancel, dol_now(), '', '', $lineDetails['batch'], 0, $codemovementCancel);
1311  }
1312  if ($idstockmove < 0) {
1313  $this->error = $stockmove->error;
1314  $this->errors = $stockmove->errors;
1315  $error++;
1316  break;
1317  }
1318 
1319  if ($also_delete_lines) {
1320  $result = $this->deleteLineCommon($user, $lineDetails['rowid'], $notrigger);
1321  if ($result < 0) {
1322  $error++;
1323  break;
1324  }
1325  }
1326  }
1327  }
1328  }
1329 
1330  // Cancel produced lines
1331  if (empty($mode) || $mode == 2) {
1332  $arrayoflines = $this->fetchLinesLinked('produced');
1333  if (!empty($arrayoflines)) {
1334  foreach ($arrayoflines as $key => $lineDetails) {
1335  $productstatic = new Product($this->db);
1336  $productstatic->fetch($lineDetails['fk_product']);
1337  $qtytoprocess = $lineDetails['qty'];
1338 
1339  // Reverse stock movement
1340  $labelmovementCancel = $langs->trans("CancelProductionForRef", $productstatic->ref);
1341  $codemovementCancel = $langs->trans("StockDecrease");
1342 
1343  $stockmove = new MouvementStock($this->db);
1344  $stockmove->setOrigin($this->element, $this->id);
1345  if ($qtytoprocess >= 0) {
1346  $idstockmove = $stockmove->livraison($user, $lineDetails['fk_product'], $lineDetails['fk_warehouse'], $qtytoprocess, 0, $labelmovementCancel, dol_now(), '', '', $lineDetails['batch'], 0, $codemovementCancel);
1347  } else {
1348  $idstockmove = $stockmove->reception($user, $lineDetails['fk_product'], $lineDetails['fk_warehouse'], $qtytoprocess, 0, $labelmovementCancel, '', '', $lineDetails['batch'], dol_now(), 0, $codemovementCancel);
1349  }
1350  if ($idstockmove < 0) {
1351  $this->error = $stockmove->error;
1352  $this->errors = $stockmove->errors;
1353  $error++;
1354  break;
1355  }
1356 
1357  if ($also_delete_lines) {
1358  $result = $this->deleteLineCommon($user, $lineDetails['rowid'], $notrigger);
1359  if ($result < 0) {
1360  $error++;
1361  break;
1362  }
1363  }
1364  }
1365  }
1366  }
1367 
1368  if ($error) {
1369  $this->db->rollback();
1370  return -1;
1371  } else {
1372  $this->db->commit();
1373  return 1;
1374  }
1375  }
1376 
1384  public function getTooltipContentArray($params)
1385  {
1386  global $langs;
1387 
1388  $langs->loadLangs(['mrp', 'products']);
1389  $nofetch = isset($params['nofetch']) ? true : false;
1390 
1391  $datas = [];
1392 
1393  $datas['picto'] = img_picto('', $this->picto).' <u class="paddingrightonly">'.$langs->trans("ManufacturingOrder").'</u>';
1394  if (isset($this->status)) {
1395  $datas['picto'] .= ' '.$this->getLibStatut(5);
1396  }
1397  $datas['ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
1398  if (isset($this->label)) {
1399  $datas['label'] = '<br><b>'.$langs->trans('Label').':</b> '.$this->label;
1400  }
1401  if (isset($this->mrptype)) {
1402  $datas['type'] = '<br><b>'.$langs->trans('Type').':</b> '.$this->fields['mrptype']['arrayofkeyval'][$this->mrptype];
1403  }
1404  if (isset($this->qty)) {
1405  $datas['qty'] = '<br><b>'.$langs->trans('QtyToProduce').':</b> '.$this->qty;
1406  }
1407  if (!$nofetch && isset($this->fk_product)) {
1408  require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
1409  $product = new Product($this->db);
1410  $product->fetch($this->fk_product);
1411  $datas['product'] = '<br><b>'.$langs->trans('Product').':</b> '.$product->getNomUrl(1, '', 0, -1, 1);
1412  }
1413  if (!$nofetch && isset($this->fk_warehouse)) {
1414  require_once DOL_DOCUMENT_ROOT . '/product/stock/class/entrepot.class.php';
1415  $warehouse = new Entrepot($this->db);
1416  $warehouse->fetch($this->fk_warehouse);
1417  $datas['warehouse'] = '<br><b>'.$langs->trans('WarehouseForProduction').':</b> '.$warehouse->getNomUrl(1, '', 0, 1);
1418  }
1419 
1420  return $datas;
1421  }
1422 
1433  public function getNomUrl($withpicto = 0, $option = '', $notooltip = 0, $morecss = '', $save_lastsearch_value = -1)
1434  {
1435  global $conf, $langs, $action, $hookmanager;
1436 
1437  if (!empty($conf->dol_no_mouse_hover)) {
1438  $notooltip = 1; // Force disable tooltips
1439  }
1440 
1441  $result = '';
1442  $params = [
1443  'id' => $this->id,
1444  'objecttype' => $this->element,
1445  'option' => $option,
1446  'nofetch' => 1,
1447  ];
1448  $classfortooltip = 'classfortooltip';
1449  $dataparams = '';
1450  if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
1451  $classfortooltip = 'classforajaxtooltip';
1452  $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
1453  $label = '';
1454  } else {
1455  $label = implode($this->getTooltipContentArray($params));
1456  }
1457 
1458  $url = DOL_URL_ROOT.'/mrp/mo_card.php?id='.$this->id;
1459  if ($option == 'production') {
1460  $url = DOL_URL_ROOT.'/mrp/mo_production.php?id='.$this->id;
1461  }
1462 
1463  if ($option != 'nolink') {
1464  // Add param to save lastsearch_values or not
1465  $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
1466  if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
1467  $add_save_lastsearch_values = 1;
1468  }
1469  if ($add_save_lastsearch_values) {
1470  $url .= '&save_lastsearch_values=1';
1471  }
1472  }
1473 
1474  $linkclose = '';
1475  if (empty($notooltip)) {
1476  if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
1477  $label = $langs->trans("ShowMo");
1478  $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
1479  }
1480  $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
1481  $linkclose .= $dataparams.' class="'.$classfortooltip.($morecss ? ' '.$morecss : '').'"';
1482  } else {
1483  $linkclose = ($morecss ? ' class="'.$morecss.'"' : '');
1484  }
1485 
1486  $linkstart = '<a href="'.$url.'"';
1487  $linkstart .= $linkclose.'>';
1488  $linkend = '</a>';
1489 
1490  $result .= $linkstart;
1491  if ($withpicto) {
1492  $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), (($withpicto != 2) ? 'class="paddingright"' : ''), 0, 0, $notooltip ? 0 : 1);
1493  }
1494  if ($withpicto != 2) {
1495  $result .= $this->ref;
1496  }
1497  $result .= $linkend;
1498  //if ($withpicto != 2) $result.=(($addlabel && $this->label) ? $sep . dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : '');
1499 
1500  $hookmanager->initHooks(array('modao'));
1501  $parameters = array('id' => $this->id, 'getnomurl' => &$result);
1502  $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1503  if ($reshook > 0) {
1504  $result = $hookmanager->resPrint;
1505  } else {
1506  $result .= $hookmanager->resPrint;
1507  }
1508 
1509  return $result;
1510  }
1511 
1518  public function getLibStatut($mode = 0)
1519  {
1520  return $this->LibStatut($this->status, $mode);
1521  }
1522 
1523  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1531  public function LibStatut($status, $mode = 0)
1532  {
1533  // phpcs:enable
1534  if (empty($this->labelStatus)) {
1535  global $langs;
1536  //$langs->load("mrp");
1537  $this->labelStatus[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv('Draft');
1538  $this->labelStatus[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv('ValidatedToProduce');
1539  $this->labelStatus[self::STATUS_INPROGRESS] = $langs->transnoentitiesnoconv('InProgress');
1540  $this->labelStatus[self::STATUS_PRODUCED] = $langs->transnoentitiesnoconv('StatusMOProduced');
1541  $this->labelStatus[self::STATUS_CANCELED] = $langs->transnoentitiesnoconv('Canceled');
1542 
1543  $this->labelStatusShort[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv('Draft');
1544  $this->labelStatusShort[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv('Validated');
1545  $this->labelStatusShort[self::STATUS_INPROGRESS] = $langs->transnoentitiesnoconv('InProgress');
1546  $this->labelStatusShort[self::STATUS_PRODUCED] = $langs->transnoentitiesnoconv('StatusMOProduced');
1547  $this->labelStatusShort[self::STATUS_CANCELED] = $langs->transnoentitiesnoconv('Canceled');
1548  }
1549 
1550  $statusType = 'status'.$status;
1551  if ($status == self::STATUS_VALIDATED) {
1552  $statusType = 'status1';
1553  }
1554  if ($status == self::STATUS_INPROGRESS) {
1555  $statusType = 'status4';
1556  }
1557  if ($status == self::STATUS_PRODUCED) {
1558  $statusType = 'status6';
1559  }
1560  if ($status == self::STATUS_CANCELED) {
1561  $statusType = 'status9';
1562  }
1563 
1564  return dolGetStatus($this->labelStatus[$status], $this->labelStatusShort[$status], '', $statusType, $mode);
1565  }
1566 
1573  public function info($id)
1574  {
1575  $sql = 'SELECT rowid, date_creation as datec, tms as datem,';
1576  $sql .= ' fk_user_creat, fk_user_modif';
1577  $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as t';
1578  $sql .= ' WHERE t.rowid = '.((int) $id);
1579  $result = $this->db->query($sql);
1580  if ($result) {
1581  if ($this->db->num_rows($result)) {
1582  $obj = $this->db->fetch_object($result);
1583 
1584  $this->id = $obj->rowid;
1585 
1586  $this->user_creation_id = $obj->fk_user_creat;
1587  $this->user_modification_id = $obj->fk_user_modif;
1588  $this->date_creation = $this->db->jdate($obj->datec);
1589  $this->date_modification = empty($obj->datem) ? '' : $this->db->jdate($obj->datem);
1590  }
1591 
1592  $this->db->free($result);
1593  } else {
1594  dol_print_error($this->db);
1595  }
1596  }
1597 
1604  public function initAsSpecimen()
1605  {
1606  $ret = $this->initAsSpecimenCommon();
1607 
1608  $this->lines = array();
1609 
1610  return $ret;
1611  }
1612 
1619  public function getLinesArray($rolefilter = '')
1620  {
1621  $this->lines = array();
1622 
1623  $objectline = new MoLine($this->db);
1624 
1625  $filter = '(fk_mo:=:'.((int) $this->id).')';
1626  if (!empty($rolefilter)) {
1627  $filter .= " AND (role:=:'".$this->db->escape($rolefilter)."')";
1628  }
1629  $result = $objectline->fetchAll('ASC', 'position', 0, 0, $filter);
1630 
1631  if (is_numeric($result)) {
1632  $this->error = $objectline->error;
1633  $this->errors = $objectline->errors;
1634  return $result;
1635  } else {
1636  $this->lines = $result;
1637  return $this->lines;
1638  }
1639  }
1640 
1652  public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
1653  {
1654  global $langs;
1655 
1656  $langs->load("mrp");
1657 
1658  if (!dol_strlen($modele)) {
1659  //$modele = 'standard';
1660  $modele = ''; // Remove this once a pdf_standard.php exists.
1661 
1662  if ($this->model_pdf) {
1663  $modele = $this->model_pdf;
1664  } elseif (getDolGlobalString('MRP_MO_ADDON_PDF')) {
1665  $modele = getDolGlobalString('MRP_MO_ADDON_PDF');
1666  }
1667  }
1668 
1669  $modelpath = "core/modules/mrp/doc/";
1670 
1671  if (empty($modele)) {
1672  return 1; // Remove this once a pdf_standard.php exists.
1673  }
1674 
1675  return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
1676  }
1677 
1688  public function printOriginLinesList($restrictlist = '', $selectedLines = array())
1689  {
1690  global $langs, $hookmanager, $form, $action;
1691 
1692  $langs->load('stocks');
1693  $text_stock_options = $langs->trans("RealStockDesc").'<br>';
1694  $text_stock_options .= $langs->trans("RealStockWillAutomaticallyWhen").'<br>';
1695  $text_stock_options .= (getDolGlobalString('STOCK_CALCULATE_ON_SHIPMENT') || getDolGlobalString('STOCK_CALCULATE_ON_SHIPMENT_CLOSE') ? '- '.$langs->trans("DeStockOnShipment").'<br>' : '');
1696  $text_stock_options .= (getDolGlobalString('STOCK_CALCULATE_ON_VALIDATE_ORDER') ? '- '.$langs->trans("DeStockOnValidateOrder").'<br>' : '');
1697  $text_stock_options .= (getDolGlobalString('STOCK_CALCULATE_ON_BILL') ? '- '.$langs->trans("DeStockOnBill").'<br>' : '');
1698  $text_stock_options .= (getDolGlobalString('STOCK_CALCULATE_ON_SUPPLIER_BILL') ? '- '.$langs->trans("ReStockOnBill").'<br>' : '');
1699  $text_stock_options .= (getDolGlobalString('STOCK_CALCULATE_ON_SUPPLIER_VALIDATE_ORDER') ? '- '.$langs->trans("ReStockOnValidateOrder").'<br>' : '');
1700  $text_stock_options .= (getDolGlobalString('STOCK_CALCULATE_ON_SUPPLIER_DISPATCH_ORDER') ? '- '.$langs->trans("ReStockOnDispatchOrder").'<br>' : '');
1701  $text_stock_options .= (getDolGlobalString('STOCK_CALCULATE_ON_RECEPTION') || getDolGlobalString('STOCK_CALCULATE_ON_RECEPTION_CLOSE') ? '- '.$langs->trans("StockOnReception").'<br>' : '');
1702 
1703  print '<tr class="liste_titre">';
1704  // Product or sub-bom
1705  print '<td class="linecoldescription">'.$langs->trans('Ref');
1706  if (getDolGlobalString('BOM_SUB_BOM')) {
1707  print ' &nbsp; <a id="show_all" href="#">'.img_picto('', 'folder-open', 'class="paddingright"').$langs->trans("ExpandAll").'</a>&nbsp;&nbsp;';
1708  print '<a id="hide_all" href="#">'.img_picto('', 'folder', 'class="paddingright"').$langs->trans("UndoExpandAll").'</a>&nbsp;';
1709  }
1710  print '</td>';
1711  // Qty
1712  print '<td class="right">'.$langs->trans('Qty');
1713  if ($this->bom->bomtype == 0) {
1714  print ' <span class="opacitymedium">('.$langs->trans("ForAQuantityOf", $this->bom->qty).')</span>';
1715  } else {
1716  print ' <span class="opacitymedium">('.$langs->trans("ForAQuantityToConsumeOf", $this->bom->qty).')</span>';
1717  }
1718  // Unit
1719  print '<td class="right">'.$langs->trans('Unit');
1720 
1721  print '</td>';
1722  print '<td class="center">'.$form->textwithpicto($langs->trans("PhysicalStock"), $text_stock_options, 1).'</td>';
1723  print '<td class="center">'.$form->textwithpicto($langs->trans("VirtualStock"), $langs->trans("VirtualStockDesc")).'</td>';
1724  print '<td class="center">'.$langs->trans('QtyFrozen').'</td>';
1725  print '<td class="center">'.$langs->trans('DisableStockChange').'</td>';
1726  print '<td class="center">'.$langs->trans('MoChildGenerate').'</td>';
1727  //print '<td class="center">'.$form->showCheckAddButtons('checkforselect', 1).'</td>';
1728  //print '<td class="center"></td>';
1729  print '</tr>';
1730  $i = 0;
1731 
1732  if (!empty($this->lines)) {
1733  foreach ($this->lines as $line) {
1734  $reshook = 0;
1735  if (is_object($hookmanager)) {
1736  $parameters = array('line' => $line, 'i' => $i, 'restrictlist' => $restrictlist, 'selectedLines' => $selectedLines);
1737  if (!empty($line->fk_parent_line)) {
1738  $parameters['fk_parent_line'] = $line->fk_parent_line;
1739  }
1740  $reshook = $hookmanager->executeHooks('printOriginObjectLine', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1741  }
1742  if (empty($reshook)) {
1743  $this->printOriginLine($line, '', $restrictlist, '/core/tpl', $selectedLines);
1744  }
1745 
1746  $i++;
1747  }
1748  }
1749  }
1750 
1751 
1765  public function printOriginLine($line, $var, $restrictlist = '', $defaulttpldir = '/core/tpl', $selectedLines = array())
1766  {
1767  $productstatic = new Product($this->db);
1768 
1769  $this->tpl['id'] = $line->id;
1770 
1771  $this->tpl['label'] = '';
1772  if (!empty($line->fk_product) && $line->fk_product > 0) {
1773  $productstatic->fetch($line->fk_product);
1774  $productstatic->load_virtual_stock();
1775  $this->tpl['label'] .= $productstatic->getNomUrl(1);
1776  //$this->tpl['label'].= ' - '.$productstatic->label;
1777  } else {
1778  // If origin MO line is not a product, but another MO
1779  // TODO
1780  }
1781 
1782  $this->tpl['qty_bom'] = 1;
1783  if (is_object($this->bom) && $this->bom->qty > 1) {
1784  $this->tpl['qty_bom'] = $this->bom->qty;
1785  }
1786 
1787  $this->tpl['stock'] = $productstatic->stock_reel;
1788  $this->tpl['seuil_stock_alerte'] = $productstatic->seuil_stock_alerte;
1789  $this->tpl['virtual_stock'] = $productstatic->stock_theorique;
1790  $this->tpl['qty'] = $line->qty;
1791  $this->tpl['fk_unit'] = $line->fk_unit;
1792  $this->tpl['qty_frozen'] = $line->qty_frozen;
1793  $this->tpl['disable_stock_change'] = $line->disable_stock_change;
1794  $this->tpl['efficiency'] = $line->efficiency;
1795 
1796  global $conf; // used into template
1797  $res = include DOL_DOCUMENT_ROOT.'/mrp/tpl/originproductline.tpl.php';
1798  }
1799 
1808  public static function replaceThirdparty($db, $origin_id, $dest_id)
1809  {
1810  $tables = array('mrp_mo');
1811 
1812  return CommonObject::commonReplaceThirdparty($db, $origin_id, $dest_id, $tables);
1813  }
1814 
1815 
1821  public function getMoChilds()
1822  {
1823  $TMoChilds = array();
1824  $error = 0;
1825 
1826  $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."mrp_mo as mo_child";
1827  $sql .= " WHERE fk_parent_line IN ";
1828  $sql .= " (SELECT rowid FROM ".MAIN_DB_PREFIX."mrp_production as line_parent";
1829  $sql .= " WHERE fk_mo=".((int) $this->id).")";
1830 
1831  $resql = $this->db->query($sql);
1832 
1833  if ($resql) {
1834  if ($this->db->num_rows($resql) > 0) {
1835  while ($obj = $this->db->fetch_object($resql)) {
1836  $MoChild = new Mo($this->db);
1837  $res = $MoChild->fetch($obj->rowid);
1838  if ($res > 0) {
1839  $TMoChilds[$MoChild->id] = $MoChild;
1840  } else {
1841  $error++;
1842  }
1843  }
1844  }
1845  } else {
1846  $error++;
1847  }
1848 
1849  if ($error) {
1850  return -1;
1851  } else {
1852  return $TMoChilds;
1853  }
1854  }
1855 
1862  public function getAllMoChilds($depth = 0)
1863  {
1864  if ($depth > 1000) {
1865  return -1;
1866  }
1867 
1868  $TMoChilds = array();
1869  $error = 0;
1870 
1871  $childMoList = $this->getMoChilds();
1872 
1873  if ($childMoList == -1) {
1874  return -1;
1875  }
1876 
1877  foreach ($childMoList as $childMo) {
1878  $TMoChilds[$childMo->id] = $childMo;
1879  }
1880 
1881  foreach ($childMoList as $childMo) {
1882  $childMoChildren = $childMo->getAllMoChilds($depth + 1);
1883 
1884  if ($childMoChildren == -1) {
1885  $error++;
1886  } else {
1887  foreach ($childMoChildren as $child) {
1888  $TMoChilds[$child->id] = $child;
1889  }
1890  }
1891  }
1892 
1893  if ($error) {
1894  return -1;
1895  } else {
1896  return $TMoChilds;
1897  }
1898  }
1899 
1900 
1901 
1907  public function getMoParent()
1908  {
1909  $MoParent = new Mo($this->db);
1910  $error = 0;
1911 
1912  $sql = "SELECT lineparent.fk_mo as id_moparent FROM ".MAIN_DB_PREFIX."mrp_mo as mo";
1913  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."mrp_production lineparent ON mo.fk_parent_line = lineparent.rowid";
1914  $sql .= " WHERE mo.rowid = ".((int) $this->id);
1915 
1916  $resql = $this->db->query($sql);
1917 
1918  if ($resql) {
1919  if ($this->db->num_rows($resql) > 0) {
1920  $obj = $this->db->fetch_object($resql);
1921  $res = $MoParent->fetch($obj->id_moparent);
1922  if ($res < 0) {
1923  $error++;
1924  }
1925  } else {
1926  return 0;
1927  }
1928  } else {
1929  $error++;
1930  }
1931 
1932  if ($error) {
1933  return -1;
1934  } else {
1935  return $MoParent;
1936  }
1937  }
1938 
1946  public function getKanbanView($option = '', $arraydata = null)
1947  {
1948  global $langs;
1949 
1950  $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
1951 
1952  $return = '<div class="box-flex-item box-flex-grow-zero">';
1953  $return .= '<div class="info-box info-box-sm">';
1954  $return .= '<span class="info-box-icon bg-infobox-action">';
1955  $return .= img_picto('', $this->picto);
1956  //$return .= '<i class="fa fa-dol-action"></i>'; // Can be image
1957  $return .= '</span>';
1958  $return .= '<div class="info-box-content">';
1959  $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref).'</span>';
1960  if ($selected >= 0) {
1961  $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
1962  }
1963  if (!empty($arraydata['bom'])) {
1964  $return .= '<br><span class="info-box-label">'.$arraydata['bom']->getNomUrl(1).'</span>';
1965  }
1966  if (!empty($arraydata['product'])) {
1967  $return .= '<br><span class="info-box-label">'.$arraydata['product']->getNomUrl(1).'</span>';
1968  if (property_exists($this, 'qty')) {
1969  $return .= ' <span class="info-box-label">('.$langs->trans("Qty").' '.$this->qty.')</span>';
1970  }
1971  } else {
1972  if (property_exists($this, 'qty')) {
1973  $return .= '<br><span class="info-box-label">'.$langs->trans('Quantity').' : '.$this->qty.'</span>';
1974  }
1975  }
1976  if (method_exists($this, 'getLibStatut')) {
1977  $return .= '<br><div class="info-box-status">'.$this->getLibStatut(3).'</div>';
1978  }
1979  $return .= '</div>';
1980  $return .= '</div>';
1981  $return .= '</div>';
1982  return $return;
1983  }
1984 }
1985 
1986 
1991 {
1995  public $element = 'mrp_production';
1996 
2000  public $table_element = 'mrp_production';
2001 
2005  public $parent_element = 'mo';
2006 
2010  public $fk_parent_attribute = 'fk_mo';
2011 
2052  public $fields = array(
2053  'rowid' => array('type' => 'integer', 'label' => 'ID', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 10),
2054  'fk_mo' => array('type' => 'integer', 'label' => 'Mo', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 15),
2055  'origin_id' => array('type' => 'integer', 'label' => 'Origin', 'enabled' => 1, 'visible' => -1, 'notnull' => 0, 'position' => 17),
2056  'origin_type' => array('type' => 'varchar(10)', 'label' => 'Origin type', 'enabled' => 1, 'visible' => -1, 'notnull' => 0, 'position' => 18),
2057  'position' => array('type' => 'integer', 'label' => 'Position', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 20),
2058  'fk_product' => array('type' => 'integer', 'label' => 'Product', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 25),
2059  'fk_warehouse' => array('type' => 'integer', 'label' => 'Warehouse', 'enabled' => 1, 'visible' => -1, 'position' => 30),
2060  'qty' => array('type' => 'real', 'label' => 'Qty', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 35),
2061  'qty_frozen' => array('type' => 'smallint', 'label' => 'QuantityFrozen', 'enabled' => 1, 'visible' => 1, 'default' => '0', 'notnull' => 1, 'position' => 105, 'css' => 'maxwidth50imp', 'help' => 'QuantityConsumedInvariable'),
2062  'disable_stock_change' => array('type' => 'smallint', 'label' => 'DisableStockChange', 'enabled' => 1, 'visible' => 1, 'default' => '0', 'notnull' => 1, 'position' => 108, 'css' => 'maxwidth50imp', 'help' => 'DisableStockChangeHelp'),
2063  'batch' => array('type' => 'varchar(30)', 'label' => 'Batch', 'enabled' => 1, 'visible' => -1, 'position' => 140),
2064  'role' => array('type' => 'varchar(10)', 'label' => 'Role', 'enabled' => 1, 'visible' => -1, 'position' => 145),
2065  'fk_mrp_production' => array('type' => 'integer', 'label' => 'Fk mrp production', 'enabled' => 1, 'visible' => -1, 'position' => 150),
2066  'fk_stock_movement' => array('type' => 'integer', 'label' => 'StockMovement', 'enabled' => 1, 'visible' => -1, 'position' => 155),
2067  'date_creation' => array('type' => 'datetime', 'label' => 'DateCreation', 'enabled' => 1, 'visible' => -2, 'notnull' => 1, 'position' => 160),
2068  'tms' => array('type' => 'timestamp', 'label' => 'Tms', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 165),
2069  'fk_user_creat' => array('type' => 'integer', 'label' => 'UserCreation', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 170),
2070  'fk_user_modif' => array('type' => 'integer', 'label' => 'UserModification', 'enabled' => 1, 'visible' => -1, 'position' => 175),
2071  'import_key' => array('type' => 'varchar(14)', 'label' => 'ImportId', 'enabled' => 1, 'visible' => -1, 'position' => 180),
2072  'fk_default_workstation' => array('type' => 'integer', 'label' => 'DefaultWorkstation', 'enabled' => 1, 'visible' => 1, 'notnull' => 0, 'position' => 185),
2073  'fk_unit' => array('type' => 'int', 'label' => 'Unit', 'enabled' => 1, 'visible' => 1, 'notnull' => 0, 'position' => 186)
2074  );
2075 
2076  public $rowid;
2077  public $fk_mo;
2078  public $origin_id;
2079  public $origin_type;
2080  public $position;
2081  public $fk_product;
2082  public $fk_warehouse;
2083 
2087  public $qty;
2088 
2092  public $qty_frozen;
2093  public $disable_stock_change;
2094  public $efficiency;
2095 
2099  public $batch;
2100  public $role;
2101  public $fk_mrp_production;
2102  public $fk_stock_movement;
2103  public $import_key;
2104  public $fk_parent_line;
2105  public $fk_unit;
2106 
2110  public $fk_default_workstation;
2111 
2117  public function __construct(DoliDB $db)
2118  {
2119  global $langs;
2120 
2121  $this->db = $db;
2122 
2123  $this->ismultientitymanaged = 0;
2124  $this->isextrafieldmanaged = 1;
2125 
2126  if (!getDolGlobalString('MAIN_SHOW_TECHNICAL_ID') && isset($this->fields['rowid'])) {
2127  $this->fields['rowid']['visible'] = 0;
2128  }
2129  if (!isModEnabled('multicompany') && isset($this->fields['entity'])) {
2130  $this->fields['entity']['enabled'] = 0;
2131  }
2132 
2133  // Unset fields that are disabled
2134  foreach ($this->fields as $key => $val) {
2135  if (isset($val['enabled']) && empty($val['enabled'])) {
2136  unset($this->fields[$key]);
2137  }
2138  }
2139 
2140  // Translate some data of arrayofkeyval
2141  if (is_object($langs)) {
2142  foreach ($this->fields as $key => $val) {
2143  if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
2144  foreach ($val['arrayofkeyval'] as $key2 => $val2) {
2145  $this->fields[$key]['arrayofkeyval'][$key2] = $langs->trans($val2);
2146  }
2147  }
2148  }
2149  }
2150  }
2151 
2159  public function create(User $user, $notrigger = 0)
2160  {
2161  if (empty($this->qty)) {
2162  $this->error = 'ErrorEmptyValueForQty';
2163  return -1;
2164  }
2165 
2166  return $this->createCommon($user, $notrigger);
2167  }
2168 
2176  public function fetch($id, $ref = null)
2177  {
2178  $result = $this->fetchCommon($id, $ref);
2179  return $result;
2180  }
2181 
2193  public function fetchAll($sortorder = '', $sortfield = '', $limit = 0, $offset = 0, $filter = '', $filtermode = 'AND')
2194  {
2195  dol_syslog(__METHOD__, LOG_DEBUG);
2196 
2197  $records = array();
2198 
2199  $sql = 'SELECT ';
2200  $sql .= $this->getFieldList();
2201  $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as t';
2202  if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) {
2203  $sql .= ' WHERE t.entity IN ('.getEntity($this->element).')';
2204  } else {
2205  $sql .= ' WHERE 1 = 1';
2206  }
2207 
2208  // Deprecated.
2209  if (is_array($filter)) {
2210  $sqlwhere = array();
2211  if (count($filter) > 0) {
2212  foreach ($filter as $key => $value) {
2213  if ($key == 't.rowid') {
2214  $sqlwhere[] = $key." = ".((int) $value);
2215  } elseif (strpos($key, 'date') !== false) {
2216  $sqlwhere[] = $key." = '".$this->db->idate($value)."'";
2217  } else {
2218  $sqlwhere[] = $key." LIKE '%".$this->db->escape($this->db->escapeforlike($value))."%'";
2219  }
2220  }
2221  }
2222  if (count($sqlwhere) > 0) {
2223  $sql .= ' AND ('.implode(' '.$this->db->escape($filtermode).' ', $sqlwhere).')';
2224  }
2225 
2226  $filter = '';
2227  }
2228 
2229  // Manage filter
2230  $errormessage = '';
2231  $sql .= forgeSQLFromUniversalSearchCriteria($filter, $errormessage);
2232  if ($errormessage) {
2233  $this->errors[] = $errormessage;
2234  dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
2235  return -1;
2236  }
2237 
2238  if (!empty($sortfield)) {
2239  $sql .= $this->db->order($sortfield, $sortorder);
2240  }
2241  if (!empty($limit)) {
2242  $sql .= $this->db->plimit($limit, $offset);
2243  }
2244 
2245  $resql = $this->db->query($sql);
2246  if ($resql) {
2247  $num = $this->db->num_rows($resql);
2248  $i = 0;
2249  while ($i < ($limit ? min($limit, $num) : $num)) {
2250  $obj = $this->db->fetch_object($resql);
2251 
2252  $record = new self($this->db);
2253  $record->setVarsFromFetchObj($obj);
2254 
2255  $records[$record->id] = $record;
2256 
2257  $i++;
2258  }
2259  $this->db->free($resql);
2260 
2261  return $records;
2262  } else {
2263  $this->errors[] = 'Error '.$this->db->lasterror();
2264  dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
2265 
2266  return -1;
2267  }
2268  }
2269 
2277  public function update(User $user, $notrigger = 0)
2278  {
2279  return $this->updateCommon($user, $notrigger);
2280  }
2281 
2289  public function delete(User $user, $notrigger = 0)
2290  {
2291  return $this->deleteCommon($user, $notrigger);
2292  //return $this->deleteCommon($user, $notrigger, 1);
2293  }
2294 }
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:624
$object ref
Definition: info.php:79
Class for BOM.
Definition: bom.class.php:45
Parent class of all other business classes (invoices, contracts, proposals, orders,...
deleteLineCommon(User $user, $idline, $notrigger=0)
Delete a line of object in database.
commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams=null)
Common function for all objects extending CommonObject for generating documents.
createCommon(User $user, $notrigger=0)
Create object in the database.
getFieldList($alias='', $excludefields=array())
Function to concat keys of fields.
updateCommon(User $user, $notrigger=0)
Update object into database.
setStatusCommon($user, $status, $notrigger=0, $triggercode='')
Set to a status.
initAsSpecimenCommon()
Initialise object with example values Id must be 0 if object instance is a specimen.
copy_linked_contact($objFrom, $source='internal')
Copy contact from one element to current.
static commonReplaceThirdparty(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a thirdparty id with another one.
fetch_product()
Load the product with id $this->fk_product into this->product.
fetchLinesCommon($morewhere='', $noextrafields=0)
Load object in memory from the database.
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 warehouses.
Class for Mo.
Definition: mo.class.php:36
__construct(DoliDB $db)
Constructor.
Definition: mo.class.php:248
fetchLinesLinked($role, $lineid=0)
Get list of lines linked to current line for a defined role.
Definition: mo.class.php:569
getMoChilds()
Function used to return children of Mo.
Definition: mo.class.php:1821
static replaceThirdparty($db, $origin_id, $dest_id)
Function used to replace a thirdparty id with another one.
Definition: mo.class.php:1808
printOriginLinesList($restrictlist='', $selectedLines=array())
Return HTML table table of source object lines TODO Move this and previous function into output html ...
Definition: mo.class.php:1688
fetchLines()
Load object lines in memory from the database.
Definition: mo.class.php:465
getNextNumRef($prod)
Returns the reference to the following non used MO depending on the active numbering module defined i...
Definition: mo.class.php:1004
create(User $user, $notrigger=0)
Create object into database.
Definition: mo.class.php:288
fetch($id, $ref=null)
Load object in memory from the database.
Definition: mo.class.php:448
fetchAll($sortorder='', $sortfield='', $limit=0, $offset=0, $filter='', $filtermode='AND')
Load list of objects in memory from the database.
Definition: mo.class.php:485
initAsSpecimen()
Initialise object with example values Id must be 0 if object instance is a specimen.
Definition: mo.class.php:1604
reopen($user, $notrigger=0)
Set back to validated status.
Definition: mo.class.php:1251
getMoParent()
Function used to return children of Mo.
Definition: mo.class.php:1907
getLinesArray($rolefilter='')
Create an array of lines.
Definition: mo.class.php:1619
updateProduction(User $user, $notrigger=0)
Update quantities in lines to consume and/or lines to produce.
Definition: mo.class.php:807
LibStatut($status, $mode=0)
Return the status.
Definition: mo.class.php:1531
cancel($user, $notrigger=0, $also_cancel_consumed_and_produced_lines=false)
Set cancel status.
Definition: mo.class.php:1204
setDraft($user, $notrigger=0)
Set draft status.
Definition: mo.class.php:1179
printOriginLine($line, $var, $restrictlist='', $defaulttpldir='/core/tpl', $selectedLines=array())
Return HTML with a line of table array of source object lines TODO Move this and previous function in...
Definition: mo.class.php:1765
update(User $user, $notrigger=0)
Update object into database.
Definition: mo.class.php:656
getAllMoChilds($depth=0)
Function used to return all child MOs recursively.
Definition: mo.class.php:1862
createProduction(User $user, $notrigger=0)
Erase and update the line to consume and to produce.
Definition: mo.class.php:690
deleteLine(User $user, $idline, $notrigger=0, $fk_movement=0)
Delete a line of object in database.
Definition: mo.class.php:898
getKanbanView($option='', $arraydata=null)
Return clicable link of object (with eventually picto)
Definition: mo.class.php:1946
validate($user, $notrigger=0)
Validate Mo.
Definition: mo.class.php:1052
cancelConsumedAndProducedLines($user, $mode=0, $also_delete_lines=false, $notrigger=0)
Cancel consumed and produced lines (movement stocks)
Definition: mo.class.php:1277
generateDocument($modele, $outputlangs, $hidedetails=0, $hidedesc=0, $hideref=0, $moreparams=null)
Create a document onto disk according to template module.
Definition: mo.class.php:1652
getTooltipContentArray($params)
getTooltipContentArray
Definition: mo.class.php:1384
getNomUrl($withpicto=0, $option='', $notooltip=0, $morecss='', $save_lastsearch_value=-1)
Return a link to the object card (with optionally the picto)
Definition: mo.class.php:1433
getLibStatut($mode=0)
Return label of the status.
Definition: mo.class.php:1518
info($id)
Load the info information in the object.
Definition: mo.class.php:1573
countMovements()
Count number of movement with origin of MO.
Definition: mo.class.php:621
createFromClone(User $user, $fromid)
Clone an object into another one.
Definition: mo.class.php:346
Class MoLine.
Definition: mo.class.php:1991
update(User $user, $notrigger=0)
Update object into database.
Definition: mo.class.php:2277
create(User $user, $notrigger=0)
Create object into database.
Definition: mo.class.php:2159
fetchAll($sortorder='', $sortfield='', $limit=0, $offset=0, $filter='', $filtermode='AND')
Load list of objects in memory from the database.
Definition: mo.class.php:2193
__construct(DoliDB $db)
Constructor.
Definition: mo.class.php:2117
$parent_element
Definition: mo.class.php:2005
fetch($id, $ref=null)
Load object in memory from the database.
Definition: mo.class.php:2176
$fields
'type' field format: 'integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter[:Sortf...
Definition: mo.class.php:2052
$fk_parent_attribute
Definition: mo.class.php:2010
Class to manage stock movements.
Class to manage products or services.
Class to manage Dolibarr users.
Definition: user.class.php:50
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:751
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)
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.
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0)
Set event messages in dol_events session object.
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.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
div float
Unit price before taxes.
Definition: style.css.php:963