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