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