dolibarr 23.0.3
bom_card.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2017-2023 Laurent Destailleur <eldy@users.sourceforge.net>
3 * Copyright (C) 2019-2025 Frédéric France <frederic.france@free.fr>
4 * Copyright (C) 2023 Charlene Benke <charlene@patas-monkey.com>
5 * Copyright (C) 2024-2025 MDW <mdeweerd@users.noreply.github.com>
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
27// Load Dolibarr environment
28require '../main.inc.php';
38require_once DOL_DOCUMENT_ROOT.'/core/class/html.formcompany.class.php';
39require_once DOL_DOCUMENT_ROOT.'/core/class/html.formfile.class.php';
40require_once DOL_DOCUMENT_ROOT.'/bom/class/bom.class.php';
41require_once DOL_DOCUMENT_ROOT.'/bom/lib/bom.lib.php';
42require_once DOL_DOCUMENT_ROOT.'/mrp/lib/mrp.lib.php';
43
44
45// Load translation files required by the page
46$langs->loadLangs(array('mrp', 'other'));
47
48// Get parameters
49$id = GETPOSTINT('id');
50$lineid = GETPOSTINT('lineid');
51$ref = GETPOST('ref', 'alpha');
52$action = GETPOST('action', 'aZ09');
53$confirm = GETPOST('confirm', 'alpha');
54$cancel = GETPOST('cancel', 'alpha');
55$contextpage = GETPOST('contextpage', 'aZ') ? GETPOST('contextpage', 'aZ') : 'bomcard'; // To manage different context of search
56$backtopage = GETPOST('backtopage', 'alpha');
57
58
59// PDF
60$hidedetails = (GETPOSTINT('hidedetails') ? GETPOSTINT('hidedetails') : (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_DETAILS') ? 1 : 0));
61$hidedesc = (GETPOSTINT('hidedesc') ? GETPOSTINT('hidedesc') : (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_DESC') ? 1 : 0));
62$hideref = (GETPOSTINT('hideref') ? GETPOSTINT('hideref') : (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_REF') ? 1 : 0));
63
64// Initialize a technical objects
65$object = new BOM($db);
66$extrafields = new ExtraFields($db);
67$diroutputmassaction = getMultidirOutput($object) . '/temp/massgeneration/'.$user->id;
68$hookmanager->initHooks(array('bomcard', 'globalcard')); // Note that conf->hooks_modules contains array
69
70// Fetch optionals attributes and labels
71$extrafields->fetch_name_optionals_label($object->table_element);
72$search_array_options = $extrafields->getOptionalsFromPost($object->table_element, '', 'search_');
73
74// Initialize array of search criteria
75$search_all = GETPOST("search_all", 'alpha');
76$search = array();
77foreach ($object->fields as $key => $val) {
78 if (GETPOST('search_'.$key, 'alpha')) {
79 $search[$key] = GETPOST('search_'.$key, 'alpha');
80 }
81}
82
83if (empty($action) && empty($id) && empty($ref)) {
84 $action = 'view';
85}
86
87// Load object
88include DOL_DOCUMENT_ROOT.'/core/actions_fetchobject.inc.php'; // Must be 'include', not 'include_once'.
89if ($object->id > 0) {
90 $object->calculateCosts();
91}
92
93
94// Security check - Protection if external user
95//if ($user->socid > 0) accessforbidden();
96//if ($user->socid > 0) $socid = $user->socid;
97$isdraft = (($object->status == $object::STATUS_DRAFT) ? 1 : 0);
98$result = restrictedArea($user, 'bom', $object->id, $object->table_element, '', '', 'rowid', $isdraft);
99
100// Permissions
101$permissionnote = $user->hasRight('bom', 'write'); // Used by the include of actions_setnotes.inc.php
102$permissiondellink = $user->hasRight('bom', 'write'); // Used by the include of actions_dellink.inc.php
103$permissiontoadd = $user->hasRight('bom', 'write'); // Used by the include of actions_addupdatedelete.inc.php and actions_lineupdown.inc.php
104$permissiontodelete = $user->hasRight('bom', 'delete') || ($permissiontoadd && isset($object->status) && $object->status == $object::STATUS_DRAFT);
105$upload_dir = $conf->bom->multidir_output[isset($object->entity) ? $object->entity : 1];
106
107
108/*
109 * Actions
110 */
111
112$parameters = array();
113$reshook = $hookmanager->executeHooks('doActions', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
114if ($reshook < 0) {
115 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
116}
117
118if (empty($reshook)) {
119 $error = 0;
120
121 $backurlforlist = DOL_URL_ROOT.'/bom/bom_list.php';
122
123 if (empty($backtopage) || ($cancel && empty($id))) {
124 if (empty($backtopage) || ($cancel && strpos($backtopage, '__ID__'))) {
125 if (empty($id) && (($action != 'add' && $action != 'create') || $cancel)) {
126 $backtopage = $backurlforlist;
127 } else {
128 $backtopage = DOL_URL_ROOT.'/bom/bom_card.php?id='.($id > 0 ? $id : '__ID__');
129 }
130 }
131 }
132
133 $triggermodname = $object->TRIGGER_PREFIX.'_MODIFY'; // Name of trigger action code to execute when we modify record. Used in actions_addupdatedelete.inc.php
134
135 // Actions cancel, add, update, delete or clone
136 include DOL_DOCUMENT_ROOT.'/core/actions_addupdatedelete.inc.php';
137
138 // The fetch/fetch_lines was redone into the inc.php so we must recall the calculateCosts()
139 if ($action == 'confirm_validate' && $object->id > 0) { // Test on permission not required
140 $object->calculateCosts();
141 }
142
143 // Actions when linking object each other
144 include DOL_DOCUMENT_ROOT.'/core/actions_dellink.inc.php';
145
146 // Actions when printing a doc from card
147 include DOL_DOCUMENT_ROOT.'/core/actions_printing.inc.php';
148
149 // Action to move up and down lines of object
150 //include DOL_DOCUMENT_ROOT.'/core/actions_lineupdown.inc.php';
151
152 // Action to build doc
153 include DOL_DOCUMENT_ROOT.'/core/actions_builddoc.inc.php';
154
155 // Actions to send emails
156 $triggersendname = 'BOM_SENTBYMAIL';
157 $autocopy = 'MAIN_MAIL_AUTOCOPY_BOM_TO';
158 $trackid = 'bom'.$object->id;
159 include DOL_DOCUMENT_ROOT.'/core/actions_sendmails.inc.php';
160
161 // Add line
162 if ($action == 'addline' && $user->hasRight('bom', 'write')) {
163 $langs->load('errors');
164 $error = 0;
165 $predef = '';
166
167 // Set if we used free entry or predefined product
168 $bom_child_id = GETPOSTINT('bom_id');
169 if ($bom_child_id > 0) {
170 $bom_child = new BOM($db);
171 $res = $bom_child->fetch($bom_child_id);
172 if ($res) {
173 $idprod = $bom_child->fk_product;
174 }
175 } else {
176 $idprod = (GETPOSTINT('idprodservice') ? GETPOSTINT('idprodservice') : GETPOSTINT('idprod'));
177 }
178
179 $qty = price2num(GETPOST('qty', 'alpha'), 'MS');
180 $qty_frozen = GETPOSTINT('qty_frozen');
181 $disable_stock_change = GETPOSTINT('disable_stock_change');
182 $fk_workstation = GETPOSTINT('idworkstations');
183 $efficiency = price2num(GETPOST('efficiency', 'alpha'));
184 $fk_unit = GETPOSTINT('fk_unit');
185
186 $fk_default_workstation = 0;
187 if (!empty($idprod) && isModEnabled('workstation')) {
188 $product = new Product($db);
189 $res = $product->fetch($idprod);
190 if ($res > 0 && $product->type == Product::TYPE_SERVICE) {
191 if ($fk_workstation > 0) {
192 $fk_default_workstation = $fk_workstation;
193 } else {
194 $fk_default_workstation = $product->fk_default_workstation;
195 }
196 }
197 if (empty($fk_unit)) {
198 $fk_unit = $product->fk_unit;
199 }
200 }
201
202 if ($qty == '') {
203 setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('Qty')), null, 'errors');
204 $error++;
205 }
206 if (!($idprod > 0)) {
207 setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('Product')), null, 'errors');
208 $error++;
209 }
210
211 if ($object->fk_product == $idprod) {
212 setEventMessages($langs->trans('TheProductXIsAlreadyTheProductToProduce'), null, 'errors');
213 $error++;
214 }
215
216 // We check if we're allowed to add this bom
217 $TParentBom = array();
218 $object->getParentBomTreeRecursive($TParentBom);
219 if ($bom_child_id > 0 && in_array($bom_child_id, $TParentBom)) {
220 $n_child = new BOM($db);
221 $n_child->fetch($bom_child_id);
222 setEventMessages($langs->transnoentities('BomCantAddChildBom', $n_child->getNomUrl(1), $object->getNomUrl(1)), null, 'errors');
223 $error++;
224 }
225
226 if (!$error) {
227 // Extrafields
228 $extralabelsline = $extrafields->fetch_name_optionals_label($object->table_element_line);
229 $array_options = $extrafields->getOptionalsFromPost($object->table_element_line, $predef);
230 // Unset extrafield
231 if (is_array($extralabelsline)) {
232 // Get extra fields
233 foreach ($extralabelsline as $key => $value) {
234 unset($_POST["options_".$key]);
235 }
236 }
237
238 $result = $object->addLine((int) $idprod, (float) $qty, $qty_frozen, $disable_stock_change, (float) $efficiency, -1, $bom_child_id, null, (int) $fk_unit, $array_options, $fk_default_workstation);
239
240 if ($result <= 0) {
241 setEventMessages($object->error, $object->errors, 'errors');
242 $action = '';
243 } else {
244 unset($_POST['idprod']);
245 unset($_POST['idprodservice']);
246 unset($_POST['qty']);
247 unset($_POST['qty_frozen']);
248 unset($_POST['disable_stock_change']);
249 }
250
251 $object->fetchLines();
252
253 $object->calculateCosts();
254 }
255 }
256
257 // Update line
258 if ($action == 'updateline' && $user->hasRight('bom', 'write')) {
259 $langs->load('errors');
260 $error = 0;
261
262 // Set if we used free entry or predefined product
263 $qty = price2num(GETPOST('qty', 'alpha'), 'MS');
264 $qty_frozen = GETPOSTINT('qty_frozen');
265 $disable_stock_change = GETPOSTINT('disable_stock_change');
266 $efficiency = price2num(GETPOST('efficiency', 'alpha'));
267 $fk_unit = GETPOSTINT('fk_unit');
268
269 if ($qty == '') {
270 setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('Qty')), null, 'errors');
271 $error++;
272 }
273
274 if (!$error) {
275 // Extrafields
276 $extralabelsline = $extrafields->fetch_name_optionals_label($object->table_element_line);
277 $array_options = $extrafields->getOptionalsFromPost($object->table_element_line);
278 // Unset extrafield
279 if (is_array($extralabelsline)) {
280 // Get extra fields
281 foreach ($extralabelsline as $key => $value) {
282 unset($_POST["options_".$key]);
283 }
284 }
285
286 $bomline = new BOMLine($db);
287 $bomline->fetch($lineid);
288
289 $fk_default_workstation = $bomline->fk_default_workstation;
290 if (isModEnabled('workstation') && GETPOSTISSET('idworkstations')) {
291 $fk_default_workstation = GETPOSTINT('idworkstations');
292 }
293
294 $result = $object->updateLine($lineid, (float) $qty, $qty_frozen, (int) $disable_stock_change, (float) $efficiency, $bomline->position, $bomline->import_key, $fk_unit, $array_options, $fk_default_workstation);
295
296 if ($result <= 0) {
297 setEventMessages($object->error, $object->errors, 'errors');
298 $action = '';
299 } else {
300 unset($_POST['idprod']);
301 unset($_POST['idprodservice']);
302 unset($_POST['qty']);
303 unset($_POST['qty_frozen']);
304 unset($_POST['disable_stock_change']);
305 }
306
307 $object->fetchLines();
308
309 $object->calculateCosts();
310 }
311 }
312}
313
314
315/*
316 * View
317 */
318
319$form = new Form($db);
320$formfile = new FormFile($db);
321
322if ($object->id > 0) {
323 $title = (string) $object->ref;
324} else {
325 $title = $langs->trans('BOM');
326}
327$help_url = 'EN:Module_BOM';
328llxHeader('', $title, $help_url, '', 0, 0, '', '', '', 'mod-bom page-card');
329
330// Part to create
331if ($action == 'create') {
332 print load_fiche_titre($langs->trans("NewBOM"), '', 'bom');
333
334 print '<form method="POST" action="'.dolBuildUrl($_SERVER["PHP_SELF"]).'">';
335 print '<input type="hidden" name="token" value="'.newToken().'">';
336 print '<input type="hidden" name="action" value="add">';
337 print '<input type="hidden" name="backtopage" value="'.$backtopage.'">';
338
339 print dol_get_fiche_head(array(), '');
340
341 print '<table class="border centpercent tableforfieldcreate">'."\n";
342
343 // Common attributes
344 include DOL_DOCUMENT_ROOT.'/core/tpl/commonfields_add.tpl.php';
345
346 // Other attributes
347 include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_add.tpl.php';
348
349 print '</table>'."\n";
350
351 print dol_get_fiche_end();
352
353 print $form->buttonsSaveCancel("Create");
354
355 print '</form>';
356}
357
358// Part to edit record
359if (($id || $ref) && $action == 'edit') {
360 print load_fiche_titre($langs->trans("BillOfMaterials"), '', 'cubes');
361
362 print '<form method="POST" action="'.dolBuildUrl($_SERVER["PHP_SELF"]).'">';
363 print '<input type="hidden" name="token" value="'.newToken().'">';
364 print '<input type="hidden" name="action" value="update">';
365 print '<input type="hidden" name="backtopage" value="'.$backtopage.'">';
366 print '<input type="hidden" name="id" value="'.$object->id.'">';
367
368 print dol_get_fiche_head();
369
370 //$object->fields['keyfield']['disabled'] = 1;
371
372 print '<table class="border centpercent tableforfieldedit">'."\n";
373
374 // Common attributes
375 include DOL_DOCUMENT_ROOT.'/core/tpl/commonfields_edit.tpl.php';
376
377 // Other attributes
378 include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_edit.tpl.php';
379
380 print '</table>';
381
382 print dol_get_fiche_end();
383
384 print $form->buttonsSaveCancel("Update");
385
386 print '</form>';
387}
388
389// Part to show record
390if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'create'))) {
391 $head = bomPrepareHead($object);
392 print dol_get_fiche_head($head, 'card', $langs->trans("BillOfMaterials"), -1, $object->picto, 0, '', '', 0, '', 1);
393
394 $formconfirm = '';
395
396 // Confirmation to delete
397 if ($action == 'delete') {
398 $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id, $langs->trans('DeleteBillOfMaterials'), $langs->trans('ConfirmDeleteBillOfMaterials'), 'confirm_delete', '', 0, 1);
399 }
400 // Confirmation to delete line
401 if ($action == 'deleteline') {
402 $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id.'&lineid='.$lineid, $langs->trans('DeleteLine'), $langs->trans('ConfirmDeleteLine'), 'confirm_deleteline', '', 0, 1);
403 }
404
405 // Confirmation of validation
406 if ($action == 'validate') {
407 $error = 0;
408 $numref = '';
409 // We check that object has a temporary ref
410 $ref = substr($object->ref, 1, 4);
411 if ($ref == 'PROV') {
412 $res = $object->fetch_product();
413 if ($res > 0 && $object->product instanceof Product) {
414 $numref = $object->getNextNumRef($object->product); // @phan-suppress-current-line PhanTypeMismatchArgumentNullable
415 } else {
416 $error++;
417 }
418 } else {
419 $numref = (string) $object->ref;
420 }
421
422 $text = $langs->trans('ConfirmValidateBom', $numref);
423 /*if (isModEnabled('notification')) {
424 require_once DOL_DOCUMENT_ROOT . '/core/class/notify.class.php';
425 $notify = new Notify($db);
426 $text .= '<br>';
427 $text .= $notify->confirmMessage('BOM_VALIDATE', $object->socid, $object);
428 }*/
429
430 $formquestion = array();
431 if (isModEnabled('bom')) {
432 $langs->load("mrp");
433 $formquestion = array(
434 // 'text' => $langs->trans("ConfirmClone"),
435 // array('type' => 'checkbox', 'name' => 'clone_content', 'label' => $langs->trans("CloneMainAttributes"), 'value' => 1),
436 // array('type' => 'checkbox', 'name' => 'update_prices', 'label' => $langs->trans("PuttingPricesUpToDate"), 'value' => 1),
437 );
438 }
439 if (!$error) {
440 $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id, $langs->trans('Validate'), $text, 'confirm_validate', $formquestion, 0, 1, 220);
441 } else {
442 setEventMessage($langs->trans('Error'), 'errors');
443 $action = '';
444 }
445 }
446
447 // Confirmation of closing
448 if ($action == 'close') {
449 $text = $langs->trans('ConfirmCloseBom', $object->ref);
450 /*if (isModEnabled('notification')) {
451 require_once DOL_DOCUMENT_ROOT . '/core/class/notify.class.php';
452 $notify = new Notify($db);
453 $text .= '<br>';
454 $text .= $notify->confirmMessage('BOM_CLOSE', $object->socid, $object);
455 }*/
456
457 $formquestion = array();
458 if (isModEnabled('bom')) {
459 $langs->load("mrp");
460 $formquestion = array(
461 // 'text' => $langs->trans("ConfirmClone"),
462 // array('type' => 'checkbox', 'name' => 'clone_content', 'label' => $langs->trans("CloneMainAttributes"), 'value' => 1),
463 // array('type' => 'checkbox', 'name' => 'update_prices', 'label' => $langs->trans("PuttingPricesUpToDate"), 'value' => 1),
464 );
465 }
466
467 $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id, $langs->trans('Close'), $text, 'confirm_close', $formquestion, 0, 1, 220);
468 }
469
470 // Confirmation of reopen
471 if ($action == 'reopen') {
472 $text = $langs->trans('ConfirmReopenBom', $object->ref);
473 /*if (isModEnabled('notification')) {
474 require_once DOL_DOCUMENT_ROOT . '/core/class/notify.class.php';
475 $notify = new Notify($db);
476 $text .= '<br>';
477 $text .= $notify->confirmMessage('BOM_CLOSE', $object->socid, $object);
478 }*/
479
480 $formquestion = array();
481 if (isModEnabled('bom')) {
482 $langs->load("mrp");
483 require_once DOL_DOCUMENT_ROOT.'/product/class/html.formproduct.class.php';
484 $formquestion = array(
485 // 'text' => $langs->trans("ConfirmClone"),
486 // array('type' => 'checkbox', 'name' => 'clone_content', 'label' => $langs->trans("CloneMainAttributes"), 'value' => 1),
487 // array('type' => 'checkbox', 'name' => 'update_prices', 'label' => $langs->trans("PuttingPricesUpToDate"), 'value' => 1),
488 );
489 }
490
491 $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id, $langs->trans('ReOpen'), $text, 'confirm_reopen', $formquestion, 0, 1, 220);
492 }
493
494 // Clone confirmation
495 if ($action == 'clone') {
496 // Create an array for form
497 $formquestion = array();
498 $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id, $langs->trans('ToClone'), $langs->trans('ConfirmCloneBillOfMaterials', $object->ref), 'confirm_clone', $formquestion, 'yes', 1);
499 }
500
501 // Confirmation of action xxxx
502 if ($action == 'setdraft') {
503 $text = $langs->trans('ConfirmSetToDraft', $object->ref);
504
505 $formquestion = array();
506 $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id, $langs->trans('SetToDraft'), $text, 'confirm_setdraft', $formquestion, 0, 1, 220);
507 }
508
509 // Call Hook formConfirm
510 $parameters = array('formConfirm' => $formconfirm, 'lineid' => $lineid);
511 $reshook = $hookmanager->executeHooks('formConfirm', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
512 if (empty($reshook)) {
513 $formconfirm .= $hookmanager->resPrint;
514 } elseif ($reshook > 0) {
515 $formconfirm = $hookmanager->resPrint;
516 }
517
518 // Print form confirm
519 print $formconfirm;
520
521
522 // Object card
523 // ------------------------------------------------------------
524 $linkback = '<a href="'.DOL_URL_ROOT.'/bom/bom_list.php?restore_lastsearch_values=1">'.$langs->trans("BackToList").'</a>';
525
526 $morehtmlref = '<div class="refidno">';
527 /*
528 // Ref bis
529 $morehtmlref.=$form->editfieldkey("RefBis", 'ref_client', $object->ref_client, $object, $user->hasRight('bom', 'creer'), 'string', '', 0, 1);
530 $morehtmlref.=$form->editfieldval("RefBis", 'ref_client', $object->ref_client, $object, $user->hasRight('bom', 'creer'), 'string', '', null, null, '', 1);
531 // Thirdparty
532 $morehtmlref.='<br>'.$langs->trans('ThirdParty') . ' : ' . $soc->getNomUrl(1);
533 // Project
534 if (isModEnabled('project')) {
535 $langs->load("projects");
536 $morehtmlref.='<br>'.$langs->trans('Project') . ' ';
537 if ($permissiontoadd) {
538 if ($action != 'classify')
539 $morehtmlref.='<a class="editfielda" href="' . dolBuildUrl($_SERVER['PHP_SELF'], ['action' => 'classify', 'id' => $object->id], true) . '">' . img_edit($langs->transnoentitiesnoconv('SetProject')) . '</a> : ';
540 if ($action == 'classify') {
541 //$morehtmlref.=$form->form_project($_SERVER['PHP_SELF'] . '?id=' . $object->id, $object->socid, $object->fk_project, 'projectid', 0, 0, 1, 1);
542 $morehtmlref.='<form method="post" action="'.$_SERVER['PHP_SELF'].'?id='.$object->id.'">';
543 $morehtmlref.='<input type="hidden" name="action" value="classin">';
544 $morehtmlref.='<input type="hidden" name="token" value="'.newToken().'">';
545 $morehtmlref.=$formproject->select_projects($object->socid, $object->fk_project, 'projectid', 0, 0, 1, 0, 1, 0, 0, '', 1);
546 $morehtmlref.='<input type="submit" class="button valignmiddle" value="'.$langs->trans("Modify").'">';
547 $morehtmlref.='</form>';
548 } else {
549 $morehtmlref.=$form->form_project($_SERVER['PHP_SELF'] . '?id=' . $object->id, $object->socid, $object->fk_project, 'none', 0, 0, 0, 1);
550 }
551 } else {
552 if (! empty($object->fk_project)) {
553 $proj = new Project($db);
554 $proj->fetch($object->fk_project);
555 $morehtmlref.=$proj->getNomUrl();
556 } else {
557 $morehtmlref.='';
558 }
559 }
560 }
561 */
562 $morehtmlref .= '</div>';
563
564
565 dol_banner_tab($object, 'ref', $linkback, 1, 'ref', 'ref', $morehtmlref);
566
567
568 print '<div class="fichecenter">';
569 print '<div class="fichehalfleft">';
570 print '<div class="underbanner clearboth"></div>';
571 print '<table class="border centpercent tableforfield">'."\n";
572
573 // Common attributes
574 $keyforbreak = 'duration';
575 include DOL_DOCUMENT_ROOT.'/core/tpl/commonfields_view.tpl.php';
576
577 // Manufacturing cost
578 print '<tr><td>'.$form->textwithpicto($langs->trans("ManufacturingCost"), $langs->trans("BOMTotalCost")).'</td><td><span class="amount">';
579 print price($object->total_cost);
580 print '</span>';
581 if ($object->total_cost != $object->unit_cost) {
582 print '&nbsp; &nbsp; <span class="opacitymedium">('.$form->textwithpicto(price($object->unit_cost), $langs->trans("ManufacturingUnitCost"), 1, 'help', '').')</span>';
583 }
584 print '</td></tr>';
585
586 // Find sell price of generated product. We suppose we sell it to a company like ours (same country...).
587 $object->fetch_product();
588 $manufacturedvalued = '';
589 if (!empty($object->product)) {
590 $tmparray = $object->product->getSellPrice($mysoc, $mysoc);
591 $manufacturedvalued = $tmparray['pu_ht'] * $object->qty;
592 }
593 print '<tr><td>'.$langs->trans("ManufacturingGeneratedValue").'</td><td><span class="amount">'.price($manufacturedvalued).'</span></td></tr>';
594
595 // Other attributes
596 include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_view.tpl.php';
597
598 print '</table>';
599 print '</div>';
600 print '</div>';
601
602 print '<div class="clearboth"></div>';
603
604 print dol_get_fiche_end();
605
606
607
608 /*
609 * Lines
610 */
611
612 if (!empty($object->table_element_line)) {
613 // Products
614
615 $res = $object->fetchLinesbytypeproduct(0); // Load all lines products into ->lines
616 $object->calculateCosts();
617
618 print ($res == 0 && $object->status >= $object::STATUS_VALIDATED) ? '' : load_fiche_titre($langs->trans('BOMProductsList'), '', 'product');
619
620 print ' <form name="addproduct" id="listbomproducts" action="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '" method="POST">
621 <input type="hidden" name="token" value="' . newToken() . '">
622 <input type="hidden" name="action" value="' . (($action != 'editline') ? 'addline' : 'updateline') . '">
623 <input type="hidden" name="mode" value="">
624 <input type="hidden" name="page_y" value="">
625 <input type="hidden" name="id" value="' . $object->id . '">
626 ';
627
628 if (!empty($conf->use_javascript_ajax) && $object->status == 0) {
629 include DOL_DOCUMENT_ROOT.'/core/tpl/ajaxrow.tpl.php';
630 }
631
632 print '<div class="div-table-responsive-no-min">';
633 if (!empty($object->lines) || ($object->status == $object::STATUS_DRAFT && $permissiontoadd && $action != 'selectlines' && $action != 'editline')) {
634 print '<table id="tablelines" class="noborder noshadow centpercent">';
635 }
636
637 if (!empty($object->lines)) {
638 $object->printObjectLines($action, $mysoc, null, GETPOSTINT('lineid'), 1, '/bom/tpl');
639 }
640
641 // Form to add new line
642 if ($object->status == 0 && $permissiontoadd && $action != 'selectlines') {
643 if ($action != 'editline') {
644 // Add products/services form
645
646
647 $parameters = array();
648 $reshook = $hookmanager->executeHooks('formAddObjectLine', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
649 if ($reshook < 0) {
650 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
651 }
652 if (empty($reshook)) {
653 $object->formAddObjectLine(1, $mysoc, null, '/bom/tpl');
654 }
655 }
656 }
657
658 if (!empty($object->lines) || ($object->status == $object::STATUS_DRAFT && $permissiontoadd && $action != 'selectlines' && $action != 'editline')) {
659 print '</table>';
660 }
661 print '</div>';
662
663 print "</form>\n";
664
665
666 // Services
667
668 $filtertype = 1;
669 $res = $object->fetchLinesbytypeproduct($filtertype); // Load all lines services into ->lines
670 $object->calculateCosts();
671
672 print ($res == 0 && $object->status >= $object::STATUS_VALIDATED) ? '' : load_fiche_titre($langs->trans('BOMServicesList'), '', 'service');
673
674 print ' <form name="addservice" id="listbomservices" action="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '" method="POST">
675 <input type="hidden" name="token" value="' . newToken() . '">
676 <input type="hidden" name="action" value="' . (($action != 'editline') ? 'addline' : 'updateline') . '">
677 <input type="hidden" name="mode" value="">
678 <input type="hidden" name="page_y" value=""> <input type="hidden" name="id" value="' . $object->id . '">
679 ';
680
681 if (!empty($conf->use_javascript_ajax) && $object->status == 0) {
682 $tagidfortablednd = 'tablelinesservice';
683 include DOL_DOCUMENT_ROOT . '/core/tpl/ajaxrow.tpl.php';
684 }
685
686 print '<div class="div-table-responsive-no-min">';
687 if (!empty($object->lines) || ($object->status == $object::STATUS_DRAFT && $permissiontoadd && $action != 'selectlines' && $action != 'editline')) {
688 print '<table id="tablelinesservice" class="noborder noshadow centpercent">';
689 }
690
691 if (!empty($object->lines)) {
692 $object->printObjectLines($action, $mysoc, null, GETPOSTINT('lineid'), 1, '/bom/tpl');
693 }
694
695 // Form to add new line
696 if ($object->status == 0 && $permissiontoadd && $action != 'selectlines') {
697 if ($action != 'editline') {
698 // Add services form
699 $parameters = array();
700 $reshook = $hookmanager->executeHooks('formAddObjectServiceLine', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
701 if ($reshook < 0) {
702 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
703 }
704 if (empty($reshook)) {
705 $object->formAddObjectLine(1, $mysoc, null, '/bom/tpl');
706 }
707 }
708 }
709 }
710
711 if (!empty($object->lines) || ($object->status == $object::STATUS_DRAFT && $permissiontoadd && $action != 'selectlines' && $action != 'editline')) {
712 print '</table>';
713 }
714 print '</div>';
715
716 print "</form>\n";
717
718
720
721
722 $res = $object->fetchLines();
723
724 // Buttons for actions
725
726 if ($action != 'presend' && $action != 'editline') {
727 print '<div class="tabsAction">'."\n";
728 $parameters = array();
729 $reshook = $hookmanager->executeHooks('addMoreActionsButtons', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
730 if ($reshook < 0) {
731 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
732 }
733
734 if (empty($reshook)) {
735 // Send
736 //if (empty($user->socid)) {
737 // print '<a class="butAction" href="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '&action=presend&mode=init#formmailbeforetitle">' . $langs->trans('SendMail') . '</a>'."\n";
738 //}
739
740 // Back to draft
741 if ($object->status == $object::STATUS_VALIDATED) {
742 if ($permissiontoadd) {
743 print '<a class="butAction" href="'.$_SERVER['PHP_SELF'].'?id='.$object->id.'&action=setdraft&token='.newToken().'">'.$langs->trans("SetToDraft").'</a>'."\n";
744 }
745 }
746
747 // Modify
748 if ($object->status == $object::STATUS_DRAFT) {
749 if ($permissiontoadd) {
750 print '<a class="butAction" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=edit&token='.newToken().'">'.$langs->trans("Modify").'</a>'."\n";
751 } else {
752 print '<a class="butActionRefused classfortooltip" href="#" title="'.dol_escape_htmltag($langs->trans("NotEnoughPermissions")).'">'.$langs->trans('Modify').'</a>'."\n";
753 }
754 }
755
756 // Validate
757 if ($object->status == $object::STATUS_DRAFT) {
758 if ($permissiontoadd) {
759 if (is_array($object->lines) && count($object->lines) > 0) {
760 print '<a class="butAction" href="'.$_SERVER['PHP_SELF'].'?id='.$object->id.'&amp;action=validate&amp;token='.newToken().'">'.$langs->trans("Validate").'</a>'."\n";
761 } else {
762 $langs->load("errors");
763 print '<a class="butActionRefused classfortooltip" href="" title="'.$langs->trans("ErrorAddAtLeastOneLineFirst").'">'.$langs->trans("Validate").'</a>'."\n";
764 }
765 }
766 }
767
768 // Re-open
769 if ($permissiontoadd && $object->status == $object::STATUS_CANCELED) {
770 print '<a class="butAction" href="'.$_SERVER['PHP_SELF'].'?id='.$object->id.'&action=reopen&token='.newToken().'">'.$langs->trans("ReOpen").'</a>'."\n";
771 }
772
773 // Create MO
774 if (isModEnabled('mrp')) {
775 if ($object->status == $object::STATUS_VALIDATED && $user->hasRight('mrp', 'write')) {
776 print '<a class="butAction" href="'.DOL_URL_ROOT.'/mrp/mo_card.php?action=create&fk_bom='.$object->id.'&token='.newToken().'&backtopageforcancel='.urlencode($_SERVER["PHP_SELF"].'?id='.$object->id).'">'.$langs->trans("CreateMO").'</a>'."\n";
777 }
778 }
779
780 // Clone
781 if ($permissiontoadd) {
782 print dolGetButtonAction($langs->trans("ToClone"), '', 'default', $_SERVER['PHP_SELF'].'?id='.$object->id.'&action=clone&object=bom', 'clone', $permissiontoadd);
783 }
784
785 // Close / Cancel
786 if ($permissiontoadd && $object->status == $object::STATUS_VALIDATED) {
787 print '<a class="butActionDelete" href="'.$_SERVER['PHP_SELF'].'?id='.$object->id.'&action=close&token='.newToken().'">'.$langs->trans("Disable").'</a>'."\n";
788 }
789
790 /*
791 if ($user->hasRight('bom', 'write')) {
792 if ($object->status == 1) {
793 print '<a class="butActionDelete" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=disable&token='.newToken().'">'.$langs->trans("Disable").'</a>'."\n";
794 } else {
795 print '<a class="butAction" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=enable&token='.newToken().'">'.$langs->trans("Enable").'</a>'."\n";
796 }
797 }
798 */
799
800 // Delete
801 print dolGetButtonAction($langs->trans("Delete"), '', 'delete', $_SERVER["PHP_SELF"].'?id='.$object->id.'&action=delete&token='.newToken(), 'delete', $permissiontodelete);
802 }
803 print '</div>'."\n";
804 }
805
806
807 // Select mail models is same action as presend
808 if (GETPOST('modelselected')) {
809 $action = 'presend';
810 }
811
812 if ($action != 'presend') {
813 print '<div class="fichecenter"><div class="fichehalfleft">';
814 print '<a name="builddoc"></a>'; // ancre
815
816 // Documents
817 $objref = dol_sanitizeFileName($object->ref);
818 $relativepath = $objref.'/'.$objref.'.pdf';
819 $filedir = getMultidirOutput($object) . '/'.$objref;
820 $urlsource = $_SERVER["PHP_SELF"]."?id=".$object->id;
821 $genallowed = $user->hasRight('bom', 'read'); // If you can read, you can build the PDF to read content
822 $delallowed = $user->hasRight('bom', 'write'); // If you can create/edit, you can remove a file on card
823 print $formfile->showdocuments('bom', $objref, $filedir, $urlsource, $genallowed, $delallowed, $object->model_pdf, 1, 0, 0, 28, 0, '', '', '', $langs->defaultlang, '', $object);
824
825 // Show links to link elements
826 $tmparray = $form->showLinkToObjectBlock($object, array(), array('bom'), 1);
827 $linktoelem = $tmparray['linktoelem'];
828 $htmltoenteralink = $tmparray['htmltoenteralink'];
829 print $htmltoenteralink;
830
831 $somethingshown = $form->showLinkedObjectBlock($object, $linktoelem);
832
833
834 print '</div><div class="fichehalfright">';
835
836 $MAXEVENT = 10;
837
838 $morehtmlcenter = dolGetButtonTitle($langs->trans('SeeAll'), '', 'fa fa-bars imgforviewmode', DOL_URL_ROOT.'/bom/bom_agenda.php?id='.$object->id);
839
840 // List of actions on element
841 include_once DOL_DOCUMENT_ROOT.'/core/class/html.formactions.class.php';
842 $formactions = new FormActions($db);
843 $somethingshown = $formactions->showactions($object, $object->element, 0, 1, '', $MAXEVENT, '', $morehtmlcenter);
844
845 print '</div></div>';
846 }
847
848 //Select mail models is same action as presend
849 if (GETPOST('modelselected')) {
850 $action = 'presend';
851 }
852
853 // Presend form
854 $modelmail = 'bom';
855 $defaulttopic = 'InformationMessage';
856 $diroutput = getMultidirOutput($object);
857 $trackid = 'bom'.$object->id;
858
859 include DOL_DOCUMENT_ROOT.'/core/tpl/card_presend.tpl.php';
860}
861
862
863// End of page
864llxFooter();
865$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
bomPrepareHead($object)
Prepare array of tabs for BillOfMaterials.
Definition bom.lib.php:92
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 to manage products or services.
const TYPE_SERVICE
Service.
global $mysoc
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0, $attop=0)
Set event messages in dol_events session object.
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 '.
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.
setEventMessage($mesgs, $style='mesgs', $noduplicate=0, $attop=0)
Set event message in dol_events session object.
dol_sanitizeFileName($str, $newstr='_', $unaccent=1, $includequotes=0, $allowdash=0)
Clean a string to use it as a file name.
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.
newToken()
Return the value of token currently saved into session with name 'newtoken'.
dolGetButtonAction($label, $text='', $actionType='default', $url='', $id='', $userRight=1, $params=array())
Function dolGetButtonAction.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
if(!function_exists( 'utf8_encode')) if(!function_exists('utf8_decode')) if(!function_exists( 'str_starts_with')) if(!function_exists('str_ends_with')) if(!function_exists( 'str_contains')) getMultidirOutput($object, $module='', $forobject=0, $mode='output')
Return the full path of the directory where a module (or an object of a module) stores its files.
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.
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.