2
0
mirror of https://github.com/inventree/InvenTree.git synced 2026-05-23 09:35:30 +00:00

Merge remote-tracking branch 'inventree/master'

This commit is contained in:
Oliver Walters
2022-09-22 08:39:52 +10:00
36 changed files with 16984 additions and 16295 deletions
@@ -839,6 +839,11 @@ input[type="submit"] {
padding: 10px; padding: 10px;
} }
.card-thumb {
max-width: 64px;
max-height: 64px;
}
.float-right { .float-right {
float: right; float: right;
} }
+15
View File
@@ -490,6 +490,21 @@ class OverallocationChoice():
class BuildCompleteSerializer(serializers.Serializer): class BuildCompleteSerializer(serializers.Serializer):
"""DRF serializer for marking a BuildOrder as complete.""" """DRF serializer for marking a BuildOrder as complete."""
def get_context_data(self):
"""Retrieve extra context data for this serializer.
This is so we can determine (at run time) whether the build is ready to be completed.
"""
build = self.context['build']
return {
'overallocated': build.has_overallocated_parts(),
'allocated': build.are_untracked_parts_allocated(),
'remaining': build.remaining,
'incomplete': build.incomplete_count,
}
accept_overallocated = serializers.ChoiceField( accept_overallocated = serializers.ChoiceField(
label=_('Overallocated Stock'), label=_('Overallocated Stock'),
choices=list(OverallocationChoice.OPTIONS.items()), choices=list(OverallocationChoice.OPTIONS.items()),
@@ -228,11 +228,7 @@ src="{% static 'img/blank_image.png' %}"
}); });
$("#build-complete").on('click', function() { $("#build-complete").on('click', function() {
completeBuildOrder({{ build.pk }}, { completeBuildOrder({{ build.pk }});
overallocated: {% if build.has_overallocated_parts %}true{% else %}false{% endif %},
allocated: {% if build.are_untracked_parts_allocated %}true{% else %}false{% endif %},
completed: {% if build.remaining == 0 %}true{% else %}false{% endif %},
});
}); });
{% endif %} {% endif %}
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
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
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
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
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
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
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
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
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+14
View File
@@ -1605,6 +1605,20 @@ class PartParameterList(ListCreateAPI):
queryset = PartParameter.objects.all() queryset = PartParameter.objects.all()
serializer_class = part_serializers.PartParameterSerializer serializer_class = part_serializers.PartParameterSerializer
def get_serializer(self, *args, **kwargs):
"""Return the serializer instance for this API endpoint.
If requested, extra detail fields are annotated to the queryset:
- template_detail
"""
try:
kwargs['template_detail'] = str2bool(self.request.GET.get('template_detail', True))
except AttributeError:
pass
return self.serializer_class(*args, **kwargs)
filter_backends = [ filter_backends = [
DjangoFilterBackend DjangoFilterBackend
] ]
+13
View File
@@ -247,6 +247,19 @@ class PartParameterTemplateSerializer(InvenTreeModelSerializer):
class PartParameterSerializer(InvenTreeModelSerializer): class PartParameterSerializer(InvenTreeModelSerializer):
"""JSON serializers for the PartParameter model.""" """JSON serializers for the PartParameter model."""
def __init__(self, *args, **kwargs):
"""Custom initialization method for the serializer.
Allows us to optionally include or exclude particular information
"""
template_detail = kwargs.pop('template_detail', False)
super().__init__(*args, **kwargs)
if not template_detail:
self.fields.pop('template_detail')
template_detail = PartParameterTemplateSerializer(source='template', many=False, read_only=True) template_detail = PartParameterTemplateSerializer(source='template', many=False, read_only=True)
class Meta: class Meta:
@@ -813,7 +813,6 @@
onPanelLoad("part-parameters", function() { onPanelLoad("part-parameters", function() {
loadPartParameterTable( loadPartParameterTable(
'#parameter-table', '#parameter-table',
'{% url "api-part-parameter-list" %}',
{ {
params: { params: {
part: {{ part.pk }}, part: {{ part.pk }},
+55 -39
View File
@@ -201,58 +201,74 @@ function cancelBuildOrder(build_id, options={}) {
/* Construct a form to "complete" (finish) a build order */ /* Construct a form to "complete" (finish) a build order */
function completeBuildOrder(build_id, options={}) { function completeBuildOrder(build_id, options={}) {
var url = `/api/build/${build_id}/finish/`; constructForm(`/api/build/${build_id}/finish/`, {
fieldsFunction: function(opts) {
var ctx = opts.context || {};
var fields = { var fields = {
accept_unallocated: {}, accept_unallocated: {},
accept_overallocated: {}, accept_overallocated: {},
accept_incomplete: {}, accept_incomplete: {},
}; };
var html = ''; // Hide "accept overallocated" field if the build is *not* overallocated
if (!ctx.overallocated) {
delete fields.accept_overallocated;
}
if (options.allocated && options.completed) { // Hide "accept incomplete" field if the build has been completed
html += ` if (!ctx.remaining || ctx.remaining == 0) {
<div class='alert alert-block alert-success'> delete fields.accept_incomplete;
{% trans "Build order is ready to be completed" %} }
</div>`;
} else {
html += `
<div class='alert alert-block alert-danger'>
<strong>{% trans "Build Order is incomplete" %}</strong>
</div>
`;
if (!options.allocated) { // Hide "accept unallocated" field if the build is fully allocated
html += `<div class='alert alert-block alert-warning'>{% trans "Required stock has not been fully allocated" %}</div>`; if (ctx.allocated) {
} delete fields.accept_unallocated;
}
if (!options.completed) { return fields;
html += `<div class='alert alert-block alert-warning'>{% trans "Required build quantity has not been completed" %}</div>`; },
} preFormContent: function(opts) {
} var ctx = opts.context || {};
// Hide particular fields if they are not required var html = '';
if (options.allocated) { if (ctx.allocated && ctx.remaining == 0 && ctx.incomplete == 0) {
delete fields.accept_unallocated; html += `
} <div class='alert alert-block alert-success'>
{% trans "Build order is ready to be completed" %}'
</div>`;
} else {
if (options.completed) { if (ctx.incomplete > 0) {
delete fields.accept_incomplete; html += `
} <div class='alert alert-block alert-danger'>
<strong>{% trans "Build order has incomplete outputs" %}</strong><br>
{% trans "This build order cannot be completed as there are incomplete outputs" %}
</div>`;
} else {
html += `
<div class='alert alert-block alert-danger'>
<strong>{% trans "Build Order is incomplete" %}</strong>
</div>
`;
}
if (!options.overallocated) { if (!ctx.allocated) {
delete fields.accept_overallocated; html += `<div class='alert alert-block alert-warning'>{% trans "Required stock has not been fully allocated" %}</div>`;
} }
constructForm(url, { if (ctx.remaining > 0) {
fields: fields, html += `<div class='alert alert-block alert-warning'>{% trans "Required build quantity has not been completed" %}</div>`;
}
}
return html;
},
reload: true, reload: true,
confirm: true, confirm: true,
method: 'POST',
title: '{% trans "Complete Build Order" %}', title: '{% trans "Complete Build Order" %}',
preFormContent: html, method: 'POST',
}); });
} }
+2 -1
View File
@@ -1727,7 +1727,8 @@ function initializeRelatedField(field, fields, options={}) {
var query = field.filters || {}; var query = field.filters || {};
// Add search and pagination options // Add search and pagination options
query.search = params.term; query.search = sanitizeInputString(params.term);
query.offset = offset; query.offset = offset;
query.limit = pageSize; query.limit = pageSize;
+5 -1
View File
@@ -258,7 +258,7 @@ function renderLink(text, url, options={}) {
return text; return text;
} }
var max_length = options.max_length || 100; var max_length = options.max_length || 0;
if (max_length > 0) { if (max_length > 0) {
text = shortenString(text, { text = shortenString(text, {
@@ -379,6 +379,10 @@ function setupNotesField(element, url, options={}) {
*/ */
function sanitizeInputString(s, options={}) { function sanitizeInputString(s, options={}) {
if (!s) {
return s;
}
// Remove ASCII control characters // Remove ASCII control characters
s = s.replace(/[\x01-\x1F]+/g, ''); s = s.replace(/[\x01-\x1F]+/g, '');
+6 -7
View File
@@ -803,7 +803,7 @@ function loadSimplePartTable(table, url, options={}) {
} }
function loadPartParameterTable(table, url, options) { function loadPartParameterTable(table, options) {
var params = options.params || {}; var params = options.params || {};
@@ -819,7 +819,7 @@ function loadPartParameterTable(table, url, options) {
setupFilterList('part-parameters', $(table), filterTarget); setupFilterList('part-parameters', $(table), filterTarget);
$(table).inventreeTable({ $(table).inventreeTable({
url: url, url: '{% url "api-part-parameter-list" %}',
original: params, original: params,
queryParams: filters, queryParams: filters,
name: 'partparameters', name: 'partparameters',
@@ -1292,13 +1292,12 @@ function loadParametricPartTable(table, options={}) {
}, },
columns: columns, columns: columns,
showColumns: true, showColumns: true,
// filterControl: true,
sidePagination: 'server', sidePagination: 'server',
idField: 'pk', idField: 'pk',
uniqueId: 'pk', uniqueId: 'pk',
onLoadSuccess: function() { onLoadSuccess: function(response) {
var data = $(table).bootstrapTable('getData'); var data = response.results;
for (var idx = 0; idx < data.length; idx++) { for (var idx = 0; idx < data.length; idx++) {
var row = data[idx]; var row = data[idx];
@@ -1309,7 +1308,7 @@ function loadParametricPartTable(table, options={}) {
row[`parameter_${parameter.template}`] = parameter.data; row[`parameter_${parameter.template}`] = parameter.data;
}); });
$(table).bootstrapTable('updateRow', pk, row); $(table).bootstrapTable('updateByUniqueId', pk, row);
} }
} }
}); });
@@ -1356,7 +1355,7 @@ function partGridTile(part) {
<div class='panel-content'> <div class='panel-content'>
<div class='row'> <div class='row'>
<div class='col-sm-4'> <div class='col-sm-4'>
<img src='${part.thumbnail}' style='width: 100%;' class='card-thumb' onclick='showModalImage("${part.image}")'> <img src='${part.thumbnail}' class='card-thumb' onclick='showModalImage("${part.image}")'>
</div> </div>
<div class='col-sm-8'> <div class='col-sm-8'>
<table class='table table-striped table-condensed'> <table class='table table-striped table-condensed'>