dolibarr 23.0.3
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 * Copyright (C) 2024-2025 Frédéric France <frederic.france@free.fr>
6 * Copyright (C) 2024-2025 MDW <mdeweerd@users.noreply.github.com>
7 * Copyright (C) 2024 Alexandre Spangaro <alexandre@inovea-conseil.com>
8 * Copyright (C) 2025 Noé Cendrier <noe.cendrier@altairis.fr>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 3 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program. If not, see <https://www.gnu.org/licenses/>.
22 */
23
30// Load Dolibarr environment
31require '../main.inc.php';
32require_once DOL_DOCUMENT_ROOT.'/bom/class/bom.class.php';
33require_once DOL_DOCUMENT_ROOT.'/core/class/html.formcompany.class.php';
34require_once DOL_DOCUMENT_ROOT.'/core/class/html.formfile.class.php';
35require_once DOL_DOCUMENT_ROOT.'/core/class/html.formprojet.class.php';
36require_once DOL_DOCUMENT_ROOT.'/mrp/class/mo.class.php';
37require_once DOL_DOCUMENT_ROOT.'/mrp/lib/mrp_mo.lib.php';
38require_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
39require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
40require_once DOL_DOCUMENT_ROOT.'/product/class/html.formproduct.class.php';
41require_once DOL_DOCUMENT_ROOT.'/product/stock/class/entrepot.class.php';
42require_once DOL_DOCUMENT_ROOT.'/product/stock/class/productlot.class.php';
43require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
44require_once DOL_DOCUMENT_ROOT.'/workstation/class/workstation.class.php';
45
46
55// Load translation files required by the page
56$langs->loadLangs(array("mrp", "stocks", "other", "product", "productbatch"));
57
58// Get parameters
59$id = GETPOSTINT('id');
60$ref = GETPOST('ref', 'alpha');
61$action = GETPOST('action', 'aZ09');
62$confirm = GETPOST('confirm', 'alpha');
63$cancel = GETPOST('cancel');
64$contextpage = GETPOST('contextpage', 'aZ') ? GETPOST('contextpage', 'aZ') : 'mocard'; // To manage different context of search
65$backtopage = GETPOST('backtopage', 'alpha');
66$lineid = GETPOSTINT('lineid');
67$fk_movement = GETPOSTINT('fk_movement');
68$fk_default_warehouse = GETPOSTINT('fk_default_warehouse');
69
70$collapse = GETPOST('collapse', 'aZ09comma');
71
72// Initialize a technical objects
73$object = new Mo($db);
74$extrafields = new ExtraFields($db);
75$diroutputmassaction = $conf->mrp->dir_output.'/temp/massgeneration/'.$user->id;
76$objectline = new MoLine($db);
77
78$hookmanager->initHooks(array('moproduction', 'globalcard')); // Note that conf->hooks_modules contains array
79
80// Fetch optionals attributes and labels
81$extrafields->fetch_name_optionals_label($object->table_element);
82
83$search_array_options = $extrafields->getOptionalsFromPost($object->table_element, '', 'search_');
84
85// Initialize array of search criteria
86$search_all = GETPOST("search_all", 'alpha');
87$search = array();
88foreach ($object->fields as $key => $val) {
89 if (GETPOST('search_'.$key, 'alpha')) {
90 $search[$key] = GETPOST('search_'.$key, 'alpha');
91 }
92}
93
94if (empty($action) && empty($id) && empty($ref)) {
95 $action = 'view';
96}
97
98// Load object
99include DOL_DOCUMENT_ROOT.'/core/actions_fetchobject.inc.php'; // Must be 'include', not 'include_once'.
100
101// Security check - Protection if external user
102//if ($user->socid > 0) accessforbidden();
103//if ($user->socid > 0) $socid = $user->socid;
104$isdraft = (($object->status == $object::STATUS_DRAFT) ? 1 : 0);
105$result = restrictedArea($user, 'mrp', $object->id, 'mrp_mo', '', 'fk_soc', 'rowid', $isdraft);
106
107// Permissions
108$permissionnote = $user->hasRight('mrp', 'write'); // Used by the include of actions_setnotes.inc.php
109$permissiondellink = $user->hasRight('mrp', 'write'); // Used by the include of actions_dellink.inc.php
110$permissiontoadd = $user->hasRight('mrp', 'write'); // Used by the include of actions_addupdatedelete.inc.php and actions_lineupdown.inc.php
111$permissiontodelete = $user->hasRight('mrp', 'delete') || ($permissiontoadd && isset($object->status) && $object->status == $object::STATUS_DRAFT);
112
113$permissiontoproduce = $permissiontoadd;
114$permissiontoupdatecost = $user->hasRight('bom', 'read'); // User who can define cost must have knowledge of pricing
115
116$upload_dir = $conf->mrp->multidir_output[isset($object->entity) ? $object->entity : 1];
117
118
119/*
120 * Actions
121 */
122
123$parameters = array();
124$reshook = $hookmanager->executeHooks('doActions', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
125if ($reshook < 0) {
126 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
127}
128
129if (empty($reshook)) {
130 $error = 0;
131
132 $backurlforlist = DOL_URL_ROOT.'/mrp/mo_list.php';
133
134 if (empty($backtopage) || ($cancel && empty($id))) {
135 //var_dump($backurlforlist);exit;
136 if (empty($id) && (($action != 'add' && $action != 'create') || $cancel)) {
137 $backtopage = $backurlforlist;
138 } else {
139 $backtopage = DOL_URL_ROOT.'/mrp/mo_production.php?id='.($id > 0 ? $id : '__ID__');
140 }
141 }
142 $triggermodname = 'MO_MODIFY'; // Name of trigger action code to execute when we modify record
143
144 if ($action == 'confirm_cancel' && $confirm == 'yes' && !empty($permissiontoadd)) {
145 $also_cancel_consumed_and_produced_lines = (GETPOST('alsoCancelConsumedAndProducedLines', 'alpha') ? 1 : 0);
146 $result = $object->cancel($user, 0, (bool) $also_cancel_consumed_and_produced_lines);
147 if ($result > 0) {
148 header("Location: " . DOL_URL_ROOT.'/mrp/mo_card.php?id=' . $object->id);
149 exit;
150 } else {
151 $action = '';
152 setEventMessages($object->error, $object->errors, 'errors');
153 }
154 } elseif ($action == 'confirm_delete' && $confirm == 'yes' && !empty($permissiontodelete)) {
155 $also_cancel_consumed_and_produced_lines = (GETPOST('alsoCancelConsumedAndProducedLines', 'alpha') ? 1 : 0);
156 $result = $object->delete($user, 0, (bool) $also_cancel_consumed_and_produced_lines);
157 if ($result > 0) {
158 header("Location: " . $backurlforlist);
159 exit;
160 } else {
161 $action = '';
162 setEventMessages($object->error, $object->errors, 'errors');
163 }
164 }
165
166 // Allow editing qty while MO is still in draft status.
167 // IMPORTANT: this handler MUST run BEFORE actions_addupdatedelete.inc.php,
168 // which has a generic 'set<key>' matcher that would intercept 'setqty' and
169 // call $object->fetch() + update() without setting $object->oldQty, so
170 // Mo::updateProduction() would skip the line scaling (its condition is
171 // !empty($this->oldQty)).
172 if ($action == 'setqty' && $permissiontoadd && $object->status == Mo::STATUS_DRAFT) {
173 $newqty = GETPOSTFLOAT('qty');
174 if ($newqty > 0) {
175 $object->oldQty = (float) $object->qty;
176 $object->qty = $newqty;
177 $res = $object->update($user);
178 if ($res > 0) {
179 // Enforce invariant: the 'toproduce' line for the MO's main product
180 // must equal the MO qty. Mo::updateProduction() scales by ratio
181 // (newQty/oldQty) which can drift if the line state was already
182 // inconsistent (e.g. legacy data from before this patch). Realign
183 // the main product line, leaving sub-products and frozen lines
184 // untouched.
185 $object->fetchLines();
186 foreach ($object->lines as $line) {
187 if ($line->role === 'toproduce'
188 && (int) $line->fk_product === (int) $object->fk_product
189 && empty($line->qty_frozen)
190 && (float) $line->qty != (float) $object->qty) {
191 $line->qty = (float) $object->qty;
192 $line->update($user);
193 }
194 }
195 setEventMessages($langs->trans("RecordSaved"), null, 'mesgs');
196 } else {
197 setEventMessages($object->error, $object->errors, 'errors');
198 }
199 } else {
200 setEventMessages($langs->trans("ErrorFieldRequired", $langs->trans("Qty")), null, 'errors');
201 }
202 header("Location: ".$_SERVER["PHP_SELF"]."?id=".$object->id);
203 exit;
204 }
205
206 // Actions cancel, add, update, delete or clone
207 include DOL_DOCUMENT_ROOT.'/core/actions_addupdatedelete.inc.php';
208
209 // Actions when linking object each other
210 include DOL_DOCUMENT_ROOT.'/core/actions_dellink.inc.php';
211
212 // Actions when printing a doc from card
213 include DOL_DOCUMENT_ROOT.'/core/actions_printing.inc.php';
214
215 // Actions to send emails
216 $triggersendname = 'MO_SENTBYMAIL';
217 $autocopy = 'MAIN_MAIL_AUTOCOPY_MO_TO';
218 $trackid = 'mo'.$object->id;
219 include DOL_DOCUMENT_ROOT.'/core/actions_sendmails.inc.php';
220
221 // Action to move up and down lines of object
222 //include DOL_DOCUMENT_ROOT.'/core/actions_lineupdown.inc.php'; // Must be 'include', not 'include_once'
223
224 if ($action == 'set_thirdparty' && $permissiontoadd) {
225 $object->setValueFrom('fk_soc', GETPOSTINT('fk_soc'), '', null, 'date', '', $user, $triggermodname);
226 }
227 if ($action == 'classin' && $permissiontoadd) {
228 $object->setProject(GETPOSTINT('projectid'));
229 }
230
231 if ($action == 'confirm_reopen' && $permissiontoadd) {
232 $result = $object->setStatut($object::STATUS_INPROGRESS, 0, '', 'MRP_REOPEN');
233 }
234
235 if (($action == 'confirm_addconsumeline' && GETPOST('addconsumelinebutton') && $permissiontoadd)
236 || ($action == 'confirm_addproduceline' && GETPOST('addproducelinebutton') && $permissiontoadd)) {
237 $moline = new MoLine($db);
238
239 // Line to produce
240 $moline->fk_mo = $object->id;
241 $moline->qty = GETPOSTFLOAT('qtytoadd');
242 $moline->fk_product = GETPOSTINT('productidtoadd');
243 if (GETPOST('addconsumelinebutton')) {
244 $moline->role = 'toconsume';
245 } else {
246 $moline->role = 'toproduce';
247 }
248 $moline->origin_type = 'free'; // free consume line
249 $moline->position = 0;
250
251 // Is it a product or a service ?
252 if (!empty($moline->fk_product)) {
253 $tmpproduct = new Product($db);
254 $tmpproduct->fetch($moline->fk_product);
255 if ($tmpproduct->type == Product::TYPE_SERVICE) {
256 $moline->fk_default_workstation = $tmpproduct->fk_default_workstation;
257 $moline->disable_stock_change = 1;
258 if ($tmpproduct->duration_unit) {
259 $moline->qty = $tmpproduct->duration_value;
260 include_once DOL_DOCUMENT_ROOT.'/core/class/cunits.class.php';
261 $cunits = new CUnits($db);
262 $res = $cunits->fetch(0, '', $tmpproduct->duration_unit, 'time');
263 if ($res > 0) {
264 $moline->fk_unit = $cunits->id;
265 }
266 }
267 } else {
268 $moline->disable_stock_change = 0;
269 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
270 $moline->fk_unit = $tmpproduct->fk_unit;
271 }
272 }
273 }
274 // Extrafields
275 $extralabelsline = $extrafields->fetch_name_optionals_label($object->table_element_line);
276 $array_options = $extrafields->getOptionalsFromPost($object->table_element_line);
277 // Unset extrafield
278 if (is_array($extralabelsline)) {
279 // Get extra fields
280 foreach ($extralabelsline as $key => $value) {
281 unset($_POST["options_".$key]);
282 }
283 }
284 if (is_array($array_options) && count($array_options) > 0) {
285 $moline->array_options = $array_options;
286 }
287
288 $resultline = $moline->create($user, 0); // Never use triggers here
289 if ($resultline <= 0) {
290 $error++;
291 setEventMessages($moline->error, $moline->errors, 'errors');
292 }
293
294 $action = '';
295 // Redirect to refresh the tab information
296 header("Location: ".$_SERVER["PHP_SELF"].'?id='.$object->id);
297 exit;
298 }
299
300 if (in_array($action, array('confirm_consumeorproduce', 'confirm_consumeandproduceall')) && $permissiontoproduce) {
301 $stockmove = new MouvementStock($db);
302
303 $labelmovement = GETPOST('inventorylabel', 'alphanohtml');
304 $codemovement = GETPOST('inventorycode', 'alphanohtml');
305
306 $db->begin();
307 $pos = 0;
308 // Process line to consume
309 foreach ($object->lines as $line) {
310 if ($line->role == 'toconsume') {
311 $tmpproduct = new Product($db);
312 $tmpproduct->fetch($line->fk_product);
313
314 $i = 1;
315 while (GETPOSTISSET('qty-'.$line->id.'-'.$i)) {
316 $qtytoprocess = (float) price2num(GETPOST('qty-'.$line->id.'-'.$i));
317
318 if ($qtytoprocess != 0) {
319 // Check warehouse is set if we should have to
320 if (GETPOSTISSET('idwarehouse-'.$line->id.'-'.$i)) { // If there is a warehouse to set
321 if (!(GETPOST('idwarehouse-'.$line->id.'-'.$i) > 0)) { // If there is no warehouse set.
322 $langs->load("errors");
323 setEventMessages($langs->trans("ErrorFieldRequiredForProduct", $langs->transnoentitiesnoconv("Warehouse"), $tmpproduct->ref), null, 'errors');
324 $error++;
325 }
326 if ($tmpproduct->status_batch && (!GETPOST('batch-'.$line->id.'-'.$i))) {
327 $langs->load("errors");
328 setEventMessages($langs->trans("ErrorFieldRequiredForProduct", $langs->transnoentitiesnoconv("Batch"), $tmpproduct->ref), null, 'errors');
329 $error++;
330 }
331 }
332
333 $idstockmove = 0;
334 if (!$error && GETPOST('idwarehouse-'.$line->id.'-'.$i) > 0) {
335 // Record stock movement
336 $id_product_batch = 0;
337 $stockmove->setOrigin($object->element, $object->id);
338 $stockmove->context['mrp_role'] = 'toconsume';
339
340 if ($qtytoprocess >= 0) {
341 $idstockmove = $stockmove->livraison($user, $line->fk_product, GETPOSTINT('idwarehouse-'.$line->id.'-'.$i), $qtytoprocess, 0, $labelmovement, dol_now(), '', '', GETPOST('batch-'.$line->id.'-'.$i), $id_product_batch, $codemovement);
342 } else {
343 $idstockmove = $stockmove->reception($user, $line->fk_product, GETPOSTINT('idwarehouse-'.$line->id.'-'.$i), $qtytoprocess * -1, 0, $labelmovement, dol_now(), '', '', GETPOST('batch-'.$line->id.'-'.$i), $id_product_batch, $codemovement);
344 }
345 if ($idstockmove < 0) {
346 $error++;
347 setEventMessages($stockmove->error, $stockmove->errors, 'errors');
348 }
349 }
350
351 if (!$error) {
352 // Record consumption
353 $moline = new MoLine($db);
354 $moline->fk_mo = $object->id;
355 $moline->position = $pos;
356 $moline->fk_product = $line->fk_product;
357 $moline->fk_warehouse = GETPOSTINT('idwarehouse-'.$line->id.'-'.$i);
358 $moline->qty = $qtytoprocess;
359 $moline->batch = GETPOST('batch-'.$line->id.'-'.$i);
360 $moline->role = 'consumed';
361 $moline->fk_mrp_production = $line->id;
362 $moline->fk_stock_movement = $idstockmove == 0 ? null : $idstockmove;
363 $moline->fk_user_creat = $user->id;
364
365 $resultmoline = $moline->create($user);
366 if ($resultmoline <= 0) {
367 $error++;
368 setEventMessages($moline->error, $moline->errors, 'errors');
369 }
370
371 $pos++;
372 }
373 }
374
375 $i++;
376 }
377 }
378 }
379
380 // Process line to produce
381 $pos = 0;
382
383 foreach ($object->lines as $line) {
384 if ($line->role == 'toproduce') {
385 $tmpproduct = new Product($db);
386 $tmpproduct->fetch($line->fk_product);
387
388 $i = 1;
389 while (GETPOSTISSET('qtytoproduce-'.$line->id.'-'.$i)) {
390 $qtytoprocess = (float) price2num(GETPOST('qtytoproduce-'.$line->id.'-'.$i));
391 $pricetoprocess = GETPOST('pricetoproduce-'.$line->id.'-'.$i) ? price2num(GETPOST('pricetoproduce-'.$line->id.'-'.$i)) : 0;
392
393 if ($qtytoprocess != 0) {
394 // Check warehouse is set if we should have to
395 if (GETPOSTISSET('idwarehousetoproduce-'.$line->id.'-'.$i)) { // If there is a warehouse to set
396 if (!(GETPOST('idwarehousetoproduce-'.$line->id.'-'.$i) > 0)) { // If there is no warehouse set.
397 $langs->load("errors");
398 setEventMessages($langs->trans("ErrorFieldRequiredForProduct", $langs->transnoentitiesnoconv("Warehouse"), $tmpproduct->ref), null, 'errors');
399 $error++;
400 }
401 if (isModEnabled('productbatch') && $tmpproduct->status_batch && (!GETPOST('batchtoproduce-'.$line->id.'-'.$i))) {
402 $langs->load("errors");
403 setEventMessages($langs->trans("ErrorFieldRequiredForProduct", $langs->transnoentitiesnoconv("Batch"), $tmpproduct->ref), null, 'errors');
404 $error++;
405 }
406 }
407
408 $idstockmove = 0;
409 if (!$error && GETPOST('idwarehousetoproduce-'.$line->id.'-'.$i) > 0) {
410 // Record stock movement
411 $id_product_batch = 0;
412 $stockmove->origin_type = $object->element;
413 $stockmove->origin_id = $object->id;
414 $stockmove->context['mrp_role'] = 'toproduce';
415
416 $idstockmove = $stockmove->reception($user, $line->fk_product, GETPOSTINT('idwarehousetoproduce-'.$line->id.'-'.$i), $qtytoprocess, $pricetoprocess, $labelmovement, GETPOSTDATE('eatby-'.$line->id.'-'.$i), GETPOSTDATE('sellby-'.$line->id.'-'.$i), GETPOST('batchtoproduce-'.$line->id.'-'.$i), dol_now(), $id_product_batch, $codemovement);
417 if ($idstockmove < 0) {
418 $error++;
419 setEventMessages($stockmove->error, $stockmove->errors, 'errors');
420 }
421 }
422
423 if (!$error) {
424 // Record production
425 $moline = new MoLine($db);
426 $moline->fk_mo = $object->id;
427 $moline->position = $pos;
428 $moline->fk_product = $line->fk_product;
429 $moline->fk_warehouse = GETPOSTINT('idwarehousetoproduce-'.$line->id.'-'.$i);
430 $moline->qty = $qtytoprocess;
431 $moline->batch = GETPOST('batchtoproduce-'.$line->id.'-'.$i);
432 $moline->role = 'produced';
433 $moline->fk_mrp_production = $line->id;
434 $moline->fk_stock_movement = (($idstockmove == 0) ? null : $idstockmove);
435 $moline->fk_user_creat = $user->id;
436
437 $resultmoline = $moline->create($user);
438 if ($resultmoline <= 0) {
439 $error++;
440 setEventMessages($moline->error, $moline->errors, 'errors');
441 }
442
443 $pos++;
444 }
445 }
446
447 $i++;
448 }
449 }
450 }
451
452 if (!$error) {
453 $consumptioncomplete = true;
454 $productioncomplete = true;
455
456 if (GETPOSTINT('autoclose')) {
457 foreach ($object->lines as $line) {
458 $tmpproduct = new Product($db);
459 $tmpproduct->fetch($line->fk_product);
460 if ((int) $tmpproduct->stockable_product > 0) {
461 if ($line->role == 'toconsume') {
462 $arrayoflines = $object->fetchLinesLinked('consumed', $line->id);
463 $alreadyconsumed = 0;
464 foreach ($arrayoflines as $line2) {
465 $alreadyconsumed += $line2['qty'];
466 }
467
468 if ($alreadyconsumed < $line->qty) {
469 $consumptioncomplete = false;
470 }
471 }
472 if ($line->role == 'toproduce') {
473 $arrayoflines = $object->fetchLinesLinked('produced', $line->id);
474 $alreadyproduced = 0;
475 foreach ($arrayoflines as $line2) {
476 $alreadyproduced += $line2['qty'];
477 }
478
479 if ($alreadyproduced < $line->qty) {
480 $productioncomplete = false;
481 }
482 }
483 }
484 }
485 } else {
486 $consumptioncomplete = false;
487 $productioncomplete = false;
488 }
489
490 // Update status of MO
491 dol_syslog("consumptioncomplete = ".json_encode($consumptioncomplete)." productioncomplete = ".json_encode($productioncomplete));
492 if ($consumptioncomplete && $productioncomplete) {
493 $result = $object->setStatut($object::STATUS_PRODUCED, 0, '', 'MRP_MO_PRODUCED');
494 } else {
495 $result = $object->setStatut($object::STATUS_INPROGRESS, 0, '', 'MRP_MO_PRODUCED');
496 }
497 if ($result <= 0) {
498 $error++;
499 setEventMessages($object->error, $object->errors, 'errors');
500 }
501 }
502
503 if ($error) {
504 $action = str_replace('confirm_', '', $action);
505 $db->rollback();
506 } else {
507 $db->commit();
508
509 // Redirect to avoid to action done a second time if we make a back from browser
510 header("Location: ".$_SERVER["PHP_SELF"].'?id='.$object->id);
511 exit;
512 }
513 }
514
515 // Action close produced
516 if ($action == 'confirm_produced' && $confirm == 'yes' && $permissiontoadd) {
517 $result = $object->setStatut($object::STATUS_PRODUCED, 0, '', 'MRP_MO_PRODUCED');
518 if ($result >= 0) {
519 // Define output language
520 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
521 $outputlangs = $langs;
522 $newlang = '';
523 if (getDolGlobalInt('MAIN_MULTILANGS') /* && empty($newlang) */ && GETPOST('lang_id', 'aZ09')) {
524 $newlang = GETPOST('lang_id', 'aZ09');
525 }
526 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
527 $newlang = $object->thirdparty->default_lang;
528 }
529 if (!empty($newlang)) {
530 $outputlangs = new Translate("", $conf);
531 $outputlangs->setDefaultLang($newlang);
532 }
533 $model = $object->model_pdf;
534 $ret = $object->fetch($id); // Reload to get new records
535
536 $object->generateDocument($model, $outputlangs, 0, 0, 0);
537 }
538 } else {
539 setEventMessages($object->error, $object->errors, 'errors');
540 }
541 }
542
543 if ($action == 'confirm_editline' && $permissiontoadd) {
544 $moline = new MoLine($db);
545 $res = $moline->fetch(GETPOSTINT('lineid'));
546 if ($result > 0) {
547 $extrafields->fetch_name_optionals_label($moline->table_element);
548 if (!empty($extrafields->attributes[$moline->table_element]['label'])) {
549 foreach ($extrafields->attributes[$moline->table_element]['label'] as $key => $label) {
550 $value = GETPOST('options_'.$key, 'alphanohtml');
551 $moline->array_options["options_".$key] = $value;
552 }
553 }
554 $moline->qty = GETPOSTFLOAT('qty_lineProduce');
555 if (GETPOSTISSET('warehouse_lineProduce')) {
556 $moline->fk_warehouse = (GETPOSTINT('warehouse_lineProduce') > 0 ? GETPOSTINT('warehouse_lineProduce') : 0);
557 }
558 if (GETPOSTISSET('workstation_lineProduce')) {
559 $moline->fk_default_workstation = (GETPOSTINT('workstation_lineProduce') > 0 ? GETPOSTINT('workstation_lineProduce') : 0);
560 }
561
562 $res = $moline->update($user);
563
564 if ($res < 0) {
565 setEventMessages($moline->error, $moline->errors, 'errors');
566 header("Location: ".$_SERVER["PHP_SELF"].'?id='.$object->id);
567 exit;
568 }
569 header("Location: ".$_SERVER["PHP_SELF"].'?id='.$object->id);
570 exit;
571 }
572 }
573}
574
575
576
577/*
578 * View
579 */
580
581$form = new Form($db);
582$formproject = new FormProjets($db);
583$formproduct = new FormProduct($db);
584$tmpwarehouse = new Entrepot($db);
585$tmpbatch = new Productlot($db);
586$tmpstockmovement = new MouvementStock($db);
587
588$title = $langs->trans('Mo');
589$help_url = 'EN:Module_Manufacturing_Orders|FR:Module_Ordres_de_Fabrication|DE:Modul_Fertigungsauftrag';
590$morejs = array('/mrp/js/lib_dispatch.js.php');
591llxHeader('', $title, $help_url, '', 0, 0, $morejs, '', '', 'mod-mrp page-card_production');
592
593$newToken = newToken();
594
595// Part to show record
596if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'create' && $action != 'reload'))) {
597 $res = $object->fetch_thirdparty();
598 $res = $object->fetch_optionals();
599
600 if (getDolGlobalString('STOCK_CONSUMPTION_FROM_MANUFACTURING_WAREHOUSE') && $object->fk_warehouse > 0) {
601 $tmpwarehouse->fetch((int) $object->fk_warehouse);
602 $fk_default_warehouse = (int) $object->fk_warehouse;
603 }
604
605 $head = moPrepareHead($object);
606
607 print dol_get_fiche_head($head, 'production', $langs->trans("ManufacturingOrder"), -1, $object->picto);
608
609 $formconfirm = '';
610
611 // Confirmation to delete
612 if ($action == 'delete') {
613 $formquestion = array(
614 array(
615 'label' => $langs->trans('MoCancelConsumedAndProducedLines'),
616 'name' => 'alsoCancelConsumedAndProducedLines',
617 'type' => 'checkbox',
618 'value' => 0
619 ),
620 );
621 $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id, $langs->trans('DeleteMo'), $langs->trans('ConfirmDeleteMo'), 'confirm_delete', $formquestion, 0, 1);
622 }
623 // Confirmation to delete line
624 if ($action == 'deleteline') {
625 $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);
626 }
627 // Clone confirmation
628 if ($action == 'clone') {
629 // Create an array for form
630 $formquestion = array();
631 $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id, $langs->trans('ToClone'), $langs->trans('ConfirmCloneMo', $object->ref), 'confirm_clone', $formquestion, 'yes', 1);
632 }
633
634 // Confirmation of validation
635 if ($action == 'validate') {
636 // We check that object has a temporary ref
637 $ref = substr($object->ref, 1, 4);
638 if ($ref == 'PROV') {
639 $object->fetch_product();
640 $numref = $object->getNextNumRef($object->product);
641 } else {
642 $numref = (string) $object->ref;
643 }
644
645 $text = $langs->trans('ConfirmValidateMo', $numref);
646 /*if (isModEnabled('notification'))
647 {
648 require_once DOL_DOCUMENT_ROOT . '/core/class/notify.class.php';
649 $notify = new Notify($db);
650 $text .= '<br>';
651 $text .= $notify->confirmMessage('BOM_VALIDATE', $object->socid, $object);
652 }*/
653
654 $formquestion = array();
655 if (isModEnabled('mrp')) {
656 $langs->load("mrp");
657 require_once DOL_DOCUMENT_ROOT.'/product/class/html.formproduct.class.php';
658 $formproduct = new FormProduct($db);
659 $forcecombo = 0;
660 if ($conf->browser->name == 'ie') {
661 $forcecombo = 1; // There is a bug in IE10 that make combo inside popup crazy
662 }
663 $formquestion = array(
664 // 'text' => $langs->trans("ConfirmClone"),
665 // array('type' => 'checkbox', 'name' => 'clone_content', 'label' => $langs->trans("CloneMainAttributes"), 'value' => 1),
666 // array('type' => 'checkbox', 'name' => 'update_prices', 'label' => $langs->trans("PuttingPricesUpToDate"), 'value' => 1),
667 );
668 }
669
670 $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id, $langs->trans('Validate'), $text, 'confirm_validate', $formquestion, 0, 1, 220);
671 }
672
673 // Confirmation to cancel
674 if ($action == 'cancel') {
675 $formquestion = array(
676 array(
677 'label' => $langs->trans('MoCancelConsumedAndProducedLines'),
678 'name' => 'alsoCancelConsumedAndProducedLines',
679 'type' => 'checkbox',
680 'value' => !getDolGlobalString('MO_ALSO_CANCEL_CONSUMED_AND_PRODUCED_LINES_BY_DEFAULT') ? 0 : 1
681 ),
682 );
683 $formconfirm = $form->formconfirm(dolBuildUrl($_SERVER["PHP_SELF"], ['id' => $object->id]), $langs->trans('CancelMo'), $langs->trans('ConfirmCancelMo'), 'confirm_cancel', $formquestion, 0, 1);
684 }
685
686 // Call Hook formConfirm
687 $parameters = array('formConfirm' => $formconfirm, 'lineid' => $lineid);
688 $reshook = $hookmanager->executeHooks('formConfirm', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
689 if (empty($reshook)) {
690 $formconfirm .= $hookmanager->resPrint;
691 } elseif ($reshook > 0) {
692 $formconfirm = $hookmanager->resPrint;
693 }
694
695 // Print form confirm
696 print $formconfirm;
697
698
699 // MO file
700 // ------------------------------------------------------------
701 $linkback = '<a href="'.DOL_URL_ROOT.'/mrp/mo_list.php?restore_lastsearch_values=1'.(!empty($socid) ? '&socid='.$socid : '').'">'.$langs->trans("BackToList").'</a>';
702
703 $morehtmlref = '<div class="refidno">';
704
705 /*
706 // Ref bis
707 $morehtmlref.=$form->editfieldkey("RefBis", 'ref_client', $object->ref_client, $object, $user->rights->mrp->creer, 'string', '', 0, 1);
708 $morehtmlref.=$form->editfieldval("RefBis", 'ref_client', $object->ref_client, $object, $user->rights->mrp->creer, 'string', '', null, null, '', 1);
709 */
710
711 // Thirdparty
712 if (is_object($object->thirdparty)) {
713 $morehtmlref .= $object->thirdparty->getNomUrl(1, 'customer');
714 if (!getDolGlobalString('MAIN_DISABLE_OTHER_LINK') && $object->thirdparty->id > 0) {
715 $morehtmlref .= ' (<a href="'.DOL_URL_ROOT.'/commande/list.php?socid='.$object->thirdparty->id.'&search_societe='.urlencode($object->thirdparty->name).'">'.$langs->trans("OtherOrders").'</a>)';
716 }
717 }
718
719 // Project
720 if (isModEnabled('project')) {
721 $langs->load("projects");
722 if (is_object($object->thirdparty)) {
723 $morehtmlref .= '<br>';
724 }
725 if ($permissiontoadd) {
726 $morehtmlref .= img_picto($langs->trans("Project"), 'project', 'class="pictofixedwidth"');
727 if ($action != 'classify') {
728 $morehtmlref .= '<a class="editfielda" href="'.dolBuildUrl($_SERVER['PHP_SELF'], ['action' => 'classify', 'id' => $object->id], true).'">'.img_edit($langs->transnoentitiesnoconv('SetProject')).'</a> ';
729 }
730 $morehtmlref .= $form->form_project($_SERVER['PHP_SELF'].'?id='.$object->id, $object->socid, (string) $object->fk_project, ($action == 'classify' ? 'projectid' : 'none'), 0, 0, 0, 1, '', 'maxwidth300');
731 } else {
732 if (!empty($object->fk_project)) {
733 $proj = new Project($db);
734 $proj->fetch($object->fk_project);
735 $morehtmlref .= $proj->getNomUrl(1);
736 if ($proj->title) {
737 $morehtmlref .= '<span class="opacitymedium"> - '.dol_escape_htmltag($proj->title).'</span>';
738 }
739 }
740 }
741 }
742 $morehtmlref .= '</div>';
743
744
745 dol_banner_tab($object, 'ref', $linkback, 1, 'ref', 'ref', $morehtmlref);
746
747
748 print '<div class="fichecenter">';
749 print '<div class="fichehalfleft">';
750 print '<div class="underbanner clearboth"></div>';
751 print '<table class="border centpercent tableforfield">'."\n";
752
753 // Common attributes
754 $keyforbreak = 'fk_warehouse';
755 unset($object->fields['fk_project']);
756 unset($object->fields['fk_soc']);
757 // Allow inline edit of qty while MO is in draft status.
758 // Note: editfieldval() does not handle type 'real' (no <input> rendered, only
759 // Save/Cancel buttons appear). So we override the type to 'numeric' only when
760 // the user has actually clicked the edit pencil (action=editqty). This keeps
761 // the read-mode rendering (price() format) untouched.
762 if ($object->status == Mo::STATUS_DRAFT && $permissiontoadd && isset($object->fields['qty'])) {
763 $object->fields['qty']['alwayseditable'] = 1;
764 if ($action == 'editqty') {
765 $object->fields['qty']['type'] = 'numeric';
766 }
767 }
768
769 include DOL_DOCUMENT_ROOT.'/core/tpl/commonfields_view.tpl.php';
770
771 // Other attributes
772 include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_view.tpl.php';
773
774 print '</table>';
775 print '</div>';
776 print '</div>';
777
778 print '<div class="clearboth"></div>';
779
780 print dol_get_fiche_end();
781
782
783 if (!in_array($action, array('consumeorproduce', 'consumeandproduceall'))) {
784 print '<div class="tabsAction">';
785
786 $parameters = array();
787 // Note that $action and $object may be modified by hook
788 $reshook = $hookmanager->executeHooks('addMoreActionsButtons', $parameters, $object, $action);
789 if (empty($reshook)) {
790 // Validate
791 if ($object->status == $object::STATUS_DRAFT) {
792 if ($permissiontoadd) {
793 if (empty($object->table_element_line) || (is_array($object->lines) && count($object->lines) > 0)) {
794 print '<a class="butAction" href="'.$_SERVER['PHP_SELF'].'?id='.$object->id.'&action=validate&token='.$newToken.'">'.$langs->trans("Validate").'</a>';
795 } else {
796 $langs->load("errors");
797 print '<a class="butActionRefused" href="" title="'.$langs->trans("ErrorAddAtLeastOneLineFirst").'">'.$langs->trans("Validate").'</a>';
798 }
799 }
800 }
801
802 // Consume or produce
803 if ($object->status == Mo::STATUS_VALIDATED || $object->status == Mo::STATUS_INPROGRESS) {
804 if ($permissiontoproduce) {
805 print '<a class="butAction" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=consumeorproduce&token='.$newToken.'">'.$langs->trans('ConsumeOrProduce').'</a>';
806 } else {
807 print '<a class="butActionRefused classfortooltip" href="#" title="'.$langs->trans("NotEnoughPermissions").'">'.$langs->trans('ConsumeOrProduce').'</a>';
808 }
809 } elseif ($object->status == Mo::STATUS_DRAFT) {
810 print '<a class="butActionRefused classfortooltip" href="#" title="'.$langs->trans("ValidateBefore").'">'.$langs->trans('ConsumeOrProduce').'</a>';
811 }
812
813 // ConsumeAndProduceAll
814 if ($object->status == Mo::STATUS_VALIDATED || $object->status == Mo::STATUS_INPROGRESS) {
815 if ($permissiontoproduce) {
816 print '<a class="butAction" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=consumeandproduceall&token='.$newToken.'">'.$langs->trans('ConsumeAndProduceAll').'</a>';
817 } else {
818 print '<a class="butActionRefused classfortooltip" href="#" title="'.$langs->trans("NotEnoughPermissions").'">'.$langs->trans('ConsumeAndProduceAll').'</a>';
819 }
820 } elseif ($object->status == Mo::STATUS_DRAFT) {
821 print '<a class="butActionRefused classfortooltip" href="#" title="'.$langs->trans("ValidateBefore").'">'.$langs->trans('ConsumeAndProduceAll').'</a>';
822 }
823
824 // Cancel - Reopen
825 if ($permissiontoadd) {
826 if ($object->status == $object::STATUS_VALIDATED || $object->status == $object::STATUS_INPROGRESS) {
827 $arrayproduced = $object->fetchLinesLinked('produced', 0);
828 $nbProduced = 0;
829 foreach ($arrayproduced as $lineproduced) {
830 $nbProduced += $lineproduced['qty'];
831 }
832 if ($nbProduced > 0) { // If production has started, we can close it
833 print '<a class="butAction" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=confirm_produced&confirm=yes&token='.$newToken.'">'.$langs->trans("Close").'</a>'."\n";
834 } else {
835 print '<a class="butActionRefused" href="#" title="'.$langs->trans("GoOnTabProductionToProduceFirst", $langs->transnoentitiesnoconv("Production")).'">'.$langs->trans("Close").'</a>'."\n";
836 }
837
838 print '<a class="butActionDelete" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=cancel&token='.$newToken.'">'.$langs->trans("Cancel").'</a>'."\n";
839 }
840
841 if ($object->status == $object::STATUS_CANCELED) {
842 print '<a class="butAction" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=confirm_reopen&confirm=yes&token='.$newToken.'">'.$langs->trans("ReOpen").'</a>'."\n";
843 }
844
845 if ($object->status == $object::STATUS_PRODUCED) {
846 if ($permissiontoproduce) {
847 print '<a class="butAction" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=confirm_reopen&token='.$newToken.'">'.$langs->trans('ReOpen').'</a>';
848 } else {
849 print '<a class="butActionRefused classfortooltip" href="#" title="'.$langs->trans("NotEnoughPermissions").'">'.$langs->trans('ReOpen').'</a>';
850 }
851 }
852 }
853 }
854
855 print '</div>';
856 }
857
858 if (in_array($action, array('consumeorproduce', 'consumeandproduceall', 'addconsumeline', 'addproduceline', 'editline'))) {
859 print '<!-- Form to enter value for consumption/production -->';
860 print '<form method="POST" action="'.dolBuildUrl($_SERVER["PHP_SELF"]).'">';
861 print '<input type="hidden" name="token" value="'.newToken().'">';
862 print '<input type="hidden" name="action" value="confirm_'.$action.'">';
863 print '<input type="hidden" name="backtopage" value="'.$backtopage.'">';
864 print '<input type="hidden" name="id" value="'.$id.'">';
865 // Note: closing form is add end of page
866
867 if (in_array($action, array('consumeorproduce', 'consumeandproduceall'))) {
868 $defaultstockmovementlabel = GETPOST('inventorylabel', 'alphanohtml') ? GETPOST('inventorylabel', 'alphanohtml') : $langs->trans("ProductionForRef", $object->ref);
869 $defaultstockmovementcode = GETPOST('inventorycode', 'alphanohtml') ? GETPOST('inventorycode', 'alphanohtml') : dol_print_date(dol_now(), 'dayhourlog');
870
871 print '<div class="center'.(in_array($action, array('consumeorproduce', 'consumeandproduceall')) ? ' formconsumeproduce' : '').'">';
872 print '<div class="opacitymedium hideonsmartphone paddingbottom marginbottomonly">'.$langs->trans("ConfirmProductionDesc", $langs->transnoentitiesnoconv("Confirm")).'<br></div>';
873 print '<span class="paddingright">'.$langs->trans("InventoryCode").':</span>';
874 print '<span class="clearbothonsmartphone"></span>';
875 print '<input type="text" class="minwidth125 maxwidth150" name="inventorycode" value="'.$defaultstockmovementcode.'">';
876 print '<span class="hideonsmartphone">&nbsp; &nbsp;</span>';
877 print '<span class="clearbothonsmartphone"></span>';
878 print '<span class="paddingright">'.$langs->trans("MovementLabel").':</span>';
879 print '<span class="clearbothonsmartphone"></span>';
880 print '<input type="text" class="minwidth300" name="inventorylabel" value="'.$defaultstockmovementlabel.'"><br><br>';
881 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>';
882 print '<input type="submit" class="button margintoponly" value="'.$langs->trans("Confirm").'" name="confirm">';
883 print ' &nbsp; ';
884 print '<input class="button margintoponly button-cancel" type="submit" value="'.$langs->trans("Cancel").'" name="cancel">';
885 print '<br><br>';
886 print '</div>';
887
888 print '<br>';
889 }
890 }
891
892
893 /*
894 * Lines
895 */
896 $collapse = 1;
897
898 if (!empty($object->table_element_line)) {
899 // Show object lines
900 $object->fetchLines();
901
902 $bomcost = 0;
903 if ($object->fk_bom > 0) {
904 $bom = new BOM($db);
905 $res = $bom->fetch($object->fk_bom);
906 if ($res > 0) {
907 $bom->calculateCosts();
908 $bomcost = $bom->unit_cost;
909 }
910 }
911
912 // Lines to consume
913
914 print '<!-- Lines to consume -->'."\n";
915 print '<div class="fichecenter">';
916 print '<div class="fichehalfleft">';
917 print '<div class="clearboth"></div>';
918
919 $url = $_SERVER["PHP_SELF"].'?id='.$object->id.'&action=addconsumeline&token='.newToken();
920 $permissiontoaddaconsumeline = ($object->status != $object::STATUS_PRODUCED && $object->status != $object::STATUS_CANCELED) ? 1 : -2;
921 $parameters = array('morecss' => 'reposition');
922 $helpText = '';
923 if ($permissiontoaddaconsumeline == -2) {
924 $helpText = $langs->trans('MOIsClosed');
925 }
926
927 $newcardbutton = '';
928 if ($action != 'consumeorproduce' && $action != 'consumeandproduceall') {
929 $newcardbutton = dolGetButtonTitle($langs->trans('AddNewConsumeLines'), $helpText, 'fa fa-plus-circle size15x', $url, '', $permissiontoaddaconsumeline, $parameters);
930 }
931
932 print load_fiche_titre($langs->trans('Consumption'), $newcardbutton, '', 0, '', '', '');
933
934 print '<div class="div-table-responsive-no-min">';
935 print '<table class="noborder noshadow centpercent nobottom">';
936
937 print '<!-- Line of title for products to consume -->'."\n";
938 print '<tr class="liste_titre trheight5em">';
939 // Product
940 print '<td>'.$langs->trans("Product").'</td>';
941 // Qty
942 print '<td class="right">'.$langs->trans("Qty").'</td>';
943 // Unit
944 print '<td></td>';
945 // Cost price
946 if ($permissiontoupdatecost && getDolGlobalString('MRP_SHOW_COST_FOR_CONSUMPTION')) {
947 print '<td class="right">'.$langs->trans("UnitCost").'</td>';
948 }
949 // Qty already consumed
950 print '<td class="right classfortooltip" title="'.$langs->trans("QtyAlreadyConsumed").'">';
951 print $langs->trans("QtyAlreadyConsumedShort");
952 print '</td>';
953 // Warehouse
954 print '<td>';
955 if ($collapse || in_array($action, array('consumeorproduce', 'consumeandproduceall'))) {
956 print $langs->trans("Warehouse");
957 if (isModEnabled('workstation')) {
958 print ' '.$langs->trans("or").' '.$langs->trans("Workstation");
959 }
960 // Select warehouse to force it everywhere
961 if (in_array($action, array('consumeorproduce', 'consumeandproduceall'))) {
962 $listwarehouses = $tmpwarehouse->list_array(1);
963 if (count($listwarehouses) > 1) {
964 print '<br>'.$form->selectarray('fk_default_warehouse', $listwarehouses, $fk_default_warehouse, $langs->trans("ForceTo"), 0, 0, '', 0, 0, 0, '', 'minwidth100 maxwidth200', 1);
965 } elseif (count($listwarehouses) == 1) {
966 print '<br>'.$form->selectarray('fk_default_warehouse', $listwarehouses, $fk_default_warehouse, 0, 0, 0, '', 0, 0, 0, '', 'minwidth100 maxwidth200', 1);
967 }
968 }
969 }
970 print '</td>';
971
972 if (isModEnabled('stock')) {
973 // Available
974 print '<td align="right">';
975 if ($collapse || in_array($action, array('consumeorproduce', 'consumeandproduceall'))) {
976 print $langs->trans("Stock");
977 }
978 print '</td>';
979 }
980 // Lot - serial
981 if (isModEnabled('productbatch')) {
982 print '<td>';
983 if ($collapse || in_array($action, array('consumeorproduce', 'consumeandproduceall'))) {
984 print $langs->trans("Batch");
985 }
986 print '</td>';
987 }
988
989 // Split
990 print '<td></td>';
991
992 // SplitAll
993 print '<td></td>';
994
995 // Edit Line
996 if ($object->status == Mo::STATUS_DRAFT) {
997 print '<td></td>';
998 }
999
1000 // Action
1001 if ($permissiontodelete) {
1002 print '<td></td>';
1003 }
1004
1005 print '</tr>';
1006
1007 if ($action == 'addconsumeline') {
1008 print '<!-- Add line to consume -->'."\n";
1009 print '<tr class="liste_titre">';
1010 // Product
1011 print '<td>';
1012 print $form->select_produits(0, 'productidtoadd', '', 0, 0, -1, 2, '', 1, array(), 0, '1', 0, 'maxwidth150');
1013 print '</td>';
1014 // Qty
1015 print '<td class="right"><input type="text" name="qtytoadd" value="1" class="width40 right"></td>';
1016 // Unit
1017 print '<td>';
1018 //if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
1019 //...
1020 //}
1021 print '</td>';
1022 // Cost price
1023 if ($permissiontoupdatecost && getDolGlobalString('MRP_SHOW_COST_FOR_CONSUMPTION')) {
1024 print '<td></td>';
1025 }
1026
1027 $colspan = 3;
1028 if (isModEnabled('stock')) {
1029 $colspan++;
1030 }
1031 if (isModEnabled('productbatch')) {
1032 $colspan++;
1033 }
1034 // Qty already consumed + Warehouse
1035 print '<td colspan="'.$colspan.'">';
1036 print '<input type="submit" class="button buttongen button-add" name="addconsumelinebutton" value="'.$langs->trans("Add").'">';
1037 print '<input type="submit" class="button buttongen button-cancel" name="canceladdconsumelinebutton" value="'.$langs->trans("Cancel").'">';
1038 print '</td>';
1039 // Split All
1040 print '<td></td>';
1041 // Edit Line
1042 if ($object->status == Mo::STATUS_DRAFT) {
1043 print '<td></td>';
1044 }
1045 // Action
1046 if ($permissiontodelete) {
1047 print '<td></td>';
1048 }
1049 print '</tr>';
1050
1051 // Extrafields Line
1052 if (is_object($objectline)) {
1053 $extrafields->fetch_name_optionals_label($object->table_element_line);
1054 $temps = $objectline->showOptionals($extrafields, 'edit', array(), '', '', '1', 'line');
1055 if (!empty($temps)) {
1056 print '<tr class="liste_titre"><td style="padding-top: 20px" colspan="9" id="extrafield_lines_area_edit" name="extrafield_lines_area_edit">';
1057 print $temps;
1058 print '</td></tr>';
1059 }
1060 }
1061 }
1062
1063 // Lines to consume
1064
1065 $bomcostupdated = 0; // We will recalculate the unitary cost to produce a product using the real "products to consume into MO"
1066
1067 if (!empty($object->lines)) {
1068 $nblinetoconsume = 0;
1069 foreach ($object->lines as $line) {
1070 if ($line->role == 'toconsume') {
1071 $nblinetoconsume++;
1072 }
1073 }
1074
1075 $nblinetoconsumecursor = 0;
1076 foreach ($object->lines as $line) {
1077 if ($line->role == 'toconsume') {
1078 $nblinetoconsumecursor++;
1079
1080 $tmpproduct = new Product($db);
1081 $tmpproduct->fetch($line->fk_product);
1082 $linecost = price2num($tmpproduct->pmp, 'MT');
1083
1084 if ($object->qty > 0) {
1085 // add free consume line cost to $bomcostupdated
1086 $costprice = price2num((!empty($tmpproduct->cost_price)) ? $tmpproduct->cost_price : $tmpproduct->pmp);
1087 if (empty($costprice)) {
1088 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
1089 $productFournisseur = new ProductFournisseur($db);
1090 if ($productFournisseur->find_min_price_product_fournisseur($line->fk_product, $line->qty) > 0) {
1091 $costprice = $productFournisseur->fourn_unitprice;
1092 } else {
1093 $costprice = 0;
1094 }
1095 }
1096
1097 $useunit = (($tmpproduct->type == Product::TYPE_PRODUCT && getDolGlobalInt('PRODUCT_USE_UNITS')) || (($tmpproduct->type == Product::TYPE_SERVICE) && ($line->fk_unit)));
1098
1099 if ($useunit && $line->fk_unit > 0) {
1100 $reg = [];
1101 $qtyhourservice = 0;
1102 if (preg_match('/^(\d+)([a-z]+)$/', $tmpproduct->duration, $reg)) {
1103 $qtyhourservice = convertDurationtoHour((float) $reg[1], (string) $reg[2]);
1104 }
1105 $qtyhourforline = 0;
1106 if ($line->fk_unit) {
1107 $unitforline = measuringUnitString($line->fk_unit, '', null, 1);
1108 $qtyhourforline = convertDurationtoHour($line->qty, $unitforline);
1109 }
1110
1111 if ($qtyhourservice && $qtyhourforline) {
1112 $linecost = price2num(($qtyhourforline / $qtyhourservice * $costprice) / $object->qty, 'MT'); // price for line for all quantities
1113 $bomcostupdated += price2num(($qtyhourforline / $qtyhourservice * $costprice) / $object->qty, 'MU'); // same but with full accuracy
1114 } else {
1115 $linecost = price2num(($line->qty * $costprice) / $object->qty, 'MT'); // price for line for all quantities
1116 $bomcostupdated += price2num(($line->qty * $costprice) / $object->qty, 'MU'); // same but with full accuracy
1117 }
1118 } else {
1119 $linecost = price2num(($line->qty * $costprice) / $object->qty, 'MT'); // price for line for all quantities
1120 $bomcostupdated += price2num(($line->qty * $costprice) / $object->qty, 'MU'); // same but with full accuracy
1121 }
1122 }
1123
1124 $bomcostupdated = price2num($bomcostupdated, 'MU');
1125 $arrayoflines = $object->fetchLinesLinked('consumed', $line->id);
1126 $alreadyconsumed = 0;
1127 foreach ($arrayoflines as $line2) {
1128 $alreadyconsumed += $line2['qty'];
1129 }
1130
1131 if ($action == 'editline' && $lineid == $line->id) {
1132 $linecost = price2num($tmpproduct->pmp, 'MT');
1133
1134 $arrayoflines = $object->fetchLinesLinked('consumed', $line->id);
1135 $alreadyconsumed = 0;
1136 if (is_array($arrayoflines) && !empty($arrayoflines)) {
1137 foreach ($arrayoflines as $line2) {
1138 $alreadyconsumed += $line2['qty'];
1139 }
1140 }
1141 $suffix = '_' . $line->id;
1142 print '<!-- Line to dispatch ' . $suffix . ' (line edited) -->' . "\n";
1143 // hidden fields for js function
1144 print '<input id="qty_ordered' . $suffix . '" type="hidden" value="' . $line->qty . '">';
1145 // Duration - Time spent
1146 print '<input id="qty_dispatched' . $suffix . '" type="hidden" value="' . $alreadyconsumed . '">';
1147 print '<tr>';
1148 print '<input name="lineid" type="hidden" value="' . $line->id . '">';
1149
1150 // Product
1151 print '<td>' . $tmpproduct->getNomUrl(1);
1152 print '<br><div class="opacitymedium small tdoverflowmax150" title="' . dol_escape_htmltag($tmpproduct->label) . '">' . $tmpproduct->label . '</span>';
1153 print '</td>';
1154
1155 // Qty
1156 print '<td class="right nowraponall">';
1157 print '<input class="width40 right" name="qty_lineProduce" value="'. $line->qty.'">';
1158 print '</td>';
1159
1160 // Unit
1161 print '<td class="right nowraponall">';
1162 $useunit = (($tmpproduct->type == Product::TYPE_PRODUCT && getDolGlobalInt('PRODUCT_USE_UNITS')) || (($tmpproduct->type == Product::TYPE_SERVICE) && ($line->fk_unit)));
1163 if ($useunit) {
1164 print measuringUnitString($line->fk_unit, '', null, 2);
1165 }
1166 print '</td>';
1167
1168 // Cost price
1169 if ($permissiontoupdatecost && getDolGlobalString('MRP_SHOW_COST_FOR_CONSUMPTION')) {
1170 print '<td></td>';
1171 }
1172
1173 // Qty consumed
1174 print '<td class="right">';
1175 print ' ' . price2num($alreadyconsumed, 'MS');
1176 print '</td>';
1177
1178 // Warehouse / Workstation
1179 print '<td>';
1180 if ($tmpproduct->type == Product::TYPE_PRODUCT) {
1181 print $formproduct->selectWarehouses($line->fk_warehouse, 'warehouse_lineProduce', 'warehouseopen', 1);
1182 } elseif (isModEnabled('workstation')) {
1183 print $formproduct->selectWorkstations($line->fk_default_workstation, 'workstation_lineProduce', 1);
1184 }
1185 print '</td>';
1186
1187 // Stock
1188 if (isModEnabled('stock')) {
1189 print '<td class="nowraponall right">';
1190 if ($tmpproduct->isStockManaged()) {
1191 if ($tmpproduct->stock_reel < ($line->qty - $alreadyconsumed)) {
1192 print img_warning($langs->trans('StockTooLow')).' ';
1193 }
1194 print '<span class="left">'. $tmpproduct->stock_reel .' </span>';
1195 }
1196 print '</td>';
1197 }
1198
1199 // Lot - serial
1200 if (isModEnabled('productbatch')) {
1201 print '<td></td>';
1202 }
1203 // Split + SplitAll + Edit line + Delete
1204 print '<td colspan="'.(3 + ($object->status == Mo::STATUS_DRAFT ? 1 : 0) + ($permissiontodelete ? 1 : 0)).'">';
1205 print '<input type="submit" class="button buttongen button-add small nominwidth" name="save" value="' . $langs->trans("Save") . '">';
1206 print '<input type="submit" class="button buttongen button-cancel small nominwidth" name="cancel" value="' . $langs->trans("Cancel") . '">';
1207 print '</td>';
1208
1209 print '</tr>';
1210
1211 // Extrafields Line
1212 if (!empty($extrafields)) {
1213 $line->fetch_optionals();
1214 $temps = $line->showOptionals($extrafields, 'edit', array(), '', '', '1', 'line');
1215 if (!empty($temps)) {
1216 $colspan = 10;
1217 print '<tr><td colspan="'.$colspan.'"><div style="padding-top: 20px" id="extrafield_lines_area_edit" name="extrafield_lines_area_edit">';
1218 print $temps;
1219 print '</div></td></tr>';
1220 }
1221 }
1222 } else {
1223 $suffix = '_' . $line->id;
1224 print '<!-- Line to dispatch ' . $suffix . ' -->' . "\n";
1225 // hidden fields for js function
1226 print '<input id="qty_ordered' . $suffix . '" type="hidden" value="' . $line->qty . '">';
1227 print '<input id="qty_dispatched' . $suffix . '" type="hidden" value="' . $alreadyconsumed . '">';
1228
1229 print '<tr data-line-id="' . $line->id . '">';
1230
1231 // Product
1232 print '<td>' . $tmpproduct->getNomUrl(1);
1233 print '<br><div class="opacitymedium small tdoverflowmax150" title="' . dol_escape_htmltag($tmpproduct->label) . '">' . $tmpproduct->label . '</div>';
1234 print '</td>';
1235
1236 // Qty
1237 print '<td class="right nowraponall">';
1238 $help = '';
1239 if ($line->qty_frozen) {
1240 $help = ($help ? '<br>' : '') . '<strong>' . $langs->trans("QuantityFrozen") . '</strong>: ' . yn(1) . ' (' . $langs->trans("QuantityConsumedInvariable") . ')';
1241 print $form->textwithpicto('', $help, -1, 'lock') . ' ';
1242 }
1243 if ($line->disable_stock_change) {
1244 $help = ($help ? '<br>' : '') . '<strong>' . $langs->trans("DisableStockChange") . '</strong>: ' . yn(1) . ' (' . (($tmpproduct->type == Product::TYPE_SERVICE && !getDolGlobalString('STOCK_SUPPORTS_SERVICES')) ? $langs->trans("NoStockChangeOnServices") : $langs->trans("DisableStockChangeHelp")) . ')';
1245 print $form->textwithpicto('', $help, -1, 'help') . ' ';
1246 }
1247 print price2num($line->qty, 'MS');
1248 print '</td>';
1249
1250 // Unit
1251 print '<td class="right nowraponall">';
1252 $useunit = (($tmpproduct->type == Product::TYPE_PRODUCT && getDolGlobalInt('PRODUCT_USE_UNITS')) || (($tmpproduct->type == Product::TYPE_SERVICE) && ($line->fk_unit)));
1253 if ($useunit) {
1254 print measuringUnitString($line->fk_unit, '', null, 2);
1255 }
1256 print '</td>';
1257
1258 // Cost price
1259 if ($permissiontoupdatecost && getDolGlobalString('MRP_SHOW_COST_FOR_CONSUMPTION')) {
1260 print '<td class="right nowraponall">';
1261 print price($linecost);
1262 print '</td>';
1263 }
1264
1265 // Already consumed
1266 print '<td class="right">';
1267 if ($alreadyconsumed) {
1268 print '<script>';
1269 print 'jQuery(document).ready(function() {
1270 jQuery("#expandtoproduce' . $line->id . '").click(function() {
1271 console.log("Expand mrp_production line ' . $line->id . '");
1272 jQuery(".expanddetail' . $line->id . '").toggle();';
1273 if ($nblinetoconsume == $nblinetoconsumecursor) { // If it is the last line
1274 print 'if (jQuery("#tablelines").hasClass("nobottom")) { jQuery("#tablelines").removeClass("nobottom"); } else { jQuery("#tablelines").addClass("nobottom"); }';
1275 }
1276 print '
1277 });
1278 });';
1279 print '</script>';
1280 if (empty($conf->use_javascript_ajax)) {
1281 print '<a href="' . $_SERVER["PHP_SELF"] . '?collapse=' . $collapse . ',' . $line->id . '">';
1282 }
1283 print img_picto($langs->trans("ShowDetails"), "chevron-down", 'id="expandtoproduce' . $line->id . '"');
1284 if (empty($conf->use_javascript_ajax)) {
1285 print '</a>';
1286 }
1287 } else {
1288 if ($nblinetoconsume == $nblinetoconsumecursor) { // If it is the last line
1289 print '<script>jQuery("#tablelines").removeClass("nobottom");</script>';
1290 }
1291 }
1292 print ' ' . price2num($alreadyconsumed, 'MS');
1293 print '</td>';
1294
1295 // Warehouse and/or workstation
1296 print '<td class="tdoverflowmax100">';
1297 if ($tmpproduct->isStockManaged()) {
1298 // When STOCK_CONSUMPTION_FROM_MANUFACTURING_WAREHOUSE is set, we always use the warehouse of the MO, the same than production.
1299 if (getDolGlobalString('STOCK_CONSUMPTION_FROM_MANUFACTURING_WAREHOUSE') && $tmpwarehouse->id > 0) {
1300 print img_picto('', $tmpwarehouse->picto) . " " . $tmpwarehouse->label;
1301 } else {
1302 if ($line->fk_warehouse > 0) {
1303 $warehouseline = new Entrepot($db);
1304 $warehouseline->fetch($line->fk_warehouse);
1305 print $warehouseline->getNomUrl(1);
1306 }
1307 }
1308 }
1309 if (isModEnabled('workstation') && $line->fk_default_workstation > 0) {
1310 $tmpworkstation = new Workstation($db);
1311 $tmpworkstation->fetch($line->fk_default_workstation);
1312 print $tmpworkstation->getNomUrl(1);
1313 }
1314 print '</td>';
1315
1316 // Stock
1317 if (isModEnabled('stock')) {
1318 print '<td class="nowraponall right">';
1319 if (!getDolGlobalString('STOCK_SUPPORTS_SERVICES') && $tmpproduct->type != Product::TYPE_SERVICE) {
1320 if (!$line->disable_stock_change && $tmpproduct->stock_reel < ($line->qty - $alreadyconsumed)) {
1321 print img_warning($langs->trans('StockTooLow')) . ' ';
1322 }
1323 if (!getDolGlobalString('STOCK_CONSUMPTION_FROM_MANUFACTURING_WAREHOUSE') || empty($tmpwarehouse->id)) {
1324 print price2num($tmpproduct->stock_reel, 'MS'); // Available
1325 } else {
1326 // Print only the stock in the selected warehouse
1327 $tmpproduct->load_stock();
1328 $wh_stock = $tmpproduct->stock_warehouse[$tmpwarehouse->id];
1329 if (!empty($wh_stock)) {
1330 print price2num($wh_stock->real, 'MS');
1331 } else {
1332 print "0";
1333 }
1334 }
1335 }
1336 print '</td>';
1337 }
1338
1339 // Lot
1340 if (isModEnabled('productbatch')) {
1341 print '<td></td>';
1342 }
1343
1344 // Split
1345 print '<td></td>';
1346
1347 // Split All
1348 print '<td></td>';
1349
1350 // Action Edit line
1351 if ($object->status == Mo::STATUS_DRAFT) {
1352 $href = $_SERVER["PHP_SELF"] . '?id=' . ((int) $object->id) . '&action=editline&token=' . newToken() . '&lineid=' . ((int) $line->id);
1353 print '<td class="center">';
1354 print '<a class="reposition editfielda" href="' . $href . '">';
1355 print img_picto($langs->trans('TooltipEditAndRevertStockMovement'), 'edit');
1356 print '</a>';
1357 print '</td>';
1358 }
1359
1360 // Action delete line, if no consumption has occurred for this product
1361 if ($permissiontodelete && empty($arrayoflines)) {
1362 $href = $_SERVER["PHP_SELF"] . '?id=' . ((int) $object->id) . '&action=deleteline&token=' . newToken() . '&lineid=' . ((int) $line->id);
1363 print '<td class="center">';
1364 print '<a class="reposition" href="' . $href . '">';
1365 print img_picto($langs->trans('TooltipDeleteAndRevertStockMovement'), 'delete');
1366 print '</a>';
1367 print '</td>';
1368 }
1369
1370 print '</tr>';
1371
1372 // Extrafields Line
1373 if (!empty($extrafields)) {
1374 $line->fetch_optionals();
1375 $temps = $line->showOptionals($extrafields, 'view', array(), '', '', '1', 'line');
1376 if (!empty($temps)) {
1377 $colspan = 10;
1378 print '<tr><td colspan="'.$colspan.'"><div id="extrafield_lines_area_'.$line->id.'" name="extrafield_lines_area_'.$line->id.'">';
1379 print $temps;
1380 print '</div></td></tr>';
1381 }
1382 }
1383 }
1384
1385 // Show detailed of already consumed with js code to collapse
1386 foreach ($arrayoflines as $line2) {
1387 print '<tr class="expanddetail'.$line->id.' hideobject opacitylow">';
1388
1389 // Date
1390 print '<td>';
1391 $tmpstockmovement->id = $line2['fk_stock_movement'];
1392 print '<a href="'.DOL_URL_ROOT.'/product/stock/movement_list.php?search_ref='.$tmpstockmovement->id.'">'.img_picto($langs->trans("StockMovement"), 'movement', 'class="paddingright"').'</a>';
1393 print dol_print_date($line2['date'], 'dayhour', 'tzuserrel');
1394 print '</td>';
1395
1396 // Qty
1397 print '<td></td>';
1398
1399 // Unit
1400 print '<td></td>';
1401
1402 // Cost price
1403 if ($permissiontoupdatecost && getDolGlobalString('MRP_SHOW_COST_FOR_CONSUMPTION')) {
1404 print '<td></td>';
1405 }
1406
1407 //Already consumed
1408 print '<td class="right">'.$line2['qty'].'</td>';
1409
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
1420 // Stock
1421 if (isModEnabled('stock')) {
1422 print '<td></td>';
1423 }
1424
1425 // Lot Batch
1426 if (isModEnabled('productbatch')) {
1427 print '<td>';
1428 if ($line2['batch'] != '') {
1429 $tmpbatch->fetch(0, $line2['fk_product'], $line2['batch']);
1430 print $tmpbatch->getNomUrl(1);
1431 }
1432 print '</td>';
1433 }
1434
1435 // Split
1436 print '<td></td>';
1437
1438 // Split All
1439 print '<td></td>';
1440
1441 // Action Edit line
1442 if ($object->status == Mo::STATUS_DRAFT) {
1443 $href = $_SERVER["PHP_SELF"] . '?id=' . ((int) $object->id) . '&action=editline&token=' . newToken() . '&lineid=' . ((int) $line2['rowid']);
1444 print '<td class="center">';
1445 print '<a class="reposition" href="' . $href . '">';
1446 print img_picto($langs->trans('TooltipEditAndRevertStockMovement'), 'edit');
1447 print '</a>';
1448 print '</td>';
1449 }
1450
1451 // Action delete line
1452 if ($permissiontodelete) {
1453 $href = $_SERVER["PHP_SELF"].'?id='.((int) $object->id).'&action=deleteline&token='.newToken().'&lineid='.((int) $line2['rowid']).'&fk_movement='.((int) $line2['fk_stock_movement']);
1454 print '<td class="center">';
1455 print '<a class="reposition" href="'.$href.'">';
1456 print img_picto($langs->trans('TooltipDeleteAndRevertStockMovement'), 'delete');
1457 print '</a>';
1458 print '</td>';
1459 }
1460
1461 print '</tr>';
1462 }
1463
1464 if (in_array($action, array('consumeorproduce', 'consumeandproduceall'))) {
1465 $i = 1;
1466 print '<!-- Enter line to consume -->'."\n";
1467 $maxQty = 1;
1468 print '<tr data-max-qty="'.$maxQty.'" name="batch_'.$line->id.'_'.$i.'">';
1469 // Ref
1470 print '<td><span class="opacitymedium">'.$langs->trans("ToConsume").'</span></td>';
1471 $preselected = (GETPOSTISSET('qty-'.$line->id.'-'.$i) ? GETPOST('qty-'.$line->id.'-'.$i) : max(0, $line->qty - $alreadyconsumed));
1472 if ($action == 'consumeorproduce' && !getDolGlobalString('MRP_AUTO_SET_REMAINING_QUANTITIES_TO_BE_CONSUMED') && !GETPOSTISSET('qty-'.$line->id.'-'.$i)) {
1473 $preselected = 0;
1474 }
1475
1476 $disable = '';
1477 if (getDolGlobalString('MRP_NEVER_CONSUME_MORE_THAN_EXPECTED') && ($line->qty - $alreadyconsumed) <= 0) {
1478 $disable = 'disabled';
1479 }
1480
1481 // input hidden with fk_product of line
1482 print '<input type="hidden" name="product-'.$line->id.'-'.$i.'" value="'.$line->fk_product.'">';
1483
1484 // Qty
1485 print '<td class="right">';
1486 if ((int) $tmpproduct->stockable_product > 0) {
1487 print '<input type="text" class="width50 right" id="qtytoconsume-' . $line->id . '-' . $i . '" name="qty-' . $line->id . '-' . $i . '" value="' . $preselected . '" ' . $disable . '>';
1488 } else {
1489 print '<input type="hidden" id="qtytoconsume-' . $line->id . '-' . $i . '" name="qty-' . $line->id . '-' . $i . '" value="0">';
1490 print '<span class="opacitymedium">' . $langs->trans("StockDisabled") . '</span>';
1491 }
1492 print '</td>';
1493
1494 // Unit
1495 print '<td></td>';
1496
1497 // Cost
1498 if ($permissiontoupdatecost && getDolGlobalString('MRP_SHOW_COST_FOR_CONSUMPTION')) {
1499 print '<td></td>';
1500 }
1501
1502 // Already consumed
1503 print '<td></td>';
1504
1505 // Warehouse
1506 print '<td>';
1507 if (($tmpproduct->type == Product::TYPE_PRODUCT || getDolGlobalString('STOCK_SUPPORTS_SERVICES'))
1508 && ((int) $tmpproduct->stockable_product > 0)) {
1509 if (empty($line->disable_stock_change)) {
1510 $preselected = (GETPOSTISSET('idwarehouse-'.$line->id.'-'.$i) ? GETPOST('idwarehouse-'.$line->id.'-'.$i) : ($tmpproduct->fk_default_warehouse > 0 ? $tmpproduct->fk_default_warehouse : 'ifone'));
1511 print $formproduct->selectWarehouses($preselected, 'idwarehouse-'.$line->id.'-'.$i, '', 1, 0, $line->fk_product, '', 1, 0, array(), 'maxwidth200 csswarehouse_'.$line->id.'_'.$i);
1512 } else {
1513 print '<span class="opacitymedium">'.$langs->trans("DisableStockChange").'</span>';
1514 }
1515 } else {
1516 if ((int) $tmpproduct->stockable_product > 0) {
1517 print '<span class="opacitymedium">' . $langs->trans("StockDisabled") . '</span>';
1518 } else {
1519 print '<span class="opacitymedium">' . $langs->trans("NoStockChangeOnServices") . '</span>';
1520 }
1521 }
1522 print '</td>';
1523
1524 // Stock
1525 if (isModEnabled('stock')) {
1526 print '<td></td>';
1527 }
1528
1529 // Lot / Batch
1530 if (isModEnabled('productbatch')) {
1531 print '<td class="nowraponall">';
1532 if ($tmpproduct->status_batch) {
1533 $preselected = (GETPOSTISSET('batch-'.$line->id.'-'.$i) ? GETPOST('batch-'.$line->id.'-'.$i) : '');
1534 print '<input type="text" class="width75" name="batch-'.$line->id.'-'.$i.'" value="'.$preselected.'" list="batch-'.$line->id.'-'.$i.'">';
1535 print $formproduct->selectLotDataList('batch-'.$line->id.'-'.$i, 0, $line->fk_product, 0, array());
1536 }
1537 print '</td>';
1538 }
1539
1540 // Split
1541 $type = 'batch';
1542 print '<td align="right" class="split">';
1543 print ' '.img_picto($langs->trans('AddStockLocationLine'), 'split', 'class="splitbutton" onClick="addDispatchLine('.((int) $line->id).', \''.dol_escape_js($type).'\', \'qtymissingconsume\')"');
1544 print '</td>';
1545
1546 // Split All
1547 print '<td align="right" class="splitall">';
1548 if (($action == 'consumeorproduce' || $action == 'consumeandproduceall') && $tmpproduct->status_batch == 2) {
1549 print img_picto($langs->trans('SplitAllQuantity'), 'split', 'class="splitbutton splitallbutton field-error-icon" data-max-qty="1" onClick="addDispatchLine('.$line->id.', \'batch\', \'allmissingconsume\')"');
1550 }
1551 print '</td>';
1552
1553 // Edit Line
1554 if ($object->status == Mo::STATUS_DRAFT) {
1555 print '<td></td>';
1556 }
1557
1558 // Action delete line
1559 if ($permissiontodelete) {
1560 print '<td></td>';
1561 }
1562
1563 print '</tr>';
1564 }
1565 }
1566 }
1567 }
1568
1569 print '</table>';
1570 print '</div>';
1571
1572 // default warehouse processing
1573 print '<script type="text/javascript">
1574 $(document).ready(function () {
1575 $("select[name=fk_default_warehouse]").change(function() {
1576 var fk_default_warehouse = $("option:selected", this).val();
1577 $("select[name^=idwarehouse-]").val(fk_default_warehouse).change();
1578 });
1579 });
1580 </script>';
1581
1582 if (in_array($action, array('consumeorproduce', 'consumeandproduceall')) &&
1583 getDolGlobalString('STOCK_CONSUMPTION_FROM_MANUFACTURING_WAREHOUSE')) {
1584 print '<script>$(document).ready(function () {
1585 $("#fk_default_warehouse").change();
1586 });</script>';
1587 }
1588
1589
1590 // Lines to produce
1591
1592 print '</div>';
1593 print '<div class="fichehalfright">';
1594 print '<div class="clearboth"></div>';
1595
1596 $nblinetoproduce = 0;
1597 $atLeastOneEatBy = false;
1598 $atLeastOneSellBy = false;
1599 foreach ($object->lines as $line) {
1600 if ($line->role == 'toproduce') {
1601 $tmpproduct = new Product($db);
1602 $tmpproduct->fetch($line->fk_product);
1603 if (
1604 $tmpproduct->sell_or_eat_by_mandatory == $tmpproduct::SELL_OR_EAT_BY_MANDATORY_ID_EAT_BY
1605 || $tmpproduct->sell_or_eat_by_mandatory == $tmpproduct::SELL_OR_EAT_BY_MANDATORY_ID_SELL_AND_EAT
1606 ) {
1607 $atLeastOneEatBy = true;
1608 }
1609 if (
1610 $tmpproduct->sell_or_eat_by_mandatory == $tmpproduct::SELL_OR_EAT_BY_MANDATORY_ID_SELL_BY
1611 || $tmpproduct->sell_or_eat_by_mandatory == $tmpproduct::SELL_OR_EAT_BY_MANDATORY_ID_SELL_AND_EAT
1612 ) {
1613 $atLeastOneSellBy = true;
1614 }
1615 $nblinetoproduce++;
1616 }
1617 }
1618
1619 $newcardbutton = '';
1620 $url = $_SERVER["PHP_SELF"].'?id='.$object->id.'&action=addproduceline&token='.newToken();
1621 $permissiontoaddaproductline = $object->status != $object::STATUS_PRODUCED && $object->status != $object::STATUS_CANCELED;
1622 $parameters = array('morecss' => 'reposition');
1623 if ($action != 'consumeorproduce' && $action != 'consumeandproduceall') {
1624 if ($nblinetoproduce == 0 || $object->mrptype == 1) {
1625 $newcardbutton = dolGetButtonTitle($langs->trans('AddNewProduceLines'), '', 'fa fa-plus-circle size15x', $url, '', (int) $permissiontoaddaproductline, $parameters);
1626 }
1627 }
1628
1629 print load_fiche_titre($langs->trans('Production'), $newcardbutton, '', 0, '', '');
1630
1631 print '<div class="div-table-responsive-no-min">';
1632 print '<table id="tablelinestoproduce" class="noborder noshadow nobottom centpercent">';
1633
1634 print '<tr class="liste_titre trheight5em">';
1635 // Product
1636 print '<td>'.$langs->trans("Product").'</td>';
1637 // Qty
1638 print '<td class="right">'.$langs->trans("Qty").'</td>';
1640 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
1641 print '<td class="right">'.$langs->trans("Unit").'</td>';
1642 }
1643 // Cost price
1644 if ($permissiontoupdatecost) {
1645 if (empty($bomcostupdated)) {
1646 print '<td class="right classfortooltip" title="'.$langs->trans("AmountUsedToUpdateWAP").'">';
1647 print $langs->trans("UnitCost");
1648 print '</td>';
1649 } else {
1650 print '<td class="right classfortooltip" title="'.$langs->trans("AmountUsedToUpdateWAP").'">';
1651 print $langs->trans("ManufacturingPrice");
1652 print '</td>';
1653 }
1654 }
1655 // Already produced
1656 print '<td class="right classfortooltip" title="'.$langs->trans("QtyAlreadyProduced").'">';
1657 print $langs->trans("QtyAlreadyProducedShort");
1658 print '</td>';
1659 // Warehouse
1660 print '<td>';
1661 if ($collapse || in_array($action, array('consumeorproduce', 'consumeandproduceall'))) {
1662 print $langs->trans("Warehouse");
1663 }
1664 print '</td>';
1665
1666 // Lot
1667 if (isModEnabled('productbatch')) {
1668 print '<td>';
1669 if ($collapse || in_array($action, array('consumeorproduce', 'consumeandproduceall'))) {
1670 print $langs->trans("Batch");
1671 }
1672 print '</td>';
1673
1674 // Split
1675 print '<td></td>';
1676
1677 // Split All
1678 print '<td></td>';
1679
1680 // sell by
1681 if ($atLeastOneEatBy) {
1682 print '<td>'.$langs->trans("EatByDate").'</td>';
1683 }
1684
1685 // eat by
1686 if ($atLeastOneSellBy) {
1687 print '<td>'.$langs->trans("SellByDate").'</td>';
1688 }
1689 }
1690
1691 // Action delete
1692 if ($permissiontodelete) {
1693 print '<td></td>';
1694 }
1695
1696 print '</tr>';
1697
1698 if ($action == 'addproduceline') {
1699 print '<!-- Add line to produce -->'."\n";
1700 print '<tr class="liste_titre">';
1701
1702 // Product
1703 print '<td>';
1704 print $form->select_produits(0, 'productidtoadd', '', 0, 0, -1, 2, '', 1, array(), 0, '1', 0, 'maxwidth300');
1705 print '</td>';
1706 // Qty
1707 print '<td class="right"><input type="text" name="qtytoadd" value="1" class="width50 right"></td>';
1708 //Unit
1709 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
1710 print '<td></td>';
1711 }
1712 // Cost price
1713 if ($permissiontoupdatecost) {
1714 print '<td></td>';
1715 }
1716 // Action (cost price + already produced)
1717 print '<td colspan="2">';
1718 print '<input type="submit" class="button buttongen button-add" name="addproducelinebutton" value="'.$langs->trans("Add").'">';
1719 print '<input type="submit" class="button buttongen button-cancel" name="canceladdproducelinebutton" value="'.$langs->trans("Cancel").'">';
1720 print '</td>';
1721 // Lot - serial
1722 if (isModEnabled('productbatch')) {
1723 print '<td></td>';
1724
1725 // Split
1726 print '<td></td>';
1727
1728 // Split All
1729 print '<td></td>';
1730 }
1731 // Action delete
1732 if ($permissiontodelete) {
1733 print '<td></td>';
1734 }
1735 print '</tr>';
1736 }
1737
1738 if (!empty($object->lines)) {
1739 $nblinetoproduce = 0;
1740 foreach ($object->lines as $line) {
1741 if ($line->role == 'toproduce') {
1742 $nblinetoproduce++;
1743 }
1744 }
1745
1746 $nblinetoproducecursor = 0;
1747 foreach ($object->lines as $line) {
1748 if ($line->role == 'toproduce') {
1749 $i = 1;
1750
1751 $nblinetoproducecursor++;
1752
1753 $tmpproduct = new Product($db);
1754 $tmpproduct->fetch($line->fk_product);
1755
1756 $arrayoflines = $object->fetchLinesLinked('produced', $line->id);
1757 $alreadyproduced = 0;
1758 foreach ($arrayoflines as $line2) {
1759 $alreadyproduced += $line2['qty'];
1760 }
1761
1762 $suffix = '_'.$line->id;
1763 print '<!-- Line to dispatch '.$suffix.' (toproduce) -->'."\n";
1764 // hidden fields for js function
1765 print '<input id="qty_ordered'.$suffix.'" type="hidden" value="'.$line->qty.'">';
1766 print '<input id="qty_dispatched'.$suffix.'" type="hidden" value="'.$alreadyproduced.'">';
1767
1768 print '<tr>';
1769 // Product
1770 print '<td>'.$tmpproduct->getNomUrl(1);
1771 print '<br><span class="opacitymedium small">'.$tmpproduct->label.'</span>';
1772 print '</td>';
1773 // Qty
1774 print '<td class="right">'.$line->qty.'</td>';
1775 // Unit
1776 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
1777 print '<td class="right">'.measuringUnitString($line->fk_unit, '', null, 1).'</td>';
1778 }
1779 // Cost price
1780 if ($permissiontoupdatecost) {
1781 // Defined $manufacturingcost
1782 $manufacturingcost = 0;
1783 $manufacturingcostsrc = '';
1784 if ($object->mrptype == 0) { // If MO is a "Manufacture" type (and not "Disassemble")
1785 $manufacturingcost = $bomcostupdated;
1786 $manufacturingcostsrc = $langs->trans("CalculatedFromProductsToConsume");
1787 if (empty($manufacturingcost)) {
1788 $manufacturingcost = $bomcost;
1789 $manufacturingcostsrc = $langs->trans("ValueFromBom");
1790 }
1791 if (empty($manufacturingcost)) {
1792 $manufacturingcost = price2num($tmpproduct->cost_price, 'MU');
1793 $manufacturingcostsrc = $langs->trans("CostPrice");
1794 }
1795 if (empty($manufacturingcost)) {
1796 $manufacturingcost = price2num($tmpproduct->pmp, 'MU');
1797 $manufacturingcostsrc = $langs->trans("PMPValue");
1798 }
1799 }
1800
1801 print '<td class="right nowraponall" title="'.dol_escape_htmltag($manufacturingcostsrc).'">';
1802 if ($manufacturingcost) {
1803 print price($manufacturingcost);
1804 }
1805 print '</td>';
1806 }
1807 // Already produced
1808 print '<td class="right nowraponall">';
1809 if ($alreadyproduced) {
1810 print '<script>';
1811 print 'jQuery(document).ready(function() {
1812 jQuery("#expandtoproduce'.$line->id.'").click(function() {
1813 console.log("Expand mrp_production line '.$line->id.'");
1814 jQuery(".expanddetailtoproduce'.$line->id.'").toggle();';
1815 if ($nblinetoproduce == $nblinetoproducecursor) {
1816 print 'if (jQuery("#tablelinestoproduce").hasClass("nobottom")) { jQuery("#tablelinestoproduce").removeClass("nobottom"); } else { jQuery("#tablelinestoproduce").addClass("nobottom"); }';
1817 }
1818 print '
1819 });
1820 });';
1821 print '</script>';
1822 if (empty($conf->use_javascript_ajax)) {
1823 print '<a href="'.$_SERVER["PHP_SELF"].'?collapse='.$collapse.','.$line->id.'">';
1824 }
1825 print img_picto($langs->trans("ShowDetails"), "chevron-down", 'id="expandtoproduce'.$line->id.'"');
1826 if (empty($conf->use_javascript_ajax)) {
1827 print '</a>';
1828 }
1829 }
1830 print ' '.$alreadyproduced;
1831 print '</td>';
1832 // Warehouse
1833 print '<td>';
1834 print '</td>';
1835 // Lot
1836 if (isModEnabled('productbatch')) {
1837 print '<td></td>';
1838
1839 // Split
1840 print '<td></td>';
1841
1842 // Split All
1843 print '<td></td>';
1844 }
1845 // Delete
1846 if ($permissiontodelete) {
1847 if ($line->origin_type == 'free') {
1848 $href = $_SERVER["PHP_SELF"];
1849 $href .= '?id='.$object->id;
1850 $href .= '&action=deleteline';
1851 $href .= '&token='.newToken();
1852 $href .= '&lineid='.$line->id;
1853 print '<td class="center">';
1854 print '<a class="reposition" href="'.$href.'">';
1855 print img_picto($langs->trans('TooltipDeleteAndRevertStockMovement'), "delete");
1856 print '</a>';
1857 print '</td>';
1858 } else {
1859 print '<td></td>';
1860 }
1861 }
1862 print '</tr>';
1863
1864 // Show detailed of already consumed with js code to collapse
1865 foreach ($arrayoflines as $line2) {
1866 print '<tr class="expanddetailtoproduce'.$line->id.' hideobject opacitylow">';
1867 // Product
1868 print '<td>';
1869 $tmpstockmovement->id = $line2['fk_stock_movement'];
1870 print '<a href="'.DOL_URL_ROOT.'/product/stock/movement_list.php?search_ref='.$tmpstockmovement->id.'">'.img_picto($langs->trans("StockMovement"), 'movement', 'class="paddingright"').'</a>';
1871 print dol_print_date($line2['date'], 'dayhour', 'tzuserrel');
1872 print '</td>';
1873 // Qty
1874 print '<td></td>';
1875 // Unit
1876 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
1877 print '<td></td>';
1878 }
1879 // Cost price
1880 if ($permissiontoupdatecost) {
1881 print '<td></td>';
1882 }
1883 // Already produced
1884 print '<td class="right">'.$line2['qty'].'</td>';
1885 // Warehouse
1886 print '<td class="tdoverflowmax150">';
1887 if ($line2['fk_warehouse'] > 0) {
1888 $result = $tmpwarehouse->fetch($line2['fk_warehouse']);
1889 if ($result > 0) {
1890 print $tmpwarehouse->getNomUrl(1);
1891 }
1892 }
1893 print '</td>';
1894 // Lot
1895 if (isModEnabled('productbatch')) {
1896 print '<td>';
1897 if ($line2['batch'] != '') {
1898 $tmpbatch->fetch(0, $line2['fk_product'], $line2['batch']);
1899 print $tmpbatch->getNomUrl(1);
1900 }
1901 print '</td>';
1902
1903 // Split
1904 print '<td></td>';
1905
1906 // Split All
1907 print '<td></td>';
1908 }
1909 // Action delete
1910 if ($permissiontodelete) {
1911 print '<td></td>';
1912 }
1913 print '</tr>';
1914 }
1915
1916 if (in_array($action, array('consumeorproduce', 'consumeandproduceall'))) {
1917 print '<!-- Enter line to produce -->'."\n";
1918 $maxQty = 1;
1919 print '<tr data-max-qty="'.$maxQty.'" name="batch_'.$line->id.'_'.$i.'">';
1920 // Product
1921 print '<td><span class="opacitymedium">'.$langs->trans("ToProduce").'</span></td>';
1922 $preselected = (GETPOSTISSET('qtytoproduce-'.$line->id.'-'.$i) ? GETPOST('qtytoproduce-'.$line->id.'-'.$i) : max(0, $line->qty - $alreadyproduced));
1923 if ($action == 'consumeorproduce' && !GETPOSTISSET('qtytoproduce-'.$line->id.'-'.$i)) {
1924 $preselected = 0;
1925 }
1926 // Qty
1927 print '<td class="right"><input type="text" class="width50 right" id="qtytoproduce-'.$line->id.'-'.$i.'" name="qtytoproduce-'.$line->id.'-'.$i.'" value="'.$preselected.'"></td>';
1928 //Unit
1929 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
1930 print '<td class="right"></td>';
1931 }
1932 // Cost
1933 if ($permissiontoupdatecost) {
1934 // Defined $manufacturingcost
1935 $manufacturingcost = 0;
1936 $manufacturingcostsrc = '';
1937 if ($object->mrptype == 0) { // If MO is a "Manufacture" type (and not "Disassemble")
1938 $manufacturingcost = $bomcostupdated;
1939 $manufacturingcostsrc = $langs->trans("CalculatedFromProductsToConsume");
1940 if (empty($manufacturingcost)) {
1941 $manufacturingcost = $bomcost;
1942 $manufacturingcostsrc = $langs->trans("ValueFromBom");
1943 }
1944 if (empty($manufacturingcost)) {
1945 $manufacturingcost = price2num($tmpproduct->cost_price, 'MU');
1946 $manufacturingcostsrc = $langs->trans("CostPrice");
1947 }
1948 if (empty($manufacturingcost)) {
1949 $manufacturingcost = price2num($tmpproduct->pmp, 'MU');
1950 $manufacturingcostsrc = $langs->trans("PMPValue");
1951 }
1952 }
1953
1954 if ($tmpproduct->type == Product::TYPE_PRODUCT || getDolGlobalString('STOCK_SUPPORTS_SERVICES')) {
1955 $preselected = (GETPOSTISSET('pricetoproduce-'.$line->id.'-'.$i) ? GETPOST('pricetoproduce-'.$line->id.'-'.$i) : ($manufacturingcost ? price($manufacturingcost) : ''));
1956 print '<td class="right"><input type="text" class="width75 right" name="pricetoproduce-'.$line->id.'-'.$i.'" value="'.$preselected.'"></td>';
1957 } else {
1958 print '<td><input type="hidden" class="width50 right" name="pricetoproduce-'.$line->id.'-'.$i.'" value="'.($manufacturingcost ? $manufacturingcost : '').'"></td>';
1959 }
1960 }
1961 // Already produced
1962 print '<td></td>';
1963 // Warehouse
1964 print '<td>';
1965 if ($tmpproduct->type == Product::TYPE_PRODUCT || getDolGlobalString('STOCK_SUPPORTS_SERVICES')) {
1966 $preselected = (GETPOSTISSET('idwarehousetoproduce-'.$line->id.'-'.$i) ? GETPOST('idwarehousetoproduce-'.$line->id.'-'.$i) : ($object->fk_warehouse > 0 ? $object->fk_warehouse : 'ifone'));
1967 print $formproduct->selectWarehouses($preselected, 'idwarehousetoproduce-'.$line->id.'-'.$i, '', 1, 0, $line->fk_product, '', 1, 0, array(), 'maxwidth200 csswarehouse_'.$line->id.'_'.$i);
1968 } else {
1969 print '<span class="opacitymedium">'.$langs->trans("NoStockChangeOnServices").'</span>';
1970 }
1971 print '</td>';
1972 // Lot
1973 if (isModEnabled('productbatch')) {
1974 print '<td>';
1975 if ($tmpproduct->status_batch) {
1976 $preselected = (GETPOSTISSET('batchtoproduce-'.$line->id.'-'.$i) ? GETPOST('batchtoproduce-'.$line->id.'-'.$i) : '');
1977 print '<input type="text" class="width75" name="batchtoproduce-'.$line->id.'-'.$i.'" value="'.$preselected.'">';
1978 }
1979 print '</td>';
1980 // Batch number in same column than the stock movement picto
1981 if ($tmpproduct->status_batch) {
1982 $type = 'batch';
1983 print '<td align="right" class="split">';
1984 print img_picto($langs->trans('AddStockLocationLine'), 'split', 'class="splitbutton" onClick="addDispatchLine('.$line->id.', \''.$type.'\', \'qtymissing\')"');
1985 print '</td>';
1986
1987 print '<td align="right" class="splitall">';
1988 if (($action == 'consumeorproduce' || $action == 'consumeandproduceall') && $tmpproduct->status_batch == 2) {
1989 print img_picto($langs->trans('SplitAllQuantity'), 'split', 'class="splitbutton splitallbutton field-error-icon" onClick="addDispatchLine('.$line->id.', \'batch\', \'alltoproduce\')"');
1990 } //
1991 print '</td>';
1992 } else {
1993 print '<td></td>';
1994
1995 print '<td></td>';
1996 }
1997 }
1998
1999 // sell by mandatory
2000 if ($tmpproduct->sell_or_eat_by_mandatory == $tmpproduct::SELL_OR_EAT_BY_MANDATORY_ID_SELL_BY || $tmpproduct->sell_or_eat_by_mandatory == $tmpproduct::SELL_OR_EAT_BY_MANDATORY_ID_SELL_AND_EAT) {
2001 print '<td align="right">';
2002 $preselectedSellBy = (GETPOSTISSET('sellby-' . $line->id . '-' . $i) ? GETPOSTDATE('sellby-' . $line->id . '-' . $i) : '');
2003 print $form->selectDate($preselectedSellBy, 'sellby-' . $line->id . '-' . $i, 0, 0, 1, '', 1, 0);
2004 print '</td>';
2005 } else {
2006 if ($atLeastOneSellBy) {
2007 print '<td></td>';
2008 }
2009 }
2010
2011 // eat by mandatory
2012 if ($tmpproduct->sell_or_eat_by_mandatory == $tmpproduct::SELL_OR_EAT_BY_MANDATORY_ID_EAT_BY || $tmpproduct->sell_or_eat_by_mandatory == $tmpproduct::SELL_OR_EAT_BY_MANDATORY_ID_SELL_AND_EAT) {
2013 print '<td align="right">';
2014 $preselectedEatBy = (GETPOSTISSET('eatby-' . $line->id . '-' . $i) ? GETPOSTDATE('eatby-' . $line->id . '-' . $i) : '');
2015 print $form->selectDate($preselectedEatBy, 'eatby-' . $line->id . '-' . $i, 0, 0, 1, '', 1, 0);
2016 print '</td>';
2017 } else {
2018 if ($atLeastOneEatBy) {
2019 print '<td></td>';
2020 }
2021 }
2022
2023 // Action delete
2024 print '<td></td>';
2025
2026 print '</tr>';
2027 }
2028 }
2029 }
2030 }
2031
2032 print '</table>';
2033 print '</div>';
2034
2035 print '</div>';
2036 print '</div>';
2037 }
2038
2039 if (in_array($action, array('consumeorproduce', 'consumeandproduceall', 'addconsumeline'))) {
2040 print "</form>\n";
2041 } ?>
2042
2043 <script type="text/javascript" language="javascript">
2044
2045 $(document).ready(function() {
2046 //Consumption : When a warehouse is selected, only the lot/serial numbers that are available in it are offered
2047 updateselectbatchbywarehouse();
2048 //Consumption : When a lot/serial number is selected and it is only available in one warehouse, the warehouse is automatically selected
2049 updateselectwarehousebybatch();
2050 });
2051
2052 function updateselectbatchbywarehouse() {
2053 $(document).on('change', "select[name*='idwarehouse']", function () {
2054 console.log("We change warehouse so we update the list of possible batch number");
2055
2056 var selectwarehouse = $(this);
2057
2058 var selectbatch_name = selectwarehouse.attr('name').replace('idwarehouse', 'batch');
2059 var selectbatch = $("datalist[id*='" + selectbatch_name + "']");
2060 var selectedbatch = selectbatch.val();
2061
2062 var product_element_name = selectwarehouse.attr('name').replace('idwarehouse', 'product');
2063
2064 $.ajax({
2065 type: "POST",
2066 url: "<?php echo DOL_URL_ROOT . '/mrp/ajax/interface.php'; ?>",
2067 data: {
2068 action: "updateselectbatchbywarehouse",
2069 permissiontoproduce: <?php echo $permissiontoproduce ?>,
2070 warehouse_id: $(this).val(),
2071 token: '<?php echo currentToken(); ?>',
2072 product_id: $("input[name='" + product_element_name + "']").val()
2073 }
2074 }).done(function (data) {
2075
2076 selectbatch.empty();
2077
2078 if (typeof data == "object") {
2079 console.log("data is already type object, no need to parse it");
2080 } else {
2081 console.log("data is type "+(typeof data));
2082 data = JSON.parse(data);
2083 }
2084
2085 selectbatch.append($('<option>', {
2086 value: '',
2087 }));
2088
2089 $.each(data, function (key, value) {
2090
2091 if(selectwarehouse.val() == -1) {
2092 var label = key + " (<?php echo $langs->trans('TotalStock') ?> : " + value + ")";
2093 } else {
2094 var label = key + " (<?php echo $langs->trans('Stock') ?> : " + value + ")";
2095 }
2096
2097 if(key === selectedbatch) {
2098 var option ='<option value="'+key+'" selected>'+ label +'</option>';
2099 } else {
2100 var option ='<option value="'+key+'">'+ label +'</option>';
2101 }
2102
2103 selectbatch.append(option);
2104 });
2105 });
2106 });
2107 }
2108
2109 function updateselectwarehousebybatch() {
2110 $(document).on('change', 'input[name*=batch]', function(){
2111 console.log("We change batch so we update the list of possible warehouses");
2112
2113 var selectbatch = $(this);
2114
2115 var selectwarehouse_name = selectbatch.attr('name').replace('batch', 'idwarehouse');
2116 var selectwarehouse = $("select[name*='" + selectwarehouse_name + "']");
2117 var selectedwarehouse = selectwarehouse.val();
2118
2119 if(selectedwarehouse != -1){
2120 return;
2121 }
2122
2123 var product_element_name = selectbatch.attr('name').replace('batch', 'product');
2124
2125 $.ajax({
2126 type: "POST",
2127 url: "<?php echo DOL_URL_ROOT . '/mrp/ajax/interface.php'; ?>",
2128 data: {
2129 action: "updateselectwarehousebybatch",
2130 permissiontoproduce: <?php echo $permissiontoproduce ?>,
2131 batch: $(this).val(),
2132 token: '<?php echo currentToken(); ?>',
2133 product_id: $("input[name='" + product_element_name + "']").val()
2134 }
2135 }).done(function (data) {
2136
2137 if (typeof data == "object") {
2138 console.log("data is already type object, no need to parse it");
2139 } else {
2140 console.log("data is type "+(typeof data));
2141 data = JSON.parse(data);
2142 }
2143
2144 if(data != 0){
2145 selectwarehouse.val(data).change();
2146 }
2147 });
2148 });
2149 }
2150
2151 </script>
2152
2153 <?php
2154}
2155
2156// End of page
2157llxFooter();
2158$db->close();
$id
Support class for third parties, contacts, members, users or resources.
Definition account.php:47
if(! $sortfield) if(! $sortorder) $object
Definition account.php:100
llxFooter($comment='', $zone='private', $disabledoutputofmessages=0)
Empty footer.
Definition wrapper.php:91
if(!defined('NOREQUIRESOC')) if(!defined( 'NOREQUIRETRAN')) if(!defined('NOTOKENRENEWAL')) if(!defined( 'NOREQUIREMENU')) if(!defined('NOREQUIREHTML')) if(!defined( 'NOREQUIREAJAX')) llxHeader($head='', $title='', $help_url='', $target='', $disablejs=0, $disablehead=0, $arrayofjs='', $arrayofcss='', $morequerystring='', $morecssonbody='', $replacemainareaby='', $disablenofollow=0, $disablenoindex=0)
Empty header.
Definition wrapper.php:73
$object ref
Definition info.php:90
Class for BOM.
Definition bom.class.php:42
Class of dictionary type of thirdparty (used by imports)
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:35
Class MoLine.
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.
Class for Workstation.
convertDurationtoHour($duration_value, $duration_unit)
Convert duration to hour.
Definition date.lib.php:341
dol_now($mode='gmt')
Return date for now.
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0, $attop=0)
Set event messages in dol_events session object.
GETPOSTDATE($prefix, $hourTime='', $gm='auto', $saverestore='')
Helper function that combines values of a dolibarr DatePicker (such as Form\selectDate) for year,...
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2, $allowothertags=array())
Show picto whatever it's its name (generic function)
img_warning($titlealt='default', $moreatt='', $morecss='pictowarning')
Show warning logo.
GETPOSTINT($paramname, $method=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
dolBuildUrl($url, $params=[], $addtoken=false)
Return path of url.
dol_get_fiche_head($links=array(), $active='', $title='', $notab=0, $picto='', $pictoisfullpath=0, $morehtmlright='', $morecss='', $limittoshow=0, $moretabssuffix='', $dragdropfile=0, $morecssdiv='')
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.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
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'.
GETPOSTFLOAT($paramname, $rounding='', $option=2)
Return the value of a $_GET or $_POST supervariable, converted into float.
yn($yesno, $format=1, $color=0)
Return yes or no in current language.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false, $decorate=0)
Output date in a string format according to outputlangs (or langs if not defined).
load_fiche_titre($title, $morehtmlright='', $picto='generic', $pictoisfullpath=0, $id='', $morecssontable='', $morehtmlcenter='', $morecssonpicto='widthpictotitle')
Load a title with picto.
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
isModEnabled($module)
Is Dolibarr module enabled.
img_edit($titlealt='default', $float=0, $other='')
Show logo edit/modify fiche.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags='', $escapeonlyhtmltags=0, $cleanalsojavascript=0)
Returns text escaped for inclusion in HTML alt or title or value tags, or into values of HTML input f...
moPrepareHead($object)
Prepare array of tabs for Mo.
measuringUnitString($unitid, $measuring_style='', $unitscale=null, $use_short_label=0, $outputlangs=null)
Return translation label of a unit key.
if(preg_match('/(crypted|dolcrypt):/i', $dolibarr_main_db_pass)||!empty($dolibarr_main_db_encrypted_pass)) $conf db type
'integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter[:Sortfield]]]',...
Definition repair.php:125
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.