dolibarr  16.0.1
mo_production.php
Go to the documentation of this file.
1 <?php
2 /* Copyright (C) 2019-2020 Laurent Destailleur <eldy@users.sourceforge.net>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
24 // Load Dolibarr environment
25 require '../main.inc.php';
26 
27 require_once DOL_DOCUMENT_ROOT.'/core/class/html.formcompany.class.php';
28 require_once DOL_DOCUMENT_ROOT.'/core/class/html.formfile.class.php';
29 require_once DOL_DOCUMENT_ROOT.'/core/class/html.formprojet.class.php';
30 require_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
31 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
32 require_once DOL_DOCUMENT_ROOT.'/product/class/html.formproduct.class.php';
33 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/entrepot.class.php';
34 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/productlot.class.php';
35 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
36 dol_include_once('/mrp/class/mo.class.php');
37 dol_include_once('/bom/class/bom.class.php');
38 dol_include_once('/mrp/lib/mrp_mo.lib.php');
39 
40 // Load translation files required by the page
41 $langs->loadLangs(array("mrp", "stocks", "other", "product", "productbatch"));
42 
43 // Get parameters
44 $id = GETPOST('id', 'int');
45 $ref = GETPOST('ref', 'alpha');
46 $action = GETPOST('action', 'aZ09');
47 $confirm = GETPOST('confirm', 'alpha');
48 $cancel = GETPOST('cancel', 'aZ09');
49 $contextpage = GETPOST('contextpage', 'aZ') ?GETPOST('contextpage', 'aZ') : 'mocard'; // To manage different context of search
50 $backtopage = GETPOST('backtopage', 'alpha');
51 $lineid = GETPOST('lineid', 'int');
52 $fk_movement = GETPOST('fk_movement', 'int');
53 $fk_default_warehouse = GETPOST('fk_default_warehouse', 'int');
54 
55 $collapse = GETPOST('collapse', 'aZ09comma');
56 
57 // Initialize technical objects
58 $object = new Mo($db);
59 $extrafields = new ExtraFields($db);
60 $diroutputmassaction = $conf->mrp->dir_output.'/temp/massgeneration/'.$user->id;
61 $hookmanager->initHooks(array('mocard', 'globalcard')); // Note that conf->hooks_modules contains array
62 
63 // Fetch optionals attributes and labels
64 $extrafields->fetch_name_optionals_label($object->table_element);
65 
66 $search_array_options = $extrafields->getOptionalsFromPost($object->table_element, '', 'search_');
67 
68 // Initialize array of search criterias
69 $search_all = GETPOST("search_all", 'alpha');
70 $search = array();
71 foreach ($object->fields as $key => $val) {
72  if (GETPOST('search_'.$key, 'alpha')) {
73  $search[$key] = GETPOST('search_'.$key, 'alpha');
74  }
75 }
76 
77 if (empty($action) && empty($id) && empty($ref)) {
78  $action = 'view';
79 }
80 
81 // Load object
82 include DOL_DOCUMENT_ROOT.'/core/actions_fetchobject.inc.php'; // Must be include, not include_once.
83 
84 // Security check - Protection if external user
85 //if ($user->socid > 0) accessforbidden();
86 //if ($user->socid > 0) $socid = $user->socid;
87 $isdraft = (($object->status == $object::STATUS_DRAFT) ? 1 : 0);
88 $result = restrictedArea($user, 'mrp', $object->id, 'mrp_mo', '', 'fk_soc', 'rowid', $isdraft);
89 
90 $permissionnote = $user->rights->mrp->write; // Used by the include of actions_setnotes.inc.php
91 $permissiondellink = $user->rights->mrp->write; // Used by the include of actions_dellink.inc.php
92 $permissiontoadd = $user->rights->mrp->write; // Used by the include of actions_addupdatedelete.inc.php and actions_lineupdown.inc.php
93 $permissiontodelete = $user->rights->mrp->delete || ($permissiontoadd && isset($object->status) && $object->status == $object::STATUS_DRAFT);
94 $upload_dir = $conf->mrp->multidir_output[isset($object->entity) ? $object->entity : 1];
95 
96 $permissiontoproduce = $permissiontoadd;
97 $permissiontoupdatecost = $user->rights->bom->read; // User who can define cost must have knowledge of pricing
98 
99 
100 /*
101  * Actions
102  */
103 
104 $parameters = array();
105 $reshook = $hookmanager->executeHooks('doActions', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
106 if ($reshook < 0) {
107  setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
108 }
109 
110 if (empty($reshook)) {
111  $error = 0;
112 
113  $backurlforlist = dol_buildpath('/mrp/mo_list.php', 1);
114 
115  if (empty($backtopage) || ($cancel && empty($id))) {
116  //var_dump($backurlforlist);exit;
117  if (empty($id) && (($action != 'add' && $action != 'create') || $cancel)) {
118  $backtopage = $backurlforlist;
119  } else {
120  $backtopage = DOL_URL_ROOT.'/mrp/mo_production.php?id='.($id > 0 ? $id : '__ID__');
121  }
122  }
123  $triggermodname = 'MRP_MO_MODIFY'; // Name of trigger action code to execute when we modify record
124 
125  // Actions cancel, add, update, delete or clone
126  include DOL_DOCUMENT_ROOT.'/core/actions_addupdatedelete.inc.php';
127 
128  // Actions when linking object each other
129  include DOL_DOCUMENT_ROOT.'/core/actions_dellink.inc.php';
130 
131  // Actions when printing a doc from card
132  include DOL_DOCUMENT_ROOT.'/core/actions_printing.inc.php';
133 
134  // Actions to send emails
135  $triggersendname = 'MO_SENTBYMAIL';
136  $autocopy = 'MAIN_MAIL_AUTOCOPY_MO_TO';
137  $trackid = 'mo'.$object->id;
138  include DOL_DOCUMENT_ROOT.'/core/actions_sendmails.inc.php';
139 
140  // Action to move up and down lines of object
141  //include DOL_DOCUMENT_ROOT.'/core/actions_lineupdown.inc.php'; // Must be include, not include_once
142 
143  if ($action == 'set_thirdparty' && $permissiontoadd) {
144  $object->setValueFrom('fk_soc', GETPOST('fk_soc', 'int'), '', '', 'date', '', $user, 'MO_MODIFY');
145  }
146  if ($action == 'classin' && $permissiontoadd) {
147  $object->setProject(GETPOST('projectid', 'int'));
148  }
149 
150  if ($action == 'confirm_reopen' && $permissiontoadd) {
151  $result = $object->setStatut($object::STATUS_INPROGRESS, 0, '', 'MRP_REOPEN');
152  }
153 
154  if (($action == 'confirm_addconsumeline' && GETPOST('addconsumelinebutton') && $permissiontoadd)
155  || ($action == 'confirm_addproduceline' && GETPOST('addproducelinebutton') && $permissiontoadd)) {
156  $moline = new MoLine($db);
157 
158  // Line to produce
159  $moline->fk_mo = $object->id;
160  $moline->qty = GETPOST('qtytoadd', 'int'); ;
161  $moline->fk_product = GETPOST('productidtoadd', 'int');
162  if (GETPOST('addconsumelinebutton')) {
163  $moline->role = 'toconsume';
164  } else {
165  $moline->role = 'toproduce';
166  }
167  $moline->origin_type = 'free'; // free consume line
168  $moline->position = 0;
169 
170  $resultline = $moline->create($user, false); // Never use triggers here
171  if ($resultline <= 0) {
172  $error++;
173  setEventMessages($moline->error, $molines->errors, 'errors');
174  }
175 
176  $action = '';
177  }
178 
179  if (in_array($action, array('confirm_consumeorproduce', 'confirm_consumeandproduceall')) && $permissiontoproduce) {
180  $stockmove = new MouvementStock($db);
181 
182  $labelmovement = GETPOST('inventorylabel', 'alphanohtml');
183  $codemovement = GETPOST('inventorycode', 'alphanohtml');
184 
185  $db->begin();
186  $pos = 0;
187  // Process line to consume
188  foreach ($object->lines as $line) {
189  if ($line->role == 'toconsume') {
190  $tmpproduct = new Product($db);
191  $tmpproduct->fetch($line->fk_product);
192 
193  $i = 1;
194  while (GETPOSTISSET('qty-'.$line->id.'-'.$i)) {
195  $qtytoprocess = price2num(GETPOST('qty-'.$line->id.'-'.$i));
196 
197  if ($qtytoprocess != 0) {
198  // Check warehouse is set if we should have to
199  if (GETPOSTISSET('idwarehouse-'.$line->id.'-'.$i)) { // If there is a warehouse to set
200  if (!(GETPOST('idwarehouse-'.$line->id.'-'.$i) > 0)) { // If there is no warehouse set.
201  $langs->load("errors");
202  setEventMessages($langs->trans("ErrorFieldRequiredForProduct", $langs->transnoentitiesnoconv("Warehouse"), $tmpproduct->ref), null, 'errors');
203  $error++;
204  }
205  if ($tmpproduct->status_batch && (!GETPOST('batch-'.$line->id.'-'.$i))) {
206  $langs->load("errors");
207  setEventMessages($langs->trans("ErrorFieldRequiredForProduct", $langs->transnoentitiesnoconv("Batch"), $tmpproduct->ref), null, 'errors');
208  $error++;
209  }
210  }
211 
212  $idstockmove = 0;
213  if (!$error && GETPOST('idwarehouse-'.$line->id.'-'.$i) > 0) {
214  // Record stock movement
215  $id_product_batch = 0;
216  $stockmove->setOrigin($object->element, $object->id);
217 
218  if ($qtytoprocess >= 0) {
219  $idstockmove = $stockmove->livraison($user, $line->fk_product, GETPOST('idwarehouse-'.$line->id.'-'.$i), $qtytoprocess, 0, $labelmovement, dol_now(), '', '', GETPOST('batch-'.$line->id.'-'.$i), $id_product_batch, $codemovement);
220  } else {
221  $idstockmove = $stockmove->reception($user, $line->fk_product, GETPOST('idwarehouse-'.$line->id.'-'.$i), $qtytoprocess, 0, $labelmovement, dol_now(), '', '', GETPOST('batch-'.$line->id.'-'.$i), $id_product_batch, $codemovement);
222  }
223  if ($idstockmove < 0) {
224  $error++;
225  setEventMessages($stockmove->error, $stockmove->errors, 'errors');
226  }
227  }
228 
229  if (!$error) {
230  // Record consumption
231  $moline = new MoLine($db);
232  $moline->fk_mo = $object->id;
233  $moline->position = $pos;
234  $moline->fk_product = $line->fk_product;
235  $moline->fk_warehouse = GETPOST('idwarehouse-'.$line->id.'-'.$i);
236  $moline->qty = $qtytoprocess;
237  $moline->batch = GETPOST('batch-'.$line->id.'-'.$i);
238  $moline->role = 'consumed';
239  $moline->fk_mrp_production = $line->id;
240  $moline->fk_stock_movement = $idstockmove;
241  $moline->fk_user_creat = $user->id;
242 
243  $resultmoline = $moline->create($user);
244  if ($resultmoline <= 0) {
245  $error++;
246  setEventMessages($moline->error, $moline->errors, 'errors');
247  }
248 
249  $pos++;
250  }
251  }
252 
253  $i++;
254  }
255  }
256  }
257 
258  // Process line to produce
259  $pos = 0;
260  foreach ($object->lines as $line) {
261  if ($line->role == 'toproduce') {
262  $tmpproduct = new Product($db);
263  $tmpproduct->fetch($line->fk_product);
264 
265  $i = 1;
266  while (GETPOSTISSET('qtytoproduce-'.$line->id.'-'.$i)) {
267  $qtytoprocess = price2num(GETPOST('qtytoproduce-'.$line->id.'-'.$i));
268  $pricetoprocess = GETPOST('pricetoproduce-'.$line->id.'-'.$i) ? price2num(GETPOST('pricetoproduce-'.$line->id.'-'.$i)) : 0;
269 
270  if ($qtytoprocess != 0) {
271  // Check warehouse is set if we should have to
272  if (GETPOSTISSET('idwarehousetoproduce-'.$line->id.'-'.$i)) { // If there is a warehouse to set
273  if (!(GETPOST('idwarehousetoproduce-'.$line->id.'-'.$i) > 0)) { // If there is no warehouse set.
274  $langs->load("errors");
275  setEventMessages($langs->trans("ErrorFieldRequiredForProduct", $langs->transnoentitiesnoconv("Warehouse"), $tmpproduct->ref), null, 'errors');
276  $error++;
277  }
278  if (!empty($conf->productbatch->enabled) && $tmpproduct->status_batch && (!GETPOST('batchtoproduce-'.$line->id.'-'.$i))) {
279  $langs->load("errors");
280  setEventMessages($langs->trans("ErrorFieldRequiredForProduct", $langs->transnoentitiesnoconv("Batch"), $tmpproduct->ref), null, 'errors');
281  $error++;
282  }
283  }
284 
285  $idstockmove = 0;
286  if (!$error && GETPOST('idwarehousetoproduce-'.$line->id.'-'.$i) > 0) {
287  // Record stock movement
288  $id_product_batch = 0;
289  $stockmove->origin_type = $object->element;
290  $stockmove->origin_id = $object->id;
291 
292  $idstockmove = $stockmove->reception($user, $line->fk_product, GETPOST('idwarehousetoproduce-'.$line->id.'-'.$i), $qtytoprocess, $pricetoprocess, $labelmovement, '', '', GETPOST('batchtoproduce-'.$line->id.'-'.$i), dol_now(), $id_product_batch, $codemovement);
293  if ($idstockmove < 0) {
294  $error++;
295  setEventMessages($stockmove->error, $stockmove->errors, 'errors');
296  }
297  }
298 
299  if (!$error) {
300  // Record production
301  $moline = new MoLine($db);
302  $moline->fk_mo = $object->id;
303  $moline->position = $pos;
304  $moline->fk_product = $line->fk_product;
305  $moline->fk_warehouse = GETPOST('idwarehousetoproduce-'.$line->id.'-'.$i);
306  $moline->qty = $qtytoprocess;
307  $moline->batch = GETPOST('batchtoproduce-'.$line->id.'-'.$i);
308  $moline->role = 'produced';
309  $moline->fk_mrp_production = $line->id;
310  $moline->fk_stock_movement = $idstockmove;
311  $moline->fk_user_creat = $user->id;
312 
313  $resultmoline = $moline->create($user);
314  if ($resultmoline <= 0) {
315  $error++;
316  setEventMessages($moline->error, $moline->errors, 'errors');
317  }
318 
319  $pos++;
320  }
321  }
322 
323  $i++;
324  }
325  }
326  }
327 
328  if (!$error) {
329  $consumptioncomplete = true;
330  $productioncomplete = true;
331 
332  if (GETPOST('autoclose', 'int')) {
333  foreach ($object->lines as $line) {
334  if ($line->role == 'toconsume') {
335  $arrayoflines = $object->fetchLinesLinked('consumed', $line->id);
336  $alreadyconsumed = 0;
337  foreach ($arrayoflines as $line2) {
338  $alreadyconsumed += $line2['qty'];
339  }
340 
341  if ($alreadyconsumed < $line->qty) {
342  $consumptioncomplete = false;
343  }
344  }
345  if ($line->role == 'toproduce') {
346  $arrayoflines = $object->fetchLinesLinked('produced', $line->id);
347  $alreadyproduced = 0;
348  foreach ($arrayoflines as $line2) {
349  $alreadyproduced += $line2['qty'];
350  }
351 
352  if ($alreadyproduced < $line->qty) {
353  $productioncomplete = false;
354  }
355  }
356  }
357  } else {
358  $consumptioncomplete = false;
359  $productioncomplete = false;
360  }
361 
362  // Update status of MO
363  dol_syslog("consumptioncomplete = ".$consumptioncomplete." productioncomplete = ".$productioncomplete);
364  //var_dump("consumptioncomplete = ".$consumptioncomplete." productioncomplete = ".$productioncomplete);
365  if ($consumptioncomplete && $productioncomplete) {
366  $result = $object->setStatut($object::STATUS_PRODUCED, 0, '', 'MRP_MO_PRODUCED');
367  } else {
368  $result = $object->setStatut($object::STATUS_INPROGRESS, 0, '', 'MRP_MO_PRODUCED');
369  }
370  if ($result <= 0) {
371  $error++;
372  setEventMessages($object->error, $object->errors, 'errors');
373  }
374  }
375 
376  if ($error) {
377  $action = str_replace('confirm_', '', $action);
378  $db->rollback();
379  } else {
380  $db->commit();
381 
382  // Redirect to avoid to action done a second time if we make a back from browser
383  header("Location: ".$_SERVER["PHP_SELF"].'?id='.$object->id);
384  exit;
385  }
386  }
387 
388  // Action close produced
389  if ($action == 'confirm_produced' && $confirm == 'yes' && $permissiontoadd) {
390  $result = $object->setStatut($object::STATUS_PRODUCED, 0, '', 'MRP_MO_PRODUCED');
391  if ($result >= 0) {
392  // Define output language
393  if (empty($conf->global->MAIN_DISABLE_PDF_AUTOUPDATE)) {
394  $outputlangs = $langs;
395  $newlang = '';
396  if ($conf->global->MAIN_MULTILANGS && empty($newlang) && GETPOST('lang_id', 'aZ09')) {
397  $newlang = GETPOST('lang_id', 'aZ09');
398  }
399  if ($conf->global->MAIN_MULTILANGS && empty($newlang)) {
400  $newlang = $object->thirdparty->default_lang;
401  }
402  if (!empty($newlang)) {
403  $outputlangs = new Translate("", $conf);
404  $outputlangs->setDefaultLang($newlang);
405  }
406  $model = $object->model_pdf;
407  $ret = $object->fetch($id); // Reload to get new records
408 
409  $object->generateDocument($model, $outputlangs, 0, 0, 0);
410  }
411  } else {
412  setEventMessages($object->error, $object->errors, 'errors');
413  }
414  }
415 }
416 
417 
418 
419 /*
420  * View
421  */
422 
423 $form = new Form($db);
424 $formproject = new FormProjets($db);
425 $formproduct = new FormProduct($db);
426 $tmpwarehouse = new Entrepot($db);
427 $tmpbatch = new Productlot($db);
428 $tmpstockmovement = new MouvementStock($db);
429 
430 $help_url = 'EN:Module_Manufacturing_Orders|FR:Module_Ordres_de_Fabrication';
431 llxHeader('', $langs->trans('Mo'), $help_url, '', 0, 0, array('/mrp/js/lib_dispatch.js.php'));
432 
433 // Part to show record
434 if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'create'))) {
435  $res = $object->fetch_thirdparty();
436  $res = $object->fetch_optionals();
437 
438  $head = moPrepareHead($object);
439 
440  print dol_get_fiche_head($head, 'production', $langs->trans("ManufacturingOrder"), -1, $object->picto);
441 
442  $formconfirm = '';
443 
444  // Confirmation to delete
445  if ($action == 'delete') {
446  $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id, $langs->trans('DeleteMo'), $langs->trans('ConfirmDeleteMo'), 'confirm_delete', '', 0, 1);
447  }
448  // Confirmation to delete line
449  if ($action == 'deleteline') {
450  $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id.'&lineid='.$lineid.'&fk_movement='.$fk_movement, $langs->trans('DeleteLine'), $langs->trans('ConfirmDeleteLine'), 'confirm_deleteline', '', 0, 1);
451  }
452  // Clone confirmation
453  if ($action == 'clone') {
454  // Create an array for form
455  $formquestion = array();
456  $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id, $langs->trans('ToClone'), $langs->trans('ConfirmCloneMo', $object->ref), 'confirm_clone', $formquestion, 'yes', 1);
457  }
458 
459  // Confirmation of validation
460  if ($action == 'validate') {
461  // We check that object has a temporary ref
462  $ref = substr($object->ref, 1, 4);
463  if ($ref == 'PROV') {
464  $object->fetch_product();
465  $numref = $object->getNextNumRef($object->fk_product);
466  } else {
467  $numref = $object->ref;
468  }
469 
470  $text = $langs->trans('ConfirmValidateMo', $numref);
471  /*if (! empty($conf->notification->enabled))
472  {
473  require_once DOL_DOCUMENT_ROOT . '/core/class/notify.class.php';
474  $notify = new Notify($db);
475  $text .= '<br>';
476  $text .= $notify->confirmMessage('BOM_VALIDATE', $object->socid, $object);
477  }*/
478 
479  $formquestion = array();
480  if (!empty($conf->mrp->enabled)) {
481  $langs->load("mrp");
482  require_once DOL_DOCUMENT_ROOT.'/product/class/html.formproduct.class.php';
483  $formproduct = new FormProduct($db);
484  $forcecombo = 0;
485  if ($conf->browser->name == 'ie') {
486  $forcecombo = 1; // There is a bug in IE10 that make combo inside popup crazy
487  }
488  $formquestion = array(
489  // 'text' => $langs->trans("ConfirmClone"),
490  // array('type' => 'checkbox', 'name' => 'clone_content', 'label' => $langs->trans("CloneMainAttributes"), 'value' => 1),
491  // array('type' => 'checkbox', 'name' => 'update_prices', 'label' => $langs->trans("PuttingPricesUpToDate"), 'value' => 1),
492  );
493  }
494 
495  $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id, $langs->trans('Validate'), $text, 'confirm_validate', $formquestion, 0, 1, 220);
496  }
497 
498  // Call Hook formConfirm
499  $parameters = array('formConfirm' => $formconfirm, 'lineid' => $lineid);
500  $reshook = $hookmanager->executeHooks('formConfirm', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
501  if (empty($reshook)) {
502  $formconfirm .= $hookmanager->resPrint;
503  } elseif ($reshook > 0) {
504  $formconfirm = $hookmanager->resPrint;
505  }
506 
507  // Print form confirm
508  print $formconfirm;
509 
510 
511  // MO file
512  // ------------------------------------------------------------
513  $linkback = '<a href="'.DOL_URL_ROOT.'/mrp/mo_list.php?restore_lastsearch_values=1'.(!empty($socid) ? '&socid='.$socid : '').'">'.$langs->trans("BackToList").'</a>';
514 
515  $morehtmlref = '<div class="refidno">';
516  /*
517  // Ref bis
518  $morehtmlref.=$form->editfieldkey("RefBis", 'ref_client', $object->ref_client, $object, $user->rights->mrp->creer, 'string', '', 0, 1);
519  $morehtmlref.=$form->editfieldval("RefBis", 'ref_client', $object->ref_client, $object, $user->rights->mrp->creer, 'string', '', null, null, '', 1);*/
520  // Thirdparty
521  $morehtmlref .= $langs->trans('ThirdParty').' : '.(is_object($object->thirdparty) ? $object->thirdparty->getNomUrl(1) : '');
522  // Project
523  if (!empty($conf->project->enabled)) {
524  $langs->load("projects");
525  $morehtmlref .= '<br>'.$langs->trans('Project').' ';
526  if ($permissiontoadd) {
527  if ($action != 'classify') {
528  $morehtmlref .= '<a class="editfielda" href="'.$_SERVER['PHP_SELF'].'?action=classify&token='.newToken().'&id='.$object->id.'">'.img_edit($langs->transnoentitiesnoconv('SetProject')).'</a> : ';
529  }
530  if ($action == 'classify') {
531  //$morehtmlref.=$form->form_project($_SERVER['PHP_SELF'] . '?id=' . $object->id, $object->fk_soc, $object->fk_project, 'projectid', 0, 0, 1, 1);
532  $morehtmlref .= '<form method="post" action="'.$_SERVER['PHP_SELF'].'?id='.$object->id.'">';
533  $morehtmlref .= '<input type="hidden" name="action" value="classin">';
534  $morehtmlref .= '<input type="hidden" name="token" value="'.newToken().'">';
535  $morehtmlref .= $formproject->select_projects($object->fk_soc, $object->fk_project, 'projectid', 0, 0, 1, 0, 1, 0, 0, '', 1);
536  $morehtmlref .= '<input type="submit" class="button valignmiddle" value="'.$langs->trans("Modify").'">';
537  $morehtmlref .= '</form>';
538  } else {
539  $morehtmlref .= $form->form_project($_SERVER['PHP_SELF'].'?id='.$object->id, $object->fk_soc, $object->fk_project, 'none', 0, 0, 0, 1);
540  }
541  } else {
542  if (!empty($object->fk_project)) {
543  $proj = new Project($db);
544  $proj->fetch($object->fk_project);
545  $morehtmlref .= $proj->getNomUrl();
546  } else {
547  $morehtmlref .= '';
548  }
549  }
550  }
551  $morehtmlref .= '</div>';
552 
553 
554  dol_banner_tab($object, 'ref', $linkback, 1, 'ref', 'ref', $morehtmlref);
555 
556 
557  print '<div class="fichecenter">';
558  print '<div class="fichehalfleft">';
559  print '<div class="underbanner clearboth"></div>';
560  print '<table class="border centpercent tableforfield">'."\n";
561 
562  // Common attributes
563  $keyforbreak = 'fk_warehouse';
564  unset($object->fields['fk_project']);
565  unset($object->fields['fk_soc']);
566  include DOL_DOCUMENT_ROOT.'/core/tpl/commonfields_view.tpl.php';
567 
568  // Other attributes
569  include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_view.tpl.php';
570 
571  print '</table>';
572  print '</div>';
573  print '</div>';
574 
575  print '<div class="clearboth"></div>';
576 
577  print dol_get_fiche_end();
578 
579 
580  if (!in_array($action, array('consumeorproduce', 'consumeandproduceall'))) {
581  print '<div class="tabsAction">';
582 
583  $parameters = array();
584  // Note that $action and $object may be modified by hook
585  $reshook = $hookmanager->executeHooks('addMoreActionsButtons', $parameters, $object, $action);
586  if (empty($reshook)) {
587  // Validate
588  if ($object->status == $object::STATUS_DRAFT) {
589  if ($permissiontoadd) {
590  if (empty($object->table_element_line) || (is_array($object->lines) && count($object->lines) > 0)) {
591  print '<a class="butAction" href="'.$_SERVER['PHP_SELF'].'?id='.$object->id.'&action=validate">'.$langs->trans("Validate").'</a>';
592  } else {
593  $langs->load("errors");
594  print '<a class="butActionRefused" href="" title="'.$langs->trans("ErrorAddAtLeastOneLineFirst").'">'.$langs->trans("Validate").'</a>';
595  }
596  }
597  }
598 
599  // Consume or produce
600  if ($object->status == Mo::STATUS_VALIDATED || $object->status == Mo::STATUS_INPROGRESS) {
601  if ($permissiontoproduce) {
602  print '<a class="butAction" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=consumeorproduce">'.$langs->trans('ConsumeOrProduce').'</a>';
603  } else {
604  print '<a class="butActionRefused classfortooltip" href="#" title="'.$langs->trans("NotEnoughPermissions").'">'.$langs->trans('ConsumeOrProduce').'</a>';
605  }
606  } elseif ($object->status == Mo::STATUS_DRAFT) {
607  print '<a class="butActionRefused classfortooltip" href="#" title="'.$langs->trans("ValidateBefore").'">'.$langs->trans('ConsumeOrProduce').'</a>';
608  }
609 
610  // ConsumeAndProduceAll
611  if ($object->status == Mo::STATUS_VALIDATED || $object->status == Mo::STATUS_INPROGRESS) {
612  if ($permissiontoproduce) {
613  print '<a class="butAction" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=consumeandproduceall">'.$langs->trans('ConsumeAndProduceAll').'</a>';
614  } else {
615  print '<a class="butActionRefused classfortooltip" href="#" title="'.$langs->trans("NotEnoughPermissions").'">'.$langs->trans('ConsumeAndProduceAll').'</a>';
616  }
617  } elseif ($object->status == Mo::STATUS_DRAFT) {
618  print '<a class="butActionRefused classfortooltip" href="#" title="'.$langs->trans("ValidateBefore").'">'.$langs->trans('ConsumeAndProduceAll').'</a>';
619  }
620 
621  // Cancel - Reopen
622  if ($permissiontoadd) {
623  if ($object->status == $object::STATUS_VALIDATED || $object->status == $object::STATUS_INPROGRESS) {
624  $arrayproduced = $object->fetchLinesLinked('produced', 0);
625  $nbProduced = 0;
626  foreach ($arrayproduced as $lineproduced) {
627  $nbProduced += $lineproduced['qty'];
628  }
629  if ($nbProduced > 0) { // If production has started, we can close it
630  print '<a class="butAction" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=confirm_produced&confirm=yes">'.$langs->trans("Close").'</a>'."\n";
631  } else {
632  print '<a class="butActionRefused" href="#" title="'.$langs->trans("GoOnTabProductionToProduceFirst", $langs->transnoentitiesnoconv("Production")).'">'.$langs->trans("Close").'</a>'."\n";
633  }
634 
635  print '<a class="butActionDelete" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=confirm_close&confirm=yes">'.$langs->trans("Cancel").'</a>'."\n";
636  }
637 
638  if ($object->status == $object::STATUS_CANCELED) {
639  print '<a class="butAction" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=confirm_reopen&confirm=yes">'.$langs->trans("Re-Open").'</a>'."\n";
640  }
641 
642  if ($object->status == $object::STATUS_PRODUCED) {
643  if ($permissiontoproduce) {
644  print '<a class="butAction" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=confirm_reopen">'.$langs->trans('ReOpen').'</a>';
645  } else {
646  print '<a class="butActionRefused classfortooltip" href="#" title="'.$langs->trans("NotEnoughPermissions").'">'.$langs->trans('ReOpen').'</a>';
647  }
648  }
649  }
650  }
651 
652  print '</div>';
653  }
654 
655  if (in_array($action, array('consumeorproduce', 'consumeandproduceall', 'addconsumeline', 'addproduceline'))) {
656  print '<form method="POST" action="'.$_SERVER["PHP_SELF"].'">';
657  print '<input type="hidden" name="token" value="'.newToken().'">';
658  print '<input type="hidden" name="action" value="confirm_'.$action.'">';
659  print '<input type="hidden" name="backtopage" value="'.$backtopage.'">';
660  print '<input type="hidden" name="id" value="'.$id.'">';
661  // Note: closing form is add end of page
662 
663  if (in_array($action, array('consumeorproduce', 'consumeandproduceall'))) {
664  $defaultstockmovementlabel = GETPOST('inventorylabel', 'alphanohtml') ? GETPOST('inventorylabel', 'alphanohtml') : $langs->trans("ProductionForRef", $object->ref);
665  $defaultstockmovementcode = GETPOST('inventorycode', 'alphanohtml') ? GETPOST('inventorycode', 'alphanohtml') : dol_print_date(dol_now(), 'dayhourlog');
666 
667  print '<div class="center'.(in_array($action, array('consumeorproduce', 'consumeandproduceall')) ? ' formconsumeproduce' : '').'">';
668  print '<div class="opacitymedium hideonsmartphone paddingbottom">'.$langs->trans("ConfirmProductionDesc", $langs->transnoentitiesnoconv("Confirm")).'<br></div>';
669  print '<span class="fieldrequired">'.$langs->trans("InventoryCode").':</span> <input type="text" class="minwidth200 maxwidth250" name="inventorycode" value="'.$defaultstockmovementcode.'"> &nbsp; ';
670  print '<span class="clearbothonsmartphone"></span>';
671  print $langs->trans("MovementLabel").': <input type="text" class="minwidth300" name="inventorylabel" value="'.$defaultstockmovementlabel.'"><br><br>';
672  print '<input type="checkbox" id="autoclose" name="autoclose" value="1"'.(GETPOSTISSET('inventorylabel') ? (GETPOST('autoclose') ? ' checked="checked"' : '') : ' checked="checked"').'> <label for="autoclose">'.$langs->trans("AutoCloseMO").'</label><br>';
673  print '<input type="submit" class="button" value="'.$langs->trans("Confirm").'" name="confirm">';
674  print ' &nbsp; ';
675  print '<input class="button button-cancel" type="submit" value="'.$langs->trans("Cancel").'" name="cancel">';
676  print '<br><br>';
677  print '</div>';
678 
679  print '<br>';
680  }
681  }
682 
683 
684  /*
685  * Lines
686  */
687  $collapse = 1;
688 
689  if (!empty($object->table_element_line)) {
690  // Show object lines
691  $object->fetchLines();
692 
693  $bomcost = 0;
694  if ($object->fk_bom > 0) {
695  $bom = new Bom($db);
696  $res = $bom->fetch($object->fk_bom);
697  if ($res > 0) {
698  $bom->calculateCosts();
699  $bomcost = $bom->unit_cost;
700  }
701  }
702 
703  // Lines to consume
704 
705  print '<div class="fichecenter">';
706  print '<div class="fichehalfleft">';
707  print '<div class="clearboth"></div>';
708 
709  $url = $_SERVER["PHP_SELF"].'?id='.$object->id.'&action=addconsumeline&token='.newToken();
710  $permissiontoaddaconsumeline = $object->status != $object::STATUS_PRODUCED && $object->status != $object::STATUS_CANCELED;
711  $parameters = array('morecss'=>'reposition');
712 
713  $newcardbutton = '';
714  if ($action != 'consumeorproduce' && $action != 'consumeandproduceall') {
715  $newcardbutton = dolGetButtonTitle($langs->trans('AddNewConsumeLines'), '', 'fa fa-plus-circle size15x', $url, '', $permissiontoaddaconsumeline, $parameters);
716  }
717 
718  print load_fiche_titre($langs->trans('Consumption'), $newcardbutton, '', 0, '', '', '');
719 
720  print '<div class="div-table-responsive-no-min">';
721  print '<table class="noborder noshadow centpercent nobottom">';
722 
723  print '<tr class="liste_titre">';
724  print '<td>'.$langs->trans("Product").'</td>';
725  // Qty
726  print '<td class="right">'.$langs->trans("Qty").'</td>';
727  // Cost price
728  if ($permissiontoupdatecost && !empty($conf->global->MRP_SHOW_COST_FOR_CONSUMPTION)) {
729  print '<td class="right">'.$langs->trans("UnitCost").'</td>';
730  }
731  // Qty already consumed
732  print '<td class="right">'.$langs->trans("QtyAlreadyConsumed").'</td>';
733  // Warehouse
734  print '<td>';
735  if ($collapse || in_array($action, array('consumeorproduce', 'consumeandproduceall'))) {
736  print $langs->trans("Warehouse");
737 
738  // Select warehouse to force it everywhere
739  if (in_array($action, array('consumeorproduce', 'consumeandproduceall'))) {
740  $listwarehouses = $tmpwarehouse->list_array(1);
741  if (count($listwarehouses) > 1) {
742  print '<br><span class="opacitymedium">' . $langs->trans("ForceTo") . '</span> ' . $form->selectarray('fk_default_warehouse', $listwarehouses, $fk_default_warehouse, 1, 0, 0, '', 0, 0, 0, '', 'minwidth100 maxwidth300', 1);
743  } elseif (count($listwarehouses) == 1) {
744  print '<br><span class="opacitymedium">' . $langs->trans("ForceTo") . '</span> ' . $form->selectarray('fk_default_warehouse', $listwarehouses, $fk_default_warehouse, 0, 0, 0, '', 0, 0, 0, '', 'minwidth100 maxwidth300', 1);
745  }
746  }
747  }
748  print '</td>';
749  if (isModEnabled('stock')) {
750  // Available
751  print '<td align="right">';
752  if ($collapse || in_array($action, array('consumeorproduce', 'consumeandproduceall'))) {
753  print $langs->trans("Stock");
754  }
755  print '</td>';
756  }
757  // Lot - serial
758  if (isModEnabled('productbatch')) {
759  print '<td>';
760  if ($collapse || in_array($action, array('consumeorproduce', 'consumeandproduceall'))) {
761  print $langs->trans("Batch");
762  }
763  print '</td>';
764  }
765  // Action
766  if ($permissiontodelete) {
767  print '<td></td>';
768  }
769  print '</tr>';
770 
771  if ($action == 'addconsumeline') {
772  print '<!-- Add line to consume -->'."\n";
773  print '<tr class="liste_titre">';
774  print '<td>';
775  print $form->select_produits('', 'productidtoadd', '', 0, 0, -1, 2, '', 1, array(), 0, '1', 0, 'maxwidth300');
776  print '</td>';
777  // Qty
778  print '<td class="right"><input type="text" name="qtytoadd" value="1" class="width50 right"></td>';
779  // Cost price
780  if ($permissiontoupdatecost && !empty($conf->global->MRP_SHOW_COST_FOR_CONSUMPTION)) {
781  print '<td></td>';
782  }
783  // Qty already consumed
784  print '<td colspan="2">';
785  // Warehouse
786  print '<input type="submit" class="button buttongen button-add" name="addconsumelinebutton" value="'.$langs->trans("Add").'">';
787  print '<input type="submit" class="button buttongen button-cancel" name="canceladdconsumelinebutton" value="'.$langs->trans("Cancel").'">';
788  print '</td>';
789  if (isModEnabled('stock')) {
790  print '<td></td>';
791  }
792  // Lot - serial
793  if (isModEnabled('productbatch')) {
794  print '<td></td>';
795  }
796  // Action
797  if ($permissiontodelete) {
798  print '<td></td>';
799  }
800  print '</tr>';
801  }
802 
803  // Lines to consume
804 
805  if (!empty($object->lines)) {
806  $nblinetoconsume = 0;
807  foreach ($object->lines as $line) {
808  if ($line->role == 'toconsume') {
809  $nblinetoconsume++;
810  }
811  }
812 
813  $nblinetoconsumecursor = 0;
814  foreach ($object->lines as $line) {
815  if ($line->role == 'toconsume') {
816  $nblinetoconsumecursor++;
817 
818  $tmpproduct = new Product($db);
819  $tmpproduct->fetch($line->fk_product);
820  $linecost = price2num($tmpproduct->pmp, 'MT');
821 
822  if ($object->qty > 0) {
823  // add free consume line cost to bomcost
824  $costprice = price2num((!empty($tmpproduct->cost_price)) ? $tmpproduct->cost_price : $tmpproduct->pmp);
825  if (empty($costprice)) {
826  require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
827  $productFournisseur = new ProductFournisseur($db);
828  if ($productFournisseur->find_min_price_product_fournisseur($line->fk_product) > 0) {
829  $costprice = $productFournisseur->fourn_unitprice;
830  } else {
831  $costprice = 0;
832  }
833  }
834  $linecost = price2num(($line->qty * $costprice) / $object->qty, 'MT');
835  $bomcost += $linecost;
836  }
837 
838  $bomcost = price2num($bomcost, 'MU');
839 
840  $arrayoflines = $object->fetchLinesLinked('consumed', $line->id);
841  $alreadyconsumed = 0;
842  foreach ($arrayoflines as $line2) {
843  $alreadyconsumed += $line2['qty'];
844  }
845 
846  $suffix = '_'.$line->id;
847  print '<!-- Line to dispatch '.$suffix.' -->'."\n";
848  // hidden fields for js function
849  print '<input id="qty_ordered'.$suffix.'" type="hidden" value="'.$line->qty.'">';
850  print '<input id="qty_dispatched'.$suffix.'" type="hidden" value="'.$alreadyconsumed.'">';
851 
852  print '<tr>';
853  // Product
854  print '<td>'.$tmpproduct->getNomUrl(1);
855  print '<br><span class="opacitymedium small">'.$tmpproduct->label.'</span>';
856  print '</td>';
857  // Qty
858  print '<td class="right nowraponall">';
859  $help = '';
860  if ($line->qty_frozen) {
861  $help .= ($help ? '<br>' : '').'<strong>'.$langs->trans("QuantityFrozen").'</strong>: '.yn(1).' ('.$langs->trans("QuantityConsumedInvariable").')';
862  }
863  if ($line->disable_stock_change) {
864  $help .= ($help ? '<br>' : '').'<strong>'.$langs->trans("DisableStockChange").'</strong>: '.yn(1).' ('.(($tmpproduct->type == Product::TYPE_SERVICE && empty($conf->global->STOCK_SUPPORTS_SERVICES)) ? $langs->trans("NoStockChangeOnServices") : $langs->trans("DisableStockChangeHelp")).')';
865  }
866  if ($help) {
867  print $form->textwithpicto($line->qty, $help, -1);
868  } else {
869  print price2num($line->qty, 'MS');
870  }
871  print '</td>';
872  // Cost price
873  if ($permissiontoupdatecost && !empty($conf->global->MRP_SHOW_COST_FOR_CONSUMPTION)) {
874  print '<td class="right nowraponall">';
875  print price($linecost);
876  print '</td>';
877  }
878  // Already consumed
879  print '<td class="right">';
880  if ($alreadyconsumed) {
881  print '<script>';
882  print 'jQuery(document).ready(function() {
883  jQuery("#expandtoproduce'.$line->id.'").click(function() {
884  console.log("Expand mrp_production line '.$line->id.'");
885  jQuery(".expanddetail'.$line->id.'").toggle();';
886  if ($nblinetoconsume == $nblinetoconsumecursor) { // If it is the last line
887  print 'if (jQuery("#tablelines").hasClass("nobottom")) { jQuery("#tablelines").removeClass("nobottom"); } else { jQuery("#tablelines").addClass("nobottom"); }';
888  }
889  print '
890  });
891  });';
892  print '</script>';
893  if (empty($conf->use_javascript_ajax)) {
894  print '<a href="'.$_SERVER["PHP_SELF"].'?collapse='.$collapse.','.$line->id.'">';
895  }
896  print img_picto($langs->trans("ShowDetails"), "chevron-down", 'id="expandtoproduce'.$line->id.'"');
897  if (empty($conf->use_javascript_ajax)) {
898  print '</a>';
899  }
900  } else {
901  if ($nblinetoconsume == $nblinetoconsumecursor) { // If it is the last line
902  print '<script>jQuery("#tablelines").removeClass("nobottom");</script>';
903  }
904  }
905  print ' '.price2num($alreadyconsumed, 'MS');
906  print '</td>';
907  // Warehouse
908  print '<td>';
909  print '</td>';
910  // Stock
911  if (isModEnabled('stock')) {
912  print '<td class="nowraponall right">';
913  if ($tmpproduct->stock_reel < ($line->qty - $alreadyconsumed)) {
914  print img_warning($langs->trans('StockTooLow')).' ';
915  }
916  print price2num($tmpproduct->stock_reel, 'MS'); // Available
917  print '</td>';
918  }
919  // Lot
920  if (isModEnabled('productbatch')) {
921  print '<td></td>';
922  }
923  // Action delete line
924  if ($permissiontodelete) {
925  $href = $_SERVER["PHP_SELF"].'?id='.((int) $object->id).'&action=deleteline&token='.newToken().'&lineid='.((int) $line->id);
926  print '<td class="center">';
927  print '<a class="reposition" href="'.$href.'">';
928  print img_picto($langs->trans('TooltipDeleteAndRevertStockMovement'), 'delete');
929  print '</a>';
930  print '</td>';
931  }
932  print '</tr>';
933 
934  // Show detailed of already consumed with js code to collapse
935  foreach ($arrayoflines as $line2) {
936  print '<tr class="expanddetail'.$line->id.' hideobject opacitylow">';
937 
938  // Date
939  print '<td>';
940  $tmpstockmovement->id = $line2['fk_stock_movement'];
941  print '<a href="'.DOL_URL_ROOT.'/product/stock/movement_list.php?search_ref='.$tmpstockmovement->id.'">'.img_picto($langs->trans("StockMovement"), 'movement', 'class="paddingright"').'</a>';
942  print dol_print_date($line2['date'], 'dayhour', 'tzuserrel');
943  print '</td>';
944 
945  // Already consumed
946  print '<td></td>';
947 
948  // Qty
949  print '<td class="right">'.$line2['qty'].'</td>';
950 
951  // Cost price
952  if ($permissiontoupdatecost && !empty($conf->global->MRP_SHOW_COST_FOR_CONSUMPTION)) {
953  print '<td></td>';
954  }
955 
956  // Warehouse
957  print '<td class="tdoverflowmax150">';
958  if ($line2['fk_warehouse'] > 0) {
959  $result = $tmpwarehouse->fetch($line2['fk_warehouse']);
960  if ($result > 0) {
961  print $tmpwarehouse->getNomUrl(1);
962  }
963  }
964  print '</td>';
965 
966  // Stock
967  if (isModEnabled('stock')) {
968  print '<td></td>';
969  }
970 
971  // Lot Batch
972  if (isModEnabled('productbatch')) {
973  print '<td>';
974  if ($line2['batch'] != '') {
975  $tmpbatch->fetch(0, $line2['fk_product'], $line2['batch']);
976  print $tmpbatch->getNomUrl(1);
977  }
978  print '</td>';
979  }
980 
981  // Action delete line
982  if ($permissiontodelete) {
983  $href = $_SERVER["PHP_SELF"].'?id='.((int) $object->id).'&action=deleteline&token='.newToken().'&lineid='.((int) $line->id).'&fk_movement='.((int) $line2['fk_stock_movement']);
984  print '<td class="center">';
985  print '<a class="reposition" href="'.$href.'">';
986  print img_picto($langs->trans('TooltipDeleteAndRevertStockMovement'), 'delete');
987  print '</a>';
988  print '</td>';
989  }
990 
991  print '</tr>';
992  }
993 
994  if (in_array($action, array('consumeorproduce', 'consumeandproduceall'))) {
995  $i = 1;
996  print '<!-- Enter line to consume -->'."\n";
997  print '<tr name="batch_'.$line->id.'_'.$i.'">';
998  // Ref
999  print '<td><span class="opacitymedium">'.$langs->trans("ToConsume").'</span></td>';
1000  $preselected = (GETPOSTISSET('qty-'.$line->id.'-'.$i) ? GETPOST('qty-'.$line->id.'-'.$i) : max(0, $line->qty - $alreadyconsumed));
1001  if ($action == 'consumeorproduce' && !GETPOSTISSET('qty-'.$line->id.'-'.$i)) {
1002  $preselected = 0;
1003  }
1004 
1005  $disable = '';
1006  if (!empty($conf->global->MRP_NEVER_CONSUME_MORE_THAN_EXPECTED) && ($line->qty - $alreadyconsumed) <= 0) {
1007  $disable = 'disabled';
1008  }
1009 
1010  // input hidden with fk_product of line
1011  print '<input type="hidden" name="product-'.$line->id.'-'.$i.'" value="'.$line->fk_product.'">';
1012 
1013  // Qty
1014  print '<td class="right"><input type="text" class="width50 right" id="qtytoconsume-'.$line->id.'-'.$i.'" name="qty-'.$line->id.'-'.$i.'" value="'.$preselected.'" '.$disable.'></td>';
1015 
1016  // Cost
1017  if ($permissiontoupdatecost && !empty($conf->global->MRP_SHOW_COST_FOR_CONSUMPTION)) {
1018  print '<td></td>';
1019  }
1020 
1021  // Already consumed
1022  print '<td></td>';
1023 
1024  // Warehouse
1025  print '<td>';
1026  if ($tmpproduct->type == Product::TYPE_PRODUCT || !empty($conf->global->STOCK_SUPPORTS_SERVICES)) {
1027  if (empty($line->disable_stock_change)) {
1028  $preselected = (GETPOSTISSET('idwarehouse-'.$line->id.'-'.$i) ? GETPOST('idwarehouse-'.$line->id.'-'.$i) : ($tmpproduct->fk_default_warehouse > 0 ? $tmpproduct->fk_default_warehouse : 'ifone'));
1029  print $formproduct->selectWarehouses($preselected, 'idwarehouse-'.$line->id.'-'.$i, '', 1, 0, $line->fk_product, '', 1, 0, null, 'maxwidth200 csswarehouse_'.$line->id.'_'.$i);
1030  } else {
1031  print '<span class="opacitymedium">'.$langs->trans("DisableStockChange").'</span>';
1032  }
1033  } else {
1034  print '<span class="opacitymedium">'.$langs->trans("NoStockChangeOnServices").'</span>';
1035  }
1036  print '</td>';
1037 
1038  // Stock
1039  if (isModEnabled('stock')) {
1040  print '<td></td>';
1041  }
1042 
1043  // Lot / Batch
1044  if (isModEnabled('productbatch')) {
1045  print '<td>';
1046  if ($tmpproduct->status_batch) {
1047  $preselected = (GETPOSTISSET('batch-'.$line->id.'-'.$i) ? GETPOST('batch-'.$line->id.'-'.$i) : '');
1048  print '<input type="text" class="width50" name="batch-'.$line->id.'-'.$i.'" value="'.$preselected.'" list="batch-'.$line->id.'-'.$i.'">';
1049  print $formproduct->selectLotDataList('batch-'.$line->id.'-'.$i, 0, $line->fk_product, '', '');
1050 
1051  $type = 'batch';
1052  print ' '.img_picto($langs->trans('AddStockLocationLine'), 'split.png', 'class="splitbutton" onClick="addDispatchLine('.((int) $line->id).', \''.dol_escape_js($type).'\', \'qtymissingconsume\')"');
1053  }
1054  print '</td>';
1055  }
1056 
1057  // Action delete line
1058  if ($permissiontodelete) {
1059  print '<td></td>';
1060  }
1061 
1062  print '</tr>';
1063  }
1064  }
1065  }
1066  }
1067 
1068  print '</table>';
1069  print '</div>';
1070 
1071  // default warehouse processing
1072  print '<script type="text/javascript">
1073  $(document).ready(function () {
1074  $("select[name=fk_default_warehouse]").change(function() {
1075  var fk_default_warehouse = $("option:selected", this).val();
1076  $("select[name^=idwarehouse-]").val(fk_default_warehouse).change();
1077  });
1078  });
1079  </script>';
1080 
1081 
1082  // Lines to produce
1083 
1084  print '</div>';
1085  print '<div class="fichehalfright">';
1086  print '<div class="clearboth"></div>';
1087 
1088  $nblinetoproduce = 0;
1089  foreach ($object->lines as $line) {
1090  if ($line->role == 'toproduce') {
1091  $nblinetoproduce++;
1092  }
1093  }
1094 
1095  $newcardbutton = '';
1096  $url = $_SERVER["PHP_SELF"].'?id='.$object->id.'&action=addproduceline&token='.newToken();
1097  $permissiontoaddaproductline = $object->status != $object::STATUS_PRODUCED && $object->status != $object::STATUS_CANCELED;
1098  $parameters = array('morecss'=>'reposition');
1099  if ($action != 'consumeorproduce' && $action != 'consumeandproduceall') {
1100  if ($nblinetoproduce == 0 || $object->mrptype == 1) {
1101  $newcardbutton = dolGetButtonTitle($langs->trans('AddNewProduceLines'), '', 'fa fa-plus-circle size15x', $url, '', $permissiontoaddaproductline, $parameters);
1102  }
1103  }
1104 
1105  print load_fiche_titre($langs->trans('Production'), $newcardbutton, '', 0, '', '');
1106 
1107  print '<div class="div-table-responsive-no-min">';
1108  print '<table id="tablelinestoproduce" class="noborder noshadow nobottom centpercent">';
1109 
1110  print '<tr class="liste_titre">';
1111  print '<td>'.$langs->trans("Product").'</td>';
1112  print '<td class="right">'.$langs->trans("Qty").'</td>';
1113  if ($permissiontoupdatecost) {
1114  if (empty($bomcost)) {
1115  print '<td class="right">'.$form->textwithpicto($langs->trans("UnitCost"), $langs->trans("AmountUsedToUpdateWAP")).'</td>';
1116  } else {
1117  print '<td class="right">'.$form->textwithpicto($langs->trans("ManufacturingPrice"), $langs->trans("AmountUsedToUpdateWAP")).'</td>';
1118  }
1119  }
1120  print '<td class="right">'.$langs->trans("QtyAlreadyProduced").'</td>';
1121  print '<td>';
1122  if ($collapse || in_array($action, array('consumeorproduce', 'consumeandproduceall'))) {
1123  print $langs->trans("Warehouse");
1124  }
1125  print '</td>';
1126  if (isModEnabled('productbatch')) {
1127  print '<td>';
1128  if ($collapse || in_array($action, array('consumeorproduce', 'consumeandproduceall'))) {
1129  print $langs->trans("Batch");
1130  }
1131  print '</td>';
1132  print '<td></td>';
1133  }
1134  print '</tr>';
1135 
1136  if ($action == 'addproduceline') {
1137  print '<!-- Add line to produce -->'."\n";
1138  print '<tr class="liste_titre">';
1139  print '<td>';
1140  print $form->select_produits('', 'productidtoadd', '', 0, 0, -1, 2, '', 1, array(), 0, '1', 0, 'maxwidth300');
1141  print '</td>';
1142  // Qty
1143  print '<td class="right"><input type="text" name="qtytoadd" value="1" class="width50 right"></td>';
1144  // Cost price
1145  print '<td></td>';
1146 
1147  // Qty already produced
1148  print '<td colspan="2">';
1149  // Warehouse
1150  print '<input type="submit" class="button buttongen button-add" name="addproducelinebutton" value="'.$langs->trans("Add").'">';
1151  print '<input type="submit" class="button buttongen button-cancel" name="canceladdproducelinebutton" value="'.$langs->trans("Cancel").'">';
1152  print '</td>';
1153  // Lot - serial
1154  if (isModEnabled('productbatch')) {
1155  print '<td></td>';
1156  }
1157  // Action
1158  if ($permissiontodelete) {
1159  print '<td></td>';
1160  }
1161  print '</tr>';
1162  }
1163 
1164  if (!empty($object->lines)) {
1165  $nblinetoproduce = 0;
1166  foreach ($object->lines as $line) {
1167  if ($line->role == 'toproduce') {
1168  $nblinetoproduce++;
1169  }
1170  }
1171 
1172  $nblinetoproducecursor = 0;
1173  foreach ($object->lines as $line) {
1174  if ($line->role == 'toproduce') {
1175  $i = 1;
1176 
1177  $nblinetoproducecursor++;
1178 
1179  $tmpproduct = new Product($db);
1180  $tmpproduct->fetch($line->fk_product);
1181 
1182  $arrayoflines = $object->fetchLinesLinked('produced', $line->id);
1183  $alreadyproduced = 0;
1184  foreach ($arrayoflines as $line2) {
1185  $alreadyproduced += $line2['qty'];
1186  }
1187 
1188  $suffix = '_'.$line->id;
1189  print '<!-- Line to dispatch '.$suffix.' -->'."\n";
1190  // hidden fields for js function
1191  print '<input id="qty_ordered'.$suffix.'" type="hidden" value="'.$line->qty.'">';
1192  print '<input id="qty_dispatched'.$suffix.'" type="hidden" value="'.$alreadyproduced.'">';
1193 
1194  print '<tr>';
1195  print '<td>'.$tmpproduct->getNomUrl(1);
1196  print '<br><span class="opacitymedium small">'.$tmpproduct->label.'</span>';
1197  print '</td>';
1198  print '<td class="right">'.$line->qty.'</td>';
1199  if ($permissiontoupdatecost) {
1200  // Defined $manufacturingcost
1201  $manufacturingcost = 0;
1202  if ($object->mrptype == 0) { // If MO is a "Manufacture" type (and not "Disassemble"
1203  $manufacturingcost = $bomcost;
1204  if (empty($manufacturingcost)) {
1205  $manufacturingcost = price2num($tmpproduct->cost_price, 'MU');
1206  }
1207  if (empty($manufacturingcost)) {
1208  $manufacturingcost = price2num($tmpproduct->pmp, 'MU');
1209  }
1210  }
1211 
1212  print '<td class="right nowraponall">';
1213  if ($manufacturingcost) {
1214  print price($manufacturingcost);
1215  }
1216  print '</td>';
1217  }
1218  print '<td class="right nowraponall">';
1219  if ($alreadyproduced) {
1220  print '<script>';
1221  print 'jQuery(document).ready(function() {
1222  jQuery("#expandtoproduce'.$line->id.'").click(function() {
1223  console.log("Expand mrp_production line '.$line->id.'");
1224  jQuery(".expanddetailtoproduce'.$line->id.'").toggle();';
1225  if ($nblinetoproduce == $nblinetoproducecursor) {
1226  print 'if (jQuery("#tablelinestoproduce").hasClass("nobottom")) { jQuery("#tablelinestoproduce").removeClass("nobottom"); } else { jQuery("#tablelinestoproduce").addClass("nobottom"); }';
1227  }
1228  print '
1229  });
1230  });';
1231  print '</script>';
1232  if (empty($conf->use_javascript_ajax)) {
1233  print '<a href="'.$_SERVER["PHP_SELF"].'?collapse='.$collapse.','.$line->id.'">';
1234  }
1235  print img_picto($langs->trans("ShowDetails"), "chevron-down", 'id="expandtoproduce'.$line->id.'"');
1236  if (empty($conf->use_javascript_ajax)) {
1237  print '</a>';
1238  }
1239  }
1240  print ' '.$alreadyproduced;
1241  print '</td>';
1242  print '<td>'; // Warehouse
1243  print '</td>';
1244  if (isModEnabled('productbatch')) {
1245  print '<td></td>'; // Lot
1246  }
1247 
1248  if ($permissiontodelete && $line->origin_type == 'free') {
1249  $href = $_SERVER["PHP_SELF"];
1250  $href .= '?id='.$object->id;
1251  $href .= '&action=deleteline';
1252  $href .= '&lineid='.$line->id;
1253  print '<td class="center">';
1254  print '<a class="reposition" href="'.$href.'">';
1255  print img_picto($langs->trans('TooltipDeleteAndRevertStockMovement'), "delete");
1256  print '</a>';
1257  print '</td>';
1258  }
1259  print '</tr>';
1260 
1261  // Show detailed of already consumed with js code to collapse
1262  foreach ($arrayoflines as $line2) {
1263  print '<tr class="expanddetailtoproduce'.$line->id.' hideobject opacitylow">';
1264  print '<td>';
1265  $tmpstockmovement->id = $line2['fk_stock_movement'];
1266  print '<a href="'.DOL_URL_ROOT.'/product/stock/movement_list.php?search_ref='.$tmpstockmovement->id.'">'.img_picto($langs->trans("StockMovement"), 'movement', 'class="paddingright"').'</a>';
1267  print dol_print_date($line2['date'], 'dayhour', 'tzuserrel');
1268  print '</td>';
1269  // Qty
1270  print '<td></td>';
1271  // Cost price
1272  if ($permissiontoupdatecost) {
1273  print '<td></td>';
1274  }
1275  // Qty already produced
1276  print '<td class="right">'.$line2['qty'].'</td>';
1277  print '<td class="tdoverflowmax150">';
1278  if ($line2['fk_warehouse'] > 0) {
1279  $result = $tmpwarehouse->fetch($line2['fk_warehouse']);
1280  if ($result > 0) {
1281  print $tmpwarehouse->getNomUrl(1);
1282  }
1283  }
1284  print '</td>';
1285  if (isModEnabled('productbatch')) {
1286  print '<td>';
1287  if ($line2['batch'] != '') {
1288  $tmpbatch->fetch(0, $line2['fk_product'], $line2['batch']);
1289  print $tmpbatch->getNomUrl(1);
1290  }
1291  print '</td>';
1292  print '<td></td>';
1293  }
1294  print '</tr>';
1295  }
1296 
1297  if (in_array($action, array('consumeorproduce', 'consumeandproduceall'))) {
1298  print '<!-- Enter line to produce -->'."\n";
1299  print '<tr name="batch_'.$line->id.'_'.$i.'">';
1300  print '<td><span class="opacitymedium">'.$langs->trans("ToProduce").'</span></td>';
1301  $preselected = (GETPOSTISSET('qtytoproduce-'.$line->id.'-'.$i) ? GETPOST('qtytoproduce-'.$line->id.'-'.$i) : max(0, $line->qty - $alreadyproduced));
1302  if ($action == 'consumeorproduce' && !GETPOSTISSET('qtytoproduce-'.$line->id.'-'.$i)) {
1303  $preselected = 0;
1304  }
1305  print '<td class="right"><input type="text" class="width50 right" id="qtytoproduce-'.$line->id.'-'.$i.'" name="qtytoproduce-'.$line->id.'-'.$i.'" value="'.$preselected.'"></td>';
1306  if ($permissiontoupdatecost) {
1307  // Defined $manufacturingcost
1308  $manufacturingcost = 0;
1309  if ($object->mrptype == 0) { // If MO is a "Manufacture" type (and not "Disassemble"
1310  $manufacturingcost = $bomcost;
1311  if (empty($manufacturingcost)) {
1312  $manufacturingcost = price2num($tmpproduct->cost_price, 'MU');
1313  }
1314  if (empty($manufacturingcost)) {
1315  $manufacturingcost = price2num($tmpproduct->pmp, 'MU');
1316  }
1317  }
1318 
1319  if ($tmpproduct->type == Product::TYPE_PRODUCT || !empty($conf->global->STOCK_SUPPORTS_SERVICES)) {
1320  $preselected = (GETPOSTISSET('pricetoproduce-'.$line->id.'-'.$i) ? GETPOST('pricetoproduce-'.$line->id.'-'.$i) : ($manufacturingcost ? price($manufacturingcost) : ''));
1321  print '<td class="right"><input type="text" class="width50 right" name="pricetoproduce-'.$line->id.'-'.$i.'" value="'.$preselected.'"></td>';
1322  } else {
1323  print '<td><input type="hidden" class="width50 right" name="pricetoproduce-'.$line->id.'-'.$i.'" value="'.($manufacturingcost ? $manufacturingcost : '').'"></td>';
1324  }
1325  }
1326  print '<td></td>';
1327  print '<td>';
1328  if ($tmpproduct->type == Product::TYPE_PRODUCT || !empty($conf->global->STOCK_SUPPORTS_SERVICES)) {
1329  $preselected = (GETPOSTISSET('idwarehousetoproduce-'.$line->id.'-'.$i) ? GETPOST('idwarehousetoproduce-'.$line->id.'-'.$i) : ($object->fk_warehouse > 0 ? $object->fk_warehouse : 'ifone'));
1330  print $formproduct->selectWarehouses($preselected, 'idwarehousetoproduce-'.$line->id.'-'.$i, '', 1, 0, $line->fk_product, '', 1, 0, null, 'maxwidth200 csswarehouse_'.$line->id.'_'.$i);
1331  } else {
1332  print '<span class="opacitymedium">'.$langs->trans("NoStockChangeOnServices").'</span>';
1333  }
1334  print '</td>';
1335  if (isModEnabled('productbatch')) {
1336  print '<td>';
1337  if ($tmpproduct->status_batch) {
1338  $preselected = (GETPOSTISSET('batchtoproduce-'.$line->id.'-'.$i) ? GETPOST('batchtoproduce-'.$line->id.'-'.$i) : '');
1339  print '<input type="text" class="width50" name="batchtoproduce-'.$line->id.'-'.$i.'" value="'.$preselected.'">';
1340  }
1341  print '</td>';
1342  // Batch number in same column than the stock movement picto
1343  print '<td>';
1344  if ($tmpproduct->status_batch) {
1345  $type = 'batch';
1346  print img_picto($langs->trans('AddStockLocationLine'), 'split.png', 'class="splitbutton" onClick="addDispatchLine('.$line->id.', \''.$type.'\', \'qtymissing\')"');
1347  }
1348  print '</td>';
1349  }
1350  print '</tr>';
1351  }
1352  }
1353  }
1354  }
1355 
1356  print '</table>';
1357  print '</div>';
1358 
1359  print '</div>';
1360  print '</div>';
1361  }
1362 
1363  if (in_array($action, array('consumeorproduce', 'consumeandproduceall', 'addconsumeline'))) {
1364  print "</form>\n";
1365  }
1366 }
1367 
1368 // End of page
1369 llxFooter();
1370 $db->close();
if(!function_exists('dol_getprefix')) dol_include_once($relpath, $classname= '')
Make an include_once using default root and alternate root if it fails.
GETPOST($paramname, $check= 'alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
Class to manage stock movements.
img_edit($titlealt= 'default', $float=0, $other= '')
Show logo editer/modifier fiche.
Class with list of lots and properties.
yn($yesno, $case=1, $color=0)
Return yes or no in current language.
if($cancel &&!$id) if($action== 'add'&&!$cancel) if($action== 'delete') if($id) $form
Actions.
Definition: card.php:142
dolGetButtonTitle($label, $helpText= '', $iconClass= 'fa fa-file', $url= '', $id= '', $status=1, $params=array())
Function dolGetButtonTitle : this kind of buttons are used in title in list.
Class to manage products or services.
dol_escape_js($stringtoescape, $mode=0, $noescapebackslashn=0)
Returns text escaped for inclusion into javascript code.
dol_now($mode= 'auto')
Return date for now.
if(!defined('NOREQUIRESOC')) if(!defined('NOREQUIRETRAN')) if(!defined('NOCSRFCHECK')) if(!defined('NOTOKENRENEWAL')) if(!defined('NOREQUIREMENU')) if(!defined('NOREQUIREHTML')) if(!defined('NOREQUIREAJAX')) llxHeader()
Empty header.
Definition: wrapper.php:59
Class for Mo.
Definition: mo.class.php:35
const TYPE_SERVICE
Service.
const TYPE_PRODUCT
Regular product.
moPrepareHead($object)
Prepare array of tabs for Mo.
Definition: mrp_mo.lib.php:30
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
Class MoLine.
Definition: mo.class.php:1562
if(GETPOST('button_removefilter_x', 'alpha')||GETPOST('button_removefilter.x', 'alpha')||GETPOST('button_removefilter', 'alpha')) if(GETPOST('button_search_x', 'alpha')||GETPOST('button_search.x', 'alpha')||GETPOST('button_search', 'alpha')) if($action=="save"&&empty($cancel)) $help_url
View.
Definition: agenda.php:116
img_warning($titlealt= 'default', $moreatt= '', $morecss= 'pictowarning')
Show warning logo.
price($amount, $form=0, $outlangs= '', $trunc=1, $rounding=-1, $forcerounding=-1, $currency_code= '')
Function to format a value into an amount for visual output Function used into PDF and HTML pages...
Class with static methods for building HTML components related to products Only components common to ...
Class to manage standard extra fields.
setEventMessages($mesg, $mesgs, $style= 'mesgs', $messagekey= '')
Set event messages in dol_events session object.
Class to manage generation of HTML components Only common components must be here.
GETPOSTISSET($paramname)
Return true if we are in a context of submitting the parameter $paramname from a POST of a form...
Class to manage projects.
load_fiche_titre($titre, $morehtmlright= '', $picto= 'generic', $pictoisfullpath=0, $id= '', $morecssontable= '', $morehtmlcenter= '')
Load a title with picto.
price2num($amount, $rounding= '', $option=0)
Function that return a number with universal decimal format (decimal separator is &#39;...
Class to manage building of HTML components.
img_picto($titlealt, $picto, $moreatt= '', $pictoisfullpath=false, $srconly=0, $notitle=0, $alt= '', $morecss= '', $marginleftonlyshort=2)
Show picto whatever it&#39;s its name (generic function)
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename= '', $restricttologhandler= '', $logcontext=null)
Write log message into outputs.
Class to manage translations.
restrictedArea($user, $features, $objectid=0, $tableandshare= '', $feature2= '', $dbt_keyfield= 'fk_soc', $dbt_select= 'rowid', $isdraft=0, $mode=0)
Check permissions of a user to show a page and an object.
dol_get_fiche_head($links=array(), $active= '', $title= '', $notab=0, $picto= '', $pictoisfullpath=0, $morehtmlright= '', $morecss= '', $limittoshow=0, $moretabssuffix= '')
Show tabs of a record.
dol_print_date($time, $format= '', $tzoutput= 'auto', $outputlangs= '', $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
$object ref
Definition: info.php:77
newToken()
Return the value of token currently saved into session with name &#39;newtoken&#39;.
dol_get_fiche_end($notab=0)
Return tab footer of a card.
isModEnabled($module)
Is Dolibarr module enabled.
dol_banner_tab($object, $paramid, $morehtml= '', $shownav=1, $fieldid= 'rowid', $fieldref= 'ref', $morehtmlref= '', $moreparam= '', $nodbprefix=0, $morehtmlleft= '', $morehtmlstatus= '', $onlybanner=0, $morehtmlright= '')
Show tab footer of a card.
llxFooter()
Empty footer.
Definition: wrapper.php:73
$formconfirm
if ($action == &#39;delbookkeepingyear&#39;) {
Class to manage predefined suppliers products.
Class to manage warehouses.