dolibarr  17.0.4
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) ---Put here your own copyright and developer email---
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program. If not, see <https://www.gnu.org/licenses/>.
18  */
19 
26 // Put here all includes required by your class file
27 require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
28 require_once DOL_DOCUMENT_ROOT.'/core/class/commonobjectline.class.php';
29 //require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php';
30 //require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
31 
35 class Mo extends CommonObject
36 {
40  public $element = 'mo';
41 
45  public $table_element = 'mrp_mo';
46 
50  public $ismultientitymanaged = 1;
51 
55  public $isextrafieldmanaged = 1;
56 
60  public $picto = 'mrp';
61 
62 
63  const STATUS_DRAFT = 0;
64  const STATUS_VALIDATED = 1; // To produce
65  const STATUS_INPROGRESS = 2;
66  const STATUS_PRODUCED = 3;
67  const STATUS_CANCELED = 9;
68 
69 
96  // BEGIN MODULEBUILDER PROPERTIES
100  public $fields = array(
101  'rowid' => array('type'=>'integer', 'label'=>'TechnicalID', 'enabled'=>1, 'visible'=>-2, 'position'=>1, 'notnull'=>1, 'index'=>1, 'comment'=>"Id",),
102  'entity' => array('type'=>'integer', 'label'=>'Entity', 'enabled'=>1, 'visible'=>0, 'position'=>5, 'notnull'=>1, 'default'=>'1', 'index'=>1),
103  '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),
104  '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'),
105  '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'),
106  'fk_product' => array('type'=>'integer:Product:product/class/product.class.php:0', 'label'=>'Product', 'enabled'=>'$conf->product->enabled', 'visible'=>1, 'position'=>35, 'notnull'=>1, 'index'=>1, 'comment'=>"Product to produce", 'css'=>'maxwidth300', 'csslist'=>'tdoverflowmax100', 'picto'=>'product'),
107  'qty' => array('type'=>'real', 'label'=>'QtyToProduce', 'enabled'=>1, 'visible'=>1, 'position'=>40, 'notnull'=>1, 'comment'=>"Qty to produce", 'css'=>'width75', 'default'=>1, 'isameasure'=>1),
108  '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),
109  'fk_soc' => array('type'=>'integer:Societe:societe/class/societe.class.php:1', 'label'=>'ThirdParty', 'picto'=>'company', 'enabled'=>'$conf->societe->enabled', 'visible'=>-1, 'position'=>50, 'notnull'=>-1, 'index'=>1, 'css'=>'maxwidth400', 'csslist'=>'tdoverflowmax150'),
110  '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'),
111  'fk_warehouse' => array('type'=>'integer:Entrepot:product/stock/class/entrepot.class.php:0', 'label'=>'WarehouseForProduction', 'picto'=>'stock', 'enabled'=>'$conf->stock->enabled', 'visible'=>1, 'position'=>52, 'css'=>'maxwidth400', 'csslist'=>'tdoverflowmax200'),
112  'note_public' => array('type'=>'html', 'label'=>'NotePublic', 'enabled'=>1, 'visible'=>0, 'position'=>61, 'notnull'=>-1,),
113  'note_private' => array('type'=>'html', 'label'=>'NotePrivate', 'enabled'=>1, 'visible'=>0, 'position'=>62, 'notnull'=>-1,),
114  'date_creation' => array('type'=>'datetime', 'label'=>'DateCreation', 'enabled'=>1, 'visible'=>-2, 'position'=>500, 'notnull'=>1,),
115  'tms' => array('type'=>'timestamp', 'label'=>'DateModification', 'enabled'=>1, 'visible'=>-2, 'position'=>501, 'notnull'=>1,),
116  'date_valid' => array('type'=>'datetime', 'label'=>'DateValidation', 'enabled'=>1, 'visible'=>-2, 'position'=>502,),
117  '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'),
118  'fk_user_modif' => array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserModif', 'enabled'=>1, 'visible'=>-2, 'position'=>511, 'notnull'=>-1, 'csslist'=>'tdoverflowmax100'),
119  'date_start_planned' => array('type'=>'datetime', 'label'=>'DateStartPlannedMo', 'enabled'=>1, 'visible'=>1, 'position'=>55, 'notnull'=>-1, 'index'=>1, 'help'=>'KeepEmptyForAsap', 'alwayseditable'=>1),
120  'date_end_planned' => array('type'=>'datetime', 'label'=>'DateEndPlannedMo', 'enabled'=>1, 'visible'=>1, 'position'=>56, 'notnull'=>-1, 'index'=>1, 'alwayseditable'=>1),
121  'import_key' => array('type'=>'varchar(14)', 'label'=>'ImportId', 'enabled'=>1, 'visible'=>-2, 'position'=>1000, 'notnull'=>-1,),
122  'model_pdf' =>array('type'=>'varchar(255)', 'label'=>'Model pdf', 'enabled'=>1, 'visible'=>0, 'position'=>1010),
123  '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')),
124  '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),
125  );
126  public $rowid;
127  public $entity;
128  public $ref;
129  public $mrptype;
130  public $label;
131  public $qty;
132  public $fk_warehouse;
133  public $fk_soc;
134  public $socid;
135 
139  public $note_public;
140 
144  public $note_private;
145 
149  public $date_creation;
150 
151 
152  public $tms;
153  public $fk_user_creat;
154  public $fk_user_modif;
155  public $import_key;
156  public $status;
157  public $fk_product;
158 
162  public $date_start_planned;
163 
167  public $date_end_planned;
168 
169 
170  public $fk_bom;
171  public $fk_project;
172  // END MODULEBUILDER PROPERTIES
173 
174 
175  // If this object has a subtable with lines
176 
180  public $table_element_line = 'mrp_production';
181 
185  public $fk_element = 'fk_mo';
186 
190  public $class_element_line = 'MoLine';
191 
195  protected $childtables = array();
196 
200  protected $childtablesoncascade = array('mrp_production');
201 
205  public $lines = array();
206 
211  public $fk_parent_line;
212 
213 
219  public function __construct(DoliDB $db)
220  {
221  global $conf, $langs;
222 
223  $this->db = $db;
224 
225  if (empty($conf->global->MAIN_SHOW_TECHNICAL_ID) && isset($this->fields['rowid'])) {
226  $this->fields['rowid']['visible'] = 0;
227  }
228  if (!isModEnabled('multicompany') && isset($this->fields['entity'])) {
229  $this->fields['entity']['enabled'] = 0;
230  }
231 
232  // Unset fields that are disabled
233  foreach ($this->fields as $key => $val) {
234  if (isset($val['enabled']) && empty($val['enabled'])) {
235  unset($this->fields[$key]);
236  }
237  }
238 
239  // Translate some data of arrayofkeyval
240  foreach ($this->fields as $key => $val) {
241  if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
242  foreach ($val['arrayofkeyval'] as $key2 => $val2) {
243  $this->fields[$key]['arrayofkeyval'][$key2] = $langs->trans($val2);
244  }
245  }
246  }
247  }
248 
256  public function create(User $user, $notrigger = false)
257  {
258  $error = 0;
259  $idcreated = 0;
260 
261  // 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
262  if (getDolGlobalString('PRODUIT_SOUSPRODUITS') && !getDolGlobalString('ALLOW_USE_KITS_INTO_BOM_AND_MO') && $this->fk_product > 0) {
263  include_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
264  $tmpproduct = new Product($this->db);
265  $tmpproduct->fetch($this->fk_product);
266  if ($tmpproduct->hasFatherOrChild(1) > 0) {
267  $this->error = 'ErrorAVirtualProductCantBeUsedIntoABomOrMo';
268  $this->errors[] = $this->error;
269  return -1;
270  }
271  }
272 
273  $this->db->begin();
274 
275  if ($this->fk_bom > 0) {
276  // If there is a nown BOM, we force the type of MO to the type of BOM
277  include_once DOL_DOCUMENT_ROOT.'/bom/class/bom.class.php';
278  $tmpbom = new BOM($this->db);
279  $tmpbom->fetch($this->fk_bom);
280 
281  $this->mrptype = $tmpbom->bomtype;
282  }
283 
284  if (!$error) {
285  $idcreated = $this->createCommon($user, $notrigger);
286  if ($idcreated <= 0) {
287  $error++;
288  }
289  }
290 
291  if (!$error) {
292  $result = $this->createProduction($user, $notrigger); // Insert lines from BOM
293  if ($result <= 0) {
294  $error++;
295  }
296  }
297 
298  if (!$error) {
299  $this->db->commit();
300  } else {
301  $this->db->rollback();
302  }
303 
304  return $idcreated;
305  }
306 
314  public function createFromClone(User $user, $fromid)
315  {
316  global $langs, $extrafields;
317  $error = 0;
318 
319  dol_syslog(__METHOD__, LOG_DEBUG);
320 
321  $object = new self($this->db);
322 
323  $this->db->begin();
324 
325  // Load source object
326  $result = $object->fetchCommon($fromid);
327  if ($result > 0 && !empty($object->table_element_line)) {
328  $object->fetchLines();
329  }
330 
331  // get lines so they will be clone
332  //foreach($this->lines as $line)
333  // $line->fetch_optionals();
334 
335  // Reset some properties
336  unset($object->id);
337  unset($object->fk_user_creat);
338  unset($object->import_key);
339 
340  // Clear fields
341  $object->ref = empty($this->fields['ref']['default']) ? "copy_of_".$object->ref : $this->fields['ref']['default'];
342  $object->label = empty($this->fields['label']['default']) ? $langs->trans("CopyOf")." ".$object->label : $this->fields['label']['default'];
343  $object->status = self::STATUS_DRAFT;
344  // ...
345  // Clear extrafields that are unique
346  if (is_array($object->array_options) && count($object->array_options) > 0) {
347  $extrafields->fetch_name_optionals_label($this->table_element);
348  foreach ($object->array_options as $key => $option) {
349  $shortkey = preg_replace('/options_/', '', $key);
350  if (!empty($extrafields->attributes[$this->element]['unique'][$shortkey])) {
351  //var_dump($key);
352  //var_dump($clonedObj->array_options[$key]); exit;
353  unset($object->array_options[$key]);
354  }
355  }
356  }
357 
358  // Create clone
359  $object->context['createfromclone'] = 'createfromclone';
360  $result = $object->createCommon($user);
361  if ($result < 0) {
362  $error++;
363  $this->error = $object->error;
364  $this->errors = $object->errors;
365  }
366 
367  if (!$error) {
368  // copy internal contacts
369  if ($this->copy_linked_contact($object, 'internal') < 0) {
370  $error++;
371  }
372  }
373 
374  if (!$error) {
375  // copy external contacts if same company
376  if (property_exists($this, 'socid') && $this->socid == $object->socid) {
377  if ($this->copy_linked_contact($object, 'external') < 0) {
378  $error++;
379  }
380  }
381  }
382 
383  unset($object->context['createfromclone']);
384 
385  // End
386  if (!$error) {
387  $this->db->commit();
388  return $object;
389  } else {
390  $this->db->rollback();
391  return -1;
392  }
393  }
394 
402  public function fetch($id, $ref = null)
403  {
404  $result = $this->fetchCommon($id, $ref);
405  if ($result > 0 && !empty($this->table_element_line)) {
406  $this->fetchLines();
407  }
408 
409  $this->socid = $this->fk_soc;
410 
411  return $result;
412  }
413 
419  public function fetchLines()
420  {
421  $this->lines = array();
422 
423  $result = $this->fetchLinesCommon();
424  return $result;
425  }
426 
427 
439  public function fetchAll($sortorder = '', $sortfield = '', $limit = 0, $offset = 0, array $filter = array(), $filtermode = 'AND')
440  {
441  global $conf;
442 
443  dol_syslog(__METHOD__, LOG_DEBUG);
444 
445  $records = array();
446 
447  $sql = 'SELECT ';
448  $sql .= $this->getFieldList();
449  $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as t';
450  if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) {
451  $sql .= ' WHERE t.entity IN ('.getEntity($this->element).')';
452  } else {
453  $sql .= ' WHERE 1 = 1';
454  }
455  // Manage filter
456  $sqlwhere = array();
457  if (count($filter) > 0) {
458  foreach ($filter as $key => $value) {
459  if ($key == 't.rowid') {
460  $sqlwhere[] = $key." = ".((int) $value);
461  } elseif (strpos($key, 'date') !== false) {
462  $sqlwhere[] = $key." = '".$this->db->idate($value)."'";
463  } elseif ($key == 'customsql') {
464  $sqlwhere[] = $value;
465  } else {
466  $sqlwhere[] = $key." LIKE '%".$this->db->escape($value)."%'";
467  }
468  }
469  }
470  if (count($sqlwhere) > 0) {
471  $sql .= ' AND ('.implode(' '.$this->db->escape($filtermode).' ', $sqlwhere).')';
472  }
473 
474  if (!empty($sortfield)) {
475  $sql .= $this->db->order($sortfield, $sortorder);
476  }
477  if (!empty($limit)) {
478  $sql .= $this->db->plimit($limit, $offset);
479  }
480 
481  $resql = $this->db->query($sql);
482  if ($resql) {
483  $num = $this->db->num_rows($resql);
484  $i = 0;
485  while ($i < min($limit, $num)) {
486  $obj = $this->db->fetch_object($resql);
487 
488  $record = new self($this->db);
489  $record->setVarsFromFetchObj($obj);
490 
491  $records[$record->id] = $record;
492 
493  $i++;
494  }
495  $this->db->free($resql);
496 
497  return $records;
498  } else {
499  $this->errors[] = 'Error '.$this->db->lasterror();
500  dol_syslog(__METHOD__.' '.join(',', $this->errors), LOG_ERR);
501 
502  return -1;
503  }
504  }
505 
513  public function fetchLinesLinked($role, $lineid = 0)
514  {
515  $resarray = array();
516  $mostatic = new MoLine($this->db);
517 
518  $sql = 'SELECT ';
519  $sql .= $mostatic->getFieldList();
520  $sql .= ' FROM '.MAIN_DB_PREFIX.$mostatic->table_element.' as t';
521  $sql .= " WHERE t.role = '".$this->db->escape($role)."'";
522  if ($lineid > 0) {
523  $sql .= ' AND t.fk_mrp_production = '.((int) $lineid);
524  } else {
525  $sql .= 'AND t.fk_mo = '.((int) $this->id);
526  }
527 
528  $resql = $this->db->query($sql);
529  if ($resql) {
530  $num = $this->db->num_rows($resql);
531 
532  $i = 0;
533  while ($i < $num) {
534  $obj = $this->db->fetch_object($resql);
535  if ($obj) {
536  $resarray[] = array(
537  'rowid'=> $obj->rowid,
538  'date'=> $this->db->jdate($obj->date_creation),
539  'qty' => $obj->qty,
540  'role' => $obj->role,
541  'fk_product' => $obj->fk_product,
542  'fk_warehouse' => $obj->fk_warehouse,
543  'batch' => $obj->batch,
544  'fk_stock_movement' => $obj->fk_stock_movement
545  );
546  }
547 
548  $i++;
549  }
550 
551  return $resarray;
552  } else {
553  $this->error = $this->db->lasterror();
554  return array();
555  }
556  }
557 
558 
564  public function countMovements()
565  {
566  $result = 0;
567 
568  $sql = 'SELECT COUNT(rowid) as nb FROM '.MAIN_DB_PREFIX.'stock_mouvement as sm';
569  $sql .= " WHERE sm.origintype = 'mo' and sm.fk_origin = ".((int) $this->id);
570 
571  $resql = $this->db->query($sql);
572  if ($resql) {
573  $num = $this->db->num_rows($resql);
574 
575  $i = 0;
576  while ($i < $num) {
577  $obj = $this->db->fetch_object($resql);
578  if ($obj) {
579  $result = $obj->nb;
580  }
581 
582  $i++;
583  }
584  } else {
585  $this->error = $this->db->lasterror();
586  }
587 
588  return $result;
589  }
590 
591 
599  public function update(User $user, $notrigger = false)
600  {
601  global $langs;
602 
603  $error = 0;
604 
605  $this->db->begin();
606 
607  $result = $this->updateCommon($user, $notrigger);
608  if ($result <= 0) {
609  $error++;
610  }
611 
612  // Update the lines (the qty) to consume or to produce
613  $result = $this->updateProduction($user, $notrigger);
614  if ($result <= 0) {
615  $error++;
616  }
617 
618  if (!$error) {
619  $this->db->commit();
620  return 1;
621  } else {
622  $this->db->rollback();
623  return -1;
624  }
625  }
626 
627 
635  public function createProduction(User $user, $notrigger = true)
636  {
637  $error = 0;
638  $role = "";
639 
640  if ($this->status != self::STATUS_DRAFT) {
641  return -1;
642  }
643 
644  $this->db->begin();
645 
646  // Insert lines in mrp_production table from BOM data
647  if (!$error) {
648  $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'mrp_production WHERE fk_mo = '.((int) $this->id);
649  $this->db->query($sql);
650 
651  $moline = new MoLine($this->db);
652 
653  // Line to produce
654  $moline->fk_mo = $this->id;
655  $moline->qty = $this->qty;
656  $moline->fk_product = $this->fk_product;
657  $moline->position = 1;
658 
659  if ($this->fk_bom > 0) { // If a BOM is defined, we know what to produce.
660  include_once DOL_DOCUMENT_ROOT.'/bom/class/bom.class.php';
661  $bom = new Bom($this->db);
662  $bom->fetch($this->fk_bom);
663  if ($bom->bomtype == 1) {
664  $role = 'toproduce';
665  $moline->role = 'toconsume';
666  } else {
667  $role = 'toconsume';
668  $moline->role = 'toproduce';
669  }
670  } else {
671  if ($this->mrptype == 1) {
672  $moline->role = 'toconsume';
673  } else {
674  $moline->role = 'toproduce';
675  }
676  }
677 
678  $resultline = $moline->create($user, false); // Never use triggers here
679  if ($resultline <= 0) {
680  $error++;
681  $this->error = $moline->error;
682  $this->errors = $moline->errors;
683  dol_print_error($this->db, $moline->error, $moline->errors);
684  }
685 
686  if ($this->fk_bom > 0) { // If a BOM is defined, we know what to consume.
687  if ($bom->id > 0) {
688  // Lines to consume
689  if (!$error) {
690  foreach ($bom->lines as $line) {
691  $moline = new MoLine($this->db);
692 
693  $moline->fk_mo = $this->id;
694  $moline->origin_id = $line->id;
695  $moline->origin_type = 'bomline';
696  if ($line->qty_frozen) {
697  $moline->qty = $line->qty; // Qty to consume does not depends on quantity to produce
698  } else {
699  $moline->qty = 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
700  }
701  if ($moline->qty <= 0) {
702  $error++;
703  $this->error = "BadValueForquantityToConsume";
704  break;
705  } else {
706  $moline->fk_product = $line->fk_product;
707  $moline->role = $role;
708  $moline->position = $line->position;
709  $moline->qty_frozen = $line->qty_frozen;
710  $moline->disable_stock_change = $line->disable_stock_change;
711 
712  $resultline = $moline->create($user, false); // Never use triggers here
713  if ($resultline <= 0) {
714  $error++;
715  $this->error = $moline->error;
716  $this->errors = $moline->errors;
717  dol_print_error($this->db, $moline->error, $moline->errors);
718  break;
719  }
720  }
721  }
722  }
723  }
724  }
725  }
726 
727  if (!$error) {
728  $this->db->commit();
729  return 1;
730  } else {
731  $this->db->rollback();
732  return -1;
733  }
734  }
735 
743  public function updateProduction(User $user, $notrigger = true)
744  {
745  $error = 0;
746 
747  if ($this->status != self::STATUS_DRAFT) return 1;
748 
749  $this->db->begin();
750 
751  $oldQty = $this->oldQty;
752  $newQty = $this->qty;
753  if ($newQty != $oldQty && !empty($this->oldQty)) {
754  $sql = "SELECT rowid FROM " . MAIN_DB_PREFIX . "mrp_production WHERE fk_mo = " . (int) $this->id;
755  $resql = $this->db->query($sql);
756  if ($resql) {
757  while ($obj = $this->db->fetch_object($resql)) {
758  $moLine = new MoLine($this->db);
759  $res = $moLine->fetch($obj->rowid);
760  if (!$res) $error++;
761 
762  if ($moLine->role == 'toconsume' || $moLine->role == 'toproduce') {
763  if (empty($moLine->qty_frozen)) {
764  $qty = $newQty * $moLine->qty / $oldQty;
765  $moLine->qty = price2num($qty * (!empty($line->efficiency) ? $line->efficiency : 1 ), 'MS'); // Calculate with Qty to produce and more presition
766  $res = $moLine->update($user);
767  if (!$res) $error++;
768  }
769  }
770  }
771  }
772  }
773 
774  if (!$error) {
775  $this->db->commit();
776  return 1;
777  } else {
778  $this->db->rollback();
779  return -1;
780  }
781  }
782 
783 
791  public function delete(User $user, $notrigger = false)
792  {
793  return $this->deleteCommon($user, $notrigger);
794  //return $this->deleteCommon($user, $notrigger, 1);
795  }
796 
805  public function deleteLine(User $user, $idline, $notrigger = false)
806  {
807  global $langs;
808  $langs->load('stocks');
809 
810  if ($this->status < 0) {
811  $this->error = 'ErrorDeleteLineNotAllowedByObjectStatus';
812  return -2;
813  }
814 
815  $productstatic = new Product($this->db);
816  $fk_movement = GETPOST('fk_movement', 'int');
817  $arrayoflines = $this->fetchLinesLinked('consumed', $idline);
818 
819  if (!empty($arrayoflines)) {
820  $this->db->begin();
821 
822  $stockmove = new MouvementStock($this->db);
823  $stockmove->setOrigin($this->element, $this->id);
824 
825  if (!empty($fk_movement)) {
826  $moline = new MoLine($this->db);
827  $TArrayMoLine = $moline->fetchAll('', '', 1, 0, array('customsql' => 'fk_stock_movement ='.$fk_movement));
828  $moline = array_shift($TArrayMoLine);
829 
830  $movement = new MouvementStock($this->db);
831  $movement->fetch($fk_movement);
832  $productstatic->fetch($movement->product_id);
833  $qtytoprocess = $movement->qty;
834 
835  // Reverse stock movement
836  $labelmovementCancel = $langs->trans("CancelProductionForRef", $productstatic->ref);
837  $codemovementCancel = $langs->trans("StockIncrease");
838 
839  if (($qtytoprocess >= 0)) {
840  $idstockmove = $stockmove->reception($user, $movement->product_id, $movement->warehouse_id, $qtytoprocess, 0, $labelmovementCancel, '', '', $movement->batch, dol_now(), 0, $codemovementCancel);
841  } else {
842  $idstockmove = $stockmove->livraison($user, $movement->product_id, $movement->warehouse_id, $qtytoprocess, 0, $labelmovementCancel, dol_now(), '', '', $movement->batch, 0, $codemovementCancel);
843  }
844  if ($idstockmove < 0) {
845  $this->error++;
846  $this->db->rollback();
847  setEventMessages($stockmove->error, $stockmove->errors, 'errors');
848  } else {
849  $this->db->commit();
850  }
851  return $moline->delete($user, $notrigger);
852  } else {
853  foreach ($arrayoflines as $key => $arrayofline) {
854  $lineDetails = $arrayoflines[$key];
855  $productstatic->fetch($lineDetails['fk_product']);
856  $qtytoprocess = $lineDetails['qty'];
857 
858  // Reverse stock movement
859  $labelmovementCancel = $langs->trans("CancelProductionForRef", $productstatic->ref);
860  $codemovementCancel = $langs->trans("StockIncrease");
861 
862  if ($qtytoprocess >= 0) {
863  $idstockmove = $stockmove->reception($user, $lineDetails['fk_product'], $lineDetails['fk_warehouse'], $qtytoprocess, 0, $labelmovementCancel, '', '', $lineDetails['batch'], dol_now(), 0, $codemovementCancel);
864  } else {
865  $idstockmove = $stockmove->livraison($user, $lineDetails['fk_product'], $lineDetails['fk_warehouse'], $qtytoprocess, 0, $labelmovementCancel, dol_now(), '', '', $lineDetails['batch'], 0, $codemovementCancel);
866  }
867  if ($idstockmove < 0) {
868  $this->error++;
869  $this->db->rollback();
870  setEventMessages($stockmove->error, $stockmove->errors, 'errors');
871  } else {
872  $this->db->commit();
873  }
874  }
875  return $this->deleteLineCommon($user, $idline, $notrigger);
876  }
877  } else {
878  return $this->deleteLineCommon($user, $idline, $notrigger);
879  }
880  }
881 
882 
890  public function getNextNumRef($prod)
891  {
892  global $langs, $conf;
893  $langs->load("mrp");
894 
895  if (!empty($conf->global->MRP_MO_ADDON)) {
896  $mybool = false;
897 
898  $file = $conf->global->MRP_MO_ADDON.".php";
899  $classname = $conf->global->MRP_MO_ADDON;
900 
901  // Include file with class
902  $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
903  foreach ($dirmodels as $reldir) {
904  $dir = dol_buildpath($reldir."core/modules/mrp/");
905 
906  // Load file with numbering class (if found)
907  $mybool |= @include_once $dir.$file;
908  }
909 
910  if ($mybool === false) {
911  dol_print_error('', "Failed to include file ".$file);
912  return '';
913  }
914 
915  $obj = new $classname();
916  $numref = $obj->getNextValue($prod, $this);
917 
918  if ($numref != "") {
919  return $numref;
920  } else {
921  $this->error = $obj->error;
922  //dol_print_error($this->db,get_class($this)."::getNextNumRef ".$obj->error);
923  return "";
924  }
925  } else {
926  print $langs->trans("Error")." ".$langs->trans("Error_MRP_MO_ADDON_NotDefined");
927  return "";
928  }
929  }
930 
938  public function validate($user, $notrigger = 0)
939  {
940  global $conf, $langs;
941 
942  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
943 
944  $error = 0;
945 
946  // Protection
947  if ($this->status == self::STATUS_VALIDATED) {
948  dol_syslog(get_class($this)."::validate action abandonned: already validated", LOG_WARNING);
949  return 0;
950  }
951 
952  /*if (! ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->mrp->create))
953  || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->mrp->mrp_advance->validate))))
954  {
955  $this->error='NotEnoughPermissions';
956  dol_syslog(get_class($this)."::valid ".$this->error, LOG_ERR);
957  return -1;
958  }*/
959 
960  $now = dol_now();
961 
962  $this->db->begin();
963 
964  // Define new ref
965  if (!$error && (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref))) { // empty should not happened, but when it occurs, the test save life
966  $this->fetch_product();
967  $num = $this->getNextNumRef($this->product);
968  } else {
969  $num = $this->ref;
970  }
971  $this->newref = $num;
972 
973  // Validate
974  $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
975  $sql .= " SET ref = '".$this->db->escape($num)."',";
976  $sql .= " status = ".self::STATUS_VALIDATED.",";
977  $sql .= " date_valid='".$this->db->idate($now)."',";
978  $sql .= " fk_user_valid = ".$user->id;
979  $sql .= " WHERE rowid = ".((int) $this->id);
980 
981  dol_syslog(get_class($this)."::validate()", LOG_DEBUG);
982  $resql = $this->db->query($sql);
983  if (!$resql) {
984  dol_print_error($this->db);
985  $this->error = $this->db->lasterror();
986  $error++;
987  }
988 
989  if (!$error && !$notrigger) {
990  // Call trigger
991  $result = $this->call_trigger('MRP_MO_VALIDATE', $user);
992  if ($result < 0) {
993  $error++;
994  }
995  // End call triggers
996  }
997 
998  if (!$error) {
999  $this->oldref = $this->ref;
1000 
1001  // Rename directory if dir was a temporary ref
1002  if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
1003  // Now we rename also files into index
1004  $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)."'";
1005  $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'mrp/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
1006  $resql = $this->db->query($sql);
1007  if (!$resql) {
1008  $error++; $this->error = $this->db->lasterror();
1009  }
1010 
1011  // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
1012  $oldref = dol_sanitizeFileName($this->ref);
1013  $newref = dol_sanitizeFileName($num);
1014  $dirsource = $conf->mrp->dir_output.'/'.$oldref;
1015  $dirdest = $conf->mrp->dir_output.'/'.$newref;
1016  if (!$error && file_exists($dirsource)) {
1017  dol_syslog(get_class($this)."::validate() rename dir ".$dirsource." into ".$dirdest);
1018 
1019  if (@rename($dirsource, $dirdest)) {
1020  dol_syslog("Rename ok");
1021  // Rename docs starting with $oldref with $newref
1022  $listoffiles = dol_dir_list($conf->mrp->dir_output.'/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
1023  foreach ($listoffiles as $fileentry) {
1024  $dirsource = $fileentry['name'];
1025  $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
1026  $dirsource = $fileentry['path'].'/'.$dirsource;
1027  $dirdest = $fileentry['path'].'/'.$dirdest;
1028  @rename($dirsource, $dirdest);
1029  }
1030  }
1031  }
1032  }
1033  }
1034 
1035  // Set new ref and current status
1036  if (!$error) {
1037  $this->ref = $num;
1038  $this->status = self::STATUS_VALIDATED;
1039  }
1040 
1041  if (!$error) {
1042  $this->db->commit();
1043  return 1;
1044  } else {
1045  $this->db->rollback();
1046  return -1;
1047  }
1048  }
1049 
1057  public function setDraft($user, $notrigger = 0)
1058  {
1059  // Protection
1060  if ($this->status <= self::STATUS_DRAFT) {
1061  return 0;
1062  }
1063 
1064  /*if (! ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->mymodule->write))
1065  || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->mymodule->mymodule_advance->validate))))
1066  {
1067  $this->error='Permission denied';
1068  return -1;
1069  }*/
1070 
1071  return $this->setStatusCommon($user, self::STATUS_DRAFT, $notrigger, 'MRP_MO_UNVALIDATE');
1072  }
1073 
1081  public function cancel($user, $notrigger = 0)
1082  {
1083  // Protection
1084  if ($this->status != self::STATUS_VALIDATED && $this->status != self::STATUS_INPROGRESS) {
1085  return 0;
1086  }
1087 
1088  /*if (! ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->mymodule->write))
1089  || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->mymodule->mymodule_advance->validate))))
1090  {
1091  $this->error='Permission denied';
1092  return -1;
1093  }*/
1094 
1095  return $this->setStatusCommon($user, self::STATUS_CANCELED, $notrigger, 'MRP_MO_CANCEL');
1096  }
1097 
1105  public function reopen($user, $notrigger = 0)
1106  {
1107  // Protection
1108  if ($this->status != self::STATUS_PRODUCED && $this->status != self::STATUS_CANCELED) {
1109  return 0;
1110  }
1111 
1112  /*if (! ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->mymodule->write))
1113  || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->mymodule->mymodule_advance->validate))))
1114  {
1115  $this->error='Permission denied';
1116  return -1;
1117  }*/
1118 
1119  return $this->setStatusCommon($user, self::STATUS_VALIDATED, $notrigger, 'MRP_MO_REOPEN');
1120  }
1121 
1132  public function getNomUrl($withpicto = 0, $option = '', $notooltip = 0, $morecss = '', $save_lastsearch_value = -1)
1133  {
1134  global $conf, $langs, $hookmanager;
1135 
1136  if (!empty($conf->dol_no_mouse_hover)) {
1137  $notooltip = 1; // Force disable tooltips
1138  }
1139 
1140  $result = '';
1141 
1142  $label = img_picto('', $this->picto).' <u class="paddingrightonly">'.$langs->trans("ManufacturingOrder").'</u>';
1143  if (isset($this->status)) {
1144  $label .= ' '.$this->getLibStatut(5);
1145  }
1146  $label .= '<br>';
1147  $label .= '<b>'.$langs->trans('Ref').':</b> '.$this->ref;
1148  if (isset($this->label)) {
1149  $label .= '<br><b>'.$langs->trans('Label').':</b> '.$this->label;
1150  }
1151 
1152  $url = DOL_URL_ROOT.'/mrp/mo_card.php?id='.$this->id;
1153  if ($option == 'production') {
1154  $url = DOL_URL_ROOT.'/mrp/mo_production.php?id='.$this->id;
1155  }
1156 
1157  if ($option != 'nolink') {
1158  // Add param to save lastsearch_values or not
1159  $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
1160  if ($save_lastsearch_value == -1 && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
1161  $add_save_lastsearch_values = 1;
1162  }
1163  if ($add_save_lastsearch_values) {
1164  $url .= '&save_lastsearch_values=1';
1165  }
1166  }
1167 
1168  $linkclose = '';
1169  if (empty($notooltip)) {
1170  if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
1171  $label = $langs->trans("ShowMo");
1172  $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
1173  }
1174  $linkclose .= ' title="'.dol_escape_htmltag($label, 1).'"';
1175  $linkclose .= ' class="classfortooltip'.($morecss ? ' '.$morecss : '').'"';
1176  } else {
1177  $linkclose = ($morecss ? ' class="'.$morecss.'"' : '');
1178  }
1179 
1180  $linkstart = '<a href="'.$url.'"';
1181  $linkstart .= $linkclose.'>';
1182  $linkend = '</a>';
1183 
1184  $result .= $linkstart;
1185  if ($withpicto) {
1186  $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);
1187  }
1188  if ($withpicto != 2) {
1189  $result .= $this->ref;
1190  }
1191  $result .= $linkend;
1192  //if ($withpicto != 2) $result.=(($addlabel && $this->label) ? $sep . dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : '');
1193 
1194  global $action, $hookmanager;
1195  $hookmanager->initHooks(array('modao'));
1196  $parameters = array('id'=>$this->id, 'getnomurl' => &$result);
1197  $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1198  if ($reshook > 0) {
1199  $result = $hookmanager->resPrint;
1200  } else {
1201  $result .= $hookmanager->resPrint;
1202  }
1203 
1204  return $result;
1205  }
1206 
1213  public function getLibStatut($mode = 0)
1214  {
1215  return $this->LibStatut($this->status, $mode);
1216  }
1217 
1218  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1226  public function LibStatut($status, $mode = 0)
1227  {
1228  // phpcs:enable
1229  if (empty($this->labelStatus)) {
1230  global $langs;
1231  //$langs->load("mrp");
1232  $this->labelStatus[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv('Draft');
1233  $this->labelStatus[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv('ValidatedToProduce');
1234  $this->labelStatus[self::STATUS_INPROGRESS] = $langs->transnoentitiesnoconv('InProgress');
1235  $this->labelStatus[self::STATUS_PRODUCED] = $langs->transnoentitiesnoconv('StatusMOProduced');
1236  $this->labelStatus[self::STATUS_CANCELED] = $langs->transnoentitiesnoconv('Canceled');
1237 
1238  $this->labelStatusShort[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv('Draft');
1239  $this->labelStatusShort[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv('Validated');
1240  $this->labelStatusShort[self::STATUS_INPROGRESS] = $langs->transnoentitiesnoconv('InProgress');
1241  $this->labelStatusShort[self::STATUS_PRODUCED] = $langs->transnoentitiesnoconv('StatusMOProduced');
1242  $this->labelStatusShort[self::STATUS_CANCELED] = $langs->transnoentitiesnoconv('Canceled');
1243  }
1244 
1245  $statusType = 'status'.$status;
1246  if ($status == self::STATUS_VALIDATED) {
1247  $statusType = 'status1';
1248  }
1249  if ($status == self::STATUS_INPROGRESS) {
1250  $statusType = 'status4';
1251  }
1252  if ($status == self::STATUS_PRODUCED) {
1253  $statusType = 'status6';
1254  }
1255  if ($status == self::STATUS_CANCELED) {
1256  $statusType = 'status9';
1257  }
1258 
1259  return dolGetStatus($this->labelStatus[$status], $this->labelStatusShort[$status], '', $statusType, $mode);
1260  }
1261 
1268  public function info($id)
1269  {
1270  $sql = 'SELECT rowid, date_creation as datec, tms as datem,';
1271  $sql .= ' fk_user_creat, fk_user_modif';
1272  $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as t';
1273  $sql .= ' WHERE t.rowid = '.((int) $id);
1274  $result = $this->db->query($sql);
1275  if ($result) {
1276  if ($this->db->num_rows($result)) {
1277  $obj = $this->db->fetch_object($result);
1278  $this->id = $obj->rowid;
1279 
1280  $this->user_creation_id = $obj->fk_user_creat;
1281  $this->user_modification_id = $obj->fk_user_modif;
1282  $this->date_creation = $this->db->jdate($obj->datec);
1283  $this->date_modification = empty($obj->datem) ? '' : $this->db->jdate($obj->datem);
1284  }
1285 
1286  $this->db->free($result);
1287  } else {
1288  dol_print_error($this->db);
1289  }
1290  }
1291 
1298  public function initAsSpecimen()
1299  {
1300  $this->initAsSpecimenCommon();
1301 
1302  $this->lines = array();
1303  }
1304 
1310  public function getLinesArray($rolefilter = '')
1311  {
1312  $this->lines = array();
1313 
1314  $objectline = new MoLine($this->db);
1315 
1316  $TFilters = array('customsql'=>'fk_mo = '.((int) $this->id));
1317  if (!empty($rolefilter)) $TFilters['role'] = $rolefilter;
1318  $result = $objectline->fetchAll('ASC', 'position', 0, 0, $TFilters);
1319 
1320  if (is_numeric($result)) {
1321  $this->error = $objectline->error;
1322  $this->errors = $objectline->errors;
1323  return $result;
1324  } else {
1325  $this->lines = $result;
1326  return $this->lines;
1327  }
1328  }
1329 
1341  public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
1342  {
1343  global $conf, $langs;
1344 
1345  $langs->load("mrp");
1346 
1347  if (!dol_strlen($modele)) {
1348  //$modele = 'standard';
1349  $modele = ''; // Remove this once a pdf_standard.php exists.
1350 
1351  if ($this->model_pdf) {
1352  $modele = $this->model_pdf;
1353  } elseif (!empty($conf->global->MO_ADDON_PDF)) {
1354  $modele = $conf->global->MO_ADDON_PDF;
1355  }
1356  }
1357 
1358  $modelpath = "core/modules/mrp/doc/";
1359 
1360  if (empty($modele)) {
1361  return 1; // Remove this once a pdf_standard.php exists.
1362  }
1363 
1364  return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
1365  }
1366 
1374  public function doScheduledJob()
1375  {
1376  global $conf, $langs;
1377 
1378  //$conf->global->SYSLOG_FILE = 'DOL_DATA_ROOT/dolibarr_mydedicatedlofile.log';
1379 
1380  $error = 0;
1381  $this->output = '';
1382  $this->error = '';
1383 
1384  dol_syslog(__METHOD__, LOG_DEBUG);
1385 
1386  $now = dol_now();
1387 
1388  $this->db->begin();
1389 
1390  // ...
1391 
1392  $this->db->commit();
1393 
1394  return $error;
1395  }
1396 
1407  public function printOriginLinesList($restrictlist = '', $selectedLines = array())
1408  {
1409  global $langs, $hookmanager, $conf, $form;
1410 
1411  $langs->load('stocks');
1412  $text_stock_options = $langs->trans("RealStockDesc").'<br>';
1413  $text_stock_options .= $langs->trans("RealStockWillAutomaticallyWhen").'<br>';
1414  $text_stock_options .= (!empty($conf->global->STOCK_CALCULATE_ON_SHIPMENT) || !empty($conf->global->STOCK_CALCULATE_ON_SHIPMENT_CLOSE) ? '- '.$langs->trans("DeStockOnShipment").'<br>' : '');
1415  $text_stock_options .= (!empty($conf->global->STOCK_CALCULATE_ON_VALIDATE_ORDER) ? '- '.$langs->trans("DeStockOnValidateOrder").'<br>' : '');
1416  $text_stock_options .= (!empty($conf->global->STOCK_CALCULATE_ON_BILL) ? '- '.$langs->trans("DeStockOnBill").'<br>' : '');
1417  $text_stock_options .= (!empty($conf->global->STOCK_CALCULATE_ON_SUPPLIER_BILL) ? '- '.$langs->trans("ReStockOnBill").'<br>' : '');
1418  $text_stock_options .= (!empty($conf->global->STOCK_CALCULATE_ON_SUPPLIER_VALIDATE_ORDER) ? '- '.$langs->trans("ReStockOnValidateOrder").'<br>' : '');
1419  $text_stock_options .= (!empty($conf->global->STOCK_CALCULATE_ON_SUPPLIER_DISPATCH_ORDER) ? '- '.$langs->trans("ReStockOnDispatchOrder").'<br>' : '');
1420  $text_stock_options .= (!empty($conf->global->STOCK_CALCULATE_ON_RECEPTION) || !empty($conf->global->STOCK_CALCULATE_ON_RECEPTION_CLOSE) ? '- '.$langs->trans("StockOnReception").'<br>' : '');
1421 
1422  print '<tr class="liste_titre">';
1423  // Product or sub-bom
1424  print '<td class="linecoldescription">'.$langs->trans('Ref');
1425  if (!empty($conf->global->BOM_SUB_BOM)) {
1426  print ' &nbsp; <a id="show_all" href="#">'.img_picto('', 'folder-open', 'class="paddingright"').$langs->trans("ExpandAll").'</a>&nbsp;&nbsp;';
1427  print '<a id="hide_all" href="#">'.img_picto('', 'folder', 'class="paddingright"').$langs->trans("UndoExpandAll").'</a>&nbsp;';
1428  }
1429  print '</td>';
1430  // Qty
1431  print '<td class="right">'.$langs->trans('Qty');
1432  if ($this->bom->bomtype == 0) {
1433  print ' <span class="opacitymedium">('.$langs->trans("ForAQuantityOf", $this->bom->qty).')</span>';
1434  } else {
1435  print ' <span class="opacitymedium">('.$langs->trans("ForAQuantityToConsumeOf", $this->bom->qty).')</span>';
1436  }
1437  print '</td>';
1438  print '<td class="center">'.$form->textwithpicto($langs->trans("PhysicalStock"), $text_stock_options, 1).'</td>';
1439  print '<td class="center">'.$form->textwithpicto($langs->trans("VirtualStock"), $langs->trans("VirtualStockDesc")).'</td>';
1440  print '<td class="center">'.$langs->trans('QtyFrozen').'</td>';
1441  print '<td class="center">'.$langs->trans('DisableStockChange').'</td>';
1442  print '<td class="center">'.$langs->trans('MoChildGenerate').'</td>';
1443  //print '<td class="center">'.$form->showCheckAddButtons('checkforselect', 1).'</td>';
1444  //print '<td class="center"></td>';
1445  print '</tr>';
1446  $i = 0;
1447 
1448  if (!empty($this->lines)) {
1449  foreach ($this->lines as $line) {
1450  $reshook = 0;
1451  if (is_object($hookmanager)) {
1452  $parameters = array('line'=>$line, 'i'=>$i, 'restrictlist'=>$restrictlist, 'selectedLines'=> $selectedLines);
1453  if (!empty($line->fk_parent_line)) { $parameters['fk_parent_line'] = $line->fk_parent_line; }
1454  $reshook = $hookmanager->executeHooks('printOriginObjectLine', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1455  }
1456  if (empty($reshook)) {
1457  $this->printOriginLine($line, '', $restrictlist, '/core/tpl', $selectedLines);
1458  }
1459 
1460  $i++;
1461  }
1462  }
1463  }
1464 
1465 
1479  public function printOriginLine($line, $var, $restrictlist = '', $defaulttpldir = '/core/tpl', $selectedLines = array())
1480  {
1481  global $langs, $conf;
1482 
1483  $this->tpl['id'] = $line->id;
1484 
1485  $this->tpl['label'] = '';
1486  if (!empty($line->fk_product)) {
1487  $productstatic = new Product($this->db);
1488  $productstatic->fetch($line->fk_product);
1489  $productstatic->load_virtual_stock();
1490  $this->tpl['label'] .= $productstatic->getNomUrl(1);
1491  //$this->tpl['label'].= ' - '.$productstatic->label;
1492  } else {
1493  // If origin MRP line is not a product, but another MRP
1494  // TODO
1495  }
1496 
1497  $this->tpl['qty_bom'] = 1;
1498  if (is_object($this->bom) && $this->bom->qty > 1) {
1499  $this->tpl['qty_bom'] = $this->bom->qty;
1500  }
1501 
1502  $this->tpl['stock'] = $productstatic->stock_reel;
1503  $this->tpl['seuil_stock_alerte'] = $productstatic->seuil_stock_alerte;
1504  $this->tpl['virtual_stock'] = $productstatic->stock_theorique;
1505  $this->tpl['qty'] = $line->qty;
1506  $this->tpl['qty_frozen'] = $line->qty_frozen;
1507  $this->tpl['disable_stock_change'] = $line->disable_stock_change;
1508  $this->tpl['efficiency'] = $line->efficiency;
1509 
1510  $tpl = DOL_DOCUMENT_ROOT.'/mrp/tpl/originproductline.tpl.php';
1511  $res = include $tpl;
1512  }
1513 
1522  public static function replaceThirdparty($db, $origin_id, $dest_id)
1523  {
1524  $tables = array('mrp_mo');
1525 
1526  return CommonObject::commonReplaceThirdparty($db, $origin_id, $dest_id, $tables);
1527  }
1528 
1529 
1535  public function getMoChilds()
1536  {
1537 
1538  $TMoChilds = array();
1539  $error = 0;
1540 
1541  $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."mrp_mo as mo_child";
1542  $sql.= " WHERE fk_parent_line IN ";
1543  $sql.= " (SELECT rowid FROM ".MAIN_DB_PREFIX."mrp_production as line_parent";
1544  $sql.= " WHERE fk_mo=".((int) $this->id).")";
1545 
1546  $resql = $this->db->query($sql);
1547 
1548  if ($resql) {
1549  if ($this->db->num_rows($resql) > 0) {
1550  while ($obj = $this->db->fetch_object($resql)) {
1551  $MoChild = new Mo($this->db);
1552  $res = $MoChild->fetch($obj->rowid);
1553  if ($res > 0) $TMoChilds[$MoChild->id] = $MoChild;
1554  else $error++;
1555  }
1556  }
1557  } else {
1558  $error++;
1559  }
1560 
1561  if ($error) {
1562  return -1;
1563  } else {
1564  return $TMoChilds;
1565  }
1566  }
1567 
1573  public function getMoParent()
1574  {
1575 
1576  $MoParent = new Mo($this->db);
1577  $error = 0;
1578 
1579  $sql = "SELECT lineparent.fk_mo as id_moparent FROM ".MAIN_DB_PREFIX."mrp_mo as mo";
1580  $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."mrp_production lineparent ON mo.fk_parent_line = lineparent.rowid";
1581  $sql.= " WHERE mo.rowid = ".((int) $this->id);
1582 
1583  $resql = $this->db->query($sql);
1584 
1585  if ($resql) {
1586  if ($this->db->num_rows($resql) > 0) {
1587  $obj = $this->db->fetch_object($resql);
1588  $res = $MoParent->fetch($obj->id_moparent);
1589  if ($res < 0) $error++;
1590  } else {
1591  return 0;
1592  }
1593  } else {
1594  $error++;
1595  }
1596 
1597  if ($error) {
1598  return -1;
1599  } else {
1600  return $MoParent;
1601  }
1602  }
1603 }
1604 
1609 {
1613  public $element = 'mrp_production';
1614 
1618  public $table_element = 'mrp_production';
1619 
1623  public $ismultientitymanaged = 0;
1624 
1628  public $isextrafieldmanaged = 0;
1629 
1630  public $fields = array(
1631  'rowid' =>array('type'=>'integer', 'label'=>'ID', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>10),
1632  'fk_mo' =>array('type'=>'integer', 'label'=>'Mo', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>15),
1633  'origin_id' =>array('type'=>'integer', 'label'=>'Origin', 'enabled'=>1, 'visible'=>-1, 'notnull'=>0, 'position'=>17),
1634  'origin_type' =>array('type'=>'varchar(10)', 'label'=>'Origin type', 'enabled'=>1, 'visible'=>-1, 'notnull'=>0, 'position'=>18),
1635  'position' =>array('type'=>'integer', 'label'=>'Position', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>20),
1636  'fk_product' =>array('type'=>'integer', 'label'=>'Product', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>25),
1637  'fk_warehouse' =>array('type'=>'integer', 'label'=>'Warehouse', 'enabled'=>1, 'visible'=>-1, 'position'=>30),
1638  'qty' =>array('type'=>'real', 'label'=>'Qty', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>35),
1639  'qty_frozen' => array('type'=>'smallint', 'label'=>'QuantityFrozen', 'enabled'=>1, 'visible'=>1, 'default'=>0, 'position'=>105, 'css'=>'maxwidth50imp', 'help'=>'QuantityConsumedInvariable'),
1640  'disable_stock_change' => array('type'=>'smallint', 'label'=>'DisableStockChange', 'enabled'=>1, 'visible'=>1, 'default'=>0, 'position'=>108, 'css'=>'maxwidth50imp', 'help'=>'DisableStockChangeHelp'),
1641  'batch' =>array('type'=>'varchar(30)', 'label'=>'Batch', 'enabled'=>1, 'visible'=>-1, 'position'=>140),
1642  'role' =>array('type'=>'varchar(10)', 'label'=>'Role', 'enabled'=>1, 'visible'=>-1, 'position'=>145),
1643  'fk_mrp_production' =>array('type'=>'integer', 'label'=>'Fk mrp production', 'enabled'=>1, 'visible'=>-1, 'position'=>150),
1644  'fk_stock_movement' =>array('type'=>'integer', 'label'=>'StockMovement', 'enabled'=>1, 'visible'=>-1, 'position'=>155),
1645  'date_creation' =>array('type'=>'datetime', 'label'=>'DateCreation', 'enabled'=>1, 'visible'=>-2, 'notnull'=>1, 'position'=>160),
1646  'tms' =>array('type'=>'timestamp', 'label'=>'Tms', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>165),
1647  'fk_user_creat' =>array('type'=>'integer', 'label'=>'UserCreation', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>170),
1648  'fk_user_modif' =>array('type'=>'integer', 'label'=>'UserModification', 'enabled'=>1, 'visible'=>-1, 'position'=>175),
1649  'import_key' =>array('type'=>'varchar(14)', 'label'=>'ImportId', 'enabled'=>1, 'visible'=>-1, 'position'=>180),
1650  );
1651 
1652  public $rowid;
1653  public $fk_mo;
1654  public $origin_id;
1655  public $origin_type;
1656  public $position;
1657  public $fk_product;
1658  public $fk_warehouse;
1659  public $qty;
1660  public $qty_frozen;
1661  public $disable_stock_change;
1662  public $batch;
1663  public $role;
1664  public $fk_mrp_production;
1665  public $fk_stock_movement;
1666  public $date_creation;
1667  public $tms;
1668  public $fk_user_creat;
1669  public $fk_user_modif;
1670  public $import_key;
1671 
1677  public function __construct(DoliDB $db)
1678  {
1679  global $conf, $langs;
1680 
1681  $this->db = $db;
1682 
1683  if (empty($conf->global->MAIN_SHOW_TECHNICAL_ID) && isset($this->fields['rowid'])) {
1684  $this->fields['rowid']['visible'] = 0;
1685  }
1686  if (!isModEnabled('multicompany') && isset($this->fields['entity'])) {
1687  $this->fields['entity']['enabled'] = 0;
1688  }
1689 
1690  // Unset fields that are disabled
1691  foreach ($this->fields as $key => $val) {
1692  if (isset($val['enabled']) && empty($val['enabled'])) {
1693  unset($this->fields[$key]);
1694  }
1695  }
1696 
1697  // Translate some data of arrayofkeyval
1698  if (is_object($langs)) {
1699  foreach ($this->fields as $key => $val) {
1700  if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
1701  foreach ($val['arrayofkeyval'] as $key2 => $val2) {
1702  $this->fields[$key]['arrayofkeyval'][$key2] = $langs->trans($val2);
1703  }
1704  }
1705  }
1706  }
1707  }
1708 
1716  public function create(User $user, $notrigger = false)
1717  {
1718  if (empty($this->qty)) {
1719  $this->error = 'BadValueForQty';
1720  return -1;
1721  }
1722 
1723  return $this->createCommon($user, $notrigger);
1724  }
1725 
1733  public function fetch($id, $ref = null)
1734  {
1735  $result = $this->fetchCommon($id, $ref);
1736  return $result;
1737  }
1738 
1750  public function fetchAll($sortorder = '', $sortfield = '', $limit = 0, $offset = 0, array $filter = array(), $filtermode = 'AND')
1751  {
1752  global $conf;
1753 
1754  dol_syslog(__METHOD__, LOG_DEBUG);
1755 
1756  $records = array();
1757 
1758  $sql = 'SELECT ';
1759  $sql .= $this->getFieldList();
1760  $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as t';
1761  if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) {
1762  $sql .= ' WHERE t.entity IN ('.getEntity($this->element).')';
1763  } else {
1764  $sql .= ' WHERE 1 = 1';
1765  }
1766  // Manage filter
1767  $sqlwhere = array();
1768  if (count($filter) > 0) {
1769  foreach ($filter as $key => $value) {
1770  if ($key == 't.rowid') {
1771  $sqlwhere[] = $key." = ".((int) $value);
1772  } elseif (strpos($key, 'date') !== false) {
1773  $sqlwhere[] = $key." = '".$this->db->idate($value)."'";
1774  } elseif ($key == 'customsql') {
1775  $sqlwhere[] = $value;
1776  } else {
1777  $sqlwhere[] = $key." LIKE '%".$this->db->escape($value)."%'";
1778  }
1779  }
1780  }
1781  if (count($sqlwhere) > 0) {
1782  $sql .= ' AND ('.implode(' '.$this->db->escape($filtermode).' ', $sqlwhere).')';
1783  }
1784 
1785  if (!empty($sortfield)) {
1786  $sql .= $this->db->order($sortfield, $sortorder);
1787  }
1788  if (!empty($limit)) {
1789  $sql .= $this->db->plimit($limit, $offset);
1790  }
1791 
1792  $resql = $this->db->query($sql);
1793  if ($resql) {
1794  $num = $this->db->num_rows($resql);
1795  $i = 0;
1796  while ($i < ($limit ? min($limit, $num) : $num)) {
1797  $obj = $this->db->fetch_object($resql);
1798 
1799  $record = new self($this->db);
1800  $record->setVarsFromFetchObj($obj);
1801 
1802  $records[$record->id] = $record;
1803 
1804  $i++;
1805  }
1806  $this->db->free($resql);
1807 
1808  return $records;
1809  } else {
1810  $this->errors[] = 'Error '.$this->db->lasterror();
1811  dol_syslog(__METHOD__.' '.join(',', $this->errors), LOG_ERR);
1812 
1813  return -1;
1814  }
1815  }
1816 
1824  public function update(User $user, $notrigger = false)
1825  {
1826  return $this->updateCommon($user, $notrigger);
1827  }
1828 
1836  public function delete(User $user, $notrigger = false)
1837  {
1838  return $this->deleteCommon($user, $notrigger);
1839  //return $this->deleteCommon($user, $notrigger, 1);
1840  }
1841 }
$object ref
Definition: info.php:78
Class for BOM.
Definition: bom.class.php:37
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.
deleteLineCommon(User $user, $idline, $notrigger=false)
Delete a line of object in database.
getFieldList($alias='')
Function to concat keys of fields.
fetchCommon($id, $ref=null, $morewhere='')
Load object in memory from the database.
createCommon(User $user, $notrigger=false)
Create object into database.
deleteCommon(User $user, $notrigger=false, $forcechilddeletion=0)
Delete object in database.
setStatusCommon($user, $status, $notrigger=0, $triggercode='')
Set to a status.
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.
static commonReplaceThirdparty(DoliDB $db, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a thirdparty id with another one.
updateCommon(User $user, $notrigger=false)
Update object into database.
fetchLinesCommon($morewhere='')
Load object in memory from the database.
call_trigger($triggerName, $user)
Call trigger based on this instance.
Parent class for class inheritance lines of business objects This class is useless for the moment so ...
Class to manage Dolibarr database access.
Class for Mo.
Definition: mo.class.php:36
__construct(DoliDB $db)
Constructor.
Definition: mo.class.php:219
update(User $user, $notrigger=false)
Update object into database.
Definition: mo.class.php:599
fetchLinesLinked($role, $lineid=0)
Get list of lines linked to current line for a defined role.
Definition: mo.class.php:513
getMoChilds()
Function used to return childs of Mo.
Definition: mo.class.php:1535
static replaceThirdparty($db, $origin_id, $dest_id)
Function used to replace a thirdparty id with another one.
Definition: mo.class.php:1522
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:1407
fetchLines()
Load object lines in memory from the database.
Definition: mo.class.php:419
getNextNumRef($prod)
Returns the reference to the following non used MO depending on the active numbering module defined i...
Definition: mo.class.php:890
deleteLine(User $user, $idline, $notrigger=false)
Delete a line of object in database.
Definition: mo.class.php:805
cancel($user, $notrigger=0)
Set cancel status.
Definition: mo.class.php:1081
fetch($id, $ref=null)
Load object in memory from the database.
Definition: mo.class.php:402
initAsSpecimen()
Initialise object with example values Id must be 0 if object instance is a specimen.
Definition: mo.class.php:1298
reopen($user, $notrigger=0)
Set back to validated status.
Definition: mo.class.php:1105
updateProduction(User $user, $notrigger=true)
Update quantities in lines to consume and to produce.
Definition: mo.class.php:743
getMoParent()
Function used to return childs of Mo.
Definition: mo.class.php:1573
getLinesArray($rolefilter='')
Create an array of lines.
Definition: mo.class.php:1310
LibStatut($status, $mode=0)
Return the status.
Definition: mo.class.php:1226
setDraft($user, $notrigger=0)
Set draft status.
Definition: mo.class.php:1057
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:1479
fetchAll($sortorder='', $sortfield='', $limit=0, $offset=0, array $filter=array(), $filtermode='AND')
Load list of objects in memory from the database.
Definition: mo.class.php:439
validate($user, $notrigger=0)
Validate Mo.
Definition: mo.class.php:938
doScheduledJob()
Action executed by scheduler CAN BE A CRON TASK.
Definition: mo.class.php:1374
generateDocument($modele, $outputlangs, $hidedetails=0, $hidedesc=0, $hideref=0, $moreparams=null)
Create a document onto disk according to template module.
Definition: mo.class.php:1341
createProduction(User $user, $notrigger=true)
Erase and update the line to consume and to produce.
Definition: mo.class.php:635
getNomUrl($withpicto=0, $option='', $notooltip=0, $morecss='', $save_lastsearch_value=-1)
Return a link to the object card (with optionaly the picto)
Definition: mo.class.php:1132
getLibStatut($mode=0)
Return label of the status.
Definition: mo.class.php:1213
info($id)
Load the info information in the object.
Definition: mo.class.php:1268
create(User $user, $notrigger=false)
Create object into database.
Definition: mo.class.php:256
countMovements()
Count number of movement with origin of MO.
Definition: mo.class.php:564
createFromClone(User $user, $fromid)
Clone an object into another one.
Definition: mo.class.php:314
Class MoLine.
Definition: mo.class.php:1609
create(User $user, $notrigger=false)
Create object into database.
Definition: mo.class.php:1716
update(User $user, $notrigger=false)
Update object into database.
Definition: mo.class.php:1824
fetchAll($sortorder='', $sortfield='', $limit=0, $offset=0, array $filter=array(), $filtermode='AND')
Load list of objects in memory from the database.
Definition: mo.class.php:1750
__construct(DoliDB $db)
Constructor.
Definition: mo.class.php:1677
fetch($id, $ref=null)
Load object in memory from the database.
Definition: mo.class.php:1733
Class to manage stock movements.
Class to manage products or services.
Class to manage Dolibarr users.
Definition: user.class.php:47
if(isModEnabled('facture') &&!empty($user->rights->facture->lire)) if((isModEnabled('fournisseur') &&empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD) && $user->hasRight("fournisseur", "facture", "lire"))||(isModEnabled('supplier_invoice') && $user->hasRight("supplier_invoice", "lire"))) if(isModEnabled('don') &&!empty($user->rights->don->lire)) if(isModEnabled('tax') &&!empty($user->rights->tax->charges->lire)) if(isModEnabled('facture') &&isModEnabled('commande') && $user->hasRight("commande", "lire") &&empty($conf->global->WORKFLOW_DISABLE_CREATE_INVOICE_FROM_ORDER)) $resql
Social contributions to pay.
Definition: index.php:745
if($cancel &&! $id) if($action=='add' &&! $cancel) if($action=='delete') if($id) $form
Actions.
Definition: card.php:143
dol_dir_list($path, $types="all", $recursive=0, $filter="", $excludefilter=null, $sortcriteria="name", $sortorder=SORT_ASC, $mode=0, $nohook=0, $relativename="", $donotfollowsymlinks=0, $nbsecondsold=0)
Scan a directory and return a list of files/directories.
Definition: files.lib.php:61
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
dol_print_error($db='', $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=false, $srconly=0, $notitle=0)
Show a picto called object_picto (generic function)
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='')
Set event messages in dol_events session object.
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
dol_now($mode='auto')
Return date for now.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=false, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (generic function)
dolGetStatus($statusLabel='', $statusLabelShort='', $html='', $statusType='status0', $displayMode=0, $url='', $params=array())
Output the badge of a status.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
if(!function_exists('utf8_encode')) if(!function_exists('utf8_decode')) getDolGlobalString($key, $default='')
Return dolibarr global constant string value.
dol_sanitizeFileName($str, $newstr='_', $unaccent=1)
Clean a string to use it as a file name.
isModEnabled($module)
Is Dolibarr module enabled.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
$conf db
API class for accounts.
Definition: inc.php:41