mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Merge pull request #952 from SchrodingersGat/variant-table
Add ability to filter part list by 'ancestor'
This commit is contained in:
commit
81b50312e2
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -275,6 +275,7 @@ class PartList(generics.ListCreateAPIView):
|
||||
- purchaseable: Filter by purcahseable field
|
||||
- salable: Filter by salable field
|
||||
- active: Filter by active field
|
||||
- ancestor: Filter parts by 'ancestor' (template / variant tree)
|
||||
"""
|
||||
|
||||
serializer_class = part_serializers.PartSerializer
|
||||
@ -387,10 +388,24 @@ class PartList(generics.ListCreateAPIView):
|
||||
We overide the DRF filter_fields here because
|
||||
"""
|
||||
|
||||
params = self.request.query_params
|
||||
|
||||
queryset = super().filter_queryset(queryset)
|
||||
|
||||
# Filter by 'ancestor'?
|
||||
ancestor = params.get('ancestor', None)
|
||||
|
||||
if ancestor is not None:
|
||||
# If an 'ancestor' part is provided, filter to match only children
|
||||
try:
|
||||
ancestor = Part.objects.get(pk=ancestor)
|
||||
descendants = ancestor.get_descendants(include_self=False)
|
||||
queryset = queryset.filter(pk__in=[d.pk for d in descendants])
|
||||
except (ValueError, Part.DoesNotExist):
|
||||
pass
|
||||
|
||||
# Filter by 'starred' parts?
|
||||
starred = self.request.query_params.get('starred', None)
|
||||
starred = params.get('starred', None)
|
||||
|
||||
if starred is not None:
|
||||
starred = str2bool(starred)
|
||||
@ -402,10 +417,10 @@ class PartList(generics.ListCreateAPIView):
|
||||
queryset = queryset.exclude(pk__in=starred_parts)
|
||||
|
||||
# Cascade?
|
||||
cascade = str2bool(self.request.query_params.get('cascade', None))
|
||||
cascade = str2bool(params.get('cascade', None))
|
||||
|
||||
# Does the user wish to filter by category?
|
||||
cat_id = self.request.query_params.get('category', None)
|
||||
cat_id = params.get('category', None)
|
||||
|
||||
if cat_id is None:
|
||||
# No category filtering if category is not specified
|
||||
@ -437,7 +452,7 @@ class PartList(generics.ListCreateAPIView):
|
||||
queryset = part_serializers.PartSerializer.annotate_queryset(queryset)
|
||||
|
||||
# Filter by whether the part has stock
|
||||
has_stock = self.request.query_params.get("has_stock", None)
|
||||
has_stock = params.get("has_stock", None)
|
||||
if has_stock is not None:
|
||||
has_stock = str2bool(has_stock)
|
||||
|
||||
@ -447,7 +462,7 @@ class PartList(generics.ListCreateAPIView):
|
||||
queryset = queryset.filter(Q(in_stock__lte=0))
|
||||
|
||||
# If we are filtering by 'low_stock' status
|
||||
low_stock = self.request.query_params.get('low_stock', None)
|
||||
low_stock = params.get('low_stock', None)
|
||||
|
||||
if low_stock is not None:
|
||||
low_stock = str2bool(low_stock)
|
||||
|
@ -1,8 +1,10 @@
|
||||
{% load i18n %}
|
||||
|
||||
<div class='navigation'>
|
||||
<nav aria-label="breadcrumb">
|
||||
<ol class="breadcrumb">
|
||||
<li><a href='#' id='toggle-part-tree'><b><span class='fas fa-stream'></span></b></a></li>
|
||||
<li class="breadcrumb-item{% if category is None %} active" aria-current="page{% endif %}"><a href="/part/">Parts</a></li>
|
||||
<li class="breadcrumb-item{% if category is None %} active" aria-current="page{% endif %}"><a href="/part/">{% trans "Parts" %}</a></li>
|
||||
{% if category %}
|
||||
{% for path_item in category.parentpath %}
|
||||
<li class="breadcrumb-item"><a href="{% url 'category-detail' path_item.id %}">{{ path_item.name }}</a></li>
|
||||
|
@ -18,7 +18,7 @@
|
||||
{% endif %}
|
||||
{% if part.variant_of %}
|
||||
<div class='alert alert-info alert-block'>
|
||||
{% trans "This part is a variant of" %} <b><a href="{% url 'part-detail' part.variant_of.id %}">{{ part.variant_of.full_name }}</a></b>
|
||||
{% trans "This part is a variant of" %} <b><a href="{% url 'part-variants' part.variant_of.id %}">{{ part.variant_of.full_name }}</a></b>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
{% extends "part/part_base.html" %}
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% load inventree_extras %}
|
||||
|
||||
{% block details %}
|
||||
@ -7,7 +8,7 @@
|
||||
|
||||
<div class='row'>
|
||||
<div class='col-sm-6'>
|
||||
<h4>Part Variants</h4>
|
||||
<h4>{% trans "Part Variants" %}</h4>
|
||||
</div>
|
||||
<div class='col-sm-6'>
|
||||
</div>
|
||||
@ -17,45 +18,32 @@
|
||||
<div id='button-toolbar'>
|
||||
<div class='btn-group'>
|
||||
{% if part.is_template and part.active %}
|
||||
<button class='btn btn-success' id='new-variant' title='Create new variant'>New Variant</button>
|
||||
<button class='btn btn-success' id='new-variant' title='{% trans "Create new variant" %}'>{% trans "New Variant" %}</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table class='table table-striped table-condensed' id='variant-table' data-toolbar='#button-toolbar'>
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-sortable='true'>Variant</th>
|
||||
<th data-sortable='true'>Description</th>
|
||||
<th data-sortable='true'>Stock</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for variant in part.variants.all %}
|
||||
<tr>
|
||||
<td>
|
||||
{% include "hover_image.html" with image=variant.image hover=True %}
|
||||
<a href="{% url 'part-detail' variant.id %}">{{ variant.full_name }}</a>
|
||||
{% if not variant.active %}
|
||||
<span class='label label-warning' style='float: right;'>INACTIVE</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ variant.description }}</td>
|
||||
<td>{% decimal variant.total_stock %}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
<table class='table table-striped table-condensed' id='variants-table' data-toolbar='#button-toolbar'>
|
||||
</table>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block js_load %}
|
||||
{{ block.super }}
|
||||
|
||||
<!-- jquery-treegrid -->
|
||||
<script type='text/javascript' src='{% static "treegrid/js/jquery.treegrid.js" %}'></script>
|
||||
<script type='text/javascript' src='{% static "treegrid/js/jquery.treegrid.bootstrap3.js" %}'></script>
|
||||
|
||||
<!-- boostrap-table-treegrid -->
|
||||
<script type='text/javascript' src='{% static "bootstrap-table/extensions/treegrid/bootstrap-table-treegrid.js" %}'></script>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block js_ready %}
|
||||
{{ block.super }}
|
||||
|
||||
|
||||
$('#variant-table').inventreeTable({
|
||||
});
|
||||
loadPartVariantTable($('#variants-table'), {{ part.pk }});
|
||||
|
||||
$('#new-variant').click(function() {
|
||||
launchModalForm(
|
||||
|
@ -301,7 +301,7 @@ class MakePartVariant(AjaxCreateView):
|
||||
form = super(AjaxCreateView, self).get_form()
|
||||
|
||||
# Hide some variant-related fields
|
||||
form.fields['variant_of'].widget = HiddenInput()
|
||||
# form.fields['variant_of'].widget = HiddenInput()
|
||||
|
||||
return form
|
||||
|
||||
|
@ -60,6 +60,98 @@ function toggleStar(options) {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
function loadPartVariantTable(table, partId, options) {
|
||||
/* Load part variant table
|
||||
*/
|
||||
|
||||
var params = {
|
||||
ancestor: partId,
|
||||
};
|
||||
|
||||
var cols = [
|
||||
{
|
||||
field: 'pk',
|
||||
title: 'ID',
|
||||
visible: false,
|
||||
switchable: false,
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
title: '{% trans "Name" %}',
|
||||
switchable: false,
|
||||
formatter: function(value, row, index, field) {
|
||||
var html = '';
|
||||
|
||||
var name = '';
|
||||
|
||||
if (row.IPN) {
|
||||
name += row.IPN;
|
||||
name += ' | ';
|
||||
}
|
||||
|
||||
name += value;
|
||||
|
||||
if (row.revision) {
|
||||
name += ' | ';
|
||||
name += row.revision;
|
||||
}
|
||||
|
||||
if (row.is_template) {
|
||||
name = '<i>' + name + '</i>';
|
||||
}
|
||||
|
||||
html += imageHoverIcon(row.thumbnail);
|
||||
html += renderLink(name, `/part/${row.pk}/`);
|
||||
|
||||
return html;
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'IPN',
|
||||
title: '{% trans 'IPN' %}',
|
||||
},
|
||||
{
|
||||
field: 'revision',
|
||||
title: '{% trans 'Revision' %}',
|
||||
},
|
||||
{
|
||||
field: 'description',
|
||||
title: '{% trans "Description" %}',
|
||||
},
|
||||
{
|
||||
field: 'in_stock',
|
||||
title: '{% trans "Stock" %}',
|
||||
}
|
||||
];
|
||||
|
||||
table.inventreeTable({
|
||||
url: "{% url 'api-part-list' %}",
|
||||
name: 'partvariants',
|
||||
showColumns: true,
|
||||
original: params,
|
||||
queryParams: params,
|
||||
formatNoMatches: function() { return "{% trans "No variants found" %}"; },
|
||||
columns: cols,
|
||||
treeEnable: true,
|
||||
rootParentId: partId,
|
||||
parentIdField: 'variant_of',
|
||||
idField: 'pk',
|
||||
uniqueId: 'pk',
|
||||
treeShowField: 'name',
|
||||
sortable: true,
|
||||
search: true,
|
||||
onPostBody: function() {
|
||||
table.treegrid({
|
||||
treeColumn: 0,
|
||||
});
|
||||
|
||||
table.treegrid('collapseAll');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function loadPartTable(table, url, options={}) {
|
||||
/* Load part listing data into specified table.
|
||||
*
|
||||
|
Loading…
Reference in New Issue
Block a user