dolibarr 23.0.3
bom_net_needs.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2017-2020 Laurent Destailleur <eldy@users.sourceforge.net>
3 * Copyright (C) 2019-2025 Frédéric France <frederic.france@free.fr>
4 * Copyright (C) 2025 MDW <mdeweerd@users.noreply.github.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
26// Load Dolibarr environment
27require '../main.inc.php';
28require_once DOL_DOCUMENT_ROOT.'/core/class/html.formcompany.class.php';
29require_once DOL_DOCUMENT_ROOT.'/core/class/html.formfile.class.php';
30require_once DOL_DOCUMENT_ROOT.'/bom/class/bom.class.php';
31require_once DOL_DOCUMENT_ROOT.'/bom/lib/bom.lib.php';
32
42// Load translation files required by the page
43$langs->loadLangs(array("mrp", "other", "stocks"));
44
45// Get parameters
46$id = GETPOSTINT('id');
47$lineid = GETPOSTINT('lineid');
48$ref = GETPOST('ref', 'alpha');
49$action = GETPOST('action', 'aZ09');
50$confirm = GETPOST('confirm', 'alpha');
51$cancel = GETPOST('cancel', 'alpha');
52$contextpage = GETPOST('contextpage', 'aZ') ? GETPOST('contextpage', 'aZ') : 'bomnet_needs'; // To manage different context of search
53$backtopage = GETPOST('backtopage', 'alpha');
54
55
56// Initialize a technical objects
57$object = new BOM($db);
58$extrafields = new ExtraFields($db);
59
60// Initialize a technical objects for hooks
61$hookmanager->initHooks(array('bomnetneeds')); // Note that conf->hooks_modules contains array
62
63// Massaction
64$diroutputmassaction = getMultidirOutput($object) . '/temp/massgeneration/'.$user->id;
65
66// Fetch optionals attributes and labels
67$extrafields->fetch_name_optionals_label($object->table_element);
68$search_array_options = $extrafields->getOptionalsFromPost($object->table_element, '', 'search_');
69
70// Initialize array of search criteria
71$search_all = GETPOST("search_all", 'alpha');
72$search = array();
73foreach ($object->fields as $key => $val) {
74 if (GETPOST('search_'.$key, 'alpha')) {
75 $search[$key] = GETPOST('search_'.$key, 'alpha');
76 }
77}
78
79if (empty($action) && empty($id) && empty($ref)) {
80 $action = 'view';
81}
82
83// Load object
84include DOL_DOCUMENT_ROOT.'/core/actions_fetchobject.inc.php'; // Must be 'include', not 'include_once'.
85if ($object->id > 0) {
86 $object->calculateCosts();
87}
88
89
90// Security check - Protection if external user
91//if ($user->socid > 0) accessforbidden();
92//if ($user->socid > 0) $socid = $user->socid;
93$isdraft = (($object->status == $object::STATUS_DRAFT) ? 1 : 0);
94restrictedArea($user, 'bom', $object->id, $object->table_element, '', '', 'rowid', $isdraft);
95
96// Permissions
97$permissionnote = $user->hasRight('bom', 'write'); // Used by the include of actions_setnotes.inc.php
98$permissiondellink = $user->hasRight('bom', 'write'); // Used by the include of actions_dellink.inc.php
99$permissiontoadd = $user->hasRight('bom', 'write'); // Used by the include of actions_addupdatedelete.inc.php and actions_lineupdown.inc.php
100$permissiontodelete = $user->hasRight('bom', 'delete') || ($permissiontoadd && isset($object->status) && $object->status == $object::STATUS_DRAFT);
101$upload_dir = $conf->bom->multidir_output[isset($object->entity) ? $object->entity : 1];
102
103
104/*
105 * Actions
106 */
107
108$parameters = array();
109$reshook = $hookmanager->executeHooks('doActions', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
110if ($reshook < 0) {
111 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
112}
113
114if (empty($reshook)) {
115 $error = 0;
116
117 $backurlforlist = DOL_URL_ROOT.'/bom/bom_list.php';
118
119 if (empty($backtopage) || ($cancel && empty($id))) {
120 if (empty($backtopage) || ($cancel && strpos($backtopage, '__ID__'))) {
121 if (empty($id) && (($action != 'add' && $action != 'create') || $cancel)) {
122 $backtopage = $backurlforlist;
123 } else {
124 $backtopage = DOL_URL_ROOT.'/bom/bom_net_needs.php?id='.($id > 0 ? $id : '__ID__');
125 }
126 }
127 }
128}
129
130
131/*
132 * View
133 */
134
135$form = new Form($db);
136$formfile = new FormFile($db);
137
138$title = $langs->trans('BOM');
139$help_url = 'EN:Module_BOM';
140
141llxHeader('', $title, $help_url, '', 0, 0, '', '', '', 'mod-bom page-net_needs');
142
143
144$TChildBom = array();
145if ($action == 'treeview') {
146 $object->getNetNeedsTree($TChildBom, 1);
147} else {
148 $object->getNetNeeds($TChildBom, 1);
149}
150
151
152// Part to show record
153if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'create'))) {
154 $head = bomPrepareHead($object);
155 print dol_get_fiche_head($head, 'net_needs', $langs->trans("BillOfMaterials"), -1, $object->picto);
156
157 $formconfirm = '';
158
159 // Call Hook formConfirm
160 $parameters = array('formConfirm' => $formconfirm, 'lineid' => $lineid);
161 $reshook = $hookmanager->executeHooks('formConfirm', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
162 if (empty($reshook)) {
163 $formconfirm .= $hookmanager->resPrint;
164 } elseif ($reshook > 0) {
165 $formconfirm = $hookmanager->resPrint;
166 }
167
168 // Print form confirm
169 print $formconfirm;
170
171
172 // Object card
173 // ------------------------------------------------------------
174 $linkback = '<a href="'.DOL_URL_ROOT.'/bom/bom_list.php?restore_lastsearch_values=1">'.$langs->trans("BackToList").'</a>';
175
176 $morehtmlref = '<div class="refidno">';
177
178 $morehtmlref .= '</div>';
179
180
181 dol_banner_tab($object, 'ref', $linkback, 1, 'ref', 'ref', $morehtmlref);
182
183
184 print '<div class="fichecenter">';
185 print '<div class="fichehalfleft">';
186 print '<div class="underbanner clearboth"></div>';
187 print '<table class="border centpercent tableforfield">'."\n";
188
189 // Common attributes
190 $keyforbreak = 'duration'; // used into tpl
191 include DOL_DOCUMENT_ROOT.'/core/tpl/commonfields_view.tpl.php';
192
193 // Manufacturing cost
194 print '<tr><td>'.$form->textwithpicto($langs->trans("ManufacturingCost"), $langs->trans("BOMTotalCost")).'</td><td><span class="amount">';
195 print price($object->total_cost);
196 print '</span>';
197 if ($object->total_cost != $object->unit_cost) {
198 print '&nbsp; &nbsp; <span class="opacitymedium">('.$form->textwithpicto(price($object->unit_cost), $langs->trans("ManufacturingUnitCost"), 1, 'help', '').')</span>';
199 }
200 print '</td></tr>';
201
202 // Find sell price of generated product. We suppose we sell it to a company like ours (same country...).
203 $res = $object->fetch_product();
204 $manufacturedvalued = '';
205 if ($res && is_object($object->product) && !$object->product->isEmpty()) {
206 global $mysoc;
207 $tmparray = $object->product->getSellPrice($mysoc, $mysoc);
208 $manufacturedvalued = $tmparray['pu_ht'] * $object->qty;
209 }
210 print '<tr><td>'.$langs->trans("ManufacturingGeneratedValue").'</td><td>'.price($manufacturedvalued).'</td></tr>';
211
212 // Other attributes
213 include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_view.tpl.php';
214
215 print '</table>';
216 print '</div>';
217 print '</div>';
218
219 print '<div class="clearboth"></div>';
220
221 print dol_get_fiche_end();
222
223 $viewlink = dolGetButtonTitle($langs->trans('GroupByX', $langs->transnoentitiesnoconv("Products")), '', 'fa fa-bars imgforviewmode', $_SERVER['PHP_SELF'].'?id='.$object->id.'&token='.newToken(), '', 1, array('morecss' => 'reposition '.($action !== 'treeview' ? 'btnTitleSelected' : '')));
224 $viewlink .= dolGetButtonTitle($langs->trans('TreeView'), '', 'fa fa-stream imgforviewmode', $_SERVER['PHP_SELF'].'?id='.$object->id.'&action=treeview&token='.newToken(), '', 1, array('morecss' => 'reposition marginleftonly '.($action == 'treeview' ? 'btnTitleSelected' : '')));
225
226 print load_fiche_titre($langs->trans("BOMNetNeeds"), $viewlink, 'product');
227
228 /*
229 * Lines
230 */
231 $text_stock_options = $langs->trans("RealStockDesc").'<br>';
232 $text_stock_options .= $langs->trans("RealStockWillAutomaticallyWhen").'<br>';
233 $text_stock_options .= (getDolGlobalString('STOCK_CALCULATE_ON_SHIPMENT') || getDolGlobalString('STOCK_CALCULATE_ON_SHIPMENT_CLOSE') ? '- '.$langs->trans("DeStockOnShipment").'<br>' : '');
234 $text_stock_options .= (getDolGlobalString('STOCK_CALCULATE_ON_VALIDATE_ORDER') ? '- '.$langs->trans("DeStockOnValidateOrder").'<br>' : '');
235 $text_stock_options .= (getDolGlobalString('STOCK_CALCULATE_ON_BILL') ? '- '.$langs->trans("DeStockOnBill").'<br>' : '');
236 $text_stock_options .= (getDolGlobalString('STOCK_CALCULATE_ON_SUPPLIER_BILL') ? '- '.$langs->trans("ReStockOnBill").'<br>' : '');
237 $text_stock_options .= (getDolGlobalString('STOCK_CALCULATE_ON_SUPPLIER_VALIDATE_ORDER') ? '- '.$langs->trans("ReStockOnValidateOrder").'<br>' : '');
238 $text_stock_options .= (getDolGlobalString('STOCK_CALCULATE_ON_SUPPLIER_DISPATCH_ORDER') ? '- '.$langs->trans("ReStockOnDispatchOrder").'<br>' : '');
239 $text_stock_options .= (getDolGlobalString('STOCK_CALCULATE_ON_RECEPTION') || getDolGlobalString('STOCK_CALCULATE_ON_RECEPTION_CLOSE') ? '- '.$langs->trans("StockOnReception").'<br>' : '');
240
241 print '<table id="tablelines" class="noborder noshadow" width="100%">';
242 print "<thead>\n";
243 print '<tr class="liste_titre nodrag nodrop">';
244 print '<td class="linecoldescription">'.$langs->trans('Product');
245 if (getDolGlobalString('BOM_SUB_BOM') && $action == 'treeview') {
246 print ' &nbsp; <a id="show_all" href="#">'.img_picto('', 'folder-open', 'class="paddingright"').$langs->trans("ExpandAll").'</a>&nbsp;&nbsp;';
247 print '<a id="hide_all" href="#">'.img_picto('', 'folder', 'class="paddingright"').$langs->trans("UndoExpandAll").'</a>&nbsp;';
248 }
249 print '</td>';
250 if ($action == 'treeview') {
251 print '<td class="left">'.$langs->trans('ProducedBy').'</td>';
252 }
253 print '<td class="linecolqty right">'.$langs->trans('Quantity').'</td>';
254 print '<td></td>'; // For unit
255 print '<td class="linecolstock right">'.$form->textwithpicto($langs->trans("PhysicalStock"), $text_stock_options, 1).'</td>';
256 print '<td class="linecoltheoricalstock right">'.$form->textwithpicto($langs->trans("VirtualStock"), $langs->trans("VirtualStockDesc")).'</td>';
257 print '</tr>';
258
259 print '</thead>';
260 print '<tbody>';
261 if (count($TChildBom) > 0) {
262 if ($action == 'treeview') {
263 foreach ($TChildBom as $fk_bom => $TProduct) {
264 $repeatChar = '&emsp;';
265 if (!empty($TProduct['bom'])) {
266 $prod = new Product($db);
267 $prod->fetch($TProduct['bom']->fk_product);
268 if ($TProduct['parentid'] != $object->id) {
269 print '<tr class="sub_bom_lines oddeven" parentid="'.$TProduct['parentid'].'">';
270 } else {
271 print '<tr class="oddeven">';
272 }
273 if ($action == 'treeview') {
274 print '<td class="linecoldescription">'.str_repeat($repeatChar, $TProduct['level']).$prod->getNomUrl(1);
275 } else {
276 print '<td class="linecoldescription">'.str_repeat($repeatChar, $TProduct['level']).$TProduct['bom']->getNomUrl(1);
277 }
278 print ' <a class="collapse_bom" id="collapse-'.$fk_bom.'" href="#">';
279 print img_picto('', 'folder-open');
280 print '</a>';
281 print '</td>';
282 if ($action == 'treeview') {
283 print '<td class="left">'.$TProduct['bom']->getNomUrl(1).'</td>';
284 }
285 print '<td class="linecolqty right">'.price(price2num($TProduct['qty'], 'MS')).'</td>';
286 print '<td>';
287 print '</td>';
288 print '<td class="linecolstock right"></td>';
289 print '<td class="linecoltheoricalstock right"></td>';
290 print '</tr>';
291 }
292 if (!empty($TProduct['product'])) {
293 foreach ($TProduct['product'] as $fk_product => $TInfos) {
294 $prod = new Product($db);
295 $prod->fetch($fk_product);
296 $prod->load_virtual_stock();
297 if (empty($prod->stock_reel)) {
298 $prod->stock_reel = 0;
299 }
300 if ($fk_bom != $object->id) {
301 print '<tr class="sub_bom_lines oddeven" parentid="'.$fk_bom.'">';
302 } else {
303 print '<tr class="oddeven">';
304 }
305 print '<td class="linecoldescription">'.str_repeat($repeatChar, $TInfos['level']).$prod->getNomUrl(1).'</td>';
306 if ($action == 'treeview') {
307 print '<td></td>';
308 }
309 print '<td class="linecolqty right">'.price(price2num($TInfos['qty'], 'MS')).'</td>';
310 print '<td>';
311 print '</td>';
312 print '<td class="linecolstock right">'.price2num($prod->stock_reel, 'MS').'</td>';
313 print '<td class="linecoltheoricalstock right">'.$prod->stock_theorique.'</td>';
314 print '</tr>';
315 }
316 }
317 }
318 } else {
319 foreach ($TChildBom as $fk_product => $elem) {
320 $prod = new Product($db);
321 $prod->fetch($fk_product);
322 $prod->load_virtual_stock();
323 if (empty($prod->stock_reel)) {
324 $prod->stock_reel = 0;
325 }
326 print '<tr class="oddeven">';
327 print '<td class="linecoldescription">'.$prod->getNomUrl(1).'</td>';
328 print '<td class="linecolqty right">'.price(price2num($elem['qty'], 'MS')).'</td>';
329 print '<td>';
330 $useunit = (($prod->type == Product::TYPE_PRODUCT && getDolGlobalInt('PRODUCT_USE_UNITS')) || (($prod->type == Product::TYPE_SERVICE) && ($elem['fk_unit'])));
331 if ($useunit) {
332 require_once DOL_DOCUMENT_ROOT.'/core/class/cunits.class.php';
333 $unit = new CUnits($db);
334 $unit->fetch((int) $elem['fk_unit']);
335 print(isset($unit->label) ? "&nbsp;".$langs->trans(ucwords((string) $unit->label))."&nbsp;" : '');
336 }
337 print '</td>';
338 print '<td class="linecolstock right">'.price(price2num($prod->stock_reel, 'MS')).'</td>';
339 print '<td class="linecoltheoricalstock right">'.$prod->stock_theorique.'</td>';
340 print '</tr>';
341 }
342 }
343 }
344 print '</tbody>';
345 print '</table>';
346
347
348
349 /*
350 * ButAction
351 */
352 print '<div class="tabsAction">'."\n";
353 $parameters = array();
354 $reshook = $hookmanager->executeHooks('addMoreActionsButtons', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
355 if ($reshook < 0) {
356 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
357 }
358 print '</div>'; ?>
359
360 <script type="text/javascript" language="javascript">
361 $(document).ready(function() {
362
363 function folderManage(element) {
364 var id_bom_line = element.attr('id').replace('collapse-', '');
365 let TSubLines = $('[parentid="'+ id_bom_line +'"]');
366
367 if(element.html().indexOf('folder-open') <= 0) {
368 $('[parentid="'+ id_bom_line +'"]').show();
369 element.html('<?php echo dol_escape_js(img_picto('', 'folder-open')); ?>');
370 }
371 else {
372 for (let i = 0; i < TSubLines.length; i++) {
373 let subBomFolder = $(TSubLines[i]).children('.linecoldescription').children('.collapse_bom');
374 if (subBomFolder.length > 0) {
375 folderManage(subBomFolder);
376 }
377 }
378 TSubLines.hide();
379 element.html('<?php echo dol_escape_js(img_picto('', 'folder')); ?>');
380 }
381 }
382
383 // When clicking on collapse
384 $(".collapse_bom").click(function() {
385 folderManage($(this));
386 return false;
387 });
388
389 // To Show all the sub bom lines
390 $("#show_all").click(function() {
391 console.log("We click on show all");
392 $("[class^=sub_bom_lines]").show();
393 $("[class^=collapse_bom]").html('<?php echo dol_escape_js(img_picto('', 'folder-open')); ?>');
394 return false;
395 });
396
397 // To Hide all the sub bom lines
398 $("#hide_all").click(function() {
399 console.log("We click on hide all");
400 $("[class^=sub_bom_lines]").hide();
401 $("[class^=collapse_bom]").html('<?php echo dol_escape_js(img_picto('', 'folder')); ?>');
402 return false;
403 });
404
405 });
406 </script>
407
408 <?php
409}
410
411// End of page
412llxFooter();
413$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
bomPrepareHead($object)
Prepare array of tabs for BillOfMaterials.
Definition bom.lib.php:92
Class for BOM.
Definition bom.class.php:42
Class of dictionary type of thirdparty (used by imports)
Class to manage standard extra fields.
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_PRODUCT
Regular product.
const TYPE_SERVICE
Service.
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.
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.
price($amount, $form=0, $outlangs='', $trunc=1, $rounding=-1, $forcerounding=-1, $currency_code='')
Function to format a value into an amount for visual output Function used into PDF and HTML pages.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
newToken()
Return the value of token currently saved into session with name 'newtoken'.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
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.
if(preg_match('/(crypted|dolcrypt):/i', $dolibarr_main_db_pass)||!empty($dolibarr_main_db_encrypted_pass)) $conf db type
'integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter[:Sortfield]]]',...
Definition repair.php:125
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.