Add forms / views for creating a new build output, and completing the build

- Also some refactoring of how forms are handled and saved
This commit is contained in:
Oliver Walters
2020-11-02 22:56:26 +11:00
parent b02c87ea50
commit 500da8099b
18 changed files with 495 additions and 521 deletions

View File

@ -18,7 +18,7 @@ from stock.models import StockLocation, StockItem
from InvenTree.views import AjaxUpdateView, AjaxCreateView, AjaxDeleteView
from InvenTree.views import InvenTreeRoleMixin
from InvenTree.helpers import str2bool, ExtractSerialNumbers, normalize
from InvenTree.helpers import str2bool, extract_serial_numbers, normalize
from InvenTree.status_codes import BuildStatus
@ -67,11 +67,13 @@ class BuildCancel(AjaxUpdateView):
if not confirm:
form.add_error('confirm_cancel', _('Confirm build cancellation'))
def post_save(self, build, form, **kwargs):
def save(self, form, **kwargs):
"""
Cancel the build.
"""
build = self.get_object()
build.cancelBuild(self.request.user)
def get_data(self):
@ -156,13 +158,12 @@ class BuildAutoAllocate(AjaxUpdateView):
if not output:
form.add_error(None, _('Build output must be specified'))
def post_save(self, build, form, **kwargs):
def save(self, build, form, **kwargs):
"""
Once the form has been validated,
perform auto-allocations
"""
build = self.get_object()
output = form.cleaned_data.get('output', None)
build.autoAllocate(output)
@ -173,6 +174,99 @@ class BuildAutoAllocate(AjaxUpdateView):
}
class BuildOutputCreate(AjaxUpdateView):
"""
Create a new build output (StockItem) for a given build.
"""
model = Build
form_class = forms.BuildOutputCreateForm
ajax_template_name = 'build/build_output_create.html'
ajax_form_title = _('Create Build Output')
role_required = 'build.change'
def validate(self, build, form, **kwargs):
"""
Validation for the form:
"""
quantity = form.cleaned_data.get('quantity', None)
serials = form.cleaned_data.get('serial_numbers', None)
# Check that the serial numbers are valid
if serials:
try:
extracted = extract_serial_numbers(serials, quantity)
if extracted:
# Check for conflicting serial numbers
conflicts = build.part.find_conflicting_serial_numbers(extracted)
if len(conflicts) > 0:
msg = ",".join([str(c) for c in conflicts])
form.add_error(
'serial_numbers',
_('Serial numbers already exist') + ': ' + msg,
)
except ValidationError as e:
form.add_error('serial_numbers', e.messages)
else:
# If no serial numbers are provided, should they be?
if build.part.trackable:
form.add_error('serial_numbers', _('Serial numbers required for trackable build output'))
def save(self, build, form, **kwargs):
"""
Create a new build output
"""
data = form.cleaned_data
quantity = data.get('quantity', None)
batch = data.get('batch', None)
serials = data.get('serial_numbers', None)
if serials:
serial_numbers = extract_serial_numbers(serials, quantity)
else:
serial_numbers = None
build.create_build_output(
quantity,
serials=serial_numbers,
batch=batch,
)
def get_initial(self):
initials = super().get_initial()
build = self.get_object()
# Calculate the required quantity
quantity = max(0, build.remaining - build.incomplete_count)
initials['quantity'] = quantity
return initials
def get_form(self):
form = super().get_form()
build = self.get_object()
part = build.part
# If the part is not trackable, hide the serial number input
if not part.trackable:
form.fields['serial_numbers'] = HiddenInput()
return form
class BuildOutputDelete(AjaxUpdateView):
"""
Delete a build output (StockItem) for a given build.
@ -182,7 +276,7 @@ class BuildOutputDelete(AjaxUpdateView):
model = Build
form_class = forms.BuildOutputDeleteForm
ajax_form_title = _('Delete build output')
ajax_form_title = _('Delete Build Output')
role_required = 'build.delete'
def get_initial(self):
@ -296,7 +390,24 @@ class BuildUnallocate(AjaxUpdateView):
class BuildComplete(AjaxUpdateView):
""" View to mark a build as Complete.
"""
View to mark the build as complete.
Requirements:
- There can be no outstanding build outputs
- The "completed" value must meet or exceed the "quantity" value
"""
model = Build
form_class = forms.CompleteBuildForm
role_required = 'build.change'
ajax_form_title = _('Complete Build Order')
ajax_template_name = 'build/complete.html'
class BuildOutputComplete(AjaxUpdateView):
"""
View to mark a particular build output as Complete.
- Notifies the user of which parts will be removed from stock.
- Removes allocated items from stock
@ -304,10 +415,10 @@ class BuildComplete(AjaxUpdateView):
"""
model = Build
form_class = forms.CompleteBuildForm
form_class = forms.CompleteBuildOutputForm
context_object_name = "build"
ajax_form_title = _("Complete Build Output")
ajax_template_name = "build/complete.html"
ajax_template_name = "build/complete_output.html"
role_required = 'build.change'
def get_form(self):
@ -422,7 +533,7 @@ class BuildComplete(AjaxUpdateView):
return context
def post_save(self, build, form, **kwargs):
def save(self, build, form, **kwargs):
data = form.cleaned_data
@ -593,7 +704,7 @@ class BuildCreate(AjaxCreateView):
# Check that the provided serial numbers are sensible
try:
extracted = ExtractSerialNumbers(serials, quantity)
extracted = extract_serial_numbers(serials, quantity)
except ValidationError as e:
extracted = None
form.add_error('serial_numbers', e.messages)
@ -912,9 +1023,14 @@ class BuildAttachmentCreate(AjaxCreateView):
ajax_form_title = _('Add Build Order Attachment')
role_required = 'build.add'
def post_save(self, **kwargs):
self.object.user = self.request.user
self.object.save()
def save(self, form, **kwargs):
"""
Add information on the user that uploaded the attachment
"""
attachment = form.save(commit=False)
attachment.user = self.request.user
attachment.save()
def get_data(self):
return {