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