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