diff --git a/InvenTree/stock/templates/stock/item_installed.html b/InvenTree/stock/templates/stock/item_installed.html
index cac55c9dce..2a6a0db057 100644
--- a/InvenTree/stock/templates/stock/item_installed.html
+++ b/InvenTree/stock/templates/stock/item_installed.html
@@ -10,19 +10,7 @@
{% trans "Installed Stock Items" %}
-
-
-
+
{% endblock %}
@@ -30,135 +18,14 @@
{{ block.super }}
-$('#installed-table').inventreeTable({
- formatNoMatches: function() {
- return '{% trans "No stock items installed" %}';
- },
- url: "{% url 'api-stock-list' %}",
- queryParams: {
- installed_in: {{ item.id }},
- part_detail: true,
- },
- name: 'stock-item-installed',
- url: "{% url 'api-stock-list' %}",
- showColumns: true,
- columns: [
- {
- checkbox: true,
- title: '{% trans 'Select' %}',
- searchable: false,
- switchable: false,
- },
- {
- field: 'pk',
- title: 'ID',
- visible: false,
- switchable: false,
- },
- {
- field: 'part_name',
- title: '{% trans "Part" %}',
- sortable: true,
- formatter: function(value, row, index, field) {
-
- var url = `/stock/item/${row.pk}/`;
- var thumb = row.part_detail.thumbnail;
- var name = row.part_detail.full_name;
-
- html = imageHoverIcon(thumb) + renderLink(name, url);
-
- return html;
- }
- },
- {
- field: 'IPN',
- title: 'IPN',
- sortable: true,
- formatter: function(value, row, index, field) {
- return row.part_detail.IPN;
- },
- },
- {
- field: 'part_description',
- title: '{% trans "Description" %}',
- sortable: true,
- formatter: function(value, row, index, field) {
- return row.part_detail.description;
- }
- },
- {
- field: 'quantity',
- title: '{% trans "Stock" %}',
- sortable: true,
- formatter: function(value, row, index, field) {
-
- var val = parseFloat(value);
-
- // If there is a single unit with a serial number, use the serial number
- if (row.serial && row.quantity == 1) {
- val = '# ' + row.serial;
- } else {
- val = +val.toFixed(5);
- }
-
- var html = renderLink(val, `/stock/item/${row.pk}/`);
-
- return html;
- }
- },
- {
- field: 'status',
- title: '{% trans "Status" %}',
- sortable: 'true',
- formatter: function(value, row, index, field) {
- return stockStatusDisplay(value);
- },
- },
- {
- field: 'batch',
- title: '{% trans "Batch" %}',
- sortable: true,
- },
- {
- field: 'actions',
- switchable: false,
- title: '',
- formatter: function(value, row) {
- var pk = row.pk;
-
- var html = ``;
-
- html += makeIconButton('fa-unlink', 'button-uninstall', pk, '{% trans "Uninstall item" %}');
-
- html += `
`;
-
- return html;
- }
- }
- ],
- onLoadSuccess: function() {
-
- var table = $('#installed-table');
-
- // Find buttons and associate actions
- table.find('.button-uninstall').click(function() {
- var pk = $(this).attr('pk');
-
- launchModalForm(
- "{% url 'stock-item-uninstall' %}",
- {
- data: {
- 'items[]': [pk],
- },
- reload: true,
- }
- );
- });
- },
- buttons: [
- '#stock-options',
- ]
-});
+loadInstalledInTable(
+ $('#installed-table'),
+ {
+ stock_item: {{ item.pk }},
+ part: {{ item.part.pk }},
+ quantity: {{ item.quantity }},
+ }
+);
$('#multi-item-uninstall').click(function() {
diff --git a/InvenTree/templates/js/stock.html b/InvenTree/templates/js/stock.html
index 4ec2507b31..330be924e5 100644
--- a/InvenTree/templates/js/stock.html
+++ b/InvenTree/templates/js/stock.html
@@ -830,8 +830,25 @@ function loadInstalledInTable(table, options) {
*
* - stock_item: The PK of the master stock_item object
* - part: The PK of the Part reference of the stock_item object
+ * - quantity: The quantity of the stock item
*/
+ function updateCallbacks() {
+ // Setup callback functions when buttons are pressed
+ table.find('.button-install').click(function() {
+ var pk = $(this).attr('pk');
+
+ launchModalForm(
+ `/stock/item/${options.stock_item}/install/`,
+ {
+ data: {
+ part: pk,
+ },
+ }
+ );
+ });
+ }
+
table.inventreeTable(
{
url: "{% url 'api-bom-list' %}",
@@ -842,6 +859,92 @@ function loadInstalledInTable(table, options) {
},
showColumns: false,
name: 'installed-in',
+ detailView: true,
+ detailViewByClick: true,
+ detailFilter: function(index, row) {
+ return row.installed_count && row.installed_count > 0;
+ },
+ detailFormatter: function(index, row, element) {
+ var subTableId = `installed-table-${row.sub_part}`;
+
+ var html = ``;
+
+ element.html(html);
+
+ var subTable = $(`#${subTableId}`);
+
+ // Display a "sub table" showing all the linked stock items
+ subTable.bootstrapTable({
+ data: row.installed_items,
+ showHeader: true,
+ columns: [
+ {
+ field: 'item',
+ title: '{% trans "Stock Item" %}',
+ formatter: function(value, subrow, index, field) {
+
+ var pk = subrow.pk;
+ var html = '';
+
+ html += row.sub_part_detail.full_name;
+ html += " | ";
+
+ if (subrow.serial && subrow.quantity == 1) {
+ html += `{% trans "Serial" %}: ${subrow.serial}`;
+ } else {
+ html += `{% trans "Quantity" %}: ${subrow.quantity}`;
+ }
+
+ return html;
+ },
+ },
+ {
+ field: 'status',
+ title: '{% trans "Status" %}',
+ formatter: function(value, subrow, index, field) {
+ return stockStatusDisplay(value);
+ }
+ },
+ {
+ field: 'actions',
+ title: '',
+ formatter: function(value, subrow, index) {
+
+ var pk = subrow.pk;
+ var html = '';
+
+ // Add some buttons yo!
+ html += ``;
+
+ html += makeIconButton('fa-unlink', 'button-uninstall', pk, "{% trans "Uninstall stock item" %}");
+
+ html += `
`;
+
+ return html;
+ }
+ }
+ ],
+ onPostBody: function() {
+ // Setup button callbacks
+ subTable.find('.button-uninstall').click(function() {
+ var pk = $(this).attr('pk');
+
+ launchModalForm(
+ "{% url 'stock-item-uninstall' %}",
+ {
+ data: {
+ 'items[]': [pk],
+ },
+ success: function() {
+ // Refresh entire table!
+ table.bootstrapTable('refresh');
+ }
+ }
+ );
+ });
+ }
+ });
+ },
columns: [
{
checkbox: true,
@@ -861,7 +964,7 @@ function loadInstalledInTable(table, options) {
sortable: true,
formatter: function(value, row, index, field) {
- var url = `/stock/item/${row.pk}/`;
+ var url = `/part/${row.sub_part}/`;
var thumb = row.sub_part_detail.thumbnail;
var name = row.sub_part_detail.full_name;
@@ -877,9 +980,11 @@ function loadInstalledInTable(table, options) {
formatter: function(value, row, index, field) {
// Construct a progress showing how many items have been installed
- var installed = row.installed || 0;
+ var installed = row.installed_count || 0;
var required = row.quantity || 0;
+ required *= options.quantity;
+
var progress = makeProgressBar(installed, required, {
id: row.sub_part.pk,
});
@@ -891,7 +996,7 @@ function loadInstalledInTable(table, options) {
field: 'actions',
switchable: false,
formatter: function(value, row) {
- var pk = row.sub_part.pk;
+ var pk = row.sub_part;
var html = ``;
@@ -904,8 +1009,63 @@ function loadInstalledInTable(table, options) {
}
],
onLoadSuccess: function() {
- console.log('data loaded!');
- }
+ // Grab a list of parts which are actually installed in this stock item
+
+ inventreeGet(
+ "{% url 'api-stock-list' %}",
+ {
+ installed_in: options.stock_item,
+ },
+ {
+ success: function(stock_items) {
+
+ var table_data = table.bootstrapTable('getData');
+
+ stock_items.forEach(function(item) {
+
+ var match = false;
+
+ for (var idx = 0; idx < table_data.length; idx++) {
+
+ var row = table_data[idx];
+
+ // Check each row in the table to see if this stock item matches
+ table_data.forEach(function(row) {
+
+ // Match on "sub_part"
+ if (row.sub_part == item.part) {
+
+ // First time?
+ if (row.installed_count == null) {
+ row.installed_count = 0;
+ row.installed_items = [];
+ }
+
+ row.installed_count += item.quantity;
+ row.installed_items.push(item);
+
+ // Push the row back into the table
+ table.bootstrapTable('updateRow', idx, row, true);
+
+ match = true;
+ }
+
+ });
+
+ if (match) {
+ break;
+ }
+ }
+ });
+
+ // Update button callback links
+ updateCallbacks();
+ }
+ }
+ );
+
+ updateCallbacks();
+ },
}
);
}
\ No newline at end of file