dolibarr 23.0.4
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.'">'.$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 // Add the workstation cost for the manufacturing order cost when a workstation is set
1112 if (isModEnabled('workstation') && $line->fk_default_workstation > 0) {
1113 $workstation = new Workstation($db);
1114 $workstation->fetch($line->fk_default_workstation);
1115 $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
1116 $bomcostupdated += price2num($qtyhourforline * ($workstation->thm_operator_estimated + $workstation->thm_machine_estimated) / $object->qty, 'MU');
1117 } elseif ($qtyhourservice && $qtyhourforline) {
1118 $linecost = price2num(($qtyhourforline / $qtyhourservice * $costprice) / $object->qty, 'MT'); // price for line for all quantities
1119 $bomcostupdated += price2num(($qtyhourforline / $qtyhourservice * $costprice) / $object->qty, 'MU'); // same but with full accuracy
1120 } else {
1121 $linecost = price2num(($line->qty * $costprice) / $object->qty, 'MT'); // price for line for all quantities
1122 $bomcostupdated += price2num(($line->qty * $costprice) / $object->qty, 'MU'); // same but with full accuracy
1123 }
1124 } else {
1125 $linecost = price2num(($line->qty * $costprice) / $object->qty, 'MT'); // price for line for all quantities
1126 $bomcostupdated += price2num(($line->qty * $costprice) / $object->qty, 'MU'); // same but with full accuracy
1127 }
1128 }
1129
1130 $bomcostupdated = price2num($bomcostupdated, 'MU');
1131 $arrayoflines = $object->fetchLinesLinked('consumed', $line->id);
1132 $alreadyconsumed = 0;
1133 foreach ($arrayoflines as $line2) {
1134 $alreadyconsumed += $line2['qty'];
1135 }
1136
1137 if ($action == 'editline' && $lineid == $line->id) {
1138 $linecost = price2num($tmpproduct->pmp, 'MT');
1139
1140 $arrayoflines = $object->fetchLinesLinked('consumed', $line->id);
1141 $alreadyconsumed = 0;
1142 if (is_array($arrayoflines) && !empty($arrayoflines)) {
1143 foreach ($arrayoflines as $line2) {
1144 $alreadyconsumed += $line2['qty'];
1145 }
1146 }
1147 $suffix = '_' . $line->id;
1148 print '<!-- Line to dispatch ' . $suffix . ' (line edited) -->' . "\n";
1149 // hidden fields for js function
1150 print '<input id="qty_ordered' . $suffix . '" type="hidden" value="' . $line->qty . '">';
1151 // Duration - Time spent
1152 print '<input id="qty_dispatched' . $suffix . '" type="hidden" value="' . $alreadyconsumed . '">';
1153 print '<tr>';
1154 print '<input name="lineid" type="hidden" value="' . $line->id . '">';
1155
1156 // Product
1157 print '<td>' . $tmpproduct->getNomUrl(1);
1158 print '<br><div class="opacitymedium small tdoverflowmax150" title="' . dol_escape_htmltag($tmpproduct->label) . '">' . $tmpproduct->label . '</span>';
1159 print '</td>';
1160
1161 // Qty
1162 print '<td class="right nowraponall">';
1163 print '<input class="width40 right" name="qty_lineProduce" value="'. $line->qty.'">';
1164 print '</td>';
1165
1166 // Unit
1167 print '<td class="right nowraponall">';
1168 $useunit = (($tmpproduct->type == Product::TYPE_PRODUCT && getDolGlobalInt('PRODUCT_USE_UNITS')) || (($tmpproduct->type == Product::TYPE_SERVICE) && ($line->fk_unit)));
1169 if ($useunit) {
1170 print measuringUnitString($line->fk_unit, '', null, 2);
1171 }
1172 print '</td>';
1173
1174 // Cost price
1175 if ($permissiontoupdatecost && getDolGlobalString('MRP_SHOW_COST_FOR_CONSUMPTION')) {
1176 print '<td></td>';
1177 }
1178
1179 // Qty consumed
1180 print '<td class="right">';
1181 print ' ' . price2num($alreadyconsumed, 'MS');
1182 print '</td>';
1183
1184 // Warehouse / Workstation
1185 print '<td>';
1186 if ($tmpproduct->type == Product::TYPE_PRODUCT) {
1187 print $formproduct->selectWarehouses($line->fk_warehouse, 'warehouse_lineProduce', 'warehouseopen', 1);
1188 } elseif (isModEnabled('workstation')) {
1189 print $formproduct->selectWorkstations($line->fk_default_workstation, 'workstation_lineProduce', 1);
1190 }
1191 print '</td>';
1192
1193 // Stock
1194 if (isModEnabled('stock')) {
1195 print '<td class="nowraponall right">';
1196 if ($tmpproduct->isStockManaged()) {
1197 if ($tmpproduct->stock_reel < ($line->qty - $alreadyconsumed)) {
1198 print img_warning($langs->trans('StockTooLow')).' ';
1199 }
1200 print '<span class="left">'. $tmpproduct->stock_reel .' </span>';
1201 }
1202 print '</td>';
1203 }
1204
1205 // Lot - serial
1206 if (isModEnabled('productbatch')) {
1207 print '<td></td>';
1208 }
1209 // Split + SplitAll + Edit line + Delete
1210 print '<td colspan="'.(3 + ($object->status == Mo::STATUS_DRAFT ? 1 : 0) + ($permissiontodelete ? 1 : 0)).'">';
1211 print '<input type="submit" class="button buttongen button-add small nominwidth" name="save" value="' . $langs->trans("Save") . '">';
1212 print '<input type="submit" class="button buttongen button-cancel small nominwidth" name="cancel" value="' . $langs->trans("Cancel") . '">';
1213 print '</td>';
1214
1215 print '</tr>';
1216
1217 // Extrafields Line
1218 if (!empty($extrafields)) {
1219 $line->fetch_optionals();
1220 $temps = $line->showOptionals($extrafields, 'edit', array(), '', '', '1', 'line');
1221 if (!empty($temps)) {
1222 $colspan = 10;
1223 print '<tr><td colspan="'.$colspan.'"><div style="padding-top: 20px" id="extrafield_lines_area_edit" name="extrafield_lines_area_edit">';
1224 print $temps;
1225 print '</div></td></tr>';
1226 }
1227 }
1228 } else {
1229 $suffix = '_' . $line->id;
1230 print '<!-- Line to dispatch ' . $suffix . ' -->' . "\n";
1231 // hidden fields for js function
1232 print '<input id="qty_ordered' . $suffix . '" type="hidden" value="' . $line->qty . '">';
1233 print '<input id="qty_dispatched' . $suffix . '" type="hidden" value="' . $alreadyconsumed . '">';
1234
1235 print '<tr data-line-id="' . $line->id . '">';
1236
1237 // Product
1238 print '<td>' . $tmpproduct->getNomUrl(1);
1239 print '<br><div class="opacitymedium small tdoverflowmax150" title="' . dol_escape_htmltag($tmpproduct->label) . '">' . $tmpproduct->label . '</div>';
1240 print '</td>';
1241
1242 // Qty
1243 print '<td class="right nowraponall">';
1244 $help = '';
1245 if ($line->qty_frozen) {
1246 $help = ($help ? '<br>' : '') . '<strong>' . $langs->trans("QuantityFrozen") . '</strong>: ' . yn(1) . ' (' . $langs->trans("QuantityConsumedInvariable") . ')';
1247 print $form->textwithpicto('', $help, -1, 'lock') . ' ';
1248 }
1249 if ($line->disable_stock_change) {
1250 $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")) . ')';
1251 print $form->textwithpicto('', $help, -1, 'help') . ' ';
1252 }
1253 print price2num($line->qty, 'MS');
1254 print '</td>';
1255
1256 // Unit
1257 print '<td class="right nowraponall">';
1258 $useunit = (($tmpproduct->type == Product::TYPE_PRODUCT && getDolGlobalInt('PRODUCT_USE_UNITS')) || (($tmpproduct->type == Product::TYPE_SERVICE) && ($line->fk_unit)));
1259 if ($useunit) {
1260 print measuringUnitString($line->fk_unit, '', null, 2);
1261 }
1262 print '</td>';
1263
1264 // Cost price
1265 if ($permissiontoupdatecost && getDolGlobalString('MRP_SHOW_COST_FOR_CONSUMPTION')) {
1266 print '<td class="right nowraponall">';
1267 print price($linecost);
1268 print '</td>';
1269 }
1270
1271 // Already consumed
1272 print '<td class="right">';
1273 if ($alreadyconsumed) {
1274 print '<script>';
1275 print 'jQuery(document).ready(function() {
1276 jQuery("#expandtoproduce' . $line->id . '").click(function() {
1277 console.log("Expand mrp_production line ' . $line->id . '");
1278 jQuery(".expanddetail' . $line->id . '").toggle();';
1279 if ($nblinetoconsume == $nblinetoconsumecursor) { // If it is the last line
1280 print 'if (jQuery("#tablelines").hasClass("nobottom")) { jQuery("#tablelines").removeClass("nobottom"); } else { jQuery("#tablelines").addClass("nobottom"); }';
1281 }
1282 print '
1283 });
1284 });';
1285 print '</script>';
1286 if (empty($conf->use_javascript_ajax)) {
1287 print '<a href="' . $_SERVER["PHP_SELF"] . '?collapse=' . $collapse . ',' . $line->id . '">';
1288 }
1289 print img_picto($langs->trans("ShowDetails"), "chevron-down", 'id="expandtoproduce' . $line->id . '"');
1290 if (empty($conf->use_javascript_ajax)) {
1291 print '</a>';
1292 }
1293 } else {
1294 if ($nblinetoconsume == $nblinetoconsumecursor) { // If it is the last line
1295 print '<script>jQuery("#tablelines").removeClass("nobottom");</script>';
1296 }
1297 }
1298 print ' ' . price2num($alreadyconsumed, 'MS');
1299 print '</td>';
1300
1301 // Warehouse and/or workstation
1302 print '<td class="tdoverflowmax100">';
1303 if ($tmpproduct->isStockManaged()) {
1304 // When STOCK_CONSUMPTION_FROM_MANUFACTURING_WAREHOUSE is set, we always use the warehouse of the MO, the same than production.
1305 if (getDolGlobalString('STOCK_CONSUMPTION_FROM_MANUFACTURING_WAREHOUSE') && $tmpwarehouse->id > 0) {
1306 print img_picto('', $tmpwarehouse->picto) . " " . $tmpwarehouse->label;
1307 } else {
1308 if ($line->fk_warehouse > 0) {
1309 $warehouseline = new Entrepot($db);
1310 $warehouseline->fetch($line->fk_warehouse);
1311 print $warehouseline->getNomUrl(1);
1312 }
1313 }
1314 }
1315 if (isModEnabled('workstation') && $line->fk_default_workstation > 0) {
1316 $tmpworkstation = new Workstation($db);
1317 $tmpworkstation->fetch($line->fk_default_workstation);
1318 print $tmpworkstation->getNomUrl(1);
1319 }
1320 print '</td>';
1321
1322 // Stock
1323 if (isModEnabled('stock')) {
1324 print '<td class="nowraponall right">';
1325 if (!getDolGlobalString('STOCK_SUPPORTS_SERVICES') && $tmpproduct->type != Product::TYPE_SERVICE) {
1326 if (!$line->disable_stock_change && $tmpproduct->stock_reel < ($line->qty - $alreadyconsumed)) {
1327 print img_warning($langs->trans('StockTooLow')) . ' ';
1328 }
1329 if (!getDolGlobalString('STOCK_CONSUMPTION_FROM_MANUFACTURING_WAREHOUSE') || empty($tmpwarehouse->id)) {
1330 print price2num($tmpproduct->stock_reel, 'MS'); // Available
1331 } else {
1332 // Print only the stock in the selected warehouse
1333 $tmpproduct->load_stock();
1334 $wh_stock = $tmpproduct->stock_warehouse[$tmpwarehouse->id];
1335 if (!empty($wh_stock)) {
1336 print price2num($wh_stock->real, 'MS');
1337 } else {
1338 print "0";
1339 }
1340 }
1341 }
1342 print '</td>';
1343 }
1344
1345 // Lot
1346 if (isModEnabled('productbatch')) {
1347 print '<td></td>';
1348 }
1349
1350 // Split
1351 print '<td></td>';
1352
1353 // Split All
1354 print '<td></td>';
1355
1356 // Action Edit line
1357 if ($object->status == Mo::STATUS_DRAFT) {
1358 $href = $_SERVER["PHP_SELF"] . '?id=' . ((int) $object->id) . '&action=editline&token=' . newToken() . '&lineid=' . ((int) $line->id);
1359 print '<td class="center">';
1360 print '<a class="reposition editfielda" href="' . $href . '">';
1361 print img_picto($langs->trans('TooltipEditAndRevertStockMovement'), 'edit');
1362 print '</a>';
1363 print '</td>';
1364 }
1365
1366 // Action delete line, if no consumption has occurred for this product
1367 if ($permissiontodelete && empty($arrayoflines)) {
1368 $href = $_SERVER["PHP_SELF"] . '?id=' . ((int) $object->id) . '&action=deleteline&token=' . newToken() . '&lineid=' . ((int) $line->id);
1369 print '<td class="center">';
1370 print '<a class="reposition" href="' . $href . '">';
1371 print img_picto($langs->trans('TooltipDeleteAndRevertStockMovement'), 'delete');
1372 print '</a>';
1373 print '</td>';
1374 }
1375
1376 print '</tr>';
1377
1378 // Extrafields Line
1379 if (!empty($extrafields)) {
1380 $line->fetch_optionals();
1381 $temps = $line->showOptionals($extrafields, 'view', array(), '', '', '1', 'line');
1382 if (!empty($temps)) {
1383 $colspan = 10;
1384 print '<tr><td colspan="'.$colspan.'"><div id="extrafield_lines_area_'.$line->id.'" name="extrafield_lines_area_'.$line->id.'">';
1385 print $temps;
1386 print '</div></td></tr>';
1387 }
1388 }
1389 }
1390
1391 // Show detailed of already consumed with js code to collapse
1392 foreach ($arrayoflines as $line2) {
1393 print '<tr class="expanddetail'.$line->id.' hideobject opacitylow">';
1394
1395 // Date
1396 print '<td>';
1397 $tmpstockmovement->id = $line2['fk_stock_movement'];
1398 print '<a href="'.DOL_URL_ROOT.'/product/stock/movement_list.php?search_ref='.$tmpstockmovement->id.'">'.img_picto($langs->trans("StockMovement"), 'movement', 'class="paddingright"').'</a>';
1399 print dol_print_date($line2['date'], 'dayhour', 'tzuserrel');
1400 print '</td>';
1401
1402 // Qty
1403 print '<td></td>';
1404
1405 // Unit
1406 print '<td></td>';
1407
1408 // Cost price
1409 if ($permissiontoupdatecost && getDolGlobalString('MRP_SHOW_COST_FOR_CONSUMPTION')) {
1410 print '<td></td>';
1411 }
1412
1413 //Already consumed
1414 print '<td class="right">'.$line2['qty'].'</td>';
1415
1416 // Warehouse
1417 print '<td class="tdoverflowmax150">';
1418 if ($line2['fk_warehouse'] > 0) {
1419 $result = $tmpwarehouse->fetch($line2['fk_warehouse']);
1420 if ($result > 0) {
1421 print $tmpwarehouse->getNomUrl(1);
1422 }
1423 }
1424 print '</td>';
1425
1426 // Stock
1427 if (isModEnabled('stock')) {
1428 print '<td></td>';
1429 }
1430
1431 // Lot Batch
1432 if (isModEnabled('productbatch')) {
1433 print '<td>';
1434 if ($line2['batch'] != '') {
1435 $tmpbatch->fetch(0, $line2['fk_product'], $line2['batch']);
1436 print $tmpbatch->getNomUrl(1);
1437 }
1438 print '</td>';
1439 }
1440
1441 // Split
1442 print '<td></td>';
1443
1444 // Split All
1445 print '<td></td>';
1446
1447 // Action Edit line
1448 if ($object->status == Mo::STATUS_DRAFT) {
1449 $href = $_SERVER["PHP_SELF"] . '?id=' . ((int) $object->id) . '&action=editline&token=' . newToken() . '&lineid=' . ((int) $line2['rowid']);
1450 print '<td class="center">';
1451 print '<a class="reposition" href="' . $href . '">';
1452 print img_picto($langs->trans('TooltipEditAndRevertStockMovement'), 'edit');
1453 print '</a>';
1454 print '</td>';
1455 }
1456
1457 // Action delete line
1458 if ($permissiontodelete) {
1459 $href = $_SERVER["PHP_SELF"].'?id='.((int) $object->id).'&action=deleteline&token='.newToken().'&lineid='.((int) $line2['rowid']).'&fk_movement='.((int) $line2['fk_stock_movement']);
1460 print '<td class="center">';
1461 print '<a class="reposition" href="'.$href.'">';
1462 print img_picto($langs->trans('TooltipDeleteAndRevertStockMovement'), 'delete');
1463 print '</a>';
1464 print '</td>';
1465 }
1466
1467 print '</tr>';
1468 }
1469
1470 if (in_array($action, array('consumeorproduce', 'consumeandproduceall'))) {
1471 $i = 1;
1472 print '<!-- Enter line to consume -->'."\n";
1473 $maxQty = 1;
1474 print '<tr data-max-qty="'.$maxQty.'" name="batch_'.$line->id.'_'.$i.'">';
1475 // Ref
1476 print '<td><span class="opacitymedium">'.$langs->trans("ToConsume").'</span></td>';
1477 $preselected = (GETPOSTISSET('qty-'.$line->id.'-'.$i) ? GETPOST('qty-'.$line->id.'-'.$i) : max(0, $line->qty - $alreadyconsumed));
1478 if ($action == 'consumeorproduce' && !getDolGlobalString('MRP_AUTO_SET_REMAINING_QUANTITIES_TO_BE_CONSUMED') && !GETPOSTISSET('qty-'.$line->id.'-'.$i)) {
1479 $preselected = 0;
1480 }
1481
1482 $disable = '';
1483 if (getDolGlobalString('MRP_NEVER_CONSUME_MORE_THAN_EXPECTED') && ($line->qty - $alreadyconsumed) <= 0) {
1484 $disable = 'disabled';
1485 }
1486
1487 // input hidden with fk_product of line
1488 print '<input type="hidden" name="product-'.$line->id.'-'.$i.'" value="'.$line->fk_product.'">';
1489
1490 // Qty
1491 print '<td class="right">';
1492 if ((int) $tmpproduct->stockable_product > 0) {
1493 print '<input type="text" class="width50 right" id="qtytoconsume-' . $line->id . '-' . $i . '" name="qty-' . $line->id . '-' . $i . '" value="' . $preselected . '" ' . $disable . '>';
1494 } else {
1495 print '<input type="hidden" id="qtytoconsume-' . $line->id . '-' . $i . '" name="qty-' . $line->id . '-' . $i . '" value="0">';
1496 print '<span class="opacitymedium">' . $langs->trans("StockDisabled") . '</span>';
1497 }
1498 print '</td>';
1499
1500 // Unit
1501 print '<td></td>';
1502
1503 // Cost
1504 if ($permissiontoupdatecost && getDolGlobalString('MRP_SHOW_COST_FOR_CONSUMPTION')) {
1505 print '<td></td>';
1506 }
1507
1508 // Already consumed
1509 print '<td></td>';
1510
1511 // Warehouse
1512 print '<td>';
1513 if (($tmpproduct->type == Product::TYPE_PRODUCT || getDolGlobalString('STOCK_SUPPORTS_SERVICES'))
1514 && ((int) $tmpproduct->stockable_product > 0)) {
1515 if (empty($line->disable_stock_change)) {
1516 $preselected = (GETPOSTISSET('idwarehouse-'.$line->id.'-'.$i) ? GETPOST('idwarehouse-'.$line->id.'-'.$i) : ($tmpproduct->fk_default_warehouse > 0 ? $tmpproduct->fk_default_warehouse : 'ifone'));
1517 print $formproduct->selectWarehouses($preselected, 'idwarehouse-'.$line->id.'-'.$i, '', 1, 0, $line->fk_product, '', 1, 0, array(), 'maxwidth200 csswarehouse_'.$line->id.'_'.$i);
1518 } else {
1519 print '<span class="opacitymedium">'.$langs->trans("DisableStockChange").'</span>';
1520 }
1521 } else {
1522 if ((int) $tmpproduct->stockable_product > 0) {
1523 print '<span class="opacitymedium">' . $langs->trans("StockDisabled") . '</span>';
1524 } else {
1525 print '<span class="opacitymedium">' . $langs->trans("NoStockChangeOnServices") . '</span>';
1526 }
1527 }
1528 print '</td>';
1529
1530 // Stock
1531 if (isModEnabled('stock')) {
1532 print '<td></td>';
1533 }
1534
1535 // Lot / Batch
1536 if (isModEnabled('productbatch')) {
1537 print '<td class="nowraponall">';
1538 if ($tmpproduct->status_batch) {
1539 $preselected = (GETPOSTISSET('batch-'.$line->id.'-'.$i) ? GETPOST('batch-'.$line->id.'-'.$i) : '');
1540 print '<input type="text" class="width75" name="batch-'.$line->id.'-'.$i.'" value="'.$preselected.'" list="batch-'.$line->id.'-'.$i.'">';
1541 print $formproduct->selectLotDataList('batch-'.$line->id.'-'.$i, 0, $line->fk_product, 0, array());
1542 }
1543 print '</td>';
1544 }
1545
1546 // Split
1547 $type = 'batch';
1548 print '<td align="right" class="split">';
1549 print ' '.img_picto($langs->trans('AddStockLocationLine'), 'split', 'class="splitbutton" onClick="addDispatchLine('.((int) $line->id).', \''.dol_escape_js($type).'\', \'qtymissingconsume\', 1)"');
1550 print '</td>';
1551
1552 // Split All
1553 print '<td align="right" class="splitall">';
1554 if (($action == 'consumeorproduce' || $action == 'consumeandproduceall') && $tmpproduct->status_batch == 2) {
1555 print img_picto($langs->trans('SplitAllQuantity'), 'split', 'class="splitbutton splitallbutton field-error-icon" data-max-qty="1" onClick="addDispatchLine('.$line->id.', \'batch\', \'allmissingconsume\', 1)"');
1556 }
1557 print '</td>';
1558
1559 // Edit Line
1560 if ($object->status == Mo::STATUS_DRAFT) {
1561 print '<td></td>';
1562 }
1563
1564 // Action delete line
1565 if ($permissiontodelete) {
1566 print '<td></td>';
1567 }
1568
1569 print '</tr>';
1570 }
1571 }
1572 }
1573 }
1574
1575 print '</table>';
1576 print '</div>';
1577
1578 // default warehouse processing
1579 print '<script type="text/javascript">
1580 $(document).ready(function () {
1581 $("select[name=fk_default_warehouse]").change(function() {
1582 var fk_default_warehouse = $("option:selected", this).val();
1583 $("select[name^=idwarehouse-]").val(fk_default_warehouse).change();
1584 });
1585 });
1586 </script>';
1587
1588 if (in_array($action, array('consumeorproduce', 'consumeandproduceall')) &&
1589 getDolGlobalString('STOCK_CONSUMPTION_FROM_MANUFACTURING_WAREHOUSE')) {
1590 print '<script>$(document).ready(function () {
1591 $("#fk_default_warehouse").change();
1592 });</script>';
1593 }
1594
1595
1596 // Lines to produce
1597
1598 print '</div>';
1599 print '<div class="fichehalfright">';
1600 print '<div class="clearboth"></div>';
1601
1602 $nblinetoproduce = 0;
1603 $atLeastOneEatBy = false;
1604 $atLeastOneSellBy = false;
1605 foreach ($object->lines as $line) {
1606 if ($line->role == 'toproduce') {
1607 $tmpproduct = new Product($db);
1608 $tmpproduct->fetch($line->fk_product);
1609 if (
1610 $tmpproduct->sell_or_eat_by_mandatory == $tmpproduct::SELL_OR_EAT_BY_MANDATORY_ID_EAT_BY
1611 || $tmpproduct->sell_or_eat_by_mandatory == $tmpproduct::SELL_OR_EAT_BY_MANDATORY_ID_SELL_AND_EAT
1612 ) {
1613 $atLeastOneEatBy = true;
1614 }
1615 if (
1616 $tmpproduct->sell_or_eat_by_mandatory == $tmpproduct::SELL_OR_EAT_BY_MANDATORY_ID_SELL_BY
1617 || $tmpproduct->sell_or_eat_by_mandatory == $tmpproduct::SELL_OR_EAT_BY_MANDATORY_ID_SELL_AND_EAT
1618 ) {
1619 $atLeastOneSellBy = true;
1620 }
1621 $nblinetoproduce++;
1622 }
1623 }
1624
1625 $newcardbutton = '';
1626 $url = $_SERVER["PHP_SELF"].'?id='.$object->id.'&action=addproduceline&token='.newToken();
1627 $permissiontoaddaproductline = $object->status != $object::STATUS_PRODUCED && $object->status != $object::STATUS_CANCELED;
1628 $parameters = array('morecss' => 'reposition');
1629 if ($action != 'consumeorproduce' && $action != 'consumeandproduceall') {
1630 if ($nblinetoproduce == 0 || $object->mrptype == 1) {
1631 $newcardbutton = dolGetButtonTitle($langs->trans('AddNewProduceLines'), '', 'fa fa-plus-circle size15x', $url, '', (int) $permissiontoaddaproductline, $parameters);
1632 }
1633 }
1634
1635 print load_fiche_titre($langs->trans('Production'), $newcardbutton, '', 0, '', '');
1636
1637 print '<div class="div-table-responsive-no-min">';
1638 print '<table id="tablelinestoproduce" class="noborder noshadow nobottom centpercent">';
1639
1640 print '<tr class="liste_titre trheight5em">';
1641 // Product
1642 print '<td>'.$langs->trans("Product").'</td>';
1643 // Qty
1644 print '<td class="right">'.$langs->trans("Qty").'</td>';
1646 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
1647 print '<td class="right">'.$langs->trans("Unit").'</td>';
1648 }
1649 // Cost price
1650 if ($permissiontoupdatecost) {
1651 if (empty($bomcostupdated)) {
1652 print '<td class="right classfortooltip" title="'.$langs->trans("AmountUsedToUpdateWAP").'">';
1653 print $langs->trans("UnitCost");
1654 print '</td>';
1655 } else {
1656 print '<td class="right classfortooltip" title="'.$langs->trans("AmountUsedToUpdateWAP").'">';
1657 print $langs->trans("ManufacturingPrice");
1658 print '</td>';
1659 }
1660 }
1661 // Already produced
1662 print '<td class="right classfortooltip" title="'.$langs->trans("QtyAlreadyProduced").'">';
1663 print $langs->trans("QtyAlreadyProducedShort");
1664 print '</td>';
1665 // Warehouse
1666 print '<td>';
1667 if ($collapse || in_array($action, array('consumeorproduce', 'consumeandproduceall'))) {
1668 print $langs->trans("Warehouse");
1669 }
1670 print '</td>';
1671
1672 // Lot
1673 if (isModEnabled('productbatch')) {
1674 print '<td>';
1675 if ($collapse || in_array($action, array('consumeorproduce', 'consumeandproduceall'))) {
1676 print $langs->trans("Batch");
1677 }
1678 print '</td>';
1679
1680 // Split
1681 print '<td></td>';
1682
1683 // Split All
1684 print '<td></td>';
1685
1686 // sell by
1687 if ($atLeastOneEatBy) {
1688 print '<td>'.$langs->trans("EatByDate").'</td>';
1689 }
1690
1691 // eat by
1692 if ($atLeastOneSellBy) {
1693 print '<td>'.$langs->trans("SellByDate").'</td>';
1694 }
1695 }
1696
1697 // Action delete
1698 if ($permissiontodelete) {
1699 print '<td></td>';
1700 }
1701
1702 print '</tr>';
1703
1704 if ($action == 'addproduceline') {
1705 print '<!-- Add line to produce -->'."\n";
1706 print '<tr class="liste_titre">';
1707
1708 // Product
1709 print '<td>';
1710 print $form->select_produits(0, 'productidtoadd', '', 0, 0, -1, 2, '', 1, array(), 0, '1', 0, 'maxwidth300');
1711 print '</td>';
1712 // Qty
1713 print '<td class="right"><input type="text" name="qtytoadd" value="1" class="width50 right"></td>';
1714 //Unit
1715 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
1716 print '<td></td>';
1717 }
1718 // Cost price
1719 if ($permissiontoupdatecost) {
1720 print '<td></td>';
1721 }
1722 // Action (cost price + already produced)
1723 print '<td colspan="2">';
1724 print '<input type="submit" class="button buttongen button-add" name="addproducelinebutton" value="'.$langs->trans("Add").'">';
1725 print '<input type="submit" class="button buttongen button-cancel" name="canceladdproducelinebutton" value="'.$langs->trans("Cancel").'">';
1726 print '</td>';
1727 // Lot - serial
1728 if (isModEnabled('productbatch')) {
1729 print '<td></td>';
1730
1731 // Split
1732 print '<td></td>';
1733
1734 // Split All
1735 print '<td></td>';
1736 }
1737 // Action delete
1738 if ($permissiontodelete) {
1739 print '<td></td>';
1740 }
1741 print '</tr>';
1742 }
1743
1744 if (!empty($object->lines)) {
1745 $nblinetoproduce = 0;
1746 foreach ($object->lines as $line) {
1747 if ($line->role == 'toproduce') {
1748 $nblinetoproduce++;
1749 }
1750 }
1751
1752 $nblinetoproducecursor = 0;
1753 foreach ($object->lines as $line) {
1754 if ($line->role == 'toproduce') {
1755 $i = 1;
1756
1757 $nblinetoproducecursor++;
1758
1759 $tmpproduct = new Product($db);
1760 $tmpproduct->fetch($line->fk_product);
1761
1762 $arrayoflines = $object->fetchLinesLinked('produced', $line->id);
1763 $alreadyproduced = 0;
1764 foreach ($arrayoflines as $line2) {
1765 $alreadyproduced += $line2['qty'];
1766 }
1767
1768 $suffix = '_'.$line->id;
1769 print '<!-- Line to dispatch '.$suffix.' (toproduce) -->'."\n";
1770 // hidden fields for js function
1771 print '<input id="qty_ordered'.$suffix.'" type="hidden" value="'.$line->qty.'">';
1772 print '<input id="qty_dispatched'.$suffix.'" type="hidden" value="'.$alreadyproduced.'">';
1773
1774 print '<tr>';
1775 // Product
1776 print '<td>'.$tmpproduct->getNomUrl(1);
1777 print '<br><span class="opacitymedium small">'.$tmpproduct->label.'</span>';
1778 print '</td>';
1779 // Qty
1780 print '<td class="right">'.$line->qty.'</td>';
1781 // Unit
1782 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
1783 print '<td class="right">'.measuringUnitString($line->fk_unit, '', null, 1).'</td>';
1784 }
1785 // Cost price
1786 if ($permissiontoupdatecost) {
1787 // Defined $manufacturingcost
1788 $manufacturingcost = 0;
1789 $manufacturingcostsrc = '';
1790 if ($object->mrptype == 0) { // If MO is a "Manufacture" type (and not "Disassemble")
1791 $manufacturingcost = $bomcostupdated;
1792 $manufacturingcostsrc = $langs->trans("CalculatedFromProductsToConsume");
1793 if (empty($manufacturingcost)) {
1794 $manufacturingcost = $bomcost;
1795 $manufacturingcostsrc = $langs->trans("ValueFromBom");
1796 }
1797 if (empty($manufacturingcost)) {
1798 $manufacturingcost = price2num($tmpproduct->cost_price, 'MU');
1799 $manufacturingcostsrc = $langs->trans("CostPrice");
1800 }
1801 if (empty($manufacturingcost)) {
1802 $manufacturingcost = price2num($tmpproduct->pmp, 'MU');
1803 $manufacturingcostsrc = $langs->trans("PMPValue");
1804 }
1805 }
1806
1807 print '<td class="right nowraponall" title="'.dol_escape_htmltag($manufacturingcostsrc).'">';
1808 if ($manufacturingcost) {
1809 print price($manufacturingcost);
1810 }
1811 print '</td>';
1812 }
1813 // Already produced
1814 print '<td class="right nowraponall">';
1815 if ($alreadyproduced) {
1816 print '<script>';
1817 print 'jQuery(document).ready(function() {
1818 jQuery("#expandtoproduce'.$line->id.'").click(function() {
1819 console.log("Expand mrp_production line '.$line->id.'");
1820 jQuery(".expanddetailtoproduce'.$line->id.'").toggle();';
1821 if ($nblinetoproduce == $nblinetoproducecursor) {
1822 print 'if (jQuery("#tablelinestoproduce").hasClass("nobottom")) { jQuery("#tablelinestoproduce").removeClass("nobottom"); } else { jQuery("#tablelinestoproduce").addClass("nobottom"); }';
1823 }
1824 print '
1825 });
1826 });';
1827 print '</script>';
1828 if (empty($conf->use_javascript_ajax)) {
1829 print '<a href="'.$_SERVER["PHP_SELF"].'?collapse='.$collapse.','.$line->id.'">';
1830 }
1831 print img_picto($langs->trans("ShowDetails"), "chevron-down", 'id="expandtoproduce'.$line->id.'"');
1832 if (empty($conf->use_javascript_ajax)) {
1833 print '</a>';
1834 }
1835 }
1836 print ' '.$alreadyproduced;
1837 print '</td>';
1838 // Warehouse
1839 print '<td>';
1840 print '</td>';
1841 // Lot
1842 if (isModEnabled('productbatch')) {
1843 print '<td></td>';
1844
1845 // Split
1846 print '<td></td>';
1847
1848 // Split All
1849 print '<td></td>';
1850 }
1851 // Delete
1852 if ($permissiontodelete) {
1853 if ($line->origin_type == 'free') {
1854 $href = $_SERVER["PHP_SELF"];
1855 $href .= '?id='.$object->id;
1856 $href .= '&action=deleteline';
1857 $href .= '&token='.newToken();
1858 $href .= '&lineid='.$line->id;
1859 print '<td class="center">';
1860 print '<a class="reposition" href="'.$href.'">';
1861 print img_picto($langs->trans('TooltipDeleteAndRevertStockMovement'), "delete");
1862 print '</a>';
1863 print '</td>';
1864 } else {
1865 print '<td></td>';
1866 }
1867 }
1868 print '</tr>';
1869
1870 // Show detailed of already consumed with js code to collapse
1871 foreach ($arrayoflines as $line2) {
1872 print '<tr class="expanddetailtoproduce'.$line->id.' hideobject opacitylow">';
1873 // Product
1874 print '<td>';
1875 $tmpstockmovement->id = $line2['fk_stock_movement'];
1876 print '<a href="'.DOL_URL_ROOT.'/product/stock/movement_list.php?search_ref='.$tmpstockmovement->id.'">'.img_picto($langs->trans("StockMovement"), 'movement', 'class="paddingright"').'</a>';
1877 print dol_print_date($line2['date'], 'dayhour', 'tzuserrel');
1878 print '</td>';
1879 // Qty
1880 print '<td></td>';
1881 // Unit
1882 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
1883 print '<td></td>';
1884 }
1885 // Cost price
1886 if ($permissiontoupdatecost) {
1887 print '<td></td>';
1888 }
1889 // Already produced
1890 print '<td class="right">'.$line2['qty'].'</td>';
1891 // Warehouse
1892 print '<td class="tdoverflowmax150">';
1893 if ($line2['fk_warehouse'] > 0) {
1894 $result = $tmpwarehouse->fetch($line2['fk_warehouse']);
1895 if ($result > 0) {
1896 print $tmpwarehouse->getNomUrl(1);
1897 }
1898 }
1899 print '</td>';
1900 // Lot
1901 if (isModEnabled('productbatch')) {
1902 print '<td>';
1903 if ($line2['batch'] != '') {
1904 $tmpbatch->fetch(0, $line2['fk_product'], $line2['batch']);
1905 print $tmpbatch->getNomUrl(1);
1906 }
1907 print '</td>';
1908
1909 // Split
1910 print '<td></td>';
1911
1912 // Split All
1913 print '<td></td>';
1914 }
1915 // Action delete
1916 if ($permissiontodelete) {
1917 print '<td></td>';
1918 }
1919 print '</tr>';
1920 }
1921
1922 if (in_array($action, array('consumeorproduce', 'consumeandproduceall'))) {
1923 print '<!-- Enter line to produce -->'."\n";
1924 $maxQty = 1;
1925 print '<tr data-max-qty="'.$maxQty.'" name="batch_'.$line->id.'_'.$i.'">';
1926 // Product
1927 print '<td><span class="opacitymedium">'.$langs->trans("ToProduce").'</span></td>';
1928 $preselected = (GETPOSTISSET('qtytoproduce-'.$line->id.'-'.$i) ? GETPOST('qtytoproduce-'.$line->id.'-'.$i) : max(0, $line->qty - $alreadyproduced));
1929 if ($action == 'consumeorproduce' && !GETPOSTISSET('qtytoproduce-'.$line->id.'-'.$i)) {
1930 $preselected = 0;
1931 }
1932 // Qty
1933 print '<td class="right"><input type="text" class="width50 right" id="qtytoproduce-'.$line->id.'-'.$i.'" name="qtytoproduce-'.$line->id.'-'.$i.'" value="'.$preselected.'"></td>';
1934 //Unit
1935 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
1936 print '<td class="right"></td>';
1937 }
1938 // Cost
1939 if ($permissiontoupdatecost) {
1940 // Defined $manufacturingcost
1941 $manufacturingcost = 0;
1942 $manufacturingcostsrc = '';
1943 if ($object->mrptype == 0) { // If MO is a "Manufacture" type (and not "Disassemble")
1944 $manufacturingcost = $bomcostupdated;
1945 $manufacturingcostsrc = $langs->trans("CalculatedFromProductsToConsume");
1946 if (empty($manufacturingcost)) {
1947 $manufacturingcost = $bomcost;
1948 $manufacturingcostsrc = $langs->trans("ValueFromBom");
1949 }
1950 if (empty($manufacturingcost)) {
1951 $manufacturingcost = price2num($tmpproduct->cost_price, 'MU');
1952 $manufacturingcostsrc = $langs->trans("CostPrice");
1953 }
1954 if (empty($manufacturingcost)) {
1955 $manufacturingcost = price2num($tmpproduct->pmp, 'MU');
1956 $manufacturingcostsrc = $langs->trans("PMPValue");
1957 }
1958 }
1959
1960 if ($tmpproduct->type == Product::TYPE_PRODUCT || getDolGlobalString('STOCK_SUPPORTS_SERVICES')) {
1961 $preselected = (GETPOSTISSET('pricetoproduce-'.$line->id.'-'.$i) ? GETPOST('pricetoproduce-'.$line->id.'-'.$i) : ($manufacturingcost ? price($manufacturingcost) : ''));
1962 print '<td class="right"><input type="text" class="width75 right" name="pricetoproduce-'.$line->id.'-'.$i.'" value="'.$preselected.'"></td>';
1963 } else {
1964 print '<td><input type="hidden" class="width50 right" name="pricetoproduce-'.$line->id.'-'.$i.'" value="'.($manufacturingcost ? $manufacturingcost : '').'"></td>';
1965 }
1966 }
1967 // Already produced
1968 print '<td></td>';
1969 // Warehouse
1970 print '<td>';
1971 if ($tmpproduct->type == Product::TYPE_PRODUCT || getDolGlobalString('STOCK_SUPPORTS_SERVICES')) {
1972 $preselected = (GETPOSTISSET('idwarehousetoproduce-'.$line->id.'-'.$i) ? GETPOST('idwarehousetoproduce-'.$line->id.'-'.$i) : ($object->fk_warehouse > 0 ? $object->fk_warehouse : 'ifone'));
1973 print $formproduct->selectWarehouses($preselected, 'idwarehousetoproduce-'.$line->id.'-'.$i, '', 1, 0, $line->fk_product, '', 1, 0, array(), 'maxwidth200 csswarehouse_'.$line->id.'_'.$i);
1974 } else {
1975 print '<span class="opacitymedium">'.$langs->trans("NoStockChangeOnServices").'</span>';
1976 }
1977 print '</td>';
1978 // Lot
1979 if (isModEnabled('productbatch')) {
1980 print '<td>';
1981 if ($tmpproduct->status_batch) {
1982 $preselected = (GETPOSTISSET('batchtoproduce-'.$line->id.'-'.$i) ? GETPOST('batchtoproduce-'.$line->id.'-'.$i) : '');
1983 print '<input type="text" class="width75" name="batchtoproduce-'.$line->id.'-'.$i.'" value="'.$preselected.'">';
1984 }
1985 print '</td>';
1986 // Batch number in same column than the stock movement picto
1987 if ($tmpproduct->status_batch) {
1988 $type = 'batch';
1989 print '<td align="right" class="split">';
1990 print img_picto($langs->trans('AddStockLocationLine'), 'split', 'class="splitbutton" onClick="addDispatchLine('.$line->id.', \''.$type.'\', \'qtymissing\')"');
1991 print '</td>';
1992
1993 print '<td align="right" class="splitall">';
1994 if (($action == 'consumeorproduce' || $action == 'consumeandproduceall') && $tmpproduct->status_batch == 2) {
1995 print img_picto($langs->trans('SplitAllQuantity'), 'split', 'class="splitbutton splitallbutton field-error-icon" onClick="addDispatchLine('.$line->id.', \'batch\', \'alltoproduce\')"');
1996 } //
1997 print '</td>';
1998 } else {
1999 print '<td></td>';
2000
2001 print '<td></td>';
2002 }
2003 }
2004
2005 // sell by mandatory
2006 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) {
2007 print '<td align="right">';
2008 $preselectedSellBy = (GETPOSTISSET('sellby-' . $line->id . '-' . $i) ? GETPOSTDATE('sellby-' . $line->id . '-' . $i) : '');
2009 print $form->selectDate($preselectedSellBy, 'sellby-' . $line->id . '-' . $i, 0, 0, 1, '', 1, 0);
2010 print '</td>';
2011 } else {
2012 if ($atLeastOneSellBy) {
2013 print '<td></td>';
2014 }
2015 }
2016
2017 // eat by mandatory
2018 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) {
2019 print '<td align="right">';
2020 $preselectedEatBy = (GETPOSTISSET('eatby-' . $line->id . '-' . $i) ? GETPOSTDATE('eatby-' . $line->id . '-' . $i) : '');
2021 print $form->selectDate($preselectedEatBy, 'eatby-' . $line->id . '-' . $i, 0, 0, 1, '', 1, 0);
2022 print '</td>';
2023 } else {
2024 if ($atLeastOneEatBy) {
2025 print '<td></td>';
2026 }
2027 }
2028
2029 // Action delete
2030 print '<td></td>';
2031
2032 print '</tr>';
2033 }
2034 }
2035 }
2036 }
2037
2038 print '</table>';
2039 print '</div>';
2040
2041 print '</div>';
2042 print '</div>';
2043 }
2044
2045 if (in_array($action, array('consumeorproduce', 'consumeandproduceall', 'addconsumeline'))) {
2046 print "</form>\n";
2047 } ?>
2048
2049 <script type="text/javascript" language="javascript">
2050
2051 $(document).ready(function() {
2052 //Consumption : When a warehouse is selected, only the lot/serial numbers that are available in it are offered
2053 updateselectbatchbywarehouse();
2054 //Consumption : When a lot/serial number is selected and it is only available in one warehouse, the warehouse is automatically selected
2055 updateselectwarehousebybatch();
2056 });
2057
2058 function updateselectbatchbywarehouse() {
2059 $(document).on('change', "select[name*='idwarehouse']", function () {
2060 console.log("We change warehouse so we update the list of possible batch number");
2061
2062 var selectwarehouse = $(this);
2063
2064 var selectbatch_name = selectwarehouse.attr('name').replace('idwarehouse', 'batch');
2065 var selectbatch = $("datalist[id*='" + selectbatch_name + "']");
2066 var selectedbatch = selectbatch.val();
2067
2068 var product_element_name = selectwarehouse.attr('name').replace('idwarehouse', 'product');
2069
2070 $.ajax({
2071 type: "POST",
2072 url: "<?php echo DOL_URL_ROOT . '/mrp/ajax/interface.php'; ?>",
2073 data: {
2074 action: "updateselectbatchbywarehouse",
2075 permissiontoproduce: <?php echo $permissiontoproduce ?>,
2076 warehouse_id: $(this).val(),
2077 token: '<?php echo currentToken(); ?>',
2078 product_id: $("input[name='" + product_element_name + "']").val()
2079 }
2080 }).done(function (data) {
2081
2082 selectbatch.empty();
2083
2084 if (typeof data == "object") {
2085 console.log("data is already type object, no need to parse it");
2086 } else {
2087 console.log("data is type "+(typeof data));
2088 data = JSON.parse(data);
2089 }
2090
2091 selectbatch.append($('<option>', {
2092 value: '',
2093 }));
2094
2095 $.each(data, function (key, value) {
2096
2097 if(selectwarehouse.val() == -1) {
2098 var label = key + " (<?php echo $langs->trans('TotalStock') ?> : " + value + ")";
2099 } else {
2100 var label = key + " (<?php echo $langs->trans('Stock') ?> : " + value + ")";
2101 }
2102
2103 if(key === selectedbatch) {
2104 var option ='<option value="'+key+'" selected>'+ label +'</option>';
2105 } else {
2106 var option ='<option value="'+key+'">'+ label +'</option>';
2107 }
2108
2109 selectbatch.append(option);
2110 });
2111 });
2112 });
2113 }
2114
2115 function updateselectwarehousebybatch() {
2116 $(document).on('change', 'input[name*=batch]', function(){
2117 console.log("We change batch so we update the list of possible warehouses");
2118
2119 var selectbatch = $(this);
2120
2121 var selectwarehouse_name = selectbatch.attr('name').replace('batch', 'idwarehouse');
2122 var selectwarehouse = $("select[name*='" + selectwarehouse_name + "']");
2123 var selectedwarehouse = selectwarehouse.val();
2124
2125 if(selectedwarehouse != -1){
2126 return;
2127 }
2128
2129 var product_element_name = selectbatch.attr('name').replace('batch', 'product');
2130
2131 $.ajax({
2132 type: "POST",
2133 url: "<?php echo DOL_URL_ROOT . '/mrp/ajax/interface.php'; ?>",
2134 data: {
2135 action: "updateselectwarehousebybatch",
2136 permissiontoproduce: <?php echo $permissiontoproduce ?>,
2137 batch: $(this).val(),
2138 token: '<?php echo currentToken(); ?>',
2139 product_id: $("input[name='" + product_element_name + "']").val()
2140 }
2141 }).done(function (data) {
2142
2143 if (typeof data == "object") {
2144 console.log("data is already type object, no need to parse it");
2145 } else {
2146 console.log("data is type "+(typeof data));
2147 data = JSON.parse(data);
2148 }
2149
2150 if(data != 0){
2151 selectwarehouse.val(data).change();
2152 }
2153 });
2154 });
2155 }
2156
2157 </script>
2158
2159 <?php
2160}
2161
2162// End of page
2163llxFooter();
2164$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:356
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:132
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.