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