diff --git a/InvenTree/InvenTree/version.py b/InvenTree/InvenTree/version.py
index 98957d7bba..32f359f5f2 100644
--- a/InvenTree/InvenTree/version.py
+++ b/InvenTree/InvenTree/version.py
@@ -12,11 +12,14 @@ import common.models
INVENTREE_SW_VERSION = "0.7.0 dev"
# InvenTree API version
-INVENTREE_API_VERSION = 37
+INVENTREE_API_VERSION = 38
"""
Increment this API version number whenever there is a significant change to the API that any clients need to know about
+v38 -> 2022-04-14 : https://github.com/inventree/InvenTree/pull/2828
+ - Adds the ability to include stock test results for "installed items"
+
v37 -> 2022-04-07 : https://github.com/inventree/InvenTree/pull/2806
- Adds extra stock availability information to the BomItem serializer
diff --git a/InvenTree/part/models.py b/InvenTree/part/models.py
index d67ff31c8c..b7269f3e5e 100644
--- a/InvenTree/part/models.py
+++ b/InvenTree/part/models.py
@@ -1313,19 +1313,31 @@ class Part(MPTTModel):
return quantity
- def build_order_allocations(self):
+ def build_order_allocations(self, **kwargs):
"""
Return all 'BuildItem' objects which allocate this part to Build objects
"""
- return BuildModels.BuildItem.objects.filter(stock_item__part__id=self.id)
+ include_variants = kwargs.get('include_variants', True)
- def build_order_allocation_count(self):
+ queryset = BuildModels.BuildItem.objects.all()
+
+ if include_variants:
+ variants = self.get_descendants(include_self=True)
+ queryset = queryset.filter(
+ stock_item__part__in=variants,
+ )
+ else:
+ queryset = queryset.filter(stock_item__part=self)
+
+ return queryset
+
+ def build_order_allocation_count(self, **kwargs):
"""
Return the total amount of this part allocated to build orders
"""
- query = self.build_order_allocations().aggregate(
+ query = self.build_order_allocations(**kwargs).aggregate(
total=Coalesce(
Sum(
'quantity',
@@ -1343,7 +1355,19 @@ class Part(MPTTModel):
Return all sales-order-allocation objects which allocate this part to a SalesOrder
"""
- queryset = OrderModels.SalesOrderAllocation.objects.filter(item__part__id=self.id)
+ include_variants = kwargs.get('include_variants', True)
+
+ queryset = OrderModels.SalesOrderAllocation.objects.all()
+
+ if include_variants:
+ # Include allocations for all variants
+ variants = self.get_descendants(include_self=True)
+ queryset = queryset.filter(
+ item__part__in=variants,
+ )
+ else:
+ # Only look at this part
+ queryset = queryset.filter(item__part=self)
# Default behaviour is to only return *pending* allocations
pending = kwargs.get('pending', True)
@@ -1381,7 +1405,7 @@ class Part(MPTTModel):
return query['total']
- def allocation_count(self):
+ def allocation_count(self, **kwargs):
"""
Return the total quantity of stock allocated for this part,
against both build orders and sales orders.
@@ -1389,8 +1413,8 @@ class Part(MPTTModel):
return sum(
[
- self.build_order_allocation_count(),
- self.sales_order_allocation_count(),
+ self.build_order_allocation_count(**kwargs),
+ self.sales_order_allocation_count(**kwargs),
],
)
diff --git a/InvenTree/part/templates/part/part_base.html b/InvenTree/part/templates/part/part_base.html
index e560ebe03c..e45620798e 100644
--- a/InvenTree/part/templates/part/part_base.html
+++ b/InvenTree/part/templates/part/part_base.html
@@ -252,7 +252,6 @@
{% endif %}
{% endif %}
- {% if not part.is_template %}
{% if part.assembly %}
|
@@ -266,7 +265,6 @@
{% decimal quantity_being_built %} |
{% endif %}
- {% endif %}
{% endif %}
{% endblock details_right %}
diff --git a/InvenTree/stock/api.py b/InvenTree/stock/api.py
index 34563b38d7..15ddfd9c07 100644
--- a/InvenTree/stock/api.py
+++ b/InvenTree/stock/api.py
@@ -1105,7 +1105,6 @@ class StockItemTestResultList(generics.ListCreateAPIView):
]
filter_fields = [
- 'stock_item',
'test',
'user',
'result',
@@ -1114,6 +1113,38 @@ class StockItemTestResultList(generics.ListCreateAPIView):
ordering = 'date'
+ def filter_queryset(self, queryset):
+
+ params = self.request.query_params
+
+ queryset = super().filter_queryset(queryset)
+
+ # Filter by stock item
+ item = params.get('stock_item', None)
+
+ if item is not None:
+ try:
+ item = StockItem.objects.get(pk=item)
+
+ items = [item]
+
+ # Do we wish to also include test results for 'installed' items?
+ include_installed = str2bool(params.get('include_installed', False))
+
+ if include_installed:
+ # Include items which are installed "underneath" this item
+ # Note that this function is recursive!
+ installed_items = item.get_installed_items(cascade=True)
+
+ items += [it for it in installed_items]
+
+ queryset = queryset.filter(stock_item__in=items)
+
+ except (ValueError, StockItem.DoesNotExist):
+ pass
+
+ return queryset
+
def get_serializer(self, *args, **kwargs):
try:
kwargs['user_detail'] = str2bool(self.request.query_params.get('user_detail', False))
diff --git a/InvenTree/templates/InvenTree/settings/plugin.html b/InvenTree/templates/InvenTree/settings/plugin.html
index 139ce0d41a..34a8ff9713 100644
--- a/InvenTree/templates/InvenTree/settings/plugin.html
+++ b/InvenTree/templates/InvenTree/settings/plugin.html
@@ -24,6 +24,7 @@
{% include "InvenTree/settings/setting.html" with key="ENABLE_PLUGINS_URL" icon="fa-link" %}
{% include "InvenTree/settings/setting.html" with key="ENABLE_PLUGINS_NAVIGATION" icon="fa-sitemap" %}
{% include "InvenTree/settings/setting.html" with key="ENABLE_PLUGINS_APP" icon="fa-rocket" %}
+ {% include "InvenTree/settings/setting.html" with key="PLUGIN_ON_STARTUP" %}
diff --git a/InvenTree/templates/js/translated/model_renderers.js b/InvenTree/templates/js/translated/model_renderers.js
index 8c98fa35de..43a2fc624c 100644
--- a/InvenTree/templates/js/translated/model_renderers.js
+++ b/InvenTree/templates/js/translated/model_renderers.js
@@ -34,8 +34,8 @@
// Should the ID be rendered for this string
function renderId(title, pk, parameters={}) {
- // Default = true
- var render = true;
+ // Default = do not display
+ var render = false;
if ('render_pk' in parameters) {
render = parameters['render_pk'];
@@ -192,7 +192,7 @@ function renderPart(name, data, parameters={}, options={}) {
${stock_data}
${extra}
- ${renderId('{% trans "Part ID" $}', data.pk, parameters)}
+ ${renderId('{% trans "Part ID" %}', data.pk, parameters)}
`;
diff --git a/InvenTree/templates/js/translated/part.js b/InvenTree/templates/js/translated/part.js
index 08b258fdc2..b0a88b2629 100644
--- a/InvenTree/templates/js/translated/part.js
+++ b/InvenTree/templates/js/translated/part.js
@@ -373,6 +373,9 @@ function duplicatePart(pk, options={}) {
// Override the "variant_of" field
data.variant_of = pk;
+
+ // By default, disable "is_template" when making a variant *of* a template
+ data.is_template = false;
}
constructForm('{% url "api-part-list" %}', {
diff --git a/InvenTree/templates/js/translated/stock.js b/InvenTree/templates/js/translated/stock.js
index b7afdbca44..6c448107fc 100644
--- a/InvenTree/templates/js/translated/stock.js
+++ b/InvenTree/templates/js/translated/stock.js
@@ -1331,14 +1331,27 @@ function loadStockTestResultsTable(table, options) {
});
// Once the test template data are loaded, query for test results
+
+ var filters = loadTableFilters(filterKey);
+
+ var query_params = {
+ stock_item: options.stock_item,
+ user_detail: true,
+ attachment_detail: true,
+ ordering: '-date',
+ };
+
+ if ('result' in filters) {
+ query_params.result = filters.result;
+ }
+
+ if ('include_installed' in filters) {
+ query_params.include_installed = filters.include_installed;
+ }
+
inventreeGet(
'{% url "api-stock-test-result-list" %}',
- {
- stock_item: options.stock_item,
- user_detail: true,
- attachment_detail: true,
- ordering: '-date',
- },
+ query_params,
{
success: function(data) {
// Iterate through the returned test data
diff --git a/InvenTree/templates/js/translated/table_filters.js b/InvenTree/templates/js/translated/table_filters.js
index 6212568950..8eca911f08 100644
--- a/InvenTree/templates/js/translated/table_filters.js
+++ b/InvenTree/templates/js/translated/table_filters.js
@@ -265,7 +265,16 @@ function getAvailableTableFilters(tableKey) {
// Filters for the 'stock test' table
if (tableKey == 'stocktests') {
- return {};
+ return {
+ result: {
+ type: 'bool',
+ title: '{% trans "Test Passed" %}',
+ },
+ include_installed: {
+ type: 'bool',
+ title: '{% trans "Include Installed Items" %}',
+ }
+ };
}
// Filters for the 'part test template' table