diff --git a/InvenTree/part/api.py b/InvenTree/part/api.py index 60bf360f73..599c4bba5d 100644 --- a/InvenTree/part/api.py +++ b/InvenTree/part/api.py @@ -489,7 +489,8 @@ class PartFilter(rest_filters.FilterSet): class PartList(generics.ListCreateAPIView): - """ API endpoint for accessing a list of Part objects + """ + API endpoint for accessing a list of Part objects - GET: Return list of objects - POST: Create a new Part object @@ -840,14 +841,54 @@ class PartParameterDetail(generics.RetrieveUpdateDestroyAPIView): serializer_class = part_serializers.PartParameterSerializer +class BomFilter(rest_filters.FilterSet): + """ + Custom filters for the BOM list + """ + + # Boolean filters for BOM item + optional = rest_filters.BooleanFilter(label='BOM line is optional') + inherited = rest_filters.BooleanFilter(label='BOM line is inherited') + allow_variants = rest_filters.BooleanFilter(label='Variants are allowed') + + validated = rest_filters.BooleanFilter(label='BOM line has been validated', method='filter_validated') + + def filter_validated(self, queryset, name, value): + + # Work out which lines have actually been validated + pks = [] + + for bom_item in queryset.all(): + if bom_item.is_line_valid(): + pks.append(bom_item.pk) + + if str2bool(value): + queryset = queryset.filter(pk__in=pks) + else: + queryset = queryset.exclude(pk__in=pks) + + return queryset + + # Filters for linked 'part' + part_active = rest_filters.BooleanFilter(label='Master part is active', field_name='part__active') + part_trackable = rest_filters.BooleanFilter(label='Master part is trackable', field_name='part__trackable') + + # Filters for linked 'sub_part' + sub_part_trackable = rest_filters.BooleanFilter(label='Sub part is trackable', field_name='sub_part__trackable') + sub_part_assembly = rest_filters.BooleanFilter(label='Sub part is an assembly', field_name='sub_part__assembly') + + class BomList(generics.ListCreateAPIView): - """ API endpoint for accessing a list of BomItem objects. + """ + API endpoint for accessing a list of BomItem objects. - GET: Return list of BomItem objects - POST: Create a new BomItem object """ serializer_class = part_serializers.BomItemSerializer + queryset = BomItem.objects.all() + filterset_class = BomFilter def list(self, request, *args, **kwargs): @@ -894,30 +935,6 @@ class BomList(generics.ListCreateAPIView): params = self.request.query_params - # Filter by "optional" status? - optional = params.get('optional', None) - - if optional is not None: - optional = str2bool(optional) - - queryset = queryset.filter(optional=optional) - - # Filter by "inherited" status - inherited = params.get('inherited', None) - - if inherited is not None: - inherited = str2bool(inherited) - - queryset = queryset.filter(inherited=inherited) - - # Filter by "allow_variants" - variants = params.get("allow_variants", None) - - if variants is not None: - variants = str2bool(variants) - - queryset = queryset.filter(allow_variants=variants) - # Filter by part? part = params.get('part', None) @@ -940,45 +957,6 @@ class BomList(generics.ListCreateAPIView): except (ValueError, Part.DoesNotExist): pass - # Filter by "active" status of the part - part_active = params.get('part_active', None) - - if part_active is not None: - part_active = str2bool(part_active) - queryset = queryset.filter(part__active=part_active) - - # Filter by "trackable" status of the part - part_trackable = params.get('part_trackable', None) - - if part_trackable is not None: - part_trackable = str2bool(part_trackable) - queryset = queryset.filter(part__trackable=part_trackable) - - # Filter by "trackable" status of the sub-part - sub_part_trackable = params.get('sub_part_trackable', None) - - if sub_part_trackable is not None: - sub_part_trackable = str2bool(sub_part_trackable) - queryset = queryset.filter(sub_part__trackable=sub_part_trackable) - - # Filter by whether the BOM line has been validated - validated = params.get('validated', None) - - if validated is not None: - validated = str2bool(validated) - - # Work out which lines have actually been validated - pks = [] - - for bom_item in queryset.all(): - if bom_item.is_line_valid: - pks.append(bom_item.pk) - - if validated: - queryset = queryset.filter(pk__in=pks) - else: - queryset = queryset.exclude(pk__in=pks) - # Annotate with purchase prices queryset = queryset.annotate( purchase_price_min=Min('sub_part__stock_items__purchase_price'), diff --git a/InvenTree/templates/js/bom.js b/InvenTree/templates/js/bom.js index 980dd82ccc..32166d972a 100644 --- a/InvenTree/templates/js/bom.js +++ b/InvenTree/templates/js/bom.js @@ -242,7 +242,7 @@ function loadBomTable(table, options) { return renderLink(text, url); } }); - + cols.push( { field: 'purchase_price_range', diff --git a/InvenTree/templates/js/filters.js b/InvenTree/templates/js/filters.js index a27e91d5dc..4ee08affdf 100644 --- a/InvenTree/templates/js/filters.js +++ b/InvenTree/templates/js/filters.js @@ -314,7 +314,7 @@ function setupFilterList(tableKey, table, target) { // Only add the new filter if it is not empty! if (tag && tag.length > 0) { var filters = addTableFilter(tableKey, tag, val); - reloadTable(table, filters); + reloadTableFilters(table, filters); // Run this function again setupFilterList(tableKey, table, target); @@ -333,7 +333,7 @@ function setupFilterList(tableKey, table, target) { element.find(`#${clear}`).click(function() { var filters = clearTableFilters(tableKey); - reloadTable(table, filters); + reloadTableFilters(table, filters); setupFilterList(tableKey, table, target); }); @@ -346,7 +346,7 @@ function setupFilterList(tableKey, table, target) { var filters = removeTableFilter(tableKey, filter); - reloadTable(table, filters); + reloadTableFilters(table, filters); // Run this function again! setupFilterList(tableKey, table, target); diff --git a/InvenTree/templates/js/table_filters.js b/InvenTree/templates/js/table_filters.js index 3dfb424edf..78632d6d56 100644 --- a/InvenTree/templates/js/table_filters.js +++ b/InvenTree/templates/js/table_filters.js @@ -42,6 +42,10 @@ function getAvailableTableFilters(tableKey) { type: 'bool', title: '{% trans "Trackable Part" %}' }, + sub_part_assembly: { + type: 'bool', + title: '{% trans "Assembled Part" %}', + }, validated: { type: 'bool', title: '{% trans "Validated" %}', diff --git a/InvenTree/templates/js/tables.js b/InvenTree/templates/js/tables.js index 96eb3f8123..8fedeb8f9e 100644 --- a/InvenTree/templates/js/tables.js +++ b/InvenTree/templates/js/tables.js @@ -68,7 +68,7 @@ function isNumeric(n) { * Reload a table which has already been made into a bootstrap table. * New filters can be optionally provided, to change the query params. */ -function reloadTable(table, filters) { +function reloadTableFilters(table, filters) { // Simply perform a refresh if (filters == null) { @@ -94,16 +94,11 @@ function reloadTable(table, filters) { } options.queryParams = function(tableParams) { - - for (key in params) { - tableParams[key] = params[key]; - } - - return tableParams; - } + return convertQueryParameters(tableParams, params); + }; table.bootstrapTable('refreshOptions', options); - table.bootstrapTable('refresh'); + table.bootstrapTable('refresh', filters); } @@ -122,6 +117,55 @@ function visibleColumnString(columns) { } +/* + * Convert bootstrap-table style parameters to "InvenTree" style +*/ +function convertQueryParameters(params, filters) { + + // Override the way that we ask the server to sort results + // It seems bootstrap-table does not offer a "native" way to do this... + if ('sort' in params) { + var order = params['order']; + + var ordering = params['sort'] || null; + + if (ordering) { + + if (order == 'desc') { + ordering = `-${ordering}`; + } + + params['ordering'] = ordering; + } + + delete params['sort']; + delete params['order']; + + } + + for (var key in filters) { + params[key] = filters[key]; + } + + // Add "order" back in (if it was originally specified by InvenTree) + // Annoyingly, "order" shadows some field names in InvenTree... + if ('order' in filters) { + params['order'] = filters['order']; + } + + // Remove searchable[] array (generated by bootstrap-table) + if ('searchable' in params) { + delete params['searchable']; + } + + if ('sortable' in params) { + delete params['sortable']; + } + + return params; +} + + /* Wrapper function for bootstrapTable. * Sets some useful defaults, and manage persistent settings. */ @@ -147,39 +191,8 @@ $.fn.inventreeTable = function(options) { var filters = options.queryParams || options.filters || {}; options.queryParams = function(params) { - - // Override the way that we ask the server to sort results - // It seems bootstrap-table does not offer a "native" way to do this... - if ('sort' in params) { - var order = params['order']; - - var ordering = params['sort'] || null; - - if (ordering) { - - if (order == 'desc') { - ordering = `-${ordering}`; - } - - params['ordering'] = ordering; - } - - delete params['sort']; - delete params['order']; - - } - - for (var key in filters) { - params[key] = filters[key]; - } - - // Add "order" back in (if it was originally specified by InvenTree) - // Annoyingly, "order" shadows some field names in InvenTree... - if ('order' in filters) { - params['order'] = filters['order']; - } - return params; - } + return convertQueryParameters(params, filters); + }; options.rememberOrder = true;