dolibarr 23.0.4
mo_card.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2017-2020 Laurent Destailleur <eldy@users.sourceforge.net>
3 * Copyright (C) 2024 Alexandre Spangaro <alexandre@inovea-conseil.com>
4 * Copyright (C) 2024-2026 MDW <mdeweerd@users.noreply.github.com>
5 * Copyright (C) 2024-2025 Frédéric France <frederic.france@free.fr>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
28// Load Dolibarr environment
29require '../main.inc.php';
30
31require_once DOL_DOCUMENT_ROOT.'/core/class/html.formcompany.class.php';
32require_once DOL_DOCUMENT_ROOT.'/core/class/html.formfile.class.php';
33require_once DOL_DOCUMENT_ROOT.'/core/class/html.formprojet.class.php';
34require_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
35require_once DOL_DOCUMENT_ROOT.'/mrp/class/mo.class.php';
36require_once DOL_DOCUMENT_ROOT.'/mrp/lib/mrp_mo.lib.php';
37require_once DOL_DOCUMENT_ROOT.'/bom/class/bom.class.php';
38require_once DOL_DOCUMENT_ROOT.'/bom/lib/bom.lib.php';
39
40if (isModEnabled('workstation')) {
41 require_once DOL_DOCUMENT_ROOT.'/workstation/class/workstation.class.php';
42}
43
44
54// Load translation files required by the page
55$langs->loadLangs(array('mrp', 'other'));
56
57
58// Get parameters
59$id = GETPOSTINT('id');
60$ref = GETPOST('ref', 'alpha');
61$action = GETPOST('action', 'aZ09');
62$confirm = GETPOST('confirm', 'alpha');
63$cancel = GETPOST('cancel');
64$contextpage = GETPOST('contextpage', 'aZ') ? GETPOST('contextpage', 'aZ') : 'mocard'; // To manage different context of search
65$backtopage = GETPOST('backtopage', 'alpha');
66$backtopageforcancel = GETPOST('backtopageforcancel', 'alpha');
67$TBomLineId = GETPOST('bomlineid', 'array:int');
68$lineid = GETPOSTINT('lineid');
69$socid = GETPOSTINT("socid");
70
71// Initialize a technical objects
72$object = new Mo($db);
73$objectbom = new BOM($db);
74
75$extrafields = new ExtraFields($db);
76$diroutputmassaction = $conf->mrp->dir_output.'/temp/massgeneration/'.$user->id;
77$hookmanager->initHooks(array('mocard', 'globalcard')); // Note that conf->hooks_modules contains array
78
79// Fetch optionals attributes and labels
80$extrafields->fetch_name_optionals_label($object->table_element);
81
82$search_array_options = $extrafields->getOptionalsFromPost($object->table_element, '', 'search_');
83
84// Initialize array of search criteria
85$search_all = GETPOST("search_all", 'alpha');
86$search = array();
87foreach ($object->fields as $key => $val) {
88 if (GETPOST('search_'.$key, 'alpha')) {
89 $search[$key] = GETPOST('search_'.$key, 'alpha');
90 }
91}
92
93if (empty($action) && empty($id) && empty($ref)) {
94 $action = 'view';
95}
96
97// Load object
98include DOL_DOCUMENT_ROOT.'/core/actions_fetchobject.inc.php'; // Must be 'include', not 'include_once'.
99
100if (GETPOSTINT('fk_bom') > 0) {
101 $objectbom->fetch(GETPOSTINT('fk_bom'));
102
103 if ($action != 'add') {
104 // We force calling parameters if we are not in the submit of creation of MO
105 $_POST['fk_product'] = $objectbom->fk_product;
106 $_POST['qty'] = $objectbom->qty;
107 $_POST['mrptype'] = $objectbom->bomtype;
108 $_POST['fk_warehouse'] = $objectbom->fk_warehouse;
109 $_POST['note_private'] = $objectbom->note_private;
110 }
111}
112
113// Security check - Protection if external user
114//if ($user->socid > 0) accessforbidden();
115//if ($user->socid > 0) $socid = $user->socid;
116$isdraft = (($object->status == $object::STATUS_DRAFT) ? 1 : 0);
117$result = restrictedArea($user, 'mrp', $object->id, 'mrp_mo', '', 'fk_soc', 'rowid', $isdraft);
118
119// Permissions
120$permissionnote = $user->hasRight('mrp', 'write'); // Used by the include of actions_setnotes.inc.php
121$permissiondellink = $user->hasRight('mrp', 'write'); // Used by the include of actions_dellink.inc.php
122$permissiontoadd = $user->hasRight('mrp', 'write'); // Used by the include of actions_addupdatedelete.inc.php and actions_lineupdown.inc.php
123$permissiontodelete = $user->hasRight('mrp', 'delete') || ($permissiontoadd && isset($object->status) && $object->status == $object::STATUS_DRAFT);
124$upload_dir = $conf->mrp->multidir_output[isset($object->entity) ? $object->entity : 1];
125
126
127/*
128 * Actions
129 */
130
131$parameters = array();
132$reshook = $hookmanager->executeHooks('doActions', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
133if ($reshook < 0) {
134 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
135}
136
137if (empty($reshook)) {
138 $error = 0;
139
140 $backurlforlist = dol_buildpath('/mrp/mo_list.php', 1);
141
142 $object->oldQty = (float) $object->qty;
143
144 if (empty($backtopage) || ($cancel && empty($id))) {
145 if (empty($backtopage) || ($cancel && strpos($backtopage, '__ID__'))) {
146 if (empty($id) && (($action != 'add' && $action != 'create') || $cancel)) {
147 $backtopage = $backurlforlist;
148 } else {
149 $backtopage = DOL_URL_ROOT.'/mrp/mo_card.php?id='.($id > 0 ? $id : '__ID__');
150 }
151 }
152 }
153 if ($cancel && !empty($backtopageforcancel)) {
154 $backtopage = $backtopageforcancel;
155 }
156 $triggermodname = 'MO_MODIFY'; // Name of trigger action code to execute when we modify record
157
158 // Create MO with Children
159 if ($action == 'add' && empty($id) && !empty($TBomLineId) && $permissiontoadd) {
160 $noback = 1;
161 // Sub-BOM lines the user checked "Generate Child MO" for must not be flattened into their
162 // raw materials on the parent MO: keep a single consume line anchored on the sub-assembly
163 // product instead, so it can be found below to create the child MO.
164 $object->noFlattenBomLineIds = array_map('intval', $TBomLineId);
165 include DOL_DOCUMENT_ROOT.'/core/actions_addupdatedelete.inc.php';
166
167 $mo_parent = $object;
168
169 $moline = new MoLine($db);
170 $objectbomchildline = new BOMLine($db);
171
172 foreach ($TBomLineId as $id_bom_line) {
173 $object = new Mo($db); // modified by the actions_addupdatedelete.inc.php
174
175 $objectbomchildline->fetch((int) $id_bom_line);
176
177 // Find the consume line of the parent MO generated from this BOM line.
178 // The lookup must be scoped to the parent MO and use an exact match on origin_id/origin_type,
179 // otherwise the default 'origin_id LIKE %..%' filter can return an unrelated line (or none),
180 // which would let the child MO be created from the leftover parent POST data (duplicate MO).
181 $filter = '(fk_mo:=:'.((int) $mo_parent->id).') AND (origin_id:=:'.((int) $id_bom_line).") AND (origin_type:=:'bomline')";
182 $TMoLines = $moline->fetchAll('DESC', 'rowid', 1, 0, $filter);
183
184 if (empty($TMoLines)) {
185 continue;
186 }
187
188 foreach ($TMoLines as $tmpmoline) {
189 $_POST['fk_bom'] = $objectbomchildline->fk_bom_child;
190 $_POST['fk_parent_line'] = $tmpmoline->id;
191 $_POST['qty'] = $tmpmoline->qty;
192 $_POST['fk_product'] = $tmpmoline->fk_product;
193 }
194
195 include DOL_DOCUMENT_ROOT.'/core/actions_addupdatedelete.inc.php';
196
197 $res = $object->add_object_linked('mo', $mo_parent->id);
198 }
199
200 header("Location: ".dol_buildpath('/mrp/mo_card.php?id='.((int) $mo_parent->id), 1));
201 exit;
202 } elseif ($action == 'confirm_cancel' && $confirm == 'yes' && !empty($permissiontoadd)) {
203 $also_cancel_consumed_and_produced_lines = (GETPOST('alsoCancelConsumedAndProducedLines', 'alpha') ? 1 : 0);
204 $result = $object->cancel($user, 0, (bool) $also_cancel_consumed_and_produced_lines);
205 if ($result > 0) {
206 header("Location: " . dol_buildpath('/mrp/mo_card.php?id=' . $object->id, 1));
207 exit;
208 } else {
209 $action = '';
210 setEventMessages($object->error, $object->errors, 'errors');
211 }
212 } elseif ($action == 'confirm_delete' && $confirm == 'yes' && !empty($permissiontodelete)) {
213 $also_cancel_consumed_and_produced_lines = (GETPOST('alsoCancelConsumedAndProducedLines', 'alpha') ? 1 : 0);
214 $result = $object->delete($user, 0, (bool) $also_cancel_consumed_and_produced_lines);
215 if ($result > 0) {
216 header("Location: " . $backurlforlist);
217 exit;
218 } else {
219 $action = '';
220 setEventMessages($object->error, $object->errors, 'errors');
221 }
222 }
223
224 if ($action == 'confirm_delete' && !empty($permissiontodelete)) {
225 if (!($object->id > 0)) {
226 dol_print_error(null, 'Error, object must be fetched before being deleted');
227 exit;
228 }
229
230 $error = 0;
231 $deleteChilds = GETPOST('deletechilds', 'aZ');
232
233 // Start the database transaction
234 $db->begin();
235
236 if ($deleteChilds === 'on') {
237 $TMoChildren = $object->getAllMoChilds();
238
239 foreach ($TMoChildren as $id => $childObject) {
240 if ($childObject->delete($user) == -1) {
241 $error++;
242 if (!empty($childObject->errors)) {
243 setEventMessages(null, $childObject->errors, 'errors');
244 } else {
245 setEventMessages($childObject->error, null, 'errors');
246 }
247 }
248 }
249 }
250
251 if (!$error) {
252 $result = $object->delete($user);
253
254 if ($result > 0) {
255 setEventMessages("RecordDeleted", null, 'mesgs');
256
257 if ($deleteChilds === 'on') {
258 setEventMessages("MoChildsDeleted", null, 'mesgs');
259 }
260
261 if (empty($noback)) {
262 header("Location: " . $backurlforlist);
263 exit;
264 }
265 } else {
266 $error++;
267 if (!empty($object->errors)) {
268 setEventMessages(null, $object->errors, 'errors');
269 } else {
270 setEventMessages($object->error, null, 'errors');
271 }
272 }
273 }
274
275 // Commit or rollback the database transaction based on whether there was an error
276 if ($error) {
277 $db->rollback();
278 } else {
279 $db->commit();
280 }
281 }
282
283
284 // Allow editing qty while MO is still in draft status.
285 // IMPORTANT: this handler MUST run BEFORE actions_addupdatedelete.inc.php,
286 // which has a generic 'set<key>' matcher that would intercept 'setqty' and
287 // call $object->fetch() + update() without setting $object->oldQty, so
288 // Mo::updateProduction() would skip the line scaling (its condition is
289 // !empty($this->oldQty)).
290 if ($action == 'setqty' && $permissiontoadd && $object->status == Mo::STATUS_DRAFT) {
291 $newqty = GETPOSTFLOAT('qty');
292 if ($newqty > 0) {
293 $object->oldQty = (float) $object->qty;
294 $object->qty = $newqty;
295 $res = $object->update($user);
296 if ($res > 0) {
297 // Enforce invariant: the 'toproduce' line for the MO's main product
298 // must equal the MO qty. Mo::updateProduction() scales by ratio
299 // (newQty/oldQty) which can drift if the line state was already
300 // inconsistent (e.g. legacy data from before this patch). Realign
301 // the main product line, leaving sub-products and frozen lines
302 // untouched.
303 $object->fetchLines();
304 foreach ($object->lines as $line) {
305 if ($line->role === 'toproduce'
306 && (int) $line->fk_product === (int) $object->fk_product
307 && empty($line->qty_frozen)
308 && (float) $line->qty != (float) $object->qty) {
309 $line->qty = (float) $object->qty;
310 $line->update($user);
311 }
312 }
313 setEventMessages($langs->trans("RecordSaved"), null, 'mesgs');
314 } else {
315 setEventMessages($object->error, $object->errors, 'errors');
316 }
317 } else {
318 setEventMessages($langs->trans("ErrorFieldRequired", $langs->trans("Qty")), null, 'errors');
319 }
320 header("Location: ".$_SERVER["PHP_SELF"]."?id=".$object->id);
321 exit;
322 }
323
324 // Actions cancel, add, update, update_extras, confirm_validate, confirm_delete, confirm_deleteline, confirm_clone, confirm_close, confirm_setdraft, confirm_reopen
325 include DOL_DOCUMENT_ROOT.'/core/actions_addupdatedelete.inc.php';
326
327 // Actions when linking object each other
328 include DOL_DOCUMENT_ROOT.'/core/actions_dellink.inc.php';
329
330 // Actions when printing a doc from card
331 include DOL_DOCUMENT_ROOT.'/core/actions_printing.inc.php';
332
333 // Action to build doc
334 include DOL_DOCUMENT_ROOT.'/core/actions_builddoc.inc.php';
335
336 if ($action == 'set_thirdparty' && $permissiontoadd) {
337 $object->setValueFrom('fk_soc', GETPOSTINT('fk_soc'), '', null, 'date', '', $user, $triggermodname);
338 }
339 if ($action == 'classin' && $permissiontoadd) {
340 $object->setProject(GETPOSTINT('projectid'));
341 }
342
343 // Actions to send emails
344 $triggersendname = 'MO_SENTBYMAIL';
345 $autocopy = 'MAIN_MAIL_AUTOCOPY_MO_TO';
346 $trackid = 'mo'.$object->id;
347 include DOL_DOCUMENT_ROOT.'/core/actions_sendmails.inc.php';
348
349 // Action to move up and down lines of object
350 //include DOL_DOCUMENT_ROOT.'/core/actions_lineupdown.inc.php'; // Must be 'include', not 'include_once'
351
352 // Action close produced
353 if ($action == 'confirm_produced' && $confirm == 'yes' && $permissiontoadd) {
354 $result = $object->setStatut($object::STATUS_PRODUCED, 0, '', 'MRP_MO_PRODUCED');
355 if ($result >= 0) {
356 // Define output language
357 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
358 $outputlangs = $langs;
359 $newlang = '';
360 if (getDolGlobalInt('MAIN_MULTILANGS') /* && empty($newlang) */ && GETPOST('lang_id', 'aZ09')) {
361 $newlang = GETPOST('lang_id', 'aZ09');
362 }
363 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
364 $newlang = $object->thirdparty->default_lang;
365 }
366 if (!empty($newlang)) {
367 $outputlangs = new Translate("", $conf);
368 $outputlangs->setDefaultLang($newlang);
369 }
370 $model = $object->model_pdf;
371 $ret = $object->fetch($id); // Reload to get new records
372
373 $object->generateDocument($model, $outputlangs, 0, 0, 0);
374 }
375 } else {
376 setEventMessages($object->error, $object->errors, 'errors');
377 }
378 }
379}
380
381
382
383
384/*
385 * View
386 */
387
388$form = new Form($db);
389$formfile = new FormFile($db);
390$formproject = new FormProjets($db);
391
392$title = $langs->trans('ManufacturingOrder')." - ".$langs->trans("Card");
393$help_url = 'EN:Module_Manufacturing_Orders|FR:Module_Ordres_de_Fabrication|DE:Modul_Fertigungsauftrag';
394
395llxHeader('', $title, $help_url, '', 0, 0, '', '', '', 'mod-mrp page-card');
396
397
398// Part to create
399if ($action == 'create') {
400 $titlelist = null;
401 if (GETPOSTINT('fk_bom') > 0) {
402 $titlelist = $langs->trans("ToConsume");
403 if ($objectbom->bomtype == 1) {
404 $titlelist = $langs->trans("ToObtain");
405 }
406 }
407
408 print load_fiche_titre($langs->trans("NewObject", $langs->transnoentitiesnoconv("Mo")), '', 'mrp');
409
410 print '<form method="POST" action="'.dolBuildUrl($_SERVER["PHP_SELF"]).'">';
411 print '<input type="hidden" name="token" value="'.newToken().'">';
412 print '<input type="hidden" name="action" value="add">';
413 if ($backtopage) {
414 print '<input type="hidden" name="backtopage" value="'.$backtopage.'">';
415 }
416 if ($backtopageforcancel) {
417 print '<input type="hidden" name="backtopageforcancel" value="'.$backtopageforcancel.'">';
418 }
419
420 print dol_get_fiche_head(array(), '');
421
422 print '<table class="border centpercent tableforfieldcreate">'."\n";
423
424 // Common attributes
425 include DOL_DOCUMENT_ROOT.'/core/tpl/commonfields_add.tpl.php';
426
427 // Other attributes
428 include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_add.tpl.php';
429
430 print '</table>'."\n";
431
432 print dol_get_fiche_end();
433
435 <script>
436 $(document).ready(function () {
437 jQuery('#fk_bom').change(function() {
438 console.log('We change value of BOM with BOM of id '+jQuery('#fk_bom').val());
439 if (jQuery('#fk_bom').val() > 0)
440 {
441 // Redirect to page with fk_bom set
442 window.location.href = '<?php echo $_SERVER["PHP_SELF"] ?>?action=create&token=<?php echo newToken(); ?>&fk_bom='+jQuery('#fk_bom').val();
443 /*
444 $.getJSON('<?php echo DOL_URL_ROOT ?>/mrp/ajax/ajax_bom.php?action=getBoms&idbom='+jQuery('#fk_bom').val(), function(data) {
445 console.log(data);
446 if (typeof data.rowid != "undefined") {
447 console.log("New BOM loaded, we set values in form");
448 console.log(data);
449 $('#qty').val(data.qty);
450 $("#mrptype").val(data.bomtype); // We set bomtype into mrptype
451 $('#mrptype').trigger('change'); // Notify any JS components that the value changed
452 $("#fk_product").val(data.fk_product);
453 $('#fk_product').trigger('change'); // Notify any JS components that the value changed
454 $('#note_private').val(data.description);
455 $('#note_private').trigger('change'); // Notify any JS components that the value changed
456 $('#fk_warehouse').val(data.fk_warehouse);
457 $('#fk_warehouse').trigger('change'); // Notify any JS components that the value changed
458 if (typeof CKEDITOR != "undefined") {
459 if (typeof CKEDITOR.instances != "undefined") {
460 if (typeof CKEDITOR.instances.note_private != "undefined") {
461 console.log(CKEDITOR.instances.note_private);
462 CKEDITOR.instances.note_private.setData(data.description);
463 }
464 }
465 }
466 } else {
467 console.log("Failed to get BOM");
468 }
469 });*/
470 }
471 else if (jQuery('#fk_bom').val() < 0) {
472 // Redirect to page with all fields defined except fk_bom set
473 console.log(jQuery('#fk_product').val());
474 window.location.href = '<?php echo $_SERVER["PHP_SELF"] ?>?action=create&token=<?php echo newToken(); ?>&qty='+jQuery('#qty').val()+'&mrptype='+jQuery('#mrptype').val()+'&fk_product='+jQuery('#fk_product').val()+'&label='+jQuery('#label').val()+'&fk_project='+jQuery('#fk_project').val()+'&fk_warehouse='+jQuery('#fk_warehouse').val();
475 /*
476 $('#qty').val('');
477 $("#fk_product").val('');
478 $('#fk_product').trigger('change'); // Notify any JS components that the value changed
479 $('#note_private').val('');
480 $('#note_private').trigger('change'); // Notify any JS components that the value changed
481 $('#fk_warehouse').val('');
482 $('#fk_warehouse').trigger('change'); // Notify any JS components that the value changed
483 */
484 }
485 });
486
487 //jQuery('#fk_bom').trigger('change');
488 })
489 </script>
490 <?php
491
492 print $form->buttonsSaveCancel("Create");
493
494 if ($objectbom->id > 0) {
495 print load_fiche_titre((string) $titlelist);
496
497 print '<!-- list of product/services to consume -->'."\n";
498 print '<div class="div-table-responsive-no-min">';
499 print '<table class="noborder centpercent">';
500
501 $arrayOfMoLines = array();
502 foreach ($objectbom->lines as $key => $val) {
503 $moLine = new MoLine($db);
504 $moLine->id = $objectbom->lines[$key]->id;
505 $moLine->position = $objectbom->lines[$key]->position;
506 $moLine->fk_product = $objectbom->lines[$key]->fk_product;
507 $moLine->fk_unit = $objectbom->lines[$key]->fk_unit;
508 $moLine->qty = $objectbom->lines[$key]->qty;
509 $moLine->qty_frozen = $objectbom->lines[$key]->qty_frozen;
510 $moLine->disable_stock_change = $objectbom->lines[$key]->disable_stock_change;
511 $moLine->fk_bom_child = $objectbom->lines[$key]->fk_bom_child;
512
513 $arrayOfMoLines[] = $moLine;
514 }
515 $object->lines = $arrayOfMoLines;
516 $object->mrptype = $objectbom->bomtype;
517 $object->bom = $objectbom;
518
519 $object->printOriginLinesList('', array());
520
521 print '</table>';
522 print '</div>';
523 }
524
525 print '</form>';
526}
527
528// Part to edit record
529if (($id || $ref) && $action == 'edit') {
530 print load_fiche_titre($langs->trans("ManufacturingOrder"), '', 'mrp');
531
532 print '<form method="POST" action="'.dolBuildUrl($_SERVER["PHP_SELF"]).'">';
533 print '<input type="hidden" name="token" value="'.newToken().'">';
534 print '<input type="hidden" name="action" value="update">';
535 print '<input type="hidden" name="id" value="'.$object->id.'">';
536 if ($backtopage) {
537 print '<input type="hidden" name="backtopage" value="'.$backtopage.'">';
538 }
539 if ($backtopageforcancel) {
540 print '<input type="hidden" name="backtopageforcancel" value="'.$backtopageforcancel.'">';
541 }
542
543 print dol_get_fiche_head();
544
545 $object->fields['fk_bom']['disabled'] = 1;
546
547 print '<table class="border centpercent tableforfieldedit">'."\n";
548
549 // Common attributes
550 include DOL_DOCUMENT_ROOT.'/core/tpl/commonfields_edit.tpl.php';
551
552 // Other attributes
553 include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_edit.tpl.php';
554
555 print '</table>';
556
557 print dol_get_fiche_end();
558
559 print $form->buttonsSaveCancel();
560
561 print '</form>';
562}
563
564// Part to show record
565if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'create'))) {
566 $res = $object->fetch_thirdparty();
567
568 $head = moPrepareHead($object);
569
570 print dol_get_fiche_head($head, 'card', $langs->trans("ManufacturingOrder"), -1, $object->picto);
571
572 $formconfirm = '';
573
574 // Confirmation to delete
575 if ($action == 'delete') {
576 $numberofmochilds = count($object->getAllMoChilds());
577
578 if ($numberofmochilds > 0) {
579 $label = $langs->trans("DeleteMoChild", '('.strval($numberofmochilds).')');
580 } else {
581 $label = $langs->trans("DeleteMoChild");
582 }
583
584 $formquestion = array(
585 array('type' => 'checkbox', 'name' => 'deletechilds', 'label' => $label, 'value' => 0),
586 array(
587 'label' => $langs->trans('MoCancelConsumedAndProducedLines'),
588 'name' => 'alsoCancelConsumedAndProducedLines',
589 'type' => 'checkbox',
590 'value' => !getDolGlobalString('MO_ALSO_CANCEL_CONSUMED_AND_PRODUCED_LINES_BY_DEFAULT') ? 0 : 1
591 )
592 );
593 $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id, $langs->trans('DeleteMo'), $langs->trans('ConfirmDeleteMo'), 'confirm_delete', $formquestion, 0, 1);
594 }
595 // Confirmation to delete line
596 if ($action == 'deleteline') {
597 $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id.'&lineid='.$lineid, $langs->trans('DeleteLine'), $langs->trans('ConfirmDeleteLine'), 'confirm_deleteline', '', 0, 1);
598 }
599
600 // Reload BOM to consume and produce
601 if ($action == 'reload') {
602 $object->createProduction($user, 0);
603 }
604
605 // Confirmation of validation
606 if ($action == 'validate') {
607 // We check that object has a temporary ref
608 $ref = substr($object->ref, 1, 4);
609 if ($ref == 'PROV') {
610 $object->fetch_product();
611 $numref = $object->getNextNumRef($object->product);
612 } else {
613 $numref = (string) $object->ref;
614 }
615
616 $text = $langs->trans('ConfirmValidateMo', $numref);
617 /*if (isModEnabled('notification'))
618 {
619 require_once DOL_DOCUMENT_ROOT . '/core/class/notify.class.php';
620 $notify = new Notify($db);
621 $text .= '<br>';
622 $text .= $notify->confirmMessage('BOM_VALIDATE', $object->socid, $object);
623 }*/
624
625 $formquestion = array();
626 if (isModEnabled('mrp')) {
627 $langs->load("mrp");
628 require_once DOL_DOCUMENT_ROOT.'/product/class/html.formproduct.class.php';
629 $formproduct = new FormProduct($db);
630 $forcecombo = 0;
631 if ($conf->browser->name == 'ie') {
632 $forcecombo = 1; // There is a bug in IE10 that make combo inside popup crazy
633 }
634 $formquestion = array(
635 // 'text' => $langs->trans("ConfirmClone"),
636 // array('type' => 'checkbox', 'name' => 'clone_content', 'label' => $langs->trans("CloneMainAttributes"), 'value' => 1),
637 // array('type' => 'checkbox', 'name' => 'update_prices', 'label' => $langs->trans("PuttingPricesUpToDate"), 'value' => 1),
638 );
639 }
640
641 $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id, $langs->trans('Validate'), $text, 'confirm_validate', $formquestion, 0, 1, 220);
642 }
643
644 // Confirmation to cancel
645 if ($action == 'cancel') {
646 $formquestion = array(
647 array(
648 'label' => $langs->trans('MoCancelConsumedAndProducedLines'),
649 'name' => 'alsoCancelConsumedAndProducedLines',
650 'type' => 'checkbox',
651 'value' => 0
652 ),
653 );
654 $formconfirm = $form->formconfirm(dolBuildUrl($_SERVER["PHP_SELF"], ['id' => $object->id]), $langs->trans('CancelMo'), $langs->trans('ConfirmCancelMo'), 'confirm_cancel', $formquestion, 0, 1);
655 }
656
657 // Clone confirmation
658 if ($action == 'clone') {
659 // Create an array for form
660 $formquestion = array();
661 $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id, $langs->trans('ToClone'), $langs->trans('ConfirmCloneMo', $object->ref), 'confirm_clone', $formquestion, 'yes', 1);
662 }
663
664 // Call Hook formConfirm
665 $parameters = array('formConfirm' => $formconfirm, 'lineid' => $lineid);
666 $reshook = $hookmanager->executeHooks('formConfirm', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
667 if (empty($reshook)) {
668 $formconfirm .= $hookmanager->resPrint;
669 } elseif ($reshook > 0) {
670 $formconfirm = $hookmanager->resPrint;
671 }
672
673 // Print form confirm
674 print $formconfirm;
675
676
677 // Object card
678 // ------------------------------------------------------------
679 $linkback = '<a href="'.dol_buildpath('/mrp/mo_list.php', 1).'?restore_lastsearch_values=1'.(!empty($socid) ? '&socid='.$socid : '').'">'.$langs->trans("BackToList").'</a>';
680
681 $morehtmlref = '<div class="refidno">';
682 /*
683 // Ref bis
684 $morehtmlref.=$form->editfieldkey("RefBis", 'ref_client', $object->ref_client, $object, $user->rights->mrp->creer, 'string', '', 0, 1);
685 $morehtmlref.=$form->editfieldval("RefBis", 'ref_client', $object->ref_client, $object, $user->rights->mrp->creer, 'string', '', null, null, '', 1);*/
686 // Thirdparty
687 if (is_object($object->thirdparty)) {
688 $morehtmlref .= $object->thirdparty->getNomUrl(1, 'customer');
689 if (!getDolGlobalString('MAIN_DISABLE_OTHER_LINK') && $object->thirdparty->id > 0) {
690 $morehtmlref .= ' (<a href="'.DOL_URL_ROOT.'/commande/list.php?socid='.$object->thirdparty->id.'">'.$langs->trans("OtherOrders").'</a>)';
691 }
692 }
693 // Project
694 if (isModEnabled('project')) {
695 $langs->load("projects");
696 if (is_object($object->thirdparty)) {
697 $morehtmlref .= '<br>';
698 }
699 if ($permissiontoadd) {
700 $morehtmlref .= img_picto($langs->trans("Project"), 'project', 'class="pictofixedwidth"');
701 if ($action != 'classify') {
702 $morehtmlref .= '<a class="editfielda" href="'.dolBuildUrl($_SERVER['PHP_SELF'], ['action' => 'classify', 'id' => $object->id], true).'">'.img_edit($langs->transnoentitiesnoconv('SetProject')).'</a> ';
703 }
704 $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');
705 } else {
706 if (!empty($object->fk_project)) {
707 $proj = new Project($db);
708 $proj->fetch($object->fk_project);
709 $morehtmlref .= $proj->getNomUrl(1);
710 if ($proj->title) {
711 $morehtmlref .= '<span class="opacitymedium"> - '.dol_escape_htmltag($proj->title).'</span>';
712 }
713 }
714 }
715 }
716 $morehtmlref .= '</div>';
717
718
719 dol_banner_tab($object, 'ref', $linkback, 1, 'ref', 'ref', $morehtmlref);
720
721
722 print '<div class="fichecenter">';
723 print '<div class="fichehalfleft">';
724 print '<div class="underbanner clearboth"></div>';
725 print '<table class="border centpercent tableforfield">'."\n";
726
727 //Mo Parent
728 $mo_parent = $object->getMoParent();
729 if (is_object($mo_parent)) {
730 print '<tr class="field_fk_mo_parent">';
731 print '<td class="titlefield fieldname_fk_mo_parent">' . $langs->trans('ParentMo') . '</td>';
732 print '<td class="valuefield fieldname_fk_mo_parent">' .$mo_parent->getNomUrl(1).'</td>';
733 print '</tr>';
734 }
735
736 // Common attributes
737 $keyforbreak = 'fk_warehouse';
738 unset($object->fields['fk_project']);
739 unset($object->fields['fk_soc']);
740 // Allow inline edit of qty while MO is in draft status.
741 // Note: editfieldval() does not handle type 'real' (no <input> rendered, only
742 // Save/Cancel buttons appear). So we override the type to 'numeric' only when
743 // the user has actually clicked the edit pencil (action=editqty). This keeps
744 // the read-mode rendering (price() format) untouched.
745 if ($object->status == Mo::STATUS_DRAFT && $permissiontoadd && isset($object->fields['qty'])) {
746 $object->fields['qty']['alwayseditable'] = 1;
747 if ($action == 'editqty') {
748 $object->fields['qty']['type'] = 'numeric';
749 }
750 }
751
752 include DOL_DOCUMENT_ROOT.'/core/tpl/commonfields_view.tpl.php';
753
754 // Other attributes
755 include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_view.tpl.php';
756
757 print '</table>';
758 print '</div>';
759 print '</div>';
760
761 print '<div class="clearboth"></div>';
762
763 print dol_get_fiche_end();
764
765
766 /*
767 * Lines
768 */
769
770 if (!empty($object->table_element_line)) {
771 // Show object lines
772 //$result = $object->getLinesArray();
773 $object->fetchLines();
774
775 print ' <form name="addproduct" id="addproduct" action="'.$_SERVER["PHP_SELF"].'?id='.$object->id.(($action != 'editline') ? '' : '#line_'.GETPOSTINT('lineid')).'" method="POST">
776 <input type="hidden" name="token" value="' . newToken().'">
777 <input type="hidden" name="action" value="' . (($action != 'editline') ? 'addline' : 'updateline').'">
778 <input type="hidden" name="mode" value="">
779 <input type="hidden" name="page_y" value="">
780 <input type="hidden" name="id" value="' . $object->id.'">
781 ';
782
783 /*if (!empty($conf->use_javascript_ajax) && $object->status == 0) {
784 include DOL_DOCUMENT_ROOT.'/core/tpl/ajaxrow.tpl.php';
785 }*/
786
787 if (!empty($object->lines)) {
788 print '<div class="div-table-responsive-no-min">';
789 print '<table id="tablelines" class="noborder noshadow" width="100%">';
790
791 print '<tr class="liste_titre">';
792 print '<td class="liste_titre">'.$langs->trans("Summary").'</td>';
793 print '<td></td>';
794 print '</tr>';
795
796 print '<tr class="oddeven">';
797 print '<td>'.$langs->trans("ProductsToConsume").'</td>';
798 print '<td>';
799 if (!empty($object->lines)) {
800 $i = 0;
801 foreach ($object->lines as $line) {
802 if ($line->role == 'toconsume') {
803 if ($i) {
804 print ', ';
805 }
806 $tmpproduct = new Product($db);
807 $tmpproduct->fetch($line->fk_product);
808 print $tmpproduct->getNomUrl(1);
809 $i++;
810 }
811 }
812 }
813 print '</td>';
814 print '</tr>';
815
816 print '<tr class="oddeven">';
817 print '<td>'.$langs->trans("ProductsToProduce").'</td>';
818 print '<td>';
819 if (!empty($object->lines)) {
820 $i = 0;
821 foreach ($object->lines as $line) {
822 if ($line->role == 'toproduce') {
823 if ($i) {
824 print ', ';
825 }
826 $tmpproduct = new Product($db);
827 $tmpproduct->fetch($line->fk_product);
828 print $tmpproduct->getNomUrl(1);
829 $i++;
830 }
831 }
832 }
833 print '</td>';
834 print '</tr>';
835
836 print '</table>';
837 print '</div>';
838 }
839
840 print "</form>\n";
841 }
842
843
844 // Buttons for actions
845
846 if ($action != 'presend' && $action != 'editline') {
847 print '<div class="tabsAction">'."\n";
848 $parameters = array();
849 $reshook = $hookmanager->executeHooks('addMoreActionsButtons', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
850 if ($reshook < 0) {
851 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
852 }
853
854 if (empty($reshook)) {
855 // Send
856 //if (empty($user->socid)) {
857 // print '<a class="butAction" href="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '&action=presend&mode=init#formmailbeforetitle">' . $langs->trans('SendMail') . '</a>'."\n";
858 //}
859
860 // Back to draft
861 if ($object->status == $object::STATUS_VALIDATED) {
862 if ($permissiontoadd) {
863 // TODO Add test that production has not started
864 print '<a class="butAction" href="'.$_SERVER['PHP_SELF'].'?id='.$object->id.'&action=confirm_setdraft&confirm=yes&token='.newToken().'">'.$langs->trans("SetToDraft").'</a>';
865 }
866 }
867
868 // Modify
869 if ($object->status == $object::STATUS_DRAFT) {
870 if ($permissiontoadd) {
871 print '<a class="butAction" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=edit&token='.newToken().'">'.$langs->trans("Modify").'</a>'."\n";
872 } else {
873 print '<a class="butActionRefused classfortooltip" href="#" title="'.dolPrintHTMLForAttribute($langs->trans("NotEnoughPermissions")).'">'.$langs->trans('Modify').'</a>'."\n";
874 }
875 }
876
877 // Reload BOM
878 if ($object->status == $object::STATUS_DRAFT && $object->fk_bom > 0) {
879 if ($permissiontoadd) {
880 print '<a class="butAction" href="'.$_SERVER['PHP_SELF'].'?id='.$object->id.'&action=reload&token='.newToken().'" title="'.dolPrintHTMLForAttribute($langs->trans("ReInitializeHelp")).'">'.$langs->trans("ReInitialize").'</a>';
881 } else {
882 print '<a class="butActionRefused classfortooltip" href="#" title="'.dolPrintHTMLForAttribute($langs->trans("ReInitializeHelp").'<br>'.$langs->trans("NotEnoughPermissions")).'">'.$langs->trans('ReInitialize').'</a>'."\n";
883 }
884 }
885
886 // Validate
887 if ($object->status == $object::STATUS_DRAFT) {
888 if ($permissiontoadd) {
889 if (empty($object->table_element_line) || (is_array($object->lines) && count($object->lines) > 0)) {
890 print '<a class="butAction" href="'.$_SERVER['PHP_SELF'].'?id='.$object->id.'&action=validate&token='.newToken().'">'.$langs->trans("Validate").'</a>';
891 } else {
892 $langs->load("errors");
893 print '<a class="butActionRefused" href="" title="'.$langs->trans("ErrorAddAtLeastOneLineFirst").'">'.$langs->trans("Validate").'</a>';
894 }
895 }
896 }
897
898 // Clone
899 if ($permissiontoadd) {
900 print dolGetButtonAction($langs->trans("ToClone"), '', 'default', $_SERVER['PHP_SELF'].'?id='.$object->id.(!empty($object->socid) ? '&socid='.$object->socid : "").'&action=clone&token='.newToken().'&object=mo', 'clone', $permissiontoadd);
901 }
902
903 // Cancel - Reopen
904 if ($permissiontoadd) {
905 if ($object->status == $object::STATUS_VALIDATED || $object->status == $object::STATUS_INPROGRESS) {
906 $arrayproduced = $object->fetchLinesLinked('produced', 0);
907 $nbProduced = 0;
908 foreach ($arrayproduced as $lineproduced) {
909 $nbProduced += $lineproduced['qty'];
910 }
911 if ($nbProduced > 0) { // If production has started, we can close it
912 print '<a class="butAction" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=confirm_produced&confirm=yes&token='.newToken().'">'.$langs->trans("Close").'</a>'."\n";
913 } else {
914 print '<a class="butActionRefused" href="#" title="'.$langs->trans("GoOnTabProductionToProduceFirst", $langs->transnoentitiesnoconv("Production")).'">'.$langs->trans("Close").'</a>'."\n";
915 }
916
917 print '<a class="butActionDelete" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=cancel&token='.newToken().'">'.$langs->trans("Cancel").'</a>'."\n";
918 }
919
920 if ($object->status == $object::STATUS_PRODUCED || $object->status == $object::STATUS_CANCELED) {
921 print '<a class="butAction" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=confirm_reopen&confirm=yes&token='.newToken().'">'.$langs->trans("ReOpen").'</a>'."\n";
922 }
923 }
924
925 // Delete
926 print dolGetButtonAction($langs->trans("Delete"), '', 'delete', $_SERVER["PHP_SELF"].'?id='.$object->id.'&action=delete&token='.newToken(), 'delete', $permissiontodelete);
927 }
928 print '</div>'."\n";
929 }
930
931
932 // Select mail models is same action as presend
933 if (GETPOST('modelselected')) {
934 $action = 'presend';
935 }
936
937 if ($action != 'presend') {
938 print '<div class="fichecenter"><div class="fichehalfleft">';
939 print '<a name="builddoc"></a>'; // ancre
940
941 // Documents
942 $objref = dol_sanitizeFileName($object->ref);
943 $relativepath = $objref.'/'.$objref.'.pdf';
944 $filedir = $conf->mrp->dir_output.'/'.$objref;
945 $urlsource = $_SERVER["PHP_SELF"]."?id=".$object->id;
946 $genallowed = $user->hasRight('mrp', 'read'); // If you can read, you can build the PDF to read content
947 $delallowed = $user->hasRight("mrp", "creer"); // If you can create/edit, you can remove a file on card
948 print $formfile->showdocuments('mrp:mo', $objref, $filedir, $urlsource, $genallowed, $delallowed, $object->model_pdf, 1, 0, 0, 28, 0, '', '', '', $mysoc->default_lang, '', $object);
949
950 // Show links to link elements
951 $tmparray = $form->showLinkToObjectBlock($object, array(), array('mo'), 1);
952 $linktoelem = $tmparray['linktoelem'];
953 $htmltoenteralink = $tmparray['htmltoenteralink'];
954 print $htmltoenteralink;
955
956 $somethingshown = $form->showLinkedObjectBlock($object, $linktoelem, array());
957
958
959 print '</div><div class="fichehalfright">';
960
961 $MAXEVENT = 10;
962
963 $morehtmlcenter = dolGetButtonTitle($langs->trans('SeeAll'), '', 'fa fa-bars imgforviewmode', DOL_URL_ROOT.'/mrp/mo_agenda.php?id='.$object->id);
964
965 // List of actions on element
966 include_once DOL_DOCUMENT_ROOT.'/core/class/html.formactions.class.php';
967 $formactions = new FormActions($db);
968 $somethingshown = $formactions->showactions($object, $object->element, $socid, 1, '', $MAXEVENT, '', $morehtmlcenter);
969
970 print '</div></div>';
971 }
972
973 //Select mail models is same action as presend
974 if (GETPOST('modelselected')) {
975 $action = 'presend';
976 }
977
978 // Presend form
979 $modelmail = 'mo';
980 $defaulttopic = 'InformationMessage';
981 $diroutput = $conf->mrp->dir_output;
982 $trackid = 'mo'.$object->id;
983
984 include DOL_DOCUMENT_ROOT.'/core/tpl/card_presend.tpl.php';
985}
986
987// End of page
988llxFooter();
989$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
mrpCollapseBomManagement()
Manage collapse bom display.
Definition bom.lib.php:166
Class for BOM.
Definition bom.class.php:42
Class for BOMLine.
Class to manage standard extra fields.
Class to manage building of HTML components.
Class to offer components to list and upload files.
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 products or services.
Class to manage projects.
Class to manage translations.
global $mysoc
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0, $attop=0)
Set event messages in dol_events session object.
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)
GETPOSTINT($paramname, $method=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
dolBuildUrl($url, $params=[], $addtoken=false)
Return path of url.
dol_get_fiche_head($links=array(), $active='', $title='', $notab=0, $picto='', $pictoisfullpath=0, $morehtmlright='', $morecss='', $limittoshow=0, $moretabssuffix='', $dragdropfile=0, $morecssdiv='')
Show tabs of a record.
dolGetButtonTitle($label, $helpText='', $iconClass='fa fa-file', $url='', $id='', $status=1, $params=array())
Function dolGetButtonTitle : this kind of buttons are used in title in list.
dol_get_fiche_end($notab=0)
Return tab footer of a card.
dol_sanitizeFileName($str, $newstr='_', $unaccent=1, $includequotes=0, $allowdash=0)
Clean a string to use it as a file name.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
newToken()
Return the value of token currently saved into session with name 'newtoken'.
GETPOSTFLOAT($paramname, $rounding='', $option=2)
Return the value of a $_GET or $_POST supervariable, converted into float.
dolGetButtonAction($label, $text='', $actionType='default', $url='', $id='', $userRight=1, $params=array())
Function dolGetButtonAction.
dolPrintHTMLForAttribute($s, $escapeonlyhtmltags=0, $allowothertags=array())
Return a string ready to be output into an HTML attribute (alt, title, data-html, ....
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
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.
moPrepareHead($object)
Prepare array of tabs for Mo.
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.