mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Refactor CancelBuild form
This commit is contained in:
parent
768e23c7b8
commit
bd3d6f47a1
@ -322,6 +322,13 @@ class BuildAllocate(BuildOrderContextMixin, generics.CreateAPIView):
|
||||
serializer_class = build.serializers.BuildAllocationSerializer
|
||||
|
||||
|
||||
class BuildCancel(BuildOrderContextMixin, generics.CreateAPIView):
|
||||
""" API endpoint for cancelling a BuildOrder """
|
||||
|
||||
queryset = Build.objects.all()
|
||||
serializer_class = build.serializers.BuildCancelSerializer
|
||||
|
||||
|
||||
class BuildItemDetail(generics.RetrieveUpdateDestroyAPIView):
|
||||
"""
|
||||
API endpoint for detail view of a BuildItem object
|
||||
@ -462,6 +469,7 @@ build_api_urls = [
|
||||
re_path(r'^create-output/', BuildOutputCreate.as_view(), name='api-build-output-create'),
|
||||
re_path(r'^delete-outputs/', BuildOutputDelete.as_view(), name='api-build-output-delete'),
|
||||
re_path(r'^finish/', BuildFinish.as_view(), name='api-build-finish'),
|
||||
re_path(r'^cancel/', BuildCancel.as_view(), name='api-build-cancel'),
|
||||
re_path(r'^unallocate/', BuildUnallocate.as_view(), name='api-build-unallocate'),
|
||||
re_path(r'^.*$', BuildDetail.as_view(), name='api-build-detail'),
|
||||
])),
|
||||
|
@ -5,22 +5,3 @@ Django Forms for interacting with Build objects
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django import forms
|
||||
|
||||
from InvenTree.forms import HelperForm
|
||||
|
||||
from .models import Build
|
||||
|
||||
|
||||
class CancelBuildForm(HelperForm):
|
||||
""" Form for cancelling a build """
|
||||
|
||||
confirm_cancel = forms.BooleanField(required=False, label=_('Confirm cancel'), help_text=_('Confirm build cancellation'))
|
||||
|
||||
class Meta:
|
||||
model = Build
|
||||
fields = [
|
||||
'confirm_cancel'
|
||||
]
|
||||
|
@ -479,6 +479,16 @@ class Build(MPTTModel, ReferenceIndexingMixin):
|
||||
|
||||
return outputs
|
||||
|
||||
@property
|
||||
def complete_count(self):
|
||||
|
||||
quantity = 0
|
||||
|
||||
for output in self.complete_outputs:
|
||||
quantity += output.quantity
|
||||
|
||||
return quantity
|
||||
|
||||
@property
|
||||
def incomplete_outputs(self):
|
||||
"""
|
||||
@ -588,7 +598,7 @@ class Build(MPTTModel, ReferenceIndexingMixin):
|
||||
trigger_event('build.completed', id=self.pk)
|
||||
|
||||
@transaction.atomic
|
||||
def cancelBuild(self, user):
|
||||
def cancel_build(self, user, **kwargs):
|
||||
""" Mark the Build as CANCELLED
|
||||
|
||||
- Delete any pending BuildItem objects (but do not remove items from stock)
|
||||
@ -596,8 +606,23 @@ class Build(MPTTModel, ReferenceIndexingMixin):
|
||||
- Save the Build object
|
||||
"""
|
||||
|
||||
for item in self.allocated_stock.all():
|
||||
item.delete()
|
||||
remove_allocated_stock = kwargs.get('remove_allocated_stock', False)
|
||||
remove_incomplete_outputs = kwargs.get('remove_incomplete_outputs', False)
|
||||
|
||||
# Handle stock allocations
|
||||
for build_item in self.allocated_stock.all():
|
||||
|
||||
if remove_allocated_stock:
|
||||
build_item.complete_allocation(user)
|
||||
|
||||
build_item.delete()
|
||||
|
||||
# Remove incomplete outputs (if required)
|
||||
if remove_incomplete_outputs:
|
||||
outputs = self.build_outputs.filter(is_building=True)
|
||||
|
||||
for output in outputs:
|
||||
output.delete()
|
||||
|
||||
# Date of 'completion' is the date the build was cancelled
|
||||
self.completion_date = datetime.now().date()
|
||||
@ -1025,6 +1050,24 @@ class Build(MPTTModel, ReferenceIndexingMixin):
|
||||
# All parts must be fully allocated!
|
||||
return True
|
||||
|
||||
def is_partially_allocated(self, output):
|
||||
"""
|
||||
Returns True if the particular build output is (at least) partially allocated
|
||||
"""
|
||||
|
||||
# If output is not specified, we are talking about "untracked" items
|
||||
if output is None:
|
||||
bom_items = self.untracked_bom_items
|
||||
else:
|
||||
bom_items = self.tracked_bom_items
|
||||
|
||||
for bom_item in bom_items:
|
||||
|
||||
if self.allocated_quantity(bom_item, output) > 0:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def are_untracked_parts_allocated(self):
|
||||
"""
|
||||
Returns True if the un-tracked parts are fully allocated for this BuildOrder
|
||||
|
@ -438,6 +438,52 @@ class BuildOutputCompleteSerializer(serializers.Serializer):
|
||||
)
|
||||
|
||||
|
||||
class BuildCancelSerializer(serializers.Serializer):
|
||||
|
||||
class Meta:
|
||||
fields = [
|
||||
'remove_allocated_stock',
|
||||
'remove_incomplete_outputs',
|
||||
]
|
||||
|
||||
def get_context_data(self):
|
||||
|
||||
build = self.context['build']
|
||||
|
||||
return {
|
||||
'has_allocated_stock': build.is_partially_allocated(None),
|
||||
'incomplete_outputs': build.incomplete_count,
|
||||
'completed_outputs': build.complete_count,
|
||||
}
|
||||
|
||||
remove_allocated_stock = serializers.BooleanField(
|
||||
label=_('Remove Allocated Stock'),
|
||||
help_text=_('Subtract any stock which has already been allocated to this build'),
|
||||
required=False,
|
||||
default=False,
|
||||
)
|
||||
|
||||
remove_incomplete_outputs = serializers.BooleanField(
|
||||
label=_('Remove Incomplete Outputs'),
|
||||
help_text=_('Delete any build outputs which have not been completed'),
|
||||
required=False,
|
||||
default=False,
|
||||
)
|
||||
|
||||
def save(self):
|
||||
|
||||
build = self.context['build']
|
||||
request = self.context['request']
|
||||
|
||||
data = self.validated_data
|
||||
|
||||
build.cancel_build(
|
||||
request.user,
|
||||
remove_allocated_stock=data.get('remove_unallocated_stock', False),
|
||||
remove_incomplete_outputs=data.get('remove_incomplete_outputs', False),
|
||||
)
|
||||
|
||||
|
||||
class BuildCompleteSerializer(serializers.Serializer):
|
||||
"""
|
||||
DRF serializer for marking a BuildOrder as complete
|
||||
|
@ -214,11 +214,13 @@ src="{% static 'img/blank_image.png' %}"
|
||||
});
|
||||
|
||||
$("#build-cancel").click(function() {
|
||||
launchModalForm("{% url 'build-cancel' build.id %}",
|
||||
{
|
||||
reload: true,
|
||||
submit_text: '{% trans "Cancel Build" %}',
|
||||
});
|
||||
|
||||
cancelBuildOrder(
|
||||
{{ build.pk }},
|
||||
{
|
||||
reload: true,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
$("#build-complete").on('click', function() {
|
||||
|
@ -1,7 +0,0 @@
|
||||
{% extends "modal_form.html" %}
|
||||
{% load i18n %}
|
||||
{% block pre_form_content %}
|
||||
|
||||
{% trans "Are you sure you wish to cancel this build?" %}
|
||||
|
||||
{% endblock %}
|
@ -304,7 +304,7 @@ class BuildTest(BuildTestBase):
|
||||
|
||||
"""
|
||||
self.allocate_stock(50, 50, 200, self.output_1)
|
||||
self.build.cancelBuild(None)
|
||||
self.build.cancel_build(None)
|
||||
|
||||
self.assertEqual(BuildItem.objects.count(), 0)
|
||||
"""
|
||||
|
@ -107,7 +107,7 @@ class BuildTestSimple(TestCase):
|
||||
|
||||
self.assertEqual(build.status, BuildStatus.PENDING)
|
||||
|
||||
build.cancelBuild(self.user)
|
||||
build.cancel_build(self.user)
|
||||
|
||||
self.assertEqual(build.status, BuildStatus.CANCELLED)
|
||||
|
||||
|
@ -7,7 +7,6 @@ from django.urls import include, re_path
|
||||
from . import views
|
||||
|
||||
build_detail_urls = [
|
||||
re_path(r'^cancel/', views.BuildCancel.as_view(), name='build-cancel'),
|
||||
re_path(r'^delete/', views.BuildDelete.as_view(), name='build-delete'),
|
||||
|
||||
re_path(r'^.*$', views.BuildDetail.as_view(), name='build-detail'),
|
||||
|
@ -43,37 +43,6 @@ class BuildIndex(InvenTreeRoleMixin, ListView):
|
||||
return context
|
||||
|
||||
|
||||
class BuildCancel(AjaxUpdateView):
|
||||
""" View to cancel a Build.
|
||||
Provides a cancellation information dialog
|
||||
"""
|
||||
|
||||
model = Build
|
||||
ajax_template_name = 'build/cancel.html'
|
||||
ajax_form_title = _('Cancel Build')
|
||||
context_object_name = 'build'
|
||||
form_class = forms.CancelBuildForm
|
||||
|
||||
def validate(self, build, form, **kwargs):
|
||||
|
||||
confirm = str2bool(form.cleaned_data.get('confirm_cancel', False))
|
||||
|
||||
if not confirm:
|
||||
form.add_error('confirm_cancel', _('Confirm build cancellation'))
|
||||
|
||||
def save(self, build, form, **kwargs):
|
||||
"""
|
||||
Cancel the build.
|
||||
"""
|
||||
|
||||
build.cancelBuild(self.request.user)
|
||||
|
||||
def get_data(self):
|
||||
return {
|
||||
'danger': _('Build was cancelled')
|
||||
}
|
||||
|
||||
|
||||
class BuildDetail(InvenTreeRoleMixin, DetailView):
|
||||
"""
|
||||
Detail view of a single Build object.
|
||||
|
@ -21,6 +21,7 @@
|
||||
/* exported
|
||||
allocateStockToBuild,
|
||||
autoAllocateStockToBuild,
|
||||
cancelBuildOrder,
|
||||
completeBuildOrder,
|
||||
createBuildOutput,
|
||||
editBuildOrder,
|
||||
@ -123,6 +124,49 @@ function newBuildOrder(options={}) {
|
||||
}
|
||||
|
||||
|
||||
/* Construct a form to cancel a build order */
|
||||
function cancelBuildOrder(build_id, options={}) {
|
||||
|
||||
constructForm(
|
||||
`/api/build/${build_id}/cancel/`,
|
||||
{
|
||||
method: 'POST',
|
||||
title: '{% trans "Cancel Build Order" %}',
|
||||
confirm: true,
|
||||
fields: {
|
||||
remove_allocated_stock: {},
|
||||
remove_incomplete_outputs: {},
|
||||
},
|
||||
preFormContent: function(opts) {
|
||||
var html = `
|
||||
<div class='alert alert-block alert-info'>
|
||||
{% trans "Are you sure you wish to cancel this build?" %}
|
||||
</div>`;
|
||||
|
||||
if (opts.context.has_allocated_stock) {
|
||||
html += `
|
||||
<div class='alert alert-block alert-warning'>
|
||||
{% trans "Stock items have been allocated to this build order" %}
|
||||
</div>`;
|
||||
}
|
||||
|
||||
if (opts.context.incomplete_outputs) {
|
||||
html += `
|
||||
<div class='alert alert-block alert-warning'>
|
||||
{% trans "There are incomplete outputs remaining for this build order" %}
|
||||
</div>`;
|
||||
}
|
||||
|
||||
return html;
|
||||
},
|
||||
onSuccess: function(response) {
|
||||
handleFormSuccess(response, options);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/* Construct a form to "complete" (finish) a build order */
|
||||
function completeBuildOrder(build_id, options={}) {
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user