mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Merge pull request #718 from SchrodingersGat/starred-filter
Starred filter
This commit is contained in:
commit
2cbf7d578b
@ -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>`;
|
||||
|
@ -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
|
||||
|
||||
|
@ -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' %}",
|
||||
},
|
||||
]
|
||||
});
|
||||
});
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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'],
|
||||
|
@ -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 %}
|
15
InvenTree/templates/InvenTree/low_stock.html
Normal file
15
InvenTree/templates/InvenTree/low_stock.html
Normal 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 %}
|
@ -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 %}
|
@ -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 %}
|
@ -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>
|
||||
|
@ -89,6 +89,10 @@ function getAvailableTableFilters(tableKey) {
|
||||
type: 'bool',
|
||||
title: '{% trans "Component" %}',
|
||||
},
|
||||
starred: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Starred" %}',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user