dolibarr  x.y.z
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 maxwidth300', 'csslist'=>'nowraponall', '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  global $conf;
259 
260  $error = 0;
261  $idcreated = 0;
262 
263  $this->db->begin();
264 
265  // Check that product is not a kit/virtual product
266  if (empty($conf->global->ALLOW_USE_KITS_INTO_BOM_AND_MO) && $this->fk_product > 0) {
267  include_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
268  $tmpproduct = new Product($this->db);
269  $tmpproduct->fetch($this->fk_product);
270  if ($tmpproduct->hasFatherOrChild(1) > 0) {
271  $this->error = 'ErrorAVirtualProductCantBeUsedIntoABomOrMo';
272  $this->errors[] = $this->error;
273  $this->db->rollback();
274  return -1;
275  }
276  }
277 
278  if ($this->fk_bom > 0) {
279  // If there is a nown BOM, we force the type of MO to the type of BOM
280  $tmpbom = new BOM($this->db);
281  $tmpbom->fetch($this->fk_bom);
282 
283  $this->mrptype = $tmpbom->bomtype;
284  }
285 
286  if (!$error) {
287  $idcreated = $this->createCommon($user, $notrigger);
288  if ($idcreated <= 0) {
289  $error++;
290  }
291  }
292 
293  if (!$error) {
294  $result = $this->updateProduction($user, $notrigger); // Insert lines from BOM
295  if ($result <= 0) {
296  $error++;
297  }
298  }
299 
300  if (!$error) {
301  $this->db->commit();
302  } else {
303  $this->db->rollback();
304  }
305 
306  return $idcreated;
307  }
308 
316  public function createFromClone(User $user, $fromid)
317  {
318  global $langs, $extrafields;
319  $error = 0;
320 
321  dol_syslog(__METHOD__, LOG_DEBUG);
322 
323  $object = new self($this->db);
324 
325  $this->db->begin();
326 
327  // Load source object
328  $result = $object->fetchCommon($fromid);
329  if ($result > 0 && !empty($object->table_element_line)) {
330  $object->fetchLines();
331  }
332 
333  // get lines so they will be clone
334  //foreach($this->lines as $line)
335  // $line->fetch_optionals();
336 
337  // Reset some properties
338  unset($object->id);
339  unset($object->fk_user_creat);
340  unset($object->import_key);
341 
342  // Clear fields
343  $object->ref = empty($this->fields['ref']['default']) ? "copy_of_".$object->ref : $this->fields['ref']['default'];
344  $object->label = empty($this->fields['label']['default']) ? $langs->trans("CopyOf")." ".$object->label : $this->fields['label']['default'];
345  $object->status = self::STATUS_DRAFT;
346  // ...
347  // Clear extrafields that are unique
348  if (is_array($object->array_options) && count($object->array_options) > 0) {
349  $extrafields->fetch_name_optionals_label($this->table_element);
350  foreach ($object->array_options as $key => $option) {
351  $shortkey = preg_replace('/options_/', '', $key);
352  if (!empty($extrafields->attributes[$this->element]['unique'][$shortkey])) {
353  //var_dump($key);
354  //var_dump($clonedObj->array_options[$key]); exit;
355  unset($object->array_options[$key]);
356  }
357  }
358  }
359 
360  // Create clone
361  $object->context['createfromclone'] = 'createfromclone';
362  $result = $object->createCommon($user);
363  if ($result < 0) {
364  $error++;
365  $this->error = $object->error;
366  $this->errors = $object->errors;
367  }
368 
369  if (!$error) {
370  // copy internal contacts
371  if ($this->copy_linked_contact($object, 'internal') < 0) {
372  $error++;
373  }
374  }
375 
376  if (!$error) {
377  // copy external contacts if same company
378  if (property_exists($this, 'socid') && $this->socid == $object->socid) {
379  if ($this->copy_linked_contact($object, 'external') < 0) {
380  $error++;
381  }
382  }
383  }
384 
385  unset($object->context['createfromclone']);
386 
387  // End
388  if (!$error) {
389  $this->db->commit();
390  return $object;
391  } else {
392  $this->db->rollback();
393  return -1;
394  }
395  }
396 
404  public function fetch($id, $ref = null)
405  {
406  $result = $this->fetchCommon($id, $ref);
407  if ($result > 0 && !empty($this->table_element_line)) {
408  $this->fetchLines();
409  }
410 
411  $this->socid = $this->fk_soc;
412 
413  return $result;
414  }
415 
421  public function fetchLines()
422  {
423  $this->lines = array();
424 
425  $result = $this->fetchLinesCommon();
426  return $result;
427  }
428 
429 
441  public function fetchAll($sortorder = '', $sortfield = '', $limit = 0, $offset = 0, array $filter = array(), $filtermode = 'AND')
442  {
443  global $conf;
444 
445  dol_syslog(__METHOD__, LOG_DEBUG);
446 
447  $records = array();
448 
449  $sql = 'SELECT ';
450  $sql .= $this->getFieldList();
451  $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as t';
452  if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) {
453  $sql .= ' WHERE t.entity IN ('.getEntity($this->table_element).')';
454  } else {
455  $sql .= ' WHERE 1 = 1';
456  }
457  // Manage filter
458  $sqlwhere = array();
459  if (count($filter) > 0) {
460  foreach ($filter as $key => $value) {
461  if ($key == 't.rowid') {
462  $sqlwhere[] = $key." = ".((int) $value);
463  } elseif (strpos($key, 'date') !== false) {
464  $sqlwhere[] = $key." = '".$this->db->idate($value)."'";
465  } elseif ($key == 'customsql') {
466  $sqlwhere[] = $value;
467  } else {
468  $sqlwhere[] = $key." LIKE '%".$this->db->escape($value)."%'";
469  }
470  }
471  }
472  if (count($sqlwhere) > 0) {
473  $sql .= ' AND ('.implode(' '.$this->db->escape($filtermode).' ', $sqlwhere).')';
474  }
475 
476  if (!empty($sortfield)) {
477  $sql .= $this->db->order($sortfield, $sortorder);
478  }
479  if (!empty($limit)) {
480  $sql .= $this->db->plimit($limit, $offset);
481  }
482 
483  $resql = $this->db->query($sql);
484  if ($resql) {
485  $num = $this->db->num_rows($resql);
486  $i = 0;
487  while ($i < min($limit, $num)) {
488  $obj = $this->db->fetch_object($resql);
489 
490  $record = new self($this->db);
491  $record->setVarsFromFetchObj($obj);
492 
493  $records[$record->id] = $record;
494 
495  $i++;
496  }
497  $this->db->free($resql);
498 
499  return $records;
500  } else {
501  $this->errors[] = 'Error '.$this->db->lasterror();
502  dol_syslog(__METHOD__.' '.join(',', $this->errors), LOG_ERR);
503 
504  return -1;
505  }
506  }
507 
515  public function fetchLinesLinked($role, $lineid = 0)
516  {
517  $resarray = array();
518  $mostatic = new MoLine($this->db);
519 
520  $sql = 'SELECT ';
521  $sql .= $mostatic->getFieldList();
522  $sql .= ' FROM '.MAIN_DB_PREFIX.$mostatic->table_element.' as t';
523  $sql .= " WHERE t.role = '".$this->db->escape($role)."'";
524  if ($lineid > 0) {
525  $sql .= ' AND t.fk_mrp_production = '.((int) $lineid);
526  } else {
527  $sql .= 'AND t.fk_mo = '.((int) $this->id);
528  }
529 
530  $resql = $this->db->query($sql);
531  if ($resql) {
532  $num = $this->db->num_rows($resql);
533 
534  $i = 0;
535  while ($i < $num) {
536  $obj = $this->db->fetch_object($resql);
537  if ($obj) {
538  $resarray[] = array(
539  'rowid'=> $obj->rowid,
540  'date'=> $this->db->jdate($obj->date_creation),
541  'qty' => $obj->qty,
542  'role' => $obj->role,
543  'fk_product' => $obj->fk_product,
544  'fk_warehouse' => $obj->fk_warehouse,
545  'batch' => $obj->batch,
546  'fk_stock_movement' => $obj->fk_stock_movement
547  );
548  }
549 
550  $i++;
551  }
552 
553  return $resarray;
554  } else {
555  $this->error = $this->db->lasterror();
556  return array();
557  }
558  }
559 
560 
566  public function countMovements()
567  {
568  $result = 0;
569 
570  $sql = 'SELECT COUNT(rowid) as nb FROM '.MAIN_DB_PREFIX.'stock_mouvement as sm';
571  $sql .= " WHERE sm.origintype = 'mo' and sm.fk_origin = ".((int) $this->id);
572 
573  $resql = $this->db->query($sql);
574  if ($resql) {
575  $num = $this->db->num_rows($resql);
576 
577  $i = 0;
578  while ($i < $num) {
579  $obj = $this->db->fetch_object($resql);
580  if ($obj) {
581  $result = $obj->nb;
582  }
583 
584  $i++;
585  }
586  } else {
587  $this->error = $this->db->lasterror();
588  }
589 
590  return $result;
591  }
592 
593 
601  public function update(User $user, $notrigger = false)
602  {
603  global $langs;
604 
605  $error = 0;
606 
607  $this->db->begin();
608 
609  $result = $this->updateCommon($user, $notrigger);
610  if ($result <= 0) {
611  $error++;
612  }
613 
614  $result = $this->updateProduction($user, $notrigger);
615  if ($result <= 0) {
616  $error++;
617  }
618 
619  if (!$error) {
620  $this->db->commit();
621  return 1;
622  } else {
623  $this->db->rollback();
624  return -1;
625  }
626  }
627 
635  public function updateProduction(User $user, $notrigger = true)
636  {
637  $error = 0;
638  $role = "";
639 
640  if ($this->status != self::STATUS_DRAFT) {
641  //$this->error = 'BadStatusForUpdateProduction';
642  //return -1;
643  return 1;
644  }
645 
646  $this->db->begin();
647 
648  // Insert lines in mrp_production table from BOM data
649  if (!$error) {
650  // TODO Check that production has not started. If yes, we stop here.
651 
652  $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'mrp_production WHERE fk_mo = '.((int) $this->id);
653  $this->db->query($sql);
654 
655  $moline = new MoLine($this->db);
656 
657  // Line to produce
658  $moline->fk_mo = $this->id;
659  $moline->qty = $this->qty;
660  $moline->fk_product = $this->fk_product;
661  $moline->position = 1;
662 
663  if ($this->fk_bom > 0) { // If a BOM is defined, we know what to produce.
664  include_once DOL_DOCUMENT_ROOT.'/bom/class/bom.class.php';
665  $bom = new Bom($this->db);
666  $bom->fetch($this->fk_bom);
667  if ($bom->bomtype == 1) {
668  $role = 'toproduce';
669  $moline->role = 'toconsume';
670  } else {
671  $role = 'toconsume';
672  $moline->role = 'toproduce';
673  }
674  } else {
675  if ($this->mrptype == 1) {
676  $moline->role = 'toconsume';
677  } else {
678  $moline->role = 'toproduce';
679  }
680  }
681 
682  $resultline = $moline->create($user, false); // Never use triggers here
683  if ($resultline <= 0) {
684  $error++;
685  $this->error = $moline->error;
686  $this->errors = $moline->errors;
687  dol_print_error($this->db, $moline->error, $moline->errors);
688  }
689 
690  if ($this->fk_bom > 0) { // If a BOM is defined, we know what to consume.
691  if ($bom->id > 0) {
692  // Lines to consume
693  if (!$error) {
694  foreach ($bom->lines as $line) {
695  $moline = new MoLine($this->db);
696 
697  $moline->fk_mo = $this->id;
698  $moline->origin_id = $line->id;
699  $moline->origin_type = 'bomline';
700  if ($line->qty_frozen) {
701  $moline->qty = $line->qty; // Qty to consume does not depends on quantity to produce
702  } else {
703  $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
704  }
705  if ($moline->qty <= 0) {
706  $error++;
707  $this->error = "BadValueForquantityToConsume";
708  break;
709  } else {
710  $moline->fk_product = $line->fk_product;
711  $moline->role = $role;
712  $moline->position = $line->position;
713  $moline->qty_frozen = $line->qty_frozen;
714  $moline->disable_stock_change = $line->disable_stock_change;
715 
716  $resultline = $moline->create($user, false); // Never use triggers here
717  if ($resultline <= 0) {
718  $error++;
719  $this->error = $moline->error;
720  $this->errors = $moline->errors;
721  dol_print_error($this->db, $moline->error, $moline->errors);
722  break;
723  }
724  }
725  }
726  }
727  }
728  }
729  }
730 
731  if (!$error) {
732  $this->db->commit();
733  return 1;
734  } else {
735  $this->db->rollback();
736  return -1;
737  }
738  }
739 
740 
748  public function delete(User $user, $notrigger = false)
749  {
750  return $this->deleteCommon($user, $notrigger);
751  //return $this->deleteCommon($user, $notrigger, 1);
752  }
753 
762  public function deleteLine(User $user, $idline, $notrigger = false)
763  {
764  global $langs;
765  $langs->load('stocks');
766 
767  if ($this->status < 0) {
768  $this->error = 'ErrorDeleteLineNotAllowedByObjectStatus';
769  return -2;
770  }
771 
772  $productstatic = new Product($this->db);
773  $fk_movement = GETPOST('fk_movement', 'int');
774  $arrayoflines = $this->fetchLinesLinked('consumed', $idline);
775 
776  if (!empty($arrayoflines)) {
777  $this->db->begin();
778 
779  $stockmove = new MouvementStock($this->db);
780  $stockmove->setOrigin($this->element, $this->id);
781 
782  if (!empty($fk_movement)) {
783  $moline = new MoLine($this->db);
784  $TArrayMoLine = $moline->fetchAll('', '', 1, 0, array('customsql' => 'fk_stock_movement ='.$fk_movement));
785  $moline = array_shift($TArrayMoLine);
786 
787  $movement = new MouvementStock($this->db);
788  $movement->fetch($fk_movement);
789  $productstatic->fetch($movement->product_id);
790  $qtytoprocess = $movement->qty;
791 
792  // Reverse stock movement
793  $labelmovementCancel = $langs->trans("CancelProductionForRef", $productstatic->ref);
794  $codemovementCancel = $langs->trans("StockIncrease");
795 
796  if (($qtytoprocess >= 0)) {
797  $idstockmove = $stockmove->reception($user, $movement->product_id, $movement->warehouse_id, $qtytoprocess, 0, $labelmovementCancel, '', '', $movement->batch, dol_now(), 0, $codemovementCancel);
798  } else {
799  $idstockmove = $stockmove->livraison($user, $movement->product_id, $movement->warehouse_id, $qtytoprocess, 0, $labelmovementCancel, dol_now(), '', '', $movement->batch, 0, $codemovementCancel);
800  }
801  if ($idstockmove < 0) {
802  $this->error++;
803  $this->db->rollback();
804  setEventMessages($stockmove->error, $stockmove->errors, 'errors');
805  } else {
806  $this->db->commit();
807  }
808  return $moline->delete($user, $notrigger);
809  } else {
810  foreach ($arrayoflines as $key => $arrayofline) {
811  $lineDetails = $arrayoflines[$key];
812  $productstatic->fetch($lineDetails['fk_product']);
813  $qtytoprocess = $lineDetails['qty'];
814 
815  // Reverse stock movement
816  $labelmovementCancel = $langs->trans("CancelProductionForRef", $productstatic->ref);
817  $codemovementCancel = $langs->trans("StockIncrease");
818 
819  if ($qtytoprocess >= 0) {
820  $idstockmove = $stockmove->reception($user, $lineDetails['fk_product'], $lineDetails['fk_warehouse'], $qtytoprocess, 0, $labelmovementCancel, '', '', $lineDetails['batch'], dol_now(), 0, $codemovementCancel);
821  } else {
822  $idstockmove = $stockmove->livraison($user, $lineDetails['fk_product'], $lineDetails['fk_warehouse'], $qtytoprocess, 0, $labelmovementCancel, dol_now(), '', '', $lineDetails['batch'], 0, $codemovementCancel);
823  }
824  if ($idstockmove < 0) {
825  $this->error++;
826  $this->db->rollback();
827  setEventMessages($stockmove->error, $stockmove->errors, 'errors');
828  } else {
829  $this->db->commit();
830  }
831  }
832  return $this->deleteLineCommon($user, $idline, $notrigger);
833  }
834  } else {
835  return $this->deleteLineCommon($user, $idline, $notrigger);
836  }
837  }
838 
839 
847  public function getNextNumRef($prod)
848  {
849  global $langs, $conf;
850  $langs->load("mrp");
851 
852  if (!empty($conf->global->MRP_MO_ADDON)) {
853  $mybool = false;
854 
855  $file = $conf->global->MRP_MO_ADDON.".php";
856  $classname = $conf->global->MRP_MO_ADDON;
857 
858  // Include file with class
859  $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
860  foreach ($dirmodels as $reldir) {
861  $dir = dol_buildpath($reldir."core/modules/mrp/");
862 
863  // Load file with numbering class (if found)
864  $mybool |= @include_once $dir.$file;
865  }
866 
867  if ($mybool === false) {
868  dol_print_error('', "Failed to include file ".$file);
869  return '';
870  }
871 
872  $obj = new $classname();
873  $numref = $obj->getNextValue($prod, $this);
874 
875  if ($numref != "") {
876  return $numref;
877  } else {
878  $this->error = $obj->error;
879  //dol_print_error($this->db,get_class($this)."::getNextNumRef ".$obj->error);
880  return "";
881  }
882  } else {
883  print $langs->trans("Error")." ".$langs->trans("Error_MRP_MO_ADDON_NotDefined");
884  return "";
885  }
886  }
887 
895  public function validate($user, $notrigger = 0)
896  {
897  global $conf, $langs;
898 
899  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
900 
901  $error = 0;
902 
903  // Protection
904  if ($this->status == self::STATUS_VALIDATED) {
905  dol_syslog(get_class($this)."::validate action abandonned: already validated", LOG_WARNING);
906  return 0;
907  }
908 
909  /*if (! ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->mrp->create))
910  || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->mrp->mrp_advance->validate))))
911  {
912  $this->error='NotEnoughPermissions';
913  dol_syslog(get_class($this)."::valid ".$this->error, LOG_ERR);
914  return -1;
915  }*/
916 
917  $now = dol_now();
918 
919  $this->db->begin();
920 
921  // Define new ref
922  if (!$error && (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref))) { // empty should not happened, but when it occurs, the test save life
923  $this->fetch_product();
924  $num = $this->getNextNumRef($this->product);
925  } else {
926  $num = $this->ref;
927  }
928  $this->newref = $num;
929 
930  // Validate
931  $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
932  $sql .= " SET ref = '".$this->db->escape($num)."',";
933  $sql .= " status = ".self::STATUS_VALIDATED.",";
934  $sql .= " date_valid='".$this->db->idate($now)."',";
935  $sql .= " fk_user_valid = ".$user->id;
936  $sql .= " WHERE rowid = ".((int) $this->id);
937 
938  dol_syslog(get_class($this)."::validate()", LOG_DEBUG);
939  $resql = $this->db->query($sql);
940  if (!$resql) {
941  dol_print_error($this->db);
942  $this->error = $this->db->lasterror();
943  $error++;
944  }
945 
946  if (!$error && !$notrigger) {
947  // Call trigger
948  $result = $this->call_trigger('MRP_MO_VALIDATE', $user);
949  if ($result < 0) {
950  $error++;
951  }
952  // End call triggers
953  }
954 
955  if (!$error) {
956  $this->oldref = $this->ref;
957 
958  // Rename directory if dir was a temporary ref
959  if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
960  // Now we rename also files into index
961  $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)."'";
962  $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'mrp/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
963  $resql = $this->db->query($sql);
964  if (!$resql) {
965  $error++; $this->error = $this->db->lasterror();
966  }
967 
968  // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
969  $oldref = dol_sanitizeFileName($this->ref);
970  $newref = dol_sanitizeFileName($num);
971  $dirsource = $conf->mrp->dir_output.'/'.$oldref;
972  $dirdest = $conf->mrp->dir_output.'/'.$newref;
973  if (!$error && file_exists($dirsource)) {
974  dol_syslog(get_class($this)."::validate() rename dir ".$dirsource." into ".$dirdest);
975 
976  if (@rename($dirsource, $dirdest)) {
977  dol_syslog("Rename ok");
978  // Rename docs starting with $oldref with $newref
979  $listoffiles = dol_dir_list($conf->mrp->dir_output.'/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
980  foreach ($listoffiles as $fileentry) {
981  $dirsource = $fileentry['name'];
982  $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
983  $dirsource = $fileentry['path'].'/'.$dirsource;
984  $dirdest = $fileentry['path'].'/'.$dirdest;
985  @rename($dirsource, $dirdest);
986  }
987  }
988  }
989  }
990  }
991 
992  // Set new ref and current status
993  if (!$error) {
994  $this->ref = $num;
995  $this->status = self::STATUS_VALIDATED;
996  }
997 
998  if (!$error) {
999  $this->db->commit();
1000  return 1;
1001  } else {
1002  $this->db->rollback();
1003  return -1;
1004  }
1005  }
1006 
1014  public function setDraft($user, $notrigger = 0)
1015  {
1016  // Protection
1017  if ($this->status <= self::STATUS_DRAFT) {
1018  return 0;
1019  }
1020 
1021  /*if (! ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->mymodule->write))
1022  || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->mymodule->mymodule_advance->validate))))
1023  {
1024  $this->error='Permission denied';
1025  return -1;
1026  }*/
1027 
1028  return $this->setStatusCommon($user, self::STATUS_DRAFT, $notrigger, 'MRP_MO_UNVALIDATE');
1029  }
1030 
1038  public function cancel($user, $notrigger = 0)
1039  {
1040  // Protection
1041  if ($this->status != self::STATUS_VALIDATED && $this->status != self::STATUS_INPROGRESS) {
1042  return 0;
1043  }
1044 
1045  /*if (! ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->mymodule->write))
1046  || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->mymodule->mymodule_advance->validate))))
1047  {
1048  $this->error='Permission denied';
1049  return -1;
1050  }*/
1051 
1052  return $this->setStatusCommon($user, self::STATUS_CANCELED, $notrigger, 'MRP_MO_CANCEL');
1053  }
1054 
1062  public function reopen($user, $notrigger = 0)
1063  {
1064  // Protection
1065  if ($this->status != self::STATUS_PRODUCED && $this->status != self::STATUS_CANCELED) {
1066  return 0;
1067  }
1068 
1069  /*if (! ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->mymodule->write))
1070  || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->mymodule->mymodule_advance->validate))))
1071  {
1072  $this->error='Permission denied';
1073  return -1;
1074  }*/
1075 
1076  return $this->setStatusCommon($user, self::STATUS_VALIDATED, $notrigger, 'MRP_MO_REOPEN');
1077  }
1078 
1089  public function getNomUrl($withpicto = 0, $option = '', $notooltip = 0, $morecss = '', $save_lastsearch_value = -1)
1090  {
1091  global $conf, $langs, $hookmanager;
1092 
1093  if (!empty($conf->dol_no_mouse_hover)) {
1094  $notooltip = 1; // Force disable tooltips
1095  }
1096 
1097  $result = '';
1098 
1099  $label = img_picto('', $this->picto).' <u class="paddingrightonly">'.$langs->trans("ManufacturingOrder").'</u>';
1100  if (isset($this->status)) {
1101  $label .= ' '.$this->getLibStatut(5);
1102  }
1103  $label .= '<br>';
1104  $label .= '<b>'.$langs->trans('Ref').':</b> '.$this->ref;
1105  if (isset($this->label)) {
1106  $label .= '<br><b>'.$langs->trans('Label').':</b> '.$this->label;
1107  }
1108 
1109  $url = DOL_URL_ROOT.'/mrp/mo_card.php?id='.$this->id;
1110  if ($option == 'production') {
1111  $url = DOL_URL_ROOT.'/mrp/mo_production.php?id='.$this->id;
1112  }
1113 
1114  if ($option != 'nolink') {
1115  // Add param to save lastsearch_values or not
1116  $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
1117  if ($save_lastsearch_value == -1 && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
1118  $add_save_lastsearch_values = 1;
1119  }
1120  if ($add_save_lastsearch_values) {
1121  $url .= '&save_lastsearch_values=1';
1122  }
1123  }
1124 
1125  $linkclose = '';
1126  if (empty($notooltip)) {
1127  if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
1128  $label = $langs->trans("ShowMo");
1129  $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
1130  }
1131  $linkclose .= ' title="'.dol_escape_htmltag($label, 1).'"';
1132  $linkclose .= ' class="classfortooltip'.($morecss ? ' '.$morecss : '').'"';
1133  } else {
1134  $linkclose = ($morecss ? ' class="'.$morecss.'"' : '');
1135  }
1136 
1137  $linkstart = '<a href="'.$url.'"';
1138  $linkstart .= $linkclose.'>';
1139  $linkend = '</a>';
1140 
1141  $result .= $linkstart;
1142  if ($withpicto) {
1143  $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);
1144  }
1145  if ($withpicto != 2) {
1146  $result .= $this->ref;
1147  }
1148  $result .= $linkend;
1149  //if ($withpicto != 2) $result.=(($addlabel && $this->label) ? $sep . dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : '');
1150 
1151  global $action, $hookmanager;
1152  $hookmanager->initHooks(array('modao'));
1153  $parameters = array('id'=>$this->id, 'getnomurl' => &$result);
1154  $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1155  if ($reshook > 0) {
1156  $result = $hookmanager->resPrint;
1157  } else {
1158  $result .= $hookmanager->resPrint;
1159  }
1160 
1161  return $result;
1162  }
1163 
1170  public function getLibStatut($mode = 0)
1171  {
1172  return $this->LibStatut($this->status, $mode);
1173  }
1174 
1175  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1183  public function LibStatut($status, $mode = 0)
1184  {
1185  // phpcs:enable
1186  if (empty($this->labelStatus)) {
1187  global $langs;
1188  //$langs->load("mrp");
1189  $this->labelStatus[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv('Draft');
1190  $this->labelStatus[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv('ValidatedToProduce');
1191  $this->labelStatus[self::STATUS_INPROGRESS] = $langs->transnoentitiesnoconv('InProgress');
1192  $this->labelStatus[self::STATUS_PRODUCED] = $langs->transnoentitiesnoconv('StatusMOProduced');
1193  $this->labelStatus[self::STATUS_CANCELED] = $langs->transnoentitiesnoconv('Canceled');
1194 
1195  $this->labelStatusShort[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv('Draft');
1196  $this->labelStatusShort[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv('Validated');
1197  $this->labelStatusShort[self::STATUS_INPROGRESS] = $langs->transnoentitiesnoconv('InProgress');
1198  $this->labelStatusShort[self::STATUS_PRODUCED] = $langs->transnoentitiesnoconv('StatusMOProduced');
1199  $this->labelStatusShort[self::STATUS_CANCELED] = $langs->transnoentitiesnoconv('Canceled');
1200  }
1201 
1202  $statusType = 'status'.$status;
1203  if ($status == self::STATUS_VALIDATED) {
1204  $statusType = 'status1';
1205  }
1206  if ($status == self::STATUS_INPROGRESS) {
1207  $statusType = 'status4';
1208  }
1209  if ($status == self::STATUS_PRODUCED) {
1210  $statusType = 'status6';
1211  }
1212  if ($status == self::STATUS_CANCELED) {
1213  $statusType = 'status9';
1214  }
1215 
1216  return dolGetStatus($this->labelStatus[$status], $this->labelStatusShort[$status], '', $statusType, $mode);
1217  }
1218 
1225  public function info($id)
1226  {
1227  $sql = 'SELECT rowid, date_creation as datec, tms as datem,';
1228  $sql .= ' fk_user_creat, fk_user_modif';
1229  $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as t';
1230  $sql .= ' WHERE t.rowid = '.((int) $id);
1231  $result = $this->db->query($sql);
1232  if ($result) {
1233  if ($this->db->num_rows($result)) {
1234  $obj = $this->db->fetch_object($result);
1235  $this->id = $obj->rowid;
1236 
1237  $this->user_creation_id = $obj->fk_user_creat;
1238  $this->user_modification_id = $obj->fk_user_modif;
1239  $this->date_creation = $this->db->jdate($obj->datec);
1240  $this->date_modification = empty($obj->datem) ? '' : $this->db->jdate($obj->datem);
1241  }
1242 
1243  $this->db->free($result);
1244  } else {
1245  dol_print_error($this->db);
1246  }
1247  }
1248 
1255  public function initAsSpecimen()
1256  {
1257  $this->initAsSpecimenCommon();
1258 
1259  $this->lines = array();
1260  }
1261 
1267  public function getLinesArray($rolefilter = '')
1268  {
1269  $this->lines = array();
1270 
1271  $objectline = new MoLine($this->db);
1272 
1273  $TFilters = array('customsql'=>'fk_mo = '.((int) $this->id));
1274  if (!empty($rolefilter)) $TFilters['role'] = $rolefilter;
1275  $result = $objectline->fetchAll('ASC', 'position', 0, 0, $TFilters);
1276 
1277  if (is_numeric($result)) {
1278  $this->error = $objectline->error;
1279  $this->errors = $objectline->errors;
1280  return $result;
1281  } else {
1282  $this->lines = $result;
1283  return $this->lines;
1284  }
1285  }
1286 
1298  public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
1299  {
1300  global $conf, $langs;
1301 
1302  $langs->load("mrp");
1303 
1304  if (!dol_strlen($modele)) {
1305  //$modele = 'standard';
1306  $modele = ''; // Remove this once a pdf_standard.php exists.
1307 
1308  if ($this->model_pdf) {
1309  $modele = $this->model_pdf;
1310  } elseif (!empty($conf->global->MO_ADDON_PDF)) {
1311  $modele = $conf->global->MO_ADDON_PDF;
1312  }
1313  }
1314 
1315  $modelpath = "core/modules/mrp/doc/";
1316 
1317  if (empty($modele)) {
1318  return 1; // Remove this once a pdf_standard.php exists.
1319  }
1320 
1321  return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
1322  }
1323 
1331  public function doScheduledJob()
1332  {
1333  global $conf, $langs;
1334 
1335  //$conf->global->SYSLOG_FILE = 'DOL_DATA_ROOT/dolibarr_mydedicatedlofile.log';
1336 
1337  $error = 0;
1338  $this->output = '';
1339  $this->error = '';
1340 
1341  dol_syslog(__METHOD__, LOG_DEBUG);
1342 
1343  $now = dol_now();
1344 
1345  $this->db->begin();
1346 
1347  // ...
1348 
1349  $this->db->commit();
1350 
1351  return $error;
1352  }
1353 
1364  public function printOriginLinesList($restrictlist = '', $selectedLines = array())
1365  {
1366  global $langs, $hookmanager, $conf, $form;
1367 
1368  $langs->load('stocks');
1369  $text_stock_options = $langs->trans("RealStockDesc").'<br>';
1370  $text_stock_options .= $langs->trans("RealStockWillAutomaticallyWhen").'<br>';
1371  $text_stock_options .= (!empty($conf->global->STOCK_CALCULATE_ON_SHIPMENT) || !empty($conf->global->STOCK_CALCULATE_ON_SHIPMENT_CLOSE) ? '- '.$langs->trans("DeStockOnShipment").'<br>' : '');
1372  $text_stock_options .= (!empty($conf->global->STOCK_CALCULATE_ON_VALIDATE_ORDER) ? '- '.$langs->trans("DeStockOnValidateOrder").'<br>' : '');
1373  $text_stock_options .= (!empty($conf->global->STOCK_CALCULATE_ON_BILL) ? '- '.$langs->trans("DeStockOnBill").'<br>' : '');
1374  $text_stock_options .= (!empty($conf->global->STOCK_CALCULATE_ON_SUPPLIER_BILL) ? '- '.$langs->trans("ReStockOnBill").'<br>' : '');
1375  $text_stock_options .= (!empty($conf->global->STOCK_CALCULATE_ON_SUPPLIER_VALIDATE_ORDER) ? '- '.$langs->trans("ReStockOnValidateOrder").'<br>' : '');
1376  $text_stock_options .= (!empty($conf->global->STOCK_CALCULATE_ON_SUPPLIER_DISPATCH_ORDER) ? '- '.$langs->trans("ReStockOnDispatchOrder").'<br>' : '');
1377  $text_stock_options .= (!empty($conf->global->STOCK_CALCULATE_ON_RECEPTION) || !empty($conf->global->STOCK_CALCULATE_ON_RECEPTION_CLOSE) ? '- '.$langs->trans("StockOnReception").'<br>' : '');
1378 
1379  print '<tr class="liste_titre">';
1380  // Product or sub-bom
1381  print '<td class="linecoldescription">'.$langs->trans('Ref');
1382  if (!empty($conf->global->BOM_SUB_BOM)) {
1383  print ' &nbsp; <a id="show_all" href="#">'.img_picto('', 'folder-open', 'class="paddingright"').$langs->trans("ExpandAll").'</a>&nbsp;&nbsp;';
1384  print '<a id="hide_all" href="#">'.img_picto('', 'folder', 'class="paddingright"').$langs->trans("UndoExpandAll").'</a>&nbsp;';
1385  }
1386  print '</td>';
1387  // Qty
1388  print '<td class="right">'.$langs->trans('Qty');
1389  if ($this->bom->bomtype == 0) {
1390  print ' <span class="opacitymedium">('.$langs->trans("ForAQuantityOf", $this->bom->qty).')</span>';
1391  } else {
1392  print ' <span class="opacitymedium">('.$langs->trans("ForAQuantityToConsumeOf", $this->bom->qty).')</span>';
1393  }
1394  print '</td>';
1395  print '<td class="center">'.$form->textwithpicto($langs->trans("PhysicalStock"), $text_stock_options, 1).'</td>';
1396  print '<td class="center">'.$form->textwithpicto($langs->trans("VirtualStock"), $langs->trans("VirtualStockDesc")).'</td>';
1397  print '<td class="center">'.$langs->trans('QtyFrozen').'</td>';
1398  print '<td class="center">'.$langs->trans('DisableStockChange').'</td>';
1399  print '<td class="center">'.$langs->trans('MoChildGenerate').'</td>';
1400  //print '<td class="center">'.$form->showCheckAddButtons('checkforselect', 1).'</td>';
1401  //print '<td class="center"></td>';
1402  print '</tr>';
1403  $i = 0;
1404 
1405  if (!empty($this->lines)) {
1406  foreach ($this->lines as $line) {
1407  $reshook = 0;
1408  if (is_object($hookmanager)) {
1409  $parameters = array('line'=>$line, 'i'=>$i, 'restrictlist'=>$restrictlist, 'selectedLines'=> $selectedLines);
1410  if (!empty($line->fk_parent_line)) { $parameters['fk_parent_line'] = $line->fk_parent_line; }
1411  $reshook = $hookmanager->executeHooks('printOriginObjectLine', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1412  }
1413  if (empty($reshook)) {
1414  $this->printOriginLine($line, '', $restrictlist, '/core/tpl', $selectedLines);
1415  }
1416 
1417  $i++;
1418  }
1419  }
1420  }
1421 
1422 
1436  public function printOriginLine($line, $var, $restrictlist = '', $defaulttpldir = '/core/tpl', $selectedLines = array())
1437  {
1438  global $langs, $conf;
1439 
1440  $this->tpl['id'] = $line->id;
1441 
1442  $this->tpl['label'] = '';
1443  if (!empty($line->fk_product)) {
1444  $productstatic = new Product($this->db);
1445  $productstatic->fetch($line->fk_product);
1446  $productstatic->load_virtual_stock();
1447  $this->tpl['label'] .= $productstatic->getNomUrl(1);
1448  //$this->tpl['label'].= ' - '.$productstatic->label;
1449  } else {
1450  // If origin MRP line is not a product, but another MRP
1451  // TODO
1452  }
1453 
1454  $this->tpl['qty_bom'] = 1;
1455  if (is_object($this->bom) && $this->bom->qty > 1) {
1456  $this->tpl['qty_bom'] = $this->bom->qty;
1457  }
1458 
1459  $this->tpl['stock'] = $productstatic->stock_reel;
1460  $this->tpl['seuil_stock_alerte'] = $productstatic->seuil_stock_alerte;
1461  $this->tpl['virtual_stock'] = $productstatic->stock_theorique;
1462  $this->tpl['qty'] = $line->qty;
1463  $this->tpl['qty_frozen'] = $line->qty_frozen;
1464  $this->tpl['disable_stock_change'] = $line->disable_stock_change;
1465  $this->tpl['efficiency'] = $line->efficiency;
1466 
1467  $tpl = DOL_DOCUMENT_ROOT.'/mrp/tpl/originproductline.tpl.php';
1468  $res = include $tpl;
1469  }
1470 
1479  public static function replaceThirdparty($db, $origin_id, $dest_id)
1480  {
1481  $tables = array('mrp_mo');
1482 
1483  return CommonObject::commonReplaceThirdparty($db, $origin_id, $dest_id, $tables);
1484  }
1485 
1486 
1492  public function getMoChilds()
1493  {
1494 
1495  $TMoChilds = array();
1496  $error = 0;
1497 
1498  $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."mrp_mo as mo_child";
1499  $sql.= " WHERE fk_parent_line IN ";
1500  $sql.= " (SELECT rowid FROM ".MAIN_DB_PREFIX."mrp_production as line_parent";
1501  $sql.= " WHERE fk_mo=".((int) $this->id).")";
1502 
1503  $resql = $this->db->query($sql);
1504 
1505  if ($resql) {
1506  if ($this->db->num_rows($resql) > 0) {
1507  while ($obj = $this->db->fetch_object($resql)) {
1508  $MoChild = new Mo($this->db);
1509  $res = $MoChild->fetch($obj->rowid);
1510  if ($res > 0) $TMoChilds[$MoChild->id] = $MoChild;
1511  else $error++;
1512  }
1513  }
1514  } else {
1515  $error++;
1516  }
1517 
1518  if ($error) {
1519  return -1;
1520  } else {
1521  return $TMoChilds;
1522  }
1523  }
1524 
1530  public function getMoParent()
1531  {
1532  $MoParent = new Mo($this->db);
1533  $error = 0;
1534 
1535  $sql = "SELECT lineparent.fk_mo as id_moparent FROM ".MAIN_DB_PREFIX."mrp_mo as mo";
1536  $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."mrp_production lineparent ON mo.fk_parent_line = lineparent.rowid";
1537  $sql.= " WHERE mo.rowid = ".((int) $this->id);
1538 
1539  $resql = $this->db->query($sql);
1540 
1541  if ($resql) {
1542  if ($this->db->num_rows($resql) > 0) {
1543  $obj = $this->db->fetch_object($resql);
1544  $res = $MoParent->fetch($obj->id_moparent);
1545  if ($res < 0) $error++;
1546  } else {
1547  return 0;
1548  }
1549  } else {
1550  $error++;
1551  }
1552 
1553  if ($error) {
1554  return -1;
1555  } else {
1556  return $MoParent;
1557  }
1558  }
1559 }
1560 
1565 {
1569  public $element = 'mrp_production';
1570 
1574  public $table_element = 'mrp_production';
1575 
1579  public $ismultientitymanaged = 0;
1580 
1584  public $isextrafieldmanaged = 0;
1585 
1586  public $fields = array(
1587  'rowid' =>array('type'=>'integer', 'label'=>'ID', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>10),
1588  'fk_mo' =>array('type'=>'integer', 'label'=>'Mo', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>15),
1589  'origin_id' =>array('type'=>'integer', 'label'=>'Origin', 'enabled'=>1, 'visible'=>-1, 'notnull'=>0, 'position'=>17),
1590  'origin_type' =>array('type'=>'varchar(10)', 'label'=>'Origin type', 'enabled'=>1, 'visible'=>-1, 'notnull'=>0, 'position'=>18),
1591  'position' =>array('type'=>'integer', 'label'=>'Position', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>20),
1592  'fk_product' =>array('type'=>'integer', 'label'=>'Product', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>25),
1593  'fk_warehouse' =>array('type'=>'integer', 'label'=>'Warehouse', 'enabled'=>1, 'visible'=>-1, 'position'=>30),
1594  'qty' =>array('type'=>'real', 'label'=>'Qty', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>35),
1595  'qty_frozen' => array('type'=>'smallint', 'label'=>'QuantityFrozen', 'enabled'=>1, 'visible'=>1, 'default'=>0, 'position'=>105, 'css'=>'maxwidth50imp', 'help'=>'QuantityConsumedInvariable'),
1596  'disable_stock_change' => array('type'=>'smallint', 'label'=>'DisableStockChange', 'enabled'=>1, 'visible'=>1, 'default'=>0, 'position'=>108, 'css'=>'maxwidth50imp', 'help'=>'DisableStockChangeHelp'),
1597  'batch' =>array('type'=>'varchar(30)', 'label'=>'Batch', 'enabled'=>1, 'visible'=>-1, 'position'=>140),
1598  'role' =>array('type'=>'varchar(10)', 'label'=>'Role', 'enabled'=>1, 'visible'=>-1, 'position'=>145),
1599  'fk_mrp_production' =>array('type'=>'integer', 'label'=>'Fk mrp production', 'enabled'=>1, 'visible'=>-1, 'position'=>150),
1600  'fk_stock_movement' =>array('type'=>'integer', 'label'=>'StockMovement', 'enabled'=>1, 'visible'=>-1, 'position'=>155),
1601  'date_creation' =>array('type'=>'datetime', 'label'=>'DateCreation', 'enabled'=>1, 'visible'=>-2, 'notnull'=>1, 'position'=>160),
1602  'tms' =>array('type'=>'timestamp', 'label'=>'Tms', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>165),
1603  'fk_user_creat' =>array('type'=>'integer', 'label'=>'UserCreation', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>170),
1604  'fk_user_modif' =>array('type'=>'integer', 'label'=>'UserModification', 'enabled'=>1, 'visible'=>-1, 'position'=>175),
1605  'import_key' =>array('type'=>'varchar(14)', 'label'=>'ImportId', 'enabled'=>1, 'visible'=>-1, 'position'=>180),
1606  );
1607 
1608  public $rowid;
1609  public $fk_mo;
1610  public $origin_id;
1611  public $origin_type;
1612  public $position;
1613  public $fk_product;
1614  public $fk_warehouse;
1615  public $qty;
1616  public $qty_frozen;
1617  public $disable_stock_change;
1618  public $batch;
1619  public $role;
1620  public $fk_mrp_production;
1621  public $fk_stock_movement;
1622  public $date_creation;
1623  public $tms;
1624  public $fk_user_creat;
1625  public $fk_user_modif;
1626  public $import_key;
1627 
1633  public function __construct(DoliDB $db)
1634  {
1635  global $conf, $langs;
1636 
1637  $this->db = $db;
1638 
1639  if (empty($conf->global->MAIN_SHOW_TECHNICAL_ID) && isset($this->fields['rowid'])) {
1640  $this->fields['rowid']['visible'] = 0;
1641  }
1642  if (!isModEnabled('multicompany') && isset($this->fields['entity'])) {
1643  $this->fields['entity']['enabled'] = 0;
1644  }
1645 
1646  // Unset fields that are disabled
1647  foreach ($this->fields as $key => $val) {
1648  if (isset($val['enabled']) && empty($val['enabled'])) {
1649  unset($this->fields[$key]);
1650  }
1651  }
1652 
1653  // Translate some data of arrayofkeyval
1654  if (is_object($langs)) {
1655  foreach ($this->fields as $key => $val) {
1656  if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
1657  foreach ($val['arrayofkeyval'] as $key2 => $val2) {
1658  $this->fields[$key]['arrayofkeyval'][$key2] = $langs->trans($val2);
1659  }
1660  }
1661  }
1662  }
1663  }
1664 
1672  public function create(User $user, $notrigger = false)
1673  {
1674  if (empty($this->qty)) {
1675  $this->error = 'BadValueForQty';
1676  return -1;
1677  }
1678 
1679  return $this->createCommon($user, $notrigger);
1680  }
1681 
1689  public function fetch($id, $ref = null)
1690  {
1691  $result = $this->fetchCommon($id, $ref);
1692  return $result;
1693  }
1694 
1706  public function fetchAll($sortorder = '', $sortfield = '', $limit = 0, $offset = 0, array $filter = array(), $filtermode = 'AND')
1707  {
1708  global $conf;
1709 
1710  dol_syslog(__METHOD__, LOG_DEBUG);
1711 
1712  $records = array();
1713 
1714  $sql = 'SELECT ';
1715  $sql .= $this->getFieldList();
1716  $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as t';
1717  if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) {
1718  $sql .= ' WHERE t.entity IN ('.getEntity($this->table_element).')';
1719  } else {
1720  $sql .= ' WHERE 1 = 1';
1721  }
1722  // Manage filter
1723  $sqlwhere = array();
1724  if (count($filter) > 0) {
1725  foreach ($filter as $key => $value) {
1726  if ($key == 't.rowid') {
1727  $sqlwhere[] = $key." = ".((int) $value);
1728  } elseif (strpos($key, 'date') !== false) {
1729  $sqlwhere[] = $key." = '".$this->db->idate($value)."'";
1730  } elseif ($key == 'customsql') {
1731  $sqlwhere[] = $value;
1732  } else {
1733  $sqlwhere[] = $key." LIKE '%".$this->db->escape($value)."%'";
1734  }
1735  }
1736  }
1737  if (count($sqlwhere) > 0) {
1738  $sql .= ' AND ('.implode(' '.$this->db->escape($filtermode).' ', $sqlwhere).')';
1739  }
1740 
1741  if (!empty($sortfield)) {
1742  $sql .= $this->db->order($sortfield, $sortorder);
1743  }
1744  if (!empty($limit)) {
1745  $sql .= $this->db->plimit($limit, $offset);
1746  }
1747 
1748  $resql = $this->db->query($sql);
1749  if ($resql) {
1750  $num = $this->db->num_rows($resql);
1751  $i = 0;
1752  while ($i < ($limit ? min($limit, $num) : $num)) {
1753  $obj = $this->db->fetch_object($resql);
1754 
1755  $record = new self($this->db);
1756  $record->setVarsFromFetchObj($obj);
1757 
1758  $records[$record->id] = $record;
1759 
1760  $i++;
1761  }
1762  $this->db->free($resql);
1763 
1764  return $records;
1765  } else {
1766  $this->errors[] = 'Error '.$this->db->lasterror();
1767  dol_syslog(__METHOD__.' '.join(',', $this->errors), LOG_ERR);
1768 
1769  return -1;
1770  }
1771  }
1772 
1780  public function update(User $user, $notrigger = false)
1781  {
1782  return $this->updateCommon($user, $notrigger);
1783  }
1784 
1792  public function delete(User $user, $notrigger = false)
1793  {
1794  return $this->deleteCommon($user, $notrigger);
1795  //return $this->deleteCommon($user, $notrigger, 1);
1796  }
1797 }
$object ref
Definition: info.php:78
Class for BOM.
Definition: bom.class.php:36
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.
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.
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:601
fetchLinesLinked($role, $lineid=0)
Get list of lines linked to current line for a defined role.
Definition: mo.class.php:515
getMoChilds()
Function used to return childs of Mo.
Definition: mo.class.php:1492
static replaceThirdparty($db, $origin_id, $dest_id)
Function used to replace a thirdparty id with another one.
Definition: mo.class.php:1479
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:1364
fetchLines()
Load object lines in memory from the database.
Definition: mo.class.php:421
getNextNumRef($prod)
Returns the reference to the following non used MO depending on the active numbering module defined i...
Definition: mo.class.php:847
deleteLine(User $user, $idline, $notrigger=false)
Delete a line of object in database.
Definition: mo.class.php:762
cancel($user, $notrigger=0)
Set cancel status.
Definition: mo.class.php:1038
fetch($id, $ref=null)
Load object in memory from the database.
Definition: mo.class.php:404
initAsSpecimen()
Initialise object with example values Id must be 0 if object instance is a specimen.
Definition: mo.class.php:1255
reopen($user, $notrigger=0)
Set back to validated status.
Definition: mo.class.php:1062
updateProduction(User $user, $notrigger=true)
Erase and update the line to consume and to produce.
Definition: mo.class.php:635
getMoParent()
Function used to return childs of Mo.
Definition: mo.class.php:1530
getLinesArray($rolefilter='')
Create an array of lines.
Definition: mo.class.php:1267
LibStatut($status, $mode=0)
Return the status.
Definition: mo.class.php:1183
setDraft($user, $notrigger=0)
Set draft status.
Definition: mo.class.php:1014
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:1436
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:441
validate($user, $notrigger=0)
Validate Mo.
Definition: mo.class.php:895
doScheduledJob()
Action executed by scheduler CAN BE A CRON TASK.
Definition: mo.class.php:1331
generateDocument($modele, $outputlangs, $hidedetails=0, $hidedesc=0, $hideref=0, $moreparams=null)
Create a document onto disk according to template module.
Definition: mo.class.php:1298
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:1089
getLibStatut($mode=0)
Return label of the status.
Definition: mo.class.php:1170
info($id)
Load the info information in the object.
Definition: mo.class.php:1225
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:566
createFromClone(User $user, $fromid)
Clone an object into another one.
Definition: mo.class.php:316
Class MoLine.
Definition: mo.class.php:1565
create(User $user, $notrigger=false)
Create object into database.
Definition: mo.class.php:1672
update(User $user, $notrigger=false)
Update object into database.
Definition: mo.class.php:1780
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:1706
__construct(DoliDB $db)
Constructor.
Definition: mo.class.php:1633
fetch($id, $ref=null)
Load object in memory from the database.
Definition: mo.class.php:1689
Class to manage stock movements.
Class to manage products or services.
Class to manage Dolibarr users.
Definition: user.class.php:45
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.
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