Merge pull request #718 from SchrodingersGat/starred-filter

Starred filter
This commit is contained in:
Oliver 2020-04-13 22:41:35 +10:00 committed by GitHub
commit 2cbf7d578b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 140 additions and 76 deletions

View File

@ -87,7 +87,7 @@ function loadPartTable(table, url, options={}) {
* buttons: If provided, link buttons to selection status of this table
*/
var params = options.parms || {};
var params = options.params || {};
var filters = loadTableFilters("parts");
@ -147,6 +147,10 @@ function loadPartTable(table, url, options={}) {
display += `<span class='fas fa-tools label-right' title='Assembled part'></span>`;
}
if (row.starred) {
display += `<span class='fas fa-star label-right' title='Starred part'></span>`;
}
/*
if (row.component) {
display = display + `<span class='fas fa-cogs label-right' title='Component part'></span>`;

View File

@ -494,15 +494,16 @@ class IndexView(TemplateView):
context = super(TemplateView, self).get_context_data(**kwargs)
context['starred'] = [star.part for star in self.request.user.starred_parts.all()]
# TODO - Re-implement this when a less expensive method is worked out
# context['starred'] = [star.part for star in self.request.user.starred_parts.all()]
# Generate a list of orderable parts which have stock below their minimum values
# TODO - Is there a less expensive way to get these from the database
context['to_order'] = [part for part in Part.objects.filter(purchaseable=True) if part.need_to_restock()]
# context['to_order'] = [part for part in Part.objects.filter(purchaseable=True) if part.need_to_restock()]
# Generate a list of assembly parts which have stock below their minimum values
# TODO - Is there a less expensive way to get these from the database
context['to_build'] = [part for part in Part.objects.filter(assembly=True) if part.need_to_restock()]
# context['to_build'] = [part for part in Part.objects.filter(assembly=True) if part.need_to_restock()]
return context

View File

@ -33,16 +33,29 @@
"{% url 'supplier-part-create' %}",
{
data: {
supplier: {{ company.id }}
{% if company.is_supplier %}supplier: {{ company.id }},{% endif %}
{% if company.is_manufacturer %}manufacturer: {{ company.id }},{% endif %}
},
reload: true,
secondary: [
{
field: 'part',
label: 'New Part',
title: 'Create New Part',
label: '{% trans "New Part" %}',
title: '{% trans "Create new Part" %}',
url: "{% url 'part-create' %}"
},
{
field: 'supplier',
label: "{% trans 'New Supplier' %}",
title: "{% trans 'Create new Supplier' %}",
url: "{% url 'supplier-create' %}",
},
{
field: 'manufacturer',
label: '{% trans "New Manufacturer" %}',
title: '{% trans "Create new Manufacturer" %}',
url: "{% url 'manufacturer-create' %}",
},
]
});
});

View File

@ -273,10 +273,6 @@ class SupplierPartCreate(AjaxCreateView):
Hide some fields if they are not appropriate in context
"""
form = super(AjaxCreateView, self).get_form()
if form.initial.get('supplier', None):
# Hide the supplier field
form.fields['supplier'].widget = HiddenInput()
if form.initial.get('part', None):
# Hide the part field
@ -292,20 +288,27 @@ class SupplierPartCreate(AjaxCreateView):
"""
initials = super(SupplierPartCreate, self).get_initial().copy()
manufacturer_id = self.get_param('manufacturer')
supplier_id = self.get_param('supplier')
part_id = self.get_param('part')
if supplier_id:
try:
initials['supplier'] = Company.objects.get(pk=supplier_id)
except Company.DoesNotExist:
initials['supplier'] = None
except (ValueError, Company.DoesNotExist):
pass
if manufacturer_id:
try:
initials['manufacturer'] = Company.objects.get(pk=manufacturer_id)
except (ValueError, Company.DoesNotExist):
pass
if part_id:
try:
initials['part'] = Part.objects.get(pk=part_id)
except Part.DoesNotExist:
initials['part'] = None
except (ValueError, Part.DoesNotExist):
pass
return initials

View File

@ -153,6 +153,7 @@ class PartList(generics.ListCreateAPIView):
The Part object list can be filtered by:
- category: Filter by PartCategory reference
- cascade: If true, include parts from sub-categories
- starred: Is the part "starred" by the current user?
- is_template: Is the part a template part?
- variant_of: Filter by variant_of Part reference
- assembly: Filter by assembly field
@ -257,12 +258,18 @@ class PartList(generics.ListCreateAPIView):
# Filter items which have an 'in_stock' level higher than 'minimum_stock'
data = data.filter(Q(in_stock__gte=F('minimum_stock')))
# Get a list of the parts that this user has starred
starred_parts = [star.part.pk for star in self.request.user.starred_parts.all()]
# Reduce the number of lookups we need to do for the part categories
categories = {}
for item in data:
if item['image']:
# Is this part 'starred' for the current user?
item['starred'] = item['pk'] in starred_parts
img = item['image']
# Use the 'thumbnail' image here instead of the full-size image
@ -294,32 +301,53 @@ class PartList(generics.ListCreateAPIView):
return Response(data)
def get_queryset(self):
# Does the user wish to filter by category?
cat_id = self.request.query_params.get('category', None)
"""
Implement custom filtering for the Part list API
"""
# Start with all objects
parts_list = Part.objects.all()
cascade = str2bool(self.request.query_params.get('cascade', False))
# Filter by 'starred' parts?
starred = str2bool(self.request.query_params.get('starred', None))
if starred is not None:
starred_parts = [star.part.pk for star in self.request.user.starred_parts.all()]
if starred:
parts_list = parts_list.filter(pk__in=starred_parts)
else:
parts_list = parts_list.exclude(pk__in=starred_parts)
cascade = str2bool(self.request.query_params.get('cascade', None))
# Does the user wish to filter by category?
cat_id = self.request.query_params.get('category', None)
if cat_id is None:
# Top-level parts
if not cascade:
parts_list = parts_list.filter(category=None)
# No category filtering if category is not specified
pass
else:
try:
category = PartCategory.objects.get(pk=cat_id)
# Category has been specified!
if isNull(cat_id):
# A 'null' category is the top-level category
if cascade is False:
# Do not cascade, only list parts in the top-level category
parts_list = parts_list.filter(category=None)
# If '?cascade=true' then include parts which exist in sub-categories
if cascade:
parts_list = parts_list.filter(category__in=category.getUniqueChildren())
# Just return parts directly in the requested category
else:
parts_list = parts_list.filter(category=cat_id)
except (ValueError, PartCategory.DoesNotExist):
pass
else:
try:
category = PartCategory.objects.get(pk=cat_id)
# If '?cascade=true' then include parts which exist in sub-categories
if cascade:
parts_list = parts_list.filter(category__in=category.getUniqueChildren())
# Just return parts directly in the requested category
else:
parts_list = parts_list.filter(category=cat_id)
except (ValueError, PartCategory.DoesNotExist):
pass
# Ensure that related models are pre-loaded to reduce DB trips
parts_list = self.get_serializer_class().setup_eager_loading(parts_list)

View File

@ -200,11 +200,11 @@
{% if category %}
$("#cat-edit").click(function () {
launchModalForm(
"{% url 'category-edit' category.id %}",
{
reload: true
},
);
"{% url 'category-edit' category.id %}",
{
reload: true
},
);
return false;
});
@ -227,9 +227,9 @@
"#part-table",
"{% url 'api-part-list' %}",
{
query: {
{% if category %}
category: {{ category.id }},
params: {
{% if category %}category: {{ category.id }},
{% else %}category: "null",
{% endif %}
},
buttons: ['#part-options'],

View File

@ -9,13 +9,7 @@ InvenTree | Index
<hr>
{% include "InvenTree/starred_parts.html" with collapse_id="starred" %}
{% if to_order %}
{% include "InvenTree/parts_to_order.html" with collapse_id="order" %}
{% endif %}
{% if to_build %}
{% include "InvenTree/parts_to_build.html" with collapse_id="build" %}
{% endif %}
{% include "InvenTree/low_stock.html" with collapse_id="order" %}
{% endblock %}
@ -25,15 +19,31 @@ InvenTree | Index
{% block js_ready %}
console.log("abcde?");
{{ block.super }}
//TODO: These calls to bootstrapTable() are failing, for some reason?
//$("#to-build-table").bootstrapTable();
//$("#to-order-table").bootstrapTable();
//$("#starred-parts-table").bootstrapTable();
loadPartTable("#starred-parts-table", "{% url 'api-part-list' %}", {
params: {
"starred": true,
}
});
loadPartTable("#low-stock-table", "{% url 'api-part-list' %}", {
params: {
"low_stock": true,
}
});
$("#starred-parts-table").on('load-success.bs.table', function() {
var count = $("#starred-parts-table").bootstrapTable('getData').length;
$("#starred-parts-count").html(count);
});
$("#low-stock-table").on('load-success.bs.table', function() {
var count = $("#low-stock-table").bootstrapTable('getData').length;
$("#low-stock-count").html(count);
});
console.log("Got to here...");
{% endblock %}

View File

@ -0,0 +1,15 @@
{% extends "collapse.html" %}
{% load i18n %}
{% block collapse_title %}
<span class='fas fa-shopping-cart icon-header'></span>
{% trans "Low Stock" %}<span class='badge' id='low-stock-count'>0</span>
{% endblock %}
{% block collapse_content %}
<table class='table table-striped table-condensed' id='low-stock-table'>
</table>
{% endblock %}

View File

@ -1,15 +0,0 @@
{% extends "collapse.html" %}
{% block collapse_title %}
<span class='fas fa-shopping-cart icon-header'></span>
Parts to Order<span class='badge'>{{ to_order | length }}</span>
{% endblock %}
{% block collapse_heading %}
There are {{ to_order | length }} parts which need to be ordered.
{% endblock %}
{% block collapse_content %}
{% include "required_part_table.html" with parts=to_order table_id="to-order-table" %}
{% endblock %}

View File

@ -1,15 +1,15 @@
{% extends "collapse.html" %}
{% load i18n %}
{% block collapse_title %}
<span class='fas fa-star icon-header'></span>
Starred Parts<span class='badge'>{{ starred | length }}</span>
{% endblock %}
{% block collapse_heading %}
You have {{ starred | length }} favourite parts
{% trans "Starred Parts" %}<span class='badge' id='starred-parts-count'>0</span>
{% endblock %}
{% block collapse_content %}
{% include "required_part_table.html" with parts=starred table_id="starred-parts-table" %}
<table class='table tabe-striped table-condensed' id='starred-parts-table'>
</table>
{% endblock %}

View File

@ -102,6 +102,7 @@ InvenTree
<script type='text/javascript' src="{% static 'script/inventree/inventree.js' %}"></script>
<script type='text/javascript' src="{% static 'script/inventree/api.js' %}"></script>
<script type='text/javascript' src="{% static 'script/inventree/part.js' %}"></script>
<script type='text/javascript' src="{% static 'script/inventree/bom.js' %}"></script>
<script type='text/javascript' src="{% static 'script/inventree/filters.js' %}"></script>
<script type='text/javascript' src="{% static 'script/inventree/tables.js' %}"></script>

View File

@ -89,6 +89,10 @@ function getAvailableTableFilters(tableKey) {
type: 'bool',
title: '{% trans "Component" %}',
},
starred: {
type: 'bool',
title: '{% trans "Starred" %}',
},
};
}