dolibarr 23.0.3
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 include DOL_DOCUMENT_ROOT.'/core/actions_addupdatedelete.inc.php';
162
163 $mo_parent = $object;
164
165 $moline = new MoLine($db);
166 $objectbomchildline = new BOMLine($db);
167
168 foreach ($TBomLineId as $id_bom_line) {
169 $object = new Mo($db); // modified by the actions_addupdatedelete.inc.php
170
171 $objectbomchildline->fetch((int) $id_bom_line);
172
173 // Find the consume line of the parent MO generated from this BOM line.
174 // The lookup must be scoped to the parent MO and use an exact match on origin_id/origin_type,
175 // otherwise the default 'origin_id LIKE %..%' filter can return an unrelated line (or none),
176 // which would let the child MO be created from the leftover parent POST data (duplicate MO).
177 $TMoLines = $moline->fetchAll('DESC', 'rowid', 1, 0, array('fk_mo' => $mo_parent->id, 'origin_id' => (int) $id_bom_line, 'origin_type' => 'bomline'));
178
179 if (empty($TMoLines)) {
180 continue;
181 }
182
183 foreach ($TMoLines as $tmpmoline) {
184 $_POST['fk_bom'] = $objectbomchildline->fk_bom_child;
185 $_POST['fk_parent_line'] = $tmpmoline->id;
186 $_POST['qty'] = $tmpmoline->qty;
187 $_POST['fk_product'] = $tmpmoline->fk_product;
188 }
189
190 include DOL_DOCUMENT_ROOT.'/core/actions_addupdatedelete.inc.php';
191
192 $res = $object->add_object_linked('mo', $mo_parent->id);
193 }
194
195 header("Location: ".dol_buildpath('/mrp/mo_card.php?id='.((int) $mo_parent->id), 1));
196 exit;
197 } elseif ($action == 'confirm_cancel' && $confirm == 'yes' && !empty($permissiontoadd)) {
198 $also_cancel_consumed_and_produced_lines = (GETPOST('alsoCancelConsumedAndProducedLines', 'alpha') ? 1 : 0);
199 $result = $object->cancel($user, 0, (bool) $also_cancel_consumed_and_produced_lines);
200 if ($result > 0) {
201 header("Location: " . dol_buildpath('/mrp/mo_card.php?id=' . $object->id, 1));
202 exit;
203 } else {
204 $action = '';
205 setEventMessages($object->error, $object->errors, 'errors');
206 }
207 } elseif ($action == 'confirm_delete' && $confirm == 'yes' && !empty($permissiontodelete)) {
208 $also_cancel_consumed_and_produced_lines = (GETPOST('alsoCancelConsumedAndProducedLines', 'alpha') ? 1 : 0);
209 $result = $object->delete($user, 0, (bool) $also_cancel_consumed_and_produced_lines);
210 if ($result > 0) {
211 header("Location: " . $backurlforlist);
212 exit;
213 } else {
214 $action = '';
215 setEventMessages($object->error, $object->errors, 'errors');
216 }
217 }
218
219 if ($action == 'confirm_delete' && !empty($permissiontodelete)) {
220 if (!($object->id > 0)) {
221 dol_print_error(null, 'Error, object must be fetched before being deleted');
222 exit;
223 }
224
225 $error = 0;
226 $deleteChilds = GETPOST('deletechilds', 'aZ');
227
228 // Start the database transaction
229 $db->begin();
230
231 if ($deleteChilds === 'on') {
232 $TMoChildren = $object->getAllMoChilds();
233
234 foreach ($TMoChildren as $id => $childObject) {
235 if ($childObject->delete($user) == -1) {
236 $error++;
237 if (!empty($childObject->errors)) {
238 setEventMessages(null, $childObject->errors, 'errors');
239 } else {
240 setEventMessages($childObject->error, null, 'errors');
241 }
242 }
243 }
244 }
245
246 if (!$error) {
247 $result = $object->delete($user);
248
249 if ($result > 0) {
250 setEventMessages("RecordDeleted", null, 'mesgs');
251
252 if ($deleteChilds === 'on') {
253 setEventMessages("MoChildsDeleted", null, 'mesgs');
254 }
255
256 if (empty($noback)) {
257 header("Location: " . $backurlforlist);
258 exit;
259 }
260 } else {
261 $error++;
262 if (!empty($object->errors)) {
263 setEventMessages(null, $object->errors, 'errors');
264 } else {
265 setEventMessages($object->error, null, 'errors');
266 }
267 }
268 }
269
270 // Commit or rollback the database transaction based on whether there was an error
271 if ($error) {
272 $db->rollback();
273 } else {
274 $db->commit();
275 }
276 }
277
278
279 // Allow editing qty while MO is still in draft status.
280 // IMPORTANT: this handler MUST run BEFORE actions_addupdatedelete.inc.php,
281 // which has a generic 'set<key>' matcher that would intercept 'setqty' and
282 // call $object->fetch() + update() without setting $object->oldQty, so
283 // Mo::updateProduction() would skip the line scaling (its condition is
284 // !empty($this->oldQty)).
285 if ($action == 'setqty' && $permissiontoadd && $object->status == Mo::STATUS_DRAFT) {
286 $newqty = GETPOSTFLOAT('qty');
287 if ($newqty > 0) {
288 $object->oldQty = (float) $object->qty;
289 $object->qty = $newqty;
290 $res = $object->update($user);
291 if ($res > 0) {
292 // Enforce invariant: the 'toproduce' line for the MO's main product
293 // must equal the MO qty. Mo::updateProduction() scales by ratio
294 // (newQty/oldQty) which can drift if the line state was already
295 // inconsistent (e.g. legacy data from before this patch). Realign
296 // the main product line, leaving sub-products and frozen lines
297 // untouched.
298 $object->fetchLines();
299 foreach ($object->lines as $line) {
300 if ($line->role === 'toproduce'
301 && (int) $line->fk_product === (int) $object->fk_product
302 && empty($line->qty_frozen)
303 && (float) $line->qty != (float) $object->qty) {
304 $line->qty = (float) $object->qty;
305 $line->update($user);
306 }
307 }
308 setEventMessages($langs->trans("RecordSaved"), null, 'mesgs');
309 } else {
310 setEventMessages($object->error, $object->errors, 'errors');
311 }
312 } else {
313 setEventMessages($langs->trans("ErrorFieldRequired", $langs->trans("Qty")), null, 'errors');
314 }
315 header("Location: ".$_SERVER["PHP_SELF"]."?id=".$object->id);
316 exit;
317 }
318
319 // Actions cancel, add, update, update_extras, confirm_validate, confirm_delete, confirm_deleteline, confirm_clone, confirm_close, confirm_setdraft, confirm_reopen
320 include DOL_DOCUMENT_ROOT.'/core/actions_addupdatedelete.inc.php';
321
322 // Actions when linking object each other
323 include DOL_DOCUMENT_ROOT.'/core/actions_dellink.inc.php';
324
325 // Actions when printing a doc from card
326 include DOL_DOCUMENT_ROOT.'/core/actions_printing.inc.php';
327
328 // Action to build doc
329 include DOL_DOCUMENT_ROOT.'/core/actions_builddoc.inc.php';
330
331 if ($action == 'set_thirdparty' && $permissiontoadd) {
332 $object->setValueFrom('fk_soc', GETPOSTINT('fk_soc'), '', null, 'date', '', $user, $triggermodname);
333 }
334 if ($action == 'classin' && $permissiontoadd) {
335 $object->setProject(GETPOSTINT('projectid'));
336 }
337
338 // Actions to send emails
339 $triggersendname = 'MO_SENTBYMAIL';
340 $autocopy = 'MAIN_MAIL_AUTOCOPY_MO_TO';
341 $trackid = 'mo'.$object->id;
342 include DOL_DOCUMENT_ROOT.'/core/actions_sendmails.inc.php';
343
344 // Action to move up and down lines of object
345 //include DOL_DOCUMENT_ROOT.'/core/actions_lineupdown.inc.php'; // Must be 'include', not 'include_once'
346
347 // Action close produced
348 if ($action == 'confirm_produced' && $confirm == 'yes' && $permissiontoadd) {
349 $result = $object->setStatut($object::STATUS_PRODUCED, 0, '', 'MRP_MO_PRODUCED');
350 if ($result >= 0) {
351 // Define output language
352 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
353 $outputlangs = $langs;
354 $newlang = '';
355 if (getDolGlobalInt('MAIN_MULTILANGS') /* && empty($newlang) */ && GETPOST('lang_id', 'aZ09')) {
356 $newlang = GETPOST('lang_id', 'aZ09');
357 }
358 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
359 $newlang = $object->thirdparty->default_lang;
360 }
361 if (!empty($newlang)) {
362 $outputlangs = new Translate("", $conf);
363 $outputlangs->setDefaultLang($newlang);
364 }
365 $model = $object->model_pdf;
366 $ret = $object->fetch($id); // Reload to get new records
367
368 $object->generateDocument($model, $outputlangs, 0, 0, 0);
369 }
370 } else {
371 setEventMessages($object->error, $object->errors, 'errors');
372 }
373 }
374}
375
376
377
378
379/*
380 * View
381 */
382
383$form = new Form($db);
384$formfile = new FormFile($db);
385$formproject = new FormProjets($db);
386
387$title = $langs->trans('ManufacturingOrder')." - ".$langs->trans("Card");
388$help_url = 'EN:Module_Manufacturing_Orders|FR:Module_Ordres_de_Fabrication|DE:Modul_Fertigungsauftrag';
389
390llxHeader('', $title, $help_url, '', 0, 0, '', '', '', 'mod-mrp page-card');
391
392
393// Part to create
394if ($action == 'create') {
395 $titlelist = null;
396 if (GETPOSTINT('fk_bom') > 0) {
397 $titlelist = $langs->trans("ToConsume");
398 if ($objectbom->bomtype == 1) {
399 $titlelist = $langs->trans("ToObtain");
400 }
401 }
402
403 print load_fiche_titre($langs->trans("NewObject", $langs->transnoentitiesnoconv("Mo")), '', 'mrp');
404
405 print '<form method="POST" action="'.dolBuildUrl($_SERVER["PHP_SELF"]).'">';
406 print '<input type="hidden" name="token" value="'.newToken().'">';
407 print '<input type="hidden" name="action" value="add">';
408 if ($backtopage) {
409 print '<input type="hidden" name="backtopage" value="'.$backtopage.'">';
410 }
411 if ($backtopageforcancel) {
412 print '<input type="hidden" name="backtopageforcancel" value="'.$backtopageforcancel.'">';
413 }
414
415 print dol_get_fiche_head(array(), '');
416
417 print '<table class="border centpercent tableforfieldcreate">'."\n";
418
419 // Common attributes
420 include DOL_DOCUMENT_ROOT.'/core/tpl/commonfields_add.tpl.php';
421
422 // Other attributes
423 include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_add.tpl.php';
424
425 print '</table>'."\n";
426
427 print dol_get_fiche_end();
428
430 <script>
431 $(document).ready(function () {
432 jQuery('#fk_bom').change(function() {
433 console.log('We change value of BOM with BOM of id '+jQuery('#fk_bom').val());
434 if (jQuery('#fk_bom').val() > 0)
435 {
436 // Redirect to page with fk_bom set
437 window.location.href = '<?php echo $_SERVER["PHP_SELF"] ?>?action=create&token=<?php echo newToken(); ?>&fk_bom='+jQuery('#fk_bom').val();
438 /*
439 $.getJSON('<?php echo DOL_URL_ROOT ?>/mrp/ajax/ajax_bom.php?action=getBoms&idbom='+jQuery('#fk_bom').val(), function(data) {
440 console.log(data);
441 if (typeof data.rowid != "undefined") {
442 console.log("New BOM loaded, we set values in form");
443 console.log(data);
444 $('#qty').val(data.qty);
445 $("#mrptype").val(data.bomtype); // We set bomtype into mrptype
446 $('#mrptype').trigger('change'); // Notify any JS components that the value changed
447 $("#fk_product").val(data.fk_product);
448 $('#fk_product').trigger('change'); // Notify any JS components that the value changed
449 $('#note_private').val(data.description);
450 $('#note_private').trigger('change'); // Notify any JS components that the value changed
451 $('#fk_warehouse').val(data.fk_warehouse);
452 $('#fk_warehouse').trigger('change'); // Notify any JS components that the value changed
453 if (typeof CKEDITOR != "undefined") {
454 if (typeof CKEDITOR.instances != "undefined") {
455 if (typeof CKEDITOR.instances.note_private != "undefined") {
456 console.log(CKEDITOR.instances.note_private);
457 CKEDITOR.instances.note_private.setData(data.description);
458 }
459 }
460 }
461 } else {
462 console.log("Failed to get BOM");
463 }
464 });*/
465 }
466 else if (jQuery('#fk_bom').val() < 0) {
467 // Redirect to page with all fields defined except fk_bom set
468 console.log(jQuery('#fk_product').val());
469 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();
470 /*
471 $('#qty').val('');
472 $("#fk_product").val('');
473 $('#fk_product').trigger('change'); // Notify any JS components that the value changed
474 $('#note_private').val('');
475 $('#note_private').trigger('change'); // Notify any JS components that the value changed
476 $('#fk_warehouse').val('');
477 $('#fk_warehouse').trigger('change'); // Notify any JS components that the value changed
478 */
479 }
480 });
481
482 //jQuery('#fk_bom').trigger('change');
483 })
484 </script>
485 <?php
486
487 print $form->buttonsSaveCancel("Create");
488
489 if ($objectbom->id > 0) {
490 print load_fiche_titre((string) $titlelist);
491
492 print '<!-- list of product/services to consume -->'."\n";
493 print '<div class="div-table-responsive-no-min">';
494 print '<table class="noborder centpercent">';
495
496 $arrayOfMoLines = array();
497 foreach ($objectbom->lines as $key => $val) {
498 $moLine = new MoLine($db);
499 $moLine->id = $objectbom->lines[$key]->id;
500 $moLine->position = $objectbom->lines[$key]->position;
501 $moLine->fk_product = $objectbom->lines[$key]->fk_product;
502 $moLine->fk_unit = $objectbom->lines[$key]->fk_unit;
503 $moLine->qty = $objectbom->lines[$key]->qty;
504 $moLine->qty_frozen = $objectbom->lines[$key]->qty_frozen;
505 $moLine->disable_stock_change = $objectbom->lines[$key]->disable_stock_change;
506 $moLine->fk_bom_child = $objectbom->lines[$key]->fk_bom_child;
507
508 $arrayOfMoLines[] = $moLine;
509 }
510 $object->lines = $arrayOfMoLines;
511 $object->mrptype = $objectbom->bomtype;
512 $object->bom = $objectbom;
513
514 $object->printOriginLinesList('', array());
515
516 print '</table>';
517 print '</div>';
518 }
519
520 print '</form>';
521}
522
523// Part to edit record
524if (($id || $ref) && $action == 'edit') {
525 print load_fiche_titre($langs->trans("ManufacturingOrder"), '', 'mrp');
526
527 print '<form method="POST" action="'.dolBuildUrl($_SERVER["PHP_SELF"]).'">';
528 print '<input type="hidden" name="token" value="'.newToken().'">';
529 print '<input type="hidden" name="action" value="update">';
530 print '<input type="hidden" name="id" value="'.$object->id.'">';
531 if ($backtopage) {
532 print '<input type="hidden" name="backtopage" value="'.$backtopage.'">';
533 }
534 if ($backtopageforcancel) {
535 print '<input type="hidden" name="backtopageforcancel" value="'.$backtopageforcancel.'">';
536 }
537
538 print dol_get_fiche_head();
539
540 $object->fields['fk_bom']['disabled'] = 1;
541
542 print '<table class="border centpercent tableforfieldedit">'."\n";
543
544 // Common attributes
545 include DOL_DOCUMENT_ROOT.'/core/tpl/commonfields_edit.tpl.php';
546
547 // Other attributes
548 include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_edit.tpl.php';
549
550 print '</table>';
551
552 print dol_get_fiche_end();
553
554 print $form->buttonsSaveCancel();
555
556 print '</form>';
557}
558
559// Part to show record
560if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'create'))) {
561 $res = $object->fetch_thirdparty();
562
563 $head = moPrepareHead($object);
564
565 print dol_get_fiche_head($head, 'card', $langs->trans("ManufacturingOrder"), -1, $object->picto);
566
567 $formconfirm = '';
568
569 // Confirmation to delete
570 if ($action == 'delete') {
571 $numberofmochilds = count($object->getAllMoChilds());
572
573 if ($numberofmochilds > 0) {
574 $label = $langs->trans("DeleteMoChild", '('.strval($numberofmochilds).')');
575 } else {
576 $label = $langs->trans("DeleteMoChild");
577 }
578
579 $formquestion = array(
580 array('type' => 'checkbox', 'name' => 'deletechilds', 'label' => $label, 'value' => 0),
581 array(
582 'label' => $langs->trans('MoCancelConsumedAndProducedLines'),
583 'name' => 'alsoCancelConsumedAndProducedLines',
584 'type' => 'checkbox',
585 'value' => !getDolGlobalString('MO_ALSO_CANCEL_CONSUMED_AND_PRODUCED_LINES_BY_DEFAULT') ? 0 : 1
586 )
587 );
588 $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id, $langs->trans('DeleteMo'), $langs->trans('ConfirmDeleteMo'), 'confirm_delete', $formquestion, 0, 1);
589 }
590 // Confirmation to delete line
591 if ($action == 'deleteline') {
592 $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id.'&lineid='.$lineid, $langs->trans('DeleteLine'), $langs->trans('ConfirmDeleteLine'), 'confirm_deleteline', '', 0, 1);
593 }
594
595 // Reload BOM to consume and produce
596 if ($action == 'reload') {
597 $object->createProduction($user, 0);
598 }
599
600 // Confirmation of validation
601 if ($action == 'validate') {
602 // We check that object has a temporary ref
603 $ref = substr($object->ref, 1, 4);
604 if ($ref == 'PROV') {
605 $object->fetch_product();
606 $numref = $object->getNextNumRef($object->product);
607 } else {
608 $numref = (string) $object->ref;
609 }
610
611 $text = $langs->trans('ConfirmValidateMo', $numref);
612 /*if (isModEnabled('notification'))
613 {
614 require_once DOL_DOCUMENT_ROOT . '/core/class/notify.class.php';
615 $notify = new Notify($db);
616 $text .= '<br>';
617 $text .= $notify->confirmMessage('BOM_VALIDATE', $object->socid, $object);
618 }*/
619
620 $formquestion = array();
621 if (isModEnabled('mrp')) {
622 $langs->load("mrp");
623 require_once DOL_DOCUMENT_ROOT.'/product/class/html.formproduct.class.php';
624 $formproduct = new FormProduct($db);
625 $forcecombo = 0;
626 if ($conf->browser->name == 'ie') {
627 $forcecombo = 1; // There is a bug in IE10 that make combo inside popup crazy
628 }
629 $formquestion = array(
630 // 'text' => $langs->trans("ConfirmClone"),
631 // array('type' => 'checkbox', 'name' => 'clone_content', 'label' => $langs->trans("CloneMainAttributes"), 'value' => 1),
632 // array('type' => 'checkbox', 'name' => 'update_prices', 'label' => $langs->trans("PuttingPricesUpToDate"), 'value' => 1),
633 );
634 }
635
636 $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id, $langs->trans('Validate'), $text, 'confirm_validate', $formquestion, 0, 1, 220);
637 }
638
639 // Confirmation to cancel
640 if ($action == 'cancel') {
641 $formquestion = array(
642 array(
643 'label' => $langs->trans('MoCancelConsumedAndProducedLines'),
644 'name' => 'alsoCancelConsumedAndProducedLines',
645 'type' => 'checkbox',
646 'value' => 0
647 ),
648 );
649 $formconfirm = $form->formconfirm(dolBuildUrl($_SERVER["PHP_SELF"], ['id' => $object->id]), $langs->trans('CancelMo'), $langs->trans('ConfirmCancelMo'), 'confirm_cancel', $formquestion, 0, 1);
650 }
651
652 // Clone confirmation
653 if ($action == 'clone') {
654 // Create an array for form
655 $formquestion = array();
656 $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id, $langs->trans('ToClone'), $langs->trans('ConfirmCloneMo', $object->ref), 'confirm_clone', $formquestion, 'yes', 1);
657 }
658
659 // Call Hook formConfirm
660 $parameters = array('formConfirm' => $formconfirm, 'lineid' => $lineid);
661 $reshook = $hookmanager->executeHooks('formConfirm', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
662 if (empty($reshook)) {
663 $formconfirm .= $hookmanager->resPrint;
664 } elseif ($reshook > 0) {
665 $formconfirm = $hookmanager->resPrint;
666 }
667
668 // Print form confirm
669 print $formconfirm;
670
671
672 // Object card
673 // ------------------------------------------------------------
674 $linkback = '<a href="'.dol_buildpath('/mrp/mo_list.php', 1).'?restore_lastsearch_values=1'.(!empty($socid) ? '&socid='.$socid : '').'">'.$langs->trans("BackToList").'</a>';
675
676 $morehtmlref = '<div class="refidno">';
677 /*
678 // Ref bis
679 $morehtmlref.=$form->editfieldkey("RefBis", 'ref_client', $object->ref_client, $object, $user->rights->mrp->creer, 'string', '', 0, 1);
680 $morehtmlref.=$form->editfieldval("RefBis", 'ref_client', $object->ref_client, $object, $user->rights->mrp->creer, 'string', '', null, null, '', 1);*/
681 // Thirdparty
682 if (is_object($object->thirdparty)) {
683 $morehtmlref .= $object->thirdparty->getNomUrl(1, 'customer');
684 if (!getDolGlobalString('MAIN_DISABLE_OTHER_LINK') && $object->thirdparty->id > 0) {
685 $morehtmlref .= ' (<a href="'.DOL_URL_ROOT.'/commande/list.php?socid='.$object->thirdparty->id.'">'.$langs->trans("OtherOrders").'</a>)';
686 }
687 }
688 // Project
689 if (isModEnabled('project')) {
690 $langs->load("projects");
691 if (is_object($object->thirdparty)) {
692 $morehtmlref .= '<br>';
693 }
694 if ($permissiontoadd) {
695 $morehtmlref .= img_picto($langs->trans("Project"), 'project', 'class="pictofixedwidth"');
696 if ($action != 'classify') {
697 $morehtmlref .= '<a class="editfielda" href="'.dolBuildUrl($_SERVER['PHP_SELF'], ['action' => 'classify', 'id' => $object->id], true).'">'.img_edit($langs->transnoentitiesnoconv('SetProject')).'</a> ';
698 }
699 $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');
700 } else {
701 if (!empty($object->fk_project)) {
702 $proj = new Project($db);
703 $proj->fetch($object->fk_project);
704 $morehtmlref .= $proj->getNomUrl(1);
705 if ($proj->title) {
706 $morehtmlref .= '<span class="opacitymedium"> - '.dol_escape_htmltag($proj->title).'</span>';
707 }
708 }
709 }
710 }
711 $morehtmlref .= '</div>';
712
713
714 dol_banner_tab($object, 'ref', $linkback, 1, 'ref', 'ref', $morehtmlref);
715
716
717 print '<div class="fichecenter">';
718 print '<div class="fichehalfleft">';
719 print '<div class="underbanner clearboth"></div>';
720 print '<table class="border centpercent tableforfield">'."\n";
721
722 //Mo Parent
723 $mo_parent = $object->getMoParent();
724 if (is_object($mo_parent)) {
725 print '<tr class="field_fk_mo_parent">';
726 print '<td class="titlefield fieldname_fk_mo_parent">' . $langs->trans('ParentMo') . '</td>';
727 print '<td class="valuefield fieldname_fk_mo_parent">' .$mo_parent->getNomUrl(1).'</td>';
728 print '</tr>';
729 }
730
731 // Common attributes
732 $keyforbreak = 'fk_warehouse';
733 unset($object->fields['fk_project']);
734 unset($object->fields['fk_soc']);
735 // Allow inline edit of qty while MO is in draft status.
736 // Note: editfieldval() does not handle type 'real' (no <input> rendered, only
737 // Save/Cancel buttons appear). So we override the type to 'numeric' only when
738 // the user has actually clicked the edit pencil (action=editqty). This keeps
739 // the read-mode rendering (price() format) untouched.
740 if ($object->status == Mo::STATUS_DRAFT && $permissiontoadd && isset($object->fields['qty'])) {
741 $object->fields['qty']['alwayseditable'] = 1;
742 if ($action == 'editqty') {
743 $object->fields['qty']['type'] = 'numeric';
744 }
745 }
746
747 include DOL_DOCUMENT_ROOT.'/core/tpl/commonfields_view.tpl.php';
748
749 // Other attributes
750 include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_view.tpl.php';
751
752 print '</table>';
753 print '</div>';
754 print '</div>';
755
756 print '<div class="clearboth"></div>';
757
758 print dol_get_fiche_end();
759
760
761 /*
762 * Lines
763 */
764
765 if (!empty($object->table_element_line)) {
766 // Show object lines
767 //$result = $object->getLinesArray();
768 $object->fetchLines();
769
770 print ' <form name="addproduct" id="addproduct" action="'.$_SERVER["PHP_SELF"].'?id='.$object->id.(($action != 'editline') ? '' : '#line_'.GETPOSTINT('lineid')).'" method="POST">
771 <input type="hidden" name="token" value="' . newToken().'">
772 <input type="hidden" name="action" value="' . (($action != 'editline') ? 'addline' : 'updateline').'">
773 <input type="hidden" name="mode" value="">
774 <input type="hidden" name="page_y" value="">
775 <input type="hidden" name="id" value="' . $object->id.'">
776 ';
777
778 /*if (!empty($conf->use_javascript_ajax) && $object->status == 0) {
779 include DOL_DOCUMENT_ROOT.'/core/tpl/ajaxrow.tpl.php';
780 }*/
781
782 if (!empty($object->lines)) {
783 print '<div class="div-table-responsive-no-min">';
784 print '<table id="tablelines" class="noborder noshadow" width="100%">';
785
786 print '<tr class="liste_titre">';
787 print '<td class="liste_titre">'.$langs->trans("Summary").'</td>';
788 print '<td></td>';
789 print '</tr>';
790
791 print '<tr class="oddeven">';
792 print '<td>'.$langs->trans("ProductsToConsume").'</td>';
793 print '<td>';
794 if (!empty($object->lines)) {
795 $i = 0;
796 foreach ($object->lines as $line) {
797 if ($line->role == 'toconsume') {
798 if ($i) {
799 print ', ';
800 }
801 $tmpproduct = new Product($db);
802 $tmpproduct->fetch($line->fk_product);
803 print $tmpproduct->getNomUrl(1);
804 $i++;
805 }
806 }
807 }
808 print '</td>';
809 print '</tr>';
810
811 print '<tr class="oddeven">';
812 print '<td>'.$langs->trans("ProductsToProduce").'</td>';
813 print '<td>';
814 if (!empty($object->lines)) {
815 $i = 0;
816 foreach ($object->lines as $line) {
817 if ($line->role == 'toproduce') {
818 if ($i) {
819 print ', ';
820 }
821 $tmpproduct = new Product($db);
822 $tmpproduct->fetch($line->fk_product);
823 print $tmpproduct->getNomUrl(1);
824 $i++;
825 }
826 }
827 }
828 print '</td>';
829 print '</tr>';
830
831 print '</table>';
832 print '</div>';
833 }
834
835 print "</form>\n";
836 }
837
838
839 // Buttons for actions
840
841 if ($action != 'presend' && $action != 'editline') {
842 print '<div class="tabsAction">'."\n";
843 $parameters = array();
844 $reshook = $hookmanager->executeHooks('addMoreActionsButtons', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
845 if ($reshook < 0) {
846 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
847 }
848
849 if (empty($reshook)) {
850 // Send
851 //if (empty($user->socid)) {
852 // print '<a class="butAction" href="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '&action=presend&mode=init#formmailbeforetitle">' . $langs->trans('SendMail') . '</a>'."\n";
853 //}
854
855 // Back to draft
856 if ($object->status == $object::STATUS_VALIDATED) {
857 if ($permissiontoadd) {
858 // TODO Add test that production has not started
859 print '<a class="butAction" href="'.$_SERVER['PHP_SELF'].'?id='.$object->id.'&action=confirm_setdraft&confirm=yes&token='.newToken().'">'.$langs->trans("SetToDraft").'</a>';
860 }
861 }
862
863 // Modify
864 if ($object->status == $object::STATUS_DRAFT) {
865 if ($permissiontoadd) {
866 print '<a class="butAction" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=edit&token='.newToken().'">'.$langs->trans("Modify").'</a>'."\n";
867 } else {
868 print '<a class="butActionRefused classfortooltip" href="#" title="'.dolPrintHTMLForAttribute($langs->trans("NotEnoughPermissions")).'">'.$langs->trans('Modify').'</a>'."\n";
869 }
870 }
871
872 // Reload BOM
873 if ($object->status == $object::STATUS_DRAFT && $object->fk_bom > 0) {
874 if ($permissiontoadd) {
875 print '<a class="butAction" href="'.$_SERVER['PHP_SELF'].'?id='.$object->id.'&action=reload&token='.newToken().'" title="'.dolPrintHTMLForAttribute($langs->trans("ReInitializeHelp")).'">'.$langs->trans("ReInitialize").'</a>';
876 } else {
877 print '<a class="butActionRefused classfortooltip" href="#" title="'.dolPrintHTMLForAttribute($langs->trans("ReInitializeHelp").'<br>'.$langs->trans("NotEnoughPermissions")).'">'.$langs->trans('ReInitialize').'</a>'."\n";
878 }
879 }
880
881 // Validate
882 if ($object->status == $object::STATUS_DRAFT) {
883 if ($permissiontoadd) {
884 if (empty($object->table_element_line) || (is_array($object->lines) && count($object->lines) > 0)) {
885 print '<a class="butAction" href="'.$_SERVER['PHP_SELF'].'?id='.$object->id.'&action=validate&token='.newToken().'">'.$langs->trans("Validate").'</a>';
886 } else {
887 $langs->load("errors");
888 print '<a class="butActionRefused" href="" title="'.$langs->trans("ErrorAddAtLeastOneLineFirst").'">'.$langs->trans("Validate").'</a>';
889 }
890 }
891 }
892
893 // Clone
894 if ($permissiontoadd) {
895 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);
896 }
897
898 // Cancel - Reopen
899 if ($permissiontoadd) {
900 if ($object->status == $object::STATUS_VALIDATED || $object->status == $object::STATUS_INPROGRESS) {
901 $arrayproduced = $object->fetchLinesLinked('produced', 0);
902 $nbProduced = 0;
903 foreach ($arrayproduced as $lineproduced) {
904 $nbProduced += $lineproduced['qty'];
905 }
906 if ($nbProduced > 0) { // If production has started, we can close it
907 print '<a class="butAction" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=confirm_produced&confirm=yes&token='.newToken().'">'.$langs->trans("Close").'</a>'."\n";
908 } else {
909 print '<a class="butActionRefused" href="#" title="'.$langs->trans("GoOnTabProductionToProduceFirst", $langs->transnoentitiesnoconv("Production")).'">'.$langs->trans("Close").'</a>'."\n";
910 }
911
912 print '<a class="butActionDelete" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=cancel&token='.newToken().'">'.$langs->trans("Cancel").'</a>'."\n";
913 }
914
915 if ($object->status == $object::STATUS_PRODUCED || $object->status == $object::STATUS_CANCELED) {
916 print '<a class="butAction" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=confirm_reopen&confirm=yes&token='.newToken().'">'.$langs->trans("ReOpen").'</a>'."\n";
917 }
918 }
919
920 // Delete
921 print dolGetButtonAction($langs->trans("Delete"), '', 'delete', $_SERVER["PHP_SELF"].'?id='.$object->id.'&action=delete&token='.newToken(), 'delete', $permissiontodelete);
922 }
923 print '</div>'."\n";
924 }
925
926
927 // Select mail models is same action as presend
928 if (GETPOST('modelselected')) {
929 $action = 'presend';
930 }
931
932 if ($action != 'presend') {
933 print '<div class="fichecenter"><div class="fichehalfleft">';
934 print '<a name="builddoc"></a>'; // ancre
935
936 // Documents
937 $objref = dol_sanitizeFileName($object->ref);
938 $relativepath = $objref.'/'.$objref.'.pdf';
939 $filedir = $conf->mrp->dir_output.'/'.$objref;
940 $urlsource = $_SERVER["PHP_SELF"]."?id=".$object->id;
941 $genallowed = $user->hasRight('mrp', 'read'); // If you can read, you can build the PDF to read content
942 $delallowed = $user->hasRight("mrp", "creer"); // If you can create/edit, you can remove a file on card
943 print $formfile->showdocuments('mrp:mo', $objref, $filedir, $urlsource, $genallowed, $delallowed, $object->model_pdf, 1, 0, 0, 28, 0, '', '', '', $mysoc->default_lang, '', $object);
944
945 // Show links to link elements
946 $tmparray = $form->showLinkToObjectBlock($object, array(), array('mo'), 1);
947 $linktoelem = $tmparray['linktoelem'];
948 $htmltoenteralink = $tmparray['htmltoenteralink'];
949 print $htmltoenteralink;
950
951 $somethingshown = $form->showLinkedObjectBlock($object, $linktoelem, array());
952
953
954 print '</div><div class="fichehalfright">';
955
956 $MAXEVENT = 10;
957
958 $morehtmlcenter = dolGetButtonTitle($langs->trans('SeeAll'), '', 'fa fa-bars imgforviewmode', DOL_URL_ROOT.'/mrp/mo_agenda.php?id='.$object->id);
959
960 // List of actions on element
961 include_once DOL_DOCUMENT_ROOT.'/core/class/html.formactions.class.php';
962 $formactions = new FormActions($db);
963 $somethingshown = $formactions->showactions($object, $object->element, $socid, 1, '', $MAXEVENT, '', $morehtmlcenter);
964
965 print '</div></div>';
966 }
967
968 //Select mail models is same action as presend
969 if (GETPOST('modelselected')) {
970 $action = 'presend';
971 }
972
973 // Presend form
974 $modelmail = 'mo';
975 $defaulttopic = 'InformationMessage';
976 $diroutput = $conf->mrp->dir_output;
977 $trackid = 'mo'.$object->id;
978
979 include DOL_DOCUMENT_ROOT.'/core/tpl/card_presend.tpl.php';
980}
981
982// End of page
983llxFooter();
984$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.