diff --git a/InvenTree/templates/js/translated/bom.js b/InvenTree/templates/js/translated/bom.js
index 71c6b0b387..798c6749ec 100644
--- a/InvenTree/templates/js/translated/bom.js
+++ b/InvenTree/templates/js/translated/bom.js
@@ -15,6 +15,7 @@
*/
/* exported
+ constructBomUploadTable,
downloadBomTemplate,
exportBom,
newPartFromBomWizard,
@@ -24,6 +25,69 @@
removeColFromBomWizard,
*/
+
+/* Construct a table of data extracted from a BOM file.
+ * This data is used to import a BOM interactively.
+ */
+function constructBomUploadTable(data, options={}) {
+
+ if (!data.rows) {
+ // TODO: Error message!
+ return;
+ }
+
+ function constructRow(row, idx) {
+ // Construct an individual row from the provided data
+
+ var part_input = constructField(
+ `part_${idx}`,
+ {
+ type: 'related field',
+ required: 'true',
+ },
+ {
+ hideLabels: true,
+ }
+ );
+
+ var html = `
+
+ ${part_input} |
+ quantity |
+ reference |
+ overage |
+ variants |
+ inherited |
+ optional |
+ note |
+
`;
+
+ $('#bom-import-table tbody').append(html);
+
+ // Initialize the "part" selector for this row
+ initializeRelatedField(
+ {
+ name: `part_${idx}`,
+ api_url: '{% url "api-part-list" %}',
+ filters: {
+ component: true,
+ },
+ model: 'part',
+ required: true,
+ auto_fill: false,
+ onSelect: function(data, field, opts) {
+ // TODO?
+ },
+ }
+ );
+ }
+
+ data.rows.forEach(function(row, idx) {
+ constructRow(row, idx);
+ });
+}
+
+
function downloadBomTemplate(options={}) {
var format = options.format;
diff --git a/InvenTree/templates/js/translated/forms.js b/InvenTree/templates/js/translated/forms.js
index 2f2d1f8ae4..ff4415404f 100644
--- a/InvenTree/templates/js/translated/forms.js
+++ b/InvenTree/templates/js/translated/forms.js
@@ -837,7 +837,15 @@ function getFormFieldElement(name, options) {
var field_name = getFieldName(name, options);
- var el = $(options.modal).find(`#id_${field_name}`);
+ var el = null;
+
+ if (options && options.modal) {
+ // Field element is associated with a model?
+ el = $(options.modal).find(`#id_${field_name}`);
+ } else {
+ // Field element is top-level
+ el = $(`#id_${field_name}`);
+ }
if (!el.exists) {
console.log(`ERROR: Could not find form element for field '${name}'`);
@@ -1330,11 +1338,13 @@ function hideFormGroup(group, options) {
$(options.modal).find(`#form-panel-${group}`).hide();
}
+
// Show a form group
function showFormGroup(group, options) {
$(options.modal).find(`#form-panel-${group}`).show();
}
+
function setFormGroupVisibility(group, vis, options) {
if (vis) {
showFormGroup(group, options);
@@ -1344,7 +1354,7 @@ function setFormGroupVisibility(group, vis, options) {
}
-function initializeRelatedFields(fields, options) {
+function initializeRelatedFields(fields, options={}) {
var field_names = options.field_names;
@@ -1452,12 +1462,11 @@ function addSecondaryModal(field, fields, options) {
* - field: Field definition from the OPTIONS request
* - options: Original options object provided by the client
*/
-function initializeRelatedField(field, fields, options) {
+function initializeRelatedField(field, fields, options={}) {
var name = field.name;
if (!field.api_url) {
- // TODO: Provide manual api_url option?
console.log(`WARNING: Related field '${name}' missing 'api_url' parameter.`);
return;
}
@@ -1654,7 +1663,7 @@ function initializeRelatedField(field, fields, options) {
* - data: JSON data representing the model instance
* - options: The modal form specifications
*/
-function setRelatedFieldData(name, data, options) {
+function setRelatedFieldData(name, data, options={}) {
var select = getFormFieldElement(name, options);
@@ -1779,10 +1788,10 @@ function renderModelData(name, model, data, parameters, options) {
/*
* Construct a field name for the given field
*/
-function getFieldName(name, options) {
+function getFieldName(name, options={}) {
var field_name = name;
- if (options.depth) {
+ if (options && options.depth) {
field_name += `_${options.depth}`;
}