{% load i18n %} /* BOM management functions. * Requires follwing files to be loaded first: * - api.js * - part.js * - modals.js */ function reloadBomTable(table, options) { table.bootstrapTable('refresh'); } function removeRowFromBomWizard(e) { /* Remove a row from BOM upload wizard */ e = e || window.event; var src = e.target || e.srcElement; var table = $(src).closest('table'); // Which column was clicked? var row = $(src).closest('tr'); row.remove(); var rowNum = 1; var colNum = 0; table.find('tr').each(function() { colNum++; if (colNum >= 3) { var cell = $(this).find('td:eq(1)'); cell.text(rowNum++); } }); } function removeColFromBomWizard(e) { /* Remove a column from BOM upload wizard */ e = e || window.event; var src = e.target || e.srcElement; // Which column was clicked? var col = $(src).closest('th').index(); var table = $(src).closest('table'); table.find('tr').each(function() { this.removeChild(this.cells[col]); }); } function newPartFromBomWizard(e) { /* Create a new part directly from the BOM wizard. */ e = e || window.event; var src = e.target || e.srcElement; var row = $(src).closest('tr'); launchModalForm('/part/new/', { data: { 'description': row.attr('part-description'), 'name': row.attr('part-name'), }, success: function(response) { /* A new part has been created! Push it as an option. */ var select = row.attr('part-select'); var option = new Option(response.text, response.pk, true, true); $(select).append(option).trigger('change'); } }); } function loadBomTable(table, options) { /* Load a BOM table with some configurable options. * * Following options are available: * editable - Should the BOM table be editable? * bom_url - Address to request BOM data from * part_url - Address to request Part data from * parent_id - Parent ID of the owning part * * BOM data are retrieved from the server via AJAX query */ // Construct the table columns var cols = []; if (options.editable) { cols.push({ field: 'ID', title: '', checkbox: true, visible: true, switchable: false, }); } // Part column cols.push( { field: 'sub_part', title: '{% trans "Part" %}', sortable: true, formatter: function(value, row, index, field) { var url = `/part/${row.sub_part}/`; var html = imageHoverIcon(row.sub_part_detail.thumbnail) + renderLink(row.sub_part_detail.full_name, url); // Display an extra icon if this part is an assembly if (row.sub_part_detail.assembly) { var text = ``; html += renderLink(text, `/part/${row.sub_part}/bom/`); } return html; } } ); // Part description cols.push( { field: 'sub_part_detail.description', title: '{% trans "Description" %}', } ); // Part reference cols.push({ field: 'reference', title: '{% trans "Reference" %}', searchable: true, sortable: true, }); // Part quantity cols.push({ field: 'quantity', title: '{% trans "Quantity" %}', searchable: false, sortable: true, formatter: function(value, row, index, field) { var text = value; // The 'value' is a text string with (potentially) multiple trailing zeros // Let's make it a bit more pretty text = parseFloat(text); if (row.optional) { text += " ({% trans "Optional" %})"; } if (row.overage) { text += " (+" + row.overage + ") "; } return text; }, }); if (!options.editable) { cols.push( { field: 'sub_part_detail.stock', title: '{% trans "Available" %}', searchable: false, sortable: true, formatter: function(value, row, index, field) { var url = `/part/${row.sub_part_detail.pk}/stock/`; var text = value; if (value == null || value <= 0) { text = `{% trans "No Stock" %}`; } return renderLink(text, url); } }); cols.push( { field: 'price_range', title: '{% trans "Price" %}', sortable: true, formatter: function(value, row, index, field) { if (value) { return value; } else { return "{% trans "No pricing available" %}"; } } }); } // Part notes cols.push( { field: 'note', title: '{% trans "Notes" %}', searchable: true, sortable: true, } ); if (options.editable) { cols.push({ title: '{% trans "Actions" %}', switchable: false, field: 'pk', visible: true, formatter: function(value, row, index, field) { if (row.part == options.parent_id) { var bValidate = ``; var bValid = ``; var bEdit = ``; var bDelt = ``; var html = "
"; html += bEdit; html += bDelt; if (!row.validated) { html += bValidate; } else { html += bValid; } html += "
"; return html; } else { return ''; } } }); } // Configure the table (bootstrap-table) var params = { part: options.parent_id, ordering: 'name', } if (options.part_detail) { params.part_detail = true; } if (options.sub_part_detail) { params.sub_part_detail = true; } // Function to request BOM data for sub-items // This function may be called recursively for multi-level BOMs function requestSubItems(bom_pk, part_pk) { inventreeGet( options.bom_url, { part: part_pk, sub_part_detail: true, }, { success: function(response) { for (var idx = 0; idx < response.length; idx++) { response[idx].parentId = bom_pk; if (response[idx].sub_part_detail.assembly) { requestSubItems(response[idx].pk, response[idx].sub_part) } } table.bootstrapTable('append', response); table.treegrid('collapseAll'); }, error: function() { console.log('Error requesting BOM for part=' + part_pk); } } ) } table.inventreeTable({ treeEnable: !options.editable, rootParentId: options.parent_id, idField: 'pk', //uniqueId: 'pk', parentIdField: 'parentId', treeShowField: 'sub_part', showColumns: true, name: 'bom', sortable: true, search: true, rowStyle: function(row, index) { if (row.validated) { return {classes: 'rowvalid'}; } else { return {classes: 'rowinvalid'}; } }, formatNoMatches: function() { return "{% trans "No BOM items found" %}"; }, clickToSelect: true, queryParams: params, columns: cols, url: options.bom_url, onPostBody: function() { if (!options.editable) { table.treegrid({ treeColumn: 0, onExpand: function() { } }); } }, onLoadSuccess: function() { if (options.editable) { table.bootstrapTable('uncheckAll'); } else { var data = table.bootstrapTable('getData'); for (var idx = 0; idx < data.length; idx++) { var row = data[idx]; // If a row already has a parent ID set, it's already been updated! if (row.parentId) { continue; } // Set the parent ID of the top-level rows row.parentId = options.parent_id; table.bootstrapTable('updateRow', idx, row, true); if (row.sub_part_detail.assembly) { requestSubItems(row.pk, row.sub_part); } } } }, }); // In editing mode, attached editables to the appropriate table elements if (options.editable) { table.on('click', '.bom-delete-button', function() { var pk = $(this).attr('pk'); var url = `/part/bom/${pk}/delete/`; launchModalForm( url, { success: function() { reloadBomTable(table); } } ); }); table.on('click', '.bom-edit-button', function() { var pk = $(this).attr('pk'); var url = `/part/bom/${pk}/edit/`; launchModalForm( url, { success: function() { reloadBomTable(table); } } ); }); table.on('click', '.bom-validate-button', function() { var pk = $(this).attr('pk'); var url = `/api/bom/${pk}/validate/`; inventreePut( url, { valid: true }, { method: 'PATCH', success: function() { reloadBomTable(table); } } ); }); } }