mirror of
https://github.com/inventree/InvenTree.git
synced 2026-05-23 01:25:45 +00:00
Merge remote-tracking branch 'inventree/master'
This commit is contained in:
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
@@ -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
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -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 }},
|
||||||
|
|||||||
@@ -201,7 +201,9 @@ 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: {},
|
||||||
@@ -209,12 +211,40 @@ function completeBuildOrder(build_id, options={}) {
|
|||||||
accept_incomplete: {},
|
accept_incomplete: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Hide "accept overallocated" field if the build is *not* overallocated
|
||||||
|
if (!ctx.overallocated) {
|
||||||
|
delete fields.accept_overallocated;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hide "accept incomplete" field if the build has been completed
|
||||||
|
if (!ctx.remaining || ctx.remaining == 0) {
|
||||||
|
delete fields.accept_incomplete;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hide "accept unallocated" field if the build is fully allocated
|
||||||
|
if (ctx.allocated) {
|
||||||
|
delete fields.accept_unallocated;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fields;
|
||||||
|
},
|
||||||
|
preFormContent: function(opts) {
|
||||||
|
var ctx = opts.context || {};
|
||||||
|
|
||||||
var html = '';
|
var html = '';
|
||||||
|
|
||||||
if (options.allocated && options.completed) {
|
if (ctx.allocated && ctx.remaining == 0 && ctx.incomplete == 0) {
|
||||||
html += `
|
html += `
|
||||||
<div class='alert alert-block alert-success'>
|
<div class='alert alert-block alert-success'>
|
||||||
{% trans "Build order is ready to be completed" %}
|
{% trans "Build order is ready to be completed" %}'
|
||||||
|
</div>`;
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if (ctx.incomplete > 0) {
|
||||||
|
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>`;
|
</div>`;
|
||||||
} else {
|
} else {
|
||||||
html += `
|
html += `
|
||||||
@@ -222,37 +252,23 @@ function completeBuildOrder(build_id, options={}) {
|
|||||||
<strong>{% trans "Build Order is incomplete" %}</strong>
|
<strong>{% trans "Build Order is incomplete" %}</strong>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
if (!options.allocated) {
|
if (!ctx.allocated) {
|
||||||
html += `<div class='alert alert-block alert-warning'>{% trans "Required stock has not been fully allocated" %}</div>`;
|
html += `<div class='alert alert-block alert-warning'>{% trans "Required stock has not been fully allocated" %}</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!options.completed) {
|
if (ctx.remaining > 0) {
|
||||||
html += `<div class='alert alert-block alert-warning'>{% trans "Required build quantity has not been completed" %}</div>`;
|
html += `<div class='alert alert-block alert-warning'>{% trans "Required build quantity has not been completed" %}</div>`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hide particular fields if they are not required
|
return html;
|
||||||
|
},
|
||||||
if (options.allocated) {
|
|
||||||
delete fields.accept_unallocated;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.completed) {
|
|
||||||
delete fields.accept_incomplete;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!options.overallocated) {
|
|
||||||
delete fields.accept_overallocated;
|
|
||||||
}
|
|
||||||
|
|
||||||
constructForm(url, {
|
|
||||||
fields: fields,
|
|
||||||
reload: true,
|
reload: true,
|
||||||
confirm: true,
|
confirm: true,
|
||||||
method: 'POST',
|
|
||||||
title: '{% trans "Complete Build Order" %}',
|
title: '{% trans "Complete Build Order" %}',
|
||||||
preFormContent: html,
|
method: 'POST',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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, '');
|
||||||
|
|
||||||
|
|||||||
@@ -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'>
|
||||||
|
|||||||
Reference in New Issue
Block a user