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