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