mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Merge pull request #1095 from SchrodingersGat/forms
Refactor how form errors are handled
This commit is contained in:
commit
ad90adbc04
@ -362,6 +362,17 @@ class AjaxCreateView(AjaxMixin, CreateView):
|
|||||||
form = self.get_form()
|
form = self.get_form()
|
||||||
return self.renderJsonResponse(request, form)
|
return self.renderJsonResponse(request, form)
|
||||||
|
|
||||||
|
def do_save(self, form):
|
||||||
|
"""
|
||||||
|
Method for actually saving the form to the database.
|
||||||
|
Default implementation is very simple,
|
||||||
|
but can be overridden if required.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.object = form.save()
|
||||||
|
|
||||||
|
return self.object
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
""" Responds to form POST. Validates POST data and returns status info.
|
""" Responds to form POST. Validates POST data and returns status info.
|
||||||
|
|
||||||
@ -385,13 +396,17 @@ class AjaxCreateView(AjaxMixin, CreateView):
|
|||||||
'form_valid': valid
|
'form_valid': valid
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Add in any extra class data
|
||||||
|
for value, key in enumerate(self.get_data()):
|
||||||
|
data[key] = value
|
||||||
|
|
||||||
if valid:
|
if valid:
|
||||||
|
|
||||||
# Perform (optional) pre-save step
|
# Perform (optional) pre-save step
|
||||||
self.pre_save(None, self.form)
|
self.pre_save(None, self.form)
|
||||||
|
|
||||||
# Save the object to the database
|
# Save the object to the database
|
||||||
self.object = self.form.save()
|
self.do_save(self.form)
|
||||||
|
|
||||||
# Perform (optional) post-save step
|
# Perform (optional) post-save step
|
||||||
self.post_save(self.object, self.form)
|
self.post_save(self.object, self.form)
|
||||||
@ -425,6 +440,17 @@ class AjaxUpdateView(AjaxMixin, UpdateView):
|
|||||||
|
|
||||||
return self.renderJsonResponse(request, self.get_form(), context=self.get_context_data())
|
return self.renderJsonResponse(request, self.get_form(), context=self.get_context_data())
|
||||||
|
|
||||||
|
def do_save(self, form):
|
||||||
|
"""
|
||||||
|
Method for updating the object in the database.
|
||||||
|
Default implementation is very simple,
|
||||||
|
but can be overridden if required.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.object = form.save()
|
||||||
|
|
||||||
|
return self.object
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
""" Respond to POST request.
|
""" Respond to POST request.
|
||||||
|
|
||||||
@ -453,13 +479,17 @@ class AjaxUpdateView(AjaxMixin, UpdateView):
|
|||||||
'form_valid': valid
|
'form_valid': valid
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Add in any extra class data
|
||||||
|
for value, key in enumerate(self.get_data()):
|
||||||
|
data[key] = value
|
||||||
|
|
||||||
if valid:
|
if valid:
|
||||||
|
|
||||||
# Perform (optional) pre-save step
|
# Perform (optional) pre-save step
|
||||||
self.pre_save(self.object, form)
|
self.pre_save(self.object, form)
|
||||||
|
|
||||||
# Save the updated objec to the database
|
# Save the updated objec to the database
|
||||||
obj = form.save()
|
obj = self.do_save(form)
|
||||||
|
|
||||||
# Perform (optional) post-save step
|
# Perform (optional) post-save step
|
||||||
self.post_save(obj, form)
|
self.post_save(obj, form)
|
||||||
@ -485,7 +515,7 @@ class AjaxDeleteView(AjaxMixin, UpdateView):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
form_class = DeleteForm
|
form_class = DeleteForm
|
||||||
ajax_form_title = "Delete Item"
|
ajax_form_title = _("Delete Item")
|
||||||
ajax_template_name = "modal_delete_form.html"
|
ajax_template_name = "modal_delete_form.html"
|
||||||
context_object_name = 'item'
|
context_object_name = 'item'
|
||||||
|
|
||||||
@ -534,7 +564,7 @@ class AjaxDeleteView(AjaxMixin, UpdateView):
|
|||||||
if confirmed:
|
if confirmed:
|
||||||
obj.delete()
|
obj.delete()
|
||||||
else:
|
else:
|
||||||
form.errors['confirm_delete'] = ['Check box to confirm item deletion']
|
form.add_error('confirm_delete', _('Check box to confirm item deletion'))
|
||||||
context[self.context_object_name] = self.get_object()
|
context[self.context_object_name] = self.get_object()
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
@ -549,7 +579,7 @@ class EditUserView(AjaxUpdateView):
|
|||||||
""" View for editing user information """
|
""" View for editing user information """
|
||||||
|
|
||||||
ajax_template_name = "modal_form.html"
|
ajax_template_name = "modal_form.html"
|
||||||
ajax_form_title = "Edit User Information"
|
ajax_form_title = _("Edit User Information")
|
||||||
form_class = EditUserForm
|
form_class = EditUserForm
|
||||||
|
|
||||||
def get_object(self):
|
def get_object(self):
|
||||||
@ -560,7 +590,7 @@ class SetPasswordView(AjaxUpdateView):
|
|||||||
""" View for setting user password """
|
""" View for setting user password """
|
||||||
|
|
||||||
ajax_template_name = "InvenTree/password.html"
|
ajax_template_name = "InvenTree/password.html"
|
||||||
ajax_form_title = "Set Password"
|
ajax_form_title = _("Set Password")
|
||||||
form_class = SetPasswordForm
|
form_class = SetPasswordForm
|
||||||
|
|
||||||
def get_object(self):
|
def get_object(self):
|
||||||
@ -579,9 +609,9 @@ class SetPasswordView(AjaxUpdateView):
|
|||||||
# Passwords must match
|
# Passwords must match
|
||||||
|
|
||||||
if not p1 == p2:
|
if not p1 == p2:
|
||||||
error = 'Password fields must match'
|
error = _('Password fields must match')
|
||||||
form.errors['enter_password'] = [error]
|
form.add_error('enter_password', error)
|
||||||
form.errors['confirm_password'] = [error]
|
form.add_error('confirm_password', error)
|
||||||
|
|
||||||
valid = False
|
valid = False
|
||||||
|
|
||||||
|
@ -60,30 +60,25 @@ class BuildCancel(AjaxUpdateView):
|
|||||||
form_class = forms.CancelBuildForm
|
form_class = forms.CancelBuildForm
|
||||||
role_required = 'build.change'
|
role_required = 'build.change'
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def validate(self, build, form, **kwargs):
|
||||||
""" Handle POST request. Mark the build status as CANCELLED """
|
|
||||||
|
|
||||||
build = self.get_object()
|
confirm = str2bool(form.cleaned_data.get('confirm_cancel', False))
|
||||||
|
|
||||||
form = self.get_form()
|
if not confirm:
|
||||||
|
form.add_error('confirm_cancel', _('Confirm build cancellation'))
|
||||||
|
|
||||||
valid = form.is_valid()
|
def post_save(self, build, form, **kwargs):
|
||||||
|
"""
|
||||||
|
Cancel the build.
|
||||||
|
"""
|
||||||
|
|
||||||
confirm = str2bool(request.POST.get('confirm_cancel', False))
|
build.cancelBuild(self.request.user)
|
||||||
|
|
||||||
if confirm:
|
def get_data(self):
|
||||||
build.cancelBuild(request.user)
|
return {
|
||||||
else:
|
|
||||||
form.errors['confirm_cancel'] = [_('Confirm build cancellation')]
|
|
||||||
valid = False
|
|
||||||
|
|
||||||
data = {
|
|
||||||
'form_valid': valid,
|
|
||||||
'danger': _('Build was cancelled')
|
'danger': _('Build was cancelled')
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.renderJsonResponse(request, form, data=data)
|
|
||||||
|
|
||||||
|
|
||||||
class BuildAutoAllocate(AjaxUpdateView):
|
class BuildAutoAllocate(AjaxUpdateView):
|
||||||
""" View to auto-allocate parts for a build.
|
""" View to auto-allocate parts for a build.
|
||||||
@ -128,8 +123,8 @@ class BuildAutoAllocate(AjaxUpdateView):
|
|||||||
valid = False
|
valid = False
|
||||||
|
|
||||||
if confirm is False:
|
if confirm is False:
|
||||||
form.errors['confirm'] = [_('Confirm stock allocation')]
|
form.add_error('confirm', _('Confirm stock allocation'))
|
||||||
form.non_field_errors = [_('Check the confirmation box at the bottom of the list')]
|
form.add_error(None, _('Check the confirmation box at the bottom of the list'))
|
||||||
else:
|
else:
|
||||||
build.autoAllocate()
|
build.autoAllocate()
|
||||||
valid = True
|
valid = True
|
||||||
@ -163,8 +158,8 @@ class BuildUnallocate(AjaxUpdateView):
|
|||||||
valid = False
|
valid = False
|
||||||
|
|
||||||
if confirm is False:
|
if confirm is False:
|
||||||
form.errors['confirm'] = [_('Confirm unallocation of build stock')]
|
form.add_error('confirm', _('Confirm unallocation of build stock'))
|
||||||
form.non_field_errors = [_('Check the confirmation box')]
|
form.add_error(None, _('Check the confirmation box'))
|
||||||
else:
|
else:
|
||||||
build.unallocateStock()
|
build.unallocateStock()
|
||||||
valid = True
|
valid = True
|
||||||
@ -266,15 +261,13 @@ class BuildComplete(AjaxUpdateView):
|
|||||||
valid = False
|
valid = False
|
||||||
|
|
||||||
if confirm is False:
|
if confirm is False:
|
||||||
form.errors['confirm'] = [
|
form.add_error('confirm', _('Confirm completion of build'))
|
||||||
_('Confirm completion of build'),
|
|
||||||
]
|
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
location = StockLocation.objects.get(id=loc_id)
|
location = StockLocation.objects.get(id=loc_id)
|
||||||
valid = True
|
valid = True
|
||||||
except (ValueError, StockLocation.DoesNotExist):
|
except (ValueError, StockLocation.DoesNotExist):
|
||||||
form.errors['location'] = [_('Invalid location selected')]
|
form.add_error('location', _('Invalid location selected'))
|
||||||
|
|
||||||
serials = []
|
serials = []
|
||||||
|
|
||||||
@ -291,24 +284,20 @@ class BuildComplete(AjaxUpdateView):
|
|||||||
# Exctract a list of provided serial numbers
|
# Exctract a list of provided serial numbers
|
||||||
serials = ExtractSerialNumbers(sn, build.quantity)
|
serials = ExtractSerialNumbers(sn, build.quantity)
|
||||||
|
|
||||||
existing = []
|
existing = build.part.find_conflicting_serial_numbers(serials)
|
||||||
|
|
||||||
for serial in serials:
|
|
||||||
if build.part.checkIfSerialNumberExists(serial):
|
|
||||||
existing.append(serial)
|
|
||||||
|
|
||||||
if len(existing) > 0:
|
if len(existing) > 0:
|
||||||
exists = ",".join([str(x) for x in existing])
|
exists = ",".join([str(x) for x in existing])
|
||||||
form.errors['serial_numbers'] = [_('The following serial numbers already exist: ({sn})'.format(sn=exists))]
|
form.add_error('serial_numbers', _('The following serial numbers already exist: ({sn})'.format(sn=exists)))
|
||||||
valid = False
|
valid = False
|
||||||
|
|
||||||
except ValidationError as e:
|
except ValidationError as e:
|
||||||
form.errors['serial_numbers'] = e.messages
|
form.add_error('serial_numbers', e.messages)
|
||||||
valid = False
|
valid = False
|
||||||
|
|
||||||
if valid:
|
if valid:
|
||||||
if not build.completeBuild(location, serials, request.user):
|
if not build.completeBuild(location, serials, request.user):
|
||||||
form.non_field_errors = [('Build could not be completed')]
|
form.add_error(None, _('Build could not be completed'))
|
||||||
valid = False
|
valid = False
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
|
@ -21,7 +21,7 @@ from .models import SalesOrderAllocation
|
|||||||
|
|
||||||
class IssuePurchaseOrderForm(HelperForm):
|
class IssuePurchaseOrderForm(HelperForm):
|
||||||
|
|
||||||
confirm = forms.BooleanField(required=False, help_text=_('Place order'))
|
confirm = forms.BooleanField(required=True, initial=False, help_text=_('Place order'))
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = PurchaseOrder
|
model = PurchaseOrder
|
||||||
@ -32,7 +32,7 @@ class IssuePurchaseOrderForm(HelperForm):
|
|||||||
|
|
||||||
class CompletePurchaseOrderForm(HelperForm):
|
class CompletePurchaseOrderForm(HelperForm):
|
||||||
|
|
||||||
confirm = forms.BooleanField(required=False, help_text=_("Mark order as complete"))
|
confirm = forms.BooleanField(required=True, help_text=_("Mark order as complete"))
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = PurchaseOrder
|
model = PurchaseOrder
|
||||||
@ -43,7 +43,7 @@ class CompletePurchaseOrderForm(HelperForm):
|
|||||||
|
|
||||||
class CancelPurchaseOrderForm(HelperForm):
|
class CancelPurchaseOrderForm(HelperForm):
|
||||||
|
|
||||||
confirm = forms.BooleanField(required=False, help_text=_('Cancel order'))
|
confirm = forms.BooleanField(required=True, help_text=_('Cancel order'))
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = PurchaseOrder
|
model = PurchaseOrder
|
||||||
@ -54,7 +54,7 @@ class CancelPurchaseOrderForm(HelperForm):
|
|||||||
|
|
||||||
class CancelSalesOrderForm(HelperForm):
|
class CancelSalesOrderForm(HelperForm):
|
||||||
|
|
||||||
confirm = forms.BooleanField(required=False, help_text=_('Cancel order'))
|
confirm = forms.BooleanField(required=True, help_text=_('Cancel order'))
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = SalesOrder
|
model = SalesOrder
|
||||||
@ -65,7 +65,7 @@ class CancelSalesOrderForm(HelperForm):
|
|||||||
|
|
||||||
class ShipSalesOrderForm(HelperForm):
|
class ShipSalesOrderForm(HelperForm):
|
||||||
|
|
||||||
confirm = forms.BooleanField(required=False, help_text=_('Ship order'))
|
confirm = forms.BooleanField(required=True, help_text=_('Ship order'))
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = SalesOrder
|
model = SalesOrder
|
||||||
|
@ -209,6 +209,7 @@ class PurchaseOrder(Order):
|
|||||||
|
|
||||||
line.save()
|
line.save()
|
||||||
|
|
||||||
|
@transaction.atomic
|
||||||
def place_order(self):
|
def place_order(self):
|
||||||
""" Marks the PurchaseOrder as PLACED. Order must be currently PENDING. """
|
""" Marks the PurchaseOrder as PLACED. Order must be currently PENDING. """
|
||||||
|
|
||||||
@ -217,6 +218,7 @@ class PurchaseOrder(Order):
|
|||||||
self.issue_date = datetime.now().date()
|
self.issue_date = datetime.now().date()
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
|
@transaction.atomic
|
||||||
def complete_order(self):
|
def complete_order(self):
|
||||||
""" Marks the PurchaseOrder as COMPLETE. Order must be currently PLACED. """
|
""" Marks the PurchaseOrder as COMPLETE. Order must be currently PLACED. """
|
||||||
|
|
||||||
@ -225,10 +227,16 @@ class PurchaseOrder(Order):
|
|||||||
self.complete_date = datetime.now().date()
|
self.complete_date = datetime.now().date()
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
|
def can_cancel(self):
|
||||||
|
return self.status not in [
|
||||||
|
PurchaseOrderStatus.PLACED,
|
||||||
|
PurchaseOrderStatus.PENDING
|
||||||
|
]
|
||||||
|
|
||||||
def cancel_order(self):
|
def cancel_order(self):
|
||||||
""" Marks the PurchaseOrder as CANCELLED. """
|
""" Marks the PurchaseOrder as CANCELLED. """
|
||||||
|
|
||||||
if self.status in [PurchaseOrderStatus.PLACED, PurchaseOrderStatus.PENDING]:
|
if self.can_cancel():
|
||||||
self.status = PurchaseOrderStatus.CANCELLED
|
self.status = PurchaseOrderStatus.CANCELLED
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
@ -377,6 +385,16 @@ class SalesOrder(Order):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def can_cancel(self):
|
||||||
|
"""
|
||||||
|
Return True if this order can be cancelled
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not self.status == SalesOrderStatus.PENDING:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
def cancel_order(self):
|
def cancel_order(self):
|
||||||
"""
|
"""
|
||||||
@ -386,7 +404,7 @@ class SalesOrder(Order):
|
|||||||
- Delete any StockItems which have been allocated
|
- Delete any StockItems which have been allocated
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not self.status == SalesOrderStatus.PENDING:
|
if not self.can_cancel():
|
||||||
return False
|
return False
|
||||||
|
|
||||||
self.status = SalesOrderStatus.CANCELLED
|
self.status = SalesOrderStatus.CANCELLED
|
||||||
|
@ -113,6 +113,7 @@ class POTests(OrderViewTestCase):
|
|||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
data = json.loads(response.content)
|
data = json.loads(response.content)
|
||||||
|
|
||||||
self.assertFalse(data['form_valid'])
|
self.assertFalse(data['form_valid'])
|
||||||
|
|
||||||
# Test WITH confirmation
|
# Test WITH confirmation
|
||||||
@ -151,7 +152,6 @@ class POTests(OrderViewTestCase):
|
|||||||
response = self.client.post(url, post_data, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
|
response = self.client.post(url, post_data, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
|
||||||
data = json.loads(response.content)
|
data = json.loads(response.content)
|
||||||
self.assertFalse(data['form_valid'])
|
self.assertFalse(data['form_valid'])
|
||||||
self.assertIn('Invalid Purchase Order', str(data['html_form']))
|
|
||||||
|
|
||||||
# POST with a part that does not match the purchase order
|
# POST with a part that does not match the purchase order
|
||||||
post_data['order'] = 1
|
post_data['order'] = 1
|
||||||
@ -159,14 +159,12 @@ class POTests(OrderViewTestCase):
|
|||||||
response = self.client.post(url, post_data, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
|
response = self.client.post(url, post_data, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
|
||||||
data = json.loads(response.content)
|
data = json.loads(response.content)
|
||||||
self.assertFalse(data['form_valid'])
|
self.assertFalse(data['form_valid'])
|
||||||
self.assertIn('must match for Part and Order', str(data['html_form']))
|
|
||||||
|
|
||||||
# POST with an invalid part
|
# POST with an invalid part
|
||||||
post_data['part'] = 12345
|
post_data['part'] = 12345
|
||||||
response = self.client.post(url, post_data, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
|
response = self.client.post(url, post_data, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
|
||||||
data = json.loads(response.content)
|
data = json.loads(response.content)
|
||||||
self.assertFalse(data['form_valid'])
|
self.assertFalse(data['form_valid'])
|
||||||
self.assertIn('Invalid SupplierPart selection', str(data['html_form']))
|
|
||||||
|
|
||||||
# POST the form with valid data
|
# POST the form with valid data
|
||||||
post_data['part'] = 100
|
post_data['part'] = 100
|
||||||
|
@ -404,29 +404,19 @@ class PurchaseOrderCancel(AjaxUpdateView):
|
|||||||
form_class = order_forms.CancelPurchaseOrderForm
|
form_class = order_forms.CancelPurchaseOrderForm
|
||||||
role_required = 'purchase_order.change'
|
role_required = 'purchase_order.change'
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def validate(self, order, form, **kwargs):
|
||||||
""" Mark the PO as 'CANCELLED' """
|
|
||||||
|
confirm = str2bool(form.cleaned_data.get('confirm', False))
|
||||||
order = self.get_object()
|
|
||||||
form = self.get_form()
|
|
||||||
|
|
||||||
confirm = str2bool(request.POST.get('confirm', False))
|
|
||||||
|
|
||||||
valid = False
|
|
||||||
|
|
||||||
if not confirm:
|
if not confirm:
|
||||||
form.errors['confirm'] = [_('Confirm order cancellation')]
|
form.add_error('confirm', _('Confirm order cancellation'))
|
||||||
else:
|
|
||||||
valid = True
|
|
||||||
|
|
||||||
data = {
|
if not order.can_cancel():
|
||||||
'form_valid': valid
|
form.add_error(None, _('Order cannot be cancelled'))
|
||||||
}
|
|
||||||
|
|
||||||
if valid:
|
def post_save(self, order, form, **kwargs):
|
||||||
order.cancel_order()
|
|
||||||
|
|
||||||
return self.renderJsonResponse(request, form, data)
|
order.cancel_order()
|
||||||
|
|
||||||
|
|
||||||
class SalesOrderCancel(AjaxUpdateView):
|
class SalesOrderCancel(AjaxUpdateView):
|
||||||
@ -438,30 +428,19 @@ class SalesOrderCancel(AjaxUpdateView):
|
|||||||
form_class = order_forms.CancelSalesOrderForm
|
form_class = order_forms.CancelSalesOrderForm
|
||||||
role_required = 'sales_order.change'
|
role_required = 'sales_order.change'
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def validate(self, order, form, **kwargs):
|
||||||
|
|
||||||
order = self.get_object()
|
confirm = str2bool(form.cleaned_data.get('confirm', False))
|
||||||
form = self.get_form()
|
|
||||||
|
|
||||||
confirm = str2bool(request.POST.get('confirm', False))
|
|
||||||
|
|
||||||
valid = False
|
|
||||||
|
|
||||||
if not confirm:
|
if not confirm:
|
||||||
form.errors['confirm'] = [_('Confirm order cancellation')]
|
form.add_error('confirm', _('Confirm order cancellation'))
|
||||||
else:
|
|
||||||
valid = True
|
|
||||||
|
|
||||||
if valid:
|
if not order.can_cancel():
|
||||||
if not order.cancel_order():
|
form.add_error(None, _('Order cannot be cancelled'))
|
||||||
form.non_field_errors = [_('Could not cancel order')]
|
|
||||||
valid = False
|
|
||||||
|
|
||||||
data = {
|
def post_save(self, order, form, **kwargs):
|
||||||
'form_valid': valid,
|
|
||||||
}
|
|
||||||
|
|
||||||
return self.renderJsonResponse(request, form, data)
|
order.cancel_order()
|
||||||
|
|
||||||
|
|
||||||
class PurchaseOrderIssue(AjaxUpdateView):
|
class PurchaseOrderIssue(AjaxUpdateView):
|
||||||
@ -473,30 +452,22 @@ class PurchaseOrderIssue(AjaxUpdateView):
|
|||||||
form_class = order_forms.IssuePurchaseOrderForm
|
form_class = order_forms.IssuePurchaseOrderForm
|
||||||
role_required = 'purchase_order.change'
|
role_required = 'purchase_order.change'
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def validate(self, order, form, **kwargs):
|
||||||
""" Mark the purchase order as 'PLACED' """
|
|
||||||
|
|
||||||
order = self.get_object()
|
confirm = str2bool(self.request.POST.get('confirm', False))
|
||||||
form = self.get_form()
|
|
||||||
|
|
||||||
confirm = str2bool(request.POST.get('confirm', False))
|
|
||||||
|
|
||||||
valid = False
|
|
||||||
|
|
||||||
if not confirm:
|
if not confirm:
|
||||||
form.errors['confirm'] = [_('Confirm order placement')]
|
form.add_error('confirm', _('Confirm order placement'))
|
||||||
else:
|
|
||||||
valid = True
|
|
||||||
|
|
||||||
data = {
|
def post_save(self, order, form, **kwargs):
|
||||||
'form_valid': valid,
|
|
||||||
|
order.place_order()
|
||||||
|
|
||||||
|
def get_data(self):
|
||||||
|
return {
|
||||||
|
'success': _('Purchase order issued')
|
||||||
}
|
}
|
||||||
|
|
||||||
if valid:
|
|
||||||
order.place_order()
|
|
||||||
|
|
||||||
return self.renderJsonResponse(request, form, data)
|
|
||||||
|
|
||||||
|
|
||||||
class PurchaseOrderComplete(AjaxUpdateView):
|
class PurchaseOrderComplete(AjaxUpdateView):
|
||||||
""" View for marking a PurchaseOrder as complete.
|
""" View for marking a PurchaseOrder as complete.
|
||||||
@ -517,23 +488,22 @@ class PurchaseOrderComplete(AjaxUpdateView):
|
|||||||
|
|
||||||
return ctx
|
return ctx
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def validate(self, order, form, **kwargs):
|
||||||
|
|
||||||
confirm = str2bool(request.POST.get('confirm', False))
|
confirm = str2bool(form.cleaned_data.get('confirm', False))
|
||||||
|
|
||||||
if confirm:
|
if not confirm:
|
||||||
po = self.get_object()
|
form.add_error('confirm', _('Confirm order completion'))
|
||||||
po.status = PurchaseOrderStatus.COMPLETE
|
|
||||||
po.save()
|
|
||||||
|
|
||||||
data = {
|
def post_save(self, order, form, **kwargs):
|
||||||
'form_valid': confirm
|
|
||||||
|
order.complete_order()
|
||||||
|
|
||||||
|
def get_data(self):
|
||||||
|
return {
|
||||||
|
'success': _('Purchase order completed')
|
||||||
}
|
}
|
||||||
|
|
||||||
form = self.get_form()
|
|
||||||
|
|
||||||
return self.renderJsonResponse(request, form, data)
|
|
||||||
|
|
||||||
|
|
||||||
class SalesOrderShip(AjaxUpdateView):
|
class SalesOrderShip(AjaxUpdateView):
|
||||||
""" View for 'shipping' a SalesOrder """
|
""" View for 'shipping' a SalesOrder """
|
||||||
@ -558,13 +528,13 @@ class SalesOrderShip(AjaxUpdateView):
|
|||||||
valid = False
|
valid = False
|
||||||
|
|
||||||
if not confirm:
|
if not confirm:
|
||||||
form.errors['confirm'] = [_('Confirm order shipment')]
|
form.add_error('confirm', _('Confirm order shipment'))
|
||||||
else:
|
else:
|
||||||
valid = True
|
valid = True
|
||||||
|
|
||||||
if valid:
|
if valid:
|
||||||
if not order.ship_order(request.user):
|
if not order.ship_order(request.user):
|
||||||
form.non_field_errors = [_('Could not ship order')]
|
form.add_error(None, _('Could not ship order'))
|
||||||
valid = False
|
valid = False
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
@ -1117,52 +1087,21 @@ class POLineItemCreate(AjaxCreateView):
|
|||||||
ajax_form_title = _('Add Line Item')
|
ajax_form_title = _('Add Line Item')
|
||||||
role_required = 'purchase_order.add'
|
role_required = 'purchase_order.add'
|
||||||
|
|
||||||
def post(self, request, *arg, **kwargs):
|
def validate(self, item, form, **kwargs):
|
||||||
|
|
||||||
self.request = request
|
order = form.cleaned_data.get('order', None)
|
||||||
|
|
||||||
form = self.get_form()
|
part = form.cleaned_data.get('part', None)
|
||||||
|
|
||||||
valid = form.is_valid()
|
if not part:
|
||||||
|
form.add_error('part', _('Supplier part must be specified'))
|
||||||
|
|
||||||
# Extract the SupplierPart ID from the form
|
if part and order:
|
||||||
part_id = form['part'].value()
|
if not part.supplier == order.supplier:
|
||||||
|
form.add_error(
|
||||||
# Extract the Order ID from the form
|
'part',
|
||||||
order_id = form['order'].value()
|
_('Supplier must match for Part and Order')
|
||||||
|
)
|
||||||
try:
|
|
||||||
order = PurchaseOrder.objects.get(id=order_id)
|
|
||||||
except (ValueError, PurchaseOrder.DoesNotExist):
|
|
||||||
order = None
|
|
||||||
form.errors['order'] = [_('Invalid Purchase Order')]
|
|
||||||
valid = False
|
|
||||||
|
|
||||||
try:
|
|
||||||
sp = SupplierPart.objects.get(id=part_id)
|
|
||||||
|
|
||||||
if order is not None:
|
|
||||||
if not sp.supplier == order.supplier:
|
|
||||||
form.errors['part'] = [_('Supplier must match for Part and Order')]
|
|
||||||
valid = False
|
|
||||||
|
|
||||||
except (SupplierPart.DoesNotExist, ValueError):
|
|
||||||
valid = False
|
|
||||||
form.errors['part'] = [_('Invalid SupplierPart selection')]
|
|
||||||
|
|
||||||
data = {
|
|
||||||
'form_valid': valid,
|
|
||||||
}
|
|
||||||
|
|
||||||
if valid:
|
|
||||||
self.object = form.save()
|
|
||||||
|
|
||||||
data['pk'] = self.object.pk
|
|
||||||
data['text'] = str(self.object)
|
|
||||||
else:
|
|
||||||
self.object = None
|
|
||||||
|
|
||||||
return self.renderJsonResponse(request, form, data,)
|
|
||||||
|
|
||||||
def get_form(self):
|
def get_form(self):
|
||||||
""" Limit choice options based on the selected order, etc
|
""" Limit choice options based on the selected order, etc
|
||||||
|
@ -360,7 +360,7 @@ class Part(MPTTModel):
|
|||||||
# And recursively check too
|
# And recursively check too
|
||||||
item.sub_part.checkAddToBOM(parent)
|
item.sub_part.checkAddToBOM(parent)
|
||||||
|
|
||||||
def checkIfSerialNumberExists(self, sn):
|
def checkIfSerialNumberExists(self, sn, exclude_self=False):
|
||||||
"""
|
"""
|
||||||
Check if a serial number exists for this Part.
|
Check if a serial number exists for this Part.
|
||||||
|
|
||||||
@ -369,10 +369,27 @@ class Part(MPTTModel):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
parts = Part.objects.filter(tree_id=self.tree_id)
|
parts = Part.objects.filter(tree_id=self.tree_id)
|
||||||
|
|
||||||
stock = StockModels.StockItem.objects.filter(part__in=parts, serial=sn)
|
stock = StockModels.StockItem.objects.filter(part__in=parts, serial=sn)
|
||||||
|
|
||||||
|
if exclude_self:
|
||||||
|
stock = stock.exclude(pk=self.pk)
|
||||||
|
|
||||||
return stock.exists()
|
return stock.exists()
|
||||||
|
|
||||||
|
def find_conflicting_serial_numbers(self, serials):
|
||||||
|
"""
|
||||||
|
For a provided list of serials, return a list of those which are conflicting.
|
||||||
|
"""
|
||||||
|
|
||||||
|
conflicts = []
|
||||||
|
|
||||||
|
for serial in serials:
|
||||||
|
if self.checkIfSerialNumberExists(serial, exclude_self=True):
|
||||||
|
conflicts.append(serial)
|
||||||
|
|
||||||
|
return conflicts
|
||||||
|
|
||||||
def getLatestSerialNumber(self):
|
def getLatestSerialNumber(self):
|
||||||
"""
|
"""
|
||||||
Return the "latest" serial number for this Part.
|
Return the "latest" serial number for this Part.
|
||||||
@ -1116,6 +1133,31 @@ class Part(MPTTModel):
|
|||||||
|
|
||||||
bom_item.save()
|
bom_item.save()
|
||||||
|
|
||||||
|
@transaction.atomic
|
||||||
|
def copy_parameters_from(self, other, **kwargs):
|
||||||
|
|
||||||
|
clear = kwargs.get('clear', True)
|
||||||
|
|
||||||
|
if clear:
|
||||||
|
self.get_parameters().delete()
|
||||||
|
|
||||||
|
for parameter in other.get_parameters():
|
||||||
|
|
||||||
|
# If this part already has a parameter pointing to the same template,
|
||||||
|
# delete that parameter from this part first!
|
||||||
|
|
||||||
|
try:
|
||||||
|
existing = PartParameter.objects.get(part=self, template=parameter.template)
|
||||||
|
existing.delete()
|
||||||
|
except (PartParameter.DoesNotExist):
|
||||||
|
pass
|
||||||
|
|
||||||
|
parameter.part = self
|
||||||
|
parameter.pk = None
|
||||||
|
|
||||||
|
parameter.save()
|
||||||
|
|
||||||
|
@transaction.atomic
|
||||||
def deepCopy(self, other, **kwargs):
|
def deepCopy(self, other, **kwargs):
|
||||||
""" Duplicates non-field data from another part.
|
""" Duplicates non-field data from another part.
|
||||||
Does not alter the normal fields of this part,
|
Does not alter the normal fields of this part,
|
||||||
@ -1139,15 +1181,8 @@ class Part(MPTTModel):
|
|||||||
|
|
||||||
# Copy the parameters data
|
# Copy the parameters data
|
||||||
if kwargs.get('parameters', True):
|
if kwargs.get('parameters', True):
|
||||||
# Get template part parameters
|
self.copy_parameters_from(other)
|
||||||
parameters = other.get_parameters()
|
|
||||||
# Copy template part parameters to new variant part
|
|
||||||
for parameter in parameters:
|
|
||||||
PartParameter.create(part=self,
|
|
||||||
template=parameter.template,
|
|
||||||
data=parameter.data,
|
|
||||||
save=True)
|
|
||||||
|
|
||||||
# Copy the fields that aren't available in the duplicate form
|
# Copy the fields that aren't available in the duplicate form
|
||||||
self.salable = other.salable
|
self.salable = other.salable
|
||||||
self.assembly = other.assembly
|
self.assembly = other.assembly
|
||||||
|
@ -371,6 +371,8 @@ class MakePartVariant(AjaxCreateView):
|
|||||||
initials = model_to_dict(part_template)
|
initials = model_to_dict(part_template)
|
||||||
initials['is_template'] = False
|
initials['is_template'] = False
|
||||||
initials['variant_of'] = part_template
|
initials['variant_of'] = part_template
|
||||||
|
initials['bom_copy'] = InvenTreeSetting.get_setting('PART_COPY_BOM')
|
||||||
|
initials['parameters_copy'] = InvenTreeSetting.get_setting('PART_COPY_PARAMETERS')
|
||||||
|
|
||||||
return initials
|
return initials
|
||||||
|
|
||||||
@ -446,9 +448,9 @@ class PartDuplicate(AjaxCreateView):
|
|||||||
confirmed = str2bool(request.POST.get('confirm_creation', False))
|
confirmed = str2bool(request.POST.get('confirm_creation', False))
|
||||||
|
|
||||||
if not confirmed:
|
if not confirmed:
|
||||||
form.errors['confirm_creation'] = ['Possible matches exist - confirm creation of new part']
|
msg = _('Possible matches exist - confirm creation of new part')
|
||||||
|
form.add_error('confirm_creation', msg)
|
||||||
form.pre_form_warning = 'Possible matches exist - confirm creation of new part'
|
form.pre_form_warning = msg
|
||||||
valid = False
|
valid = False
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
@ -576,9 +578,10 @@ class PartCreate(AjaxCreateView):
|
|||||||
confirmed = str2bool(request.POST.get('confirm_creation', False))
|
confirmed = str2bool(request.POST.get('confirm_creation', False))
|
||||||
|
|
||||||
if not confirmed:
|
if not confirmed:
|
||||||
form.errors['confirm_creation'] = ['Possible matches exist - confirm creation of new part']
|
msg = _('Possible matches exist - confirm creation of new part')
|
||||||
|
form.add_error('confirm_creation', msg)
|
||||||
|
|
||||||
form.pre_form_warning = 'Possible matches exist - confirm creation of new part'
|
form.pre_form_warning = msg
|
||||||
valid = False
|
valid = False
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
@ -905,24 +908,22 @@ class BomValidate(AjaxUpdateView):
|
|||||||
|
|
||||||
return self.renderJsonResponse(request, form, context=self.get_context())
|
return self.renderJsonResponse(request, form, context=self.get_context())
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def validate(self, part, form, **kwargs):
|
||||||
|
|
||||||
form = self.get_form()
|
confirm = str2bool(form.cleaned_data.get('validate', False))
|
||||||
part = self.get_object()
|
|
||||||
|
|
||||||
confirmed = str2bool(request.POST.get('validate', False))
|
if not confirm:
|
||||||
|
form.add_error('validate', _('Confirm that the BOM is valid'))
|
||||||
|
|
||||||
if confirmed:
|
def post_save(self, part, form, **kwargs):
|
||||||
part.validate_bom(request.user)
|
|
||||||
else:
|
|
||||||
form.errors['validate'] = ['Confirm that the BOM is valid']
|
|
||||||
|
|
||||||
data = {
|
part.validate_bom(self.request.user)
|
||||||
'form_valid': confirmed
|
|
||||||
|
def get_data(self):
|
||||||
|
return {
|
||||||
|
'success': _('Validated Bill of Materials')
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.renderJsonResponse(request, form, data, context=self.get_context())
|
|
||||||
|
|
||||||
|
|
||||||
class BomUpload(InvenTreeRoleMixin, FormView):
|
class BomUpload(InvenTreeRoleMixin, FormView):
|
||||||
""" View for uploading a BOM file, and handling BOM data importing.
|
""" View for uploading a BOM file, and handling BOM data importing.
|
||||||
@ -1054,7 +1055,7 @@ class BomUpload(InvenTreeRoleMixin, FormView):
|
|||||||
bom_file_valid = False
|
bom_file_valid = False
|
||||||
|
|
||||||
if bom_file is None:
|
if bom_file is None:
|
||||||
self.form.errors['bom_file'] = [_('No BOM file provided')]
|
self.form.add_error('bom_file', _('No BOM file provided'))
|
||||||
else:
|
else:
|
||||||
# Create a BomUploadManager object - will perform initial data validation
|
# Create a BomUploadManager object - will perform initial data validation
|
||||||
# (and raise a ValidationError if there is something wrong with the file)
|
# (and raise a ValidationError if there is something wrong with the file)
|
||||||
@ -1065,7 +1066,7 @@ class BomUpload(InvenTreeRoleMixin, FormView):
|
|||||||
errors = e.error_dict
|
errors = e.error_dict
|
||||||
|
|
||||||
for k, v in errors.items():
|
for k, v in errors.items():
|
||||||
self.form.errors[k] = v
|
self.form.add_error(k, v)
|
||||||
|
|
||||||
if bom_file_valid:
|
if bom_file_valid:
|
||||||
# BOM file is valid? Proceed to the next step!
|
# BOM file is valid? Proceed to the next step!
|
||||||
|
@ -814,14 +814,11 @@ class StockItem(MPTTModel):
|
|||||||
raise ValidationError({"quantity": _("Quantity does not match serial numbers")})
|
raise ValidationError({"quantity": _("Quantity does not match serial numbers")})
|
||||||
|
|
||||||
# Test if each of the serial numbers are valid
|
# Test if each of the serial numbers are valid
|
||||||
existing = []
|
existing = self.part.find_conflicting_serial_numbers(serials)
|
||||||
|
|
||||||
for serial in serials:
|
|
||||||
if self.part.checkIfSerialNumberExists(serial):
|
|
||||||
existing.append(serial)
|
|
||||||
|
|
||||||
if len(existing) > 0:
|
if len(existing) > 0:
|
||||||
raise ValidationError({"serial_numbers": _("Serial numbers already exist: ") + str(existing)})
|
exists = ','.join([str(x) for x in existing])
|
||||||
|
raise ValidationError({"serial_numbers": _("Serial numbers already exist") + ': ' + exists})
|
||||||
|
|
||||||
# Create a new stock item for each unique serial number
|
# Create a new stock item for each unique serial number
|
||||||
for serial in serials:
|
for serial in serials:
|
||||||
@ -1129,6 +1126,22 @@ class StockItem(MPTTModel):
|
|||||||
|
|
||||||
return s
|
return s
|
||||||
|
|
||||||
|
@transaction.atomic
|
||||||
|
def clear_test_results(self, **kwargs):
|
||||||
|
"""
|
||||||
|
Remove all test results
|
||||||
|
|
||||||
|
kwargs:
|
||||||
|
TODO
|
||||||
|
"""
|
||||||
|
|
||||||
|
# All test results
|
||||||
|
results = self.test_results.all()
|
||||||
|
|
||||||
|
# TODO - Perhaps some filtering options supplied by kwargs?
|
||||||
|
|
||||||
|
results.delete()
|
||||||
|
|
||||||
def getTestResults(self, test=None, result=None, user=None):
|
def getTestResults(self, test=None, result=None, user=None):
|
||||||
"""
|
"""
|
||||||
Return all test results associated with this StockItem.
|
Return all test results associated with this StockItem.
|
||||||
|
@ -407,6 +407,13 @@ class VariantTest(StockTest):
|
|||||||
|
|
||||||
self.assertEqual(chair.getLatestSerialNumber(), '22')
|
self.assertEqual(chair.getLatestSerialNumber(), '22')
|
||||||
|
|
||||||
|
# Check for conflicting serial numbers
|
||||||
|
to_check = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
||||||
|
|
||||||
|
conflicts = chair.find_conflicting_serial_numbers(to_check)
|
||||||
|
|
||||||
|
self.assertEqual(len(conflicts), 6)
|
||||||
|
|
||||||
# Same operations on a sub-item
|
# Same operations on a sub-item
|
||||||
variant = Part.objects.get(pk=10003)
|
variant = Part.objects.get(pk=10003)
|
||||||
self.assertEqual(variant.getLatestSerialNumber(), '22')
|
self.assertEqual(variant.getLatestSerialNumber(), '22')
|
||||||
|
@ -245,32 +245,28 @@ class StockItemAssignToCustomer(AjaxUpdateView):
|
|||||||
form_class = StockForms.AssignStockItemToCustomerForm
|
form_class = StockForms.AssignStockItemToCustomerForm
|
||||||
role_required = 'stock.change'
|
role_required = 'stock.change'
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def validate(self, item, form, **kwargs):
|
||||||
|
|
||||||
customer = request.POST.get('customer', None)
|
customer = form.cleaned_data.get('customer', None)
|
||||||
|
|
||||||
|
if not customer:
|
||||||
|
form.add_error('customer', _('Customer must be specified'))
|
||||||
|
|
||||||
|
def post_save(self, item, form, **kwargs):
|
||||||
|
"""
|
||||||
|
Assign the stock item to the customer.
|
||||||
|
"""
|
||||||
|
|
||||||
|
customer = form.cleaned_data.get('customer', None)
|
||||||
|
|
||||||
if customer:
|
if customer:
|
||||||
try:
|
item = item.allocateToCustomer(
|
||||||
customer = Company.objects.get(pk=customer)
|
|
||||||
except (ValueError, Company.DoesNotExist):
|
|
||||||
customer = None
|
|
||||||
|
|
||||||
if customer is not None:
|
|
||||||
stock_item = self.get_object()
|
|
||||||
|
|
||||||
item = stock_item.allocateToCustomer(
|
|
||||||
customer,
|
customer,
|
||||||
user=request.user
|
user=self.request.user
|
||||||
)
|
)
|
||||||
|
|
||||||
item.clearAllocations()
|
item.clearAllocations()
|
||||||
|
|
||||||
data = {
|
|
||||||
'form_valid': True,
|
|
||||||
}
|
|
||||||
|
|
||||||
return self.renderJsonResponse(request, self.get_form(), data)
|
|
||||||
|
|
||||||
|
|
||||||
class StockItemReturnToStock(AjaxUpdateView):
|
class StockItemReturnToStock(AjaxUpdateView):
|
||||||
"""
|
"""
|
||||||
@ -283,30 +279,25 @@ class StockItemReturnToStock(AjaxUpdateView):
|
|||||||
form_class = StockForms.ReturnStockItemForm
|
form_class = StockForms.ReturnStockItemForm
|
||||||
role_required = 'stock.change'
|
role_required = 'stock.change'
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def validate(self, item, form, **kwargs):
|
||||||
|
|
||||||
location = request.POST.get('location', None)
|
location = form.cleaned_data.get('location', None)
|
||||||
|
|
||||||
|
if not location:
|
||||||
|
form.add_error('location', _('Specify a valid location'))
|
||||||
|
|
||||||
|
def post_save(self, item, form, **kwargs):
|
||||||
|
|
||||||
|
location = form.cleaned_data.get('location', None)
|
||||||
|
|
||||||
if location:
|
if location:
|
||||||
try:
|
item.returnFromCustomer(location, self.request.user)
|
||||||
location = StockLocation.objects.get(pk=location)
|
|
||||||
except (ValueError, StockLocation.DoesNotExist):
|
|
||||||
location = None
|
|
||||||
|
|
||||||
if location:
|
def get_data(self):
|
||||||
stock_item = self.get_object()
|
return {
|
||||||
|
'success': _('Stock item returned from customer')
|
||||||
stock_item.returnFromCustomer(location, request.user)
|
|
||||||
else:
|
|
||||||
raise ValidationError({'location': _("Specify a valid location")})
|
|
||||||
|
|
||||||
data = {
|
|
||||||
'form_valid': True,
|
|
||||||
'success': _("Stock item returned from customer")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.renderJsonResponse(request, self.get_form(), data)
|
|
||||||
|
|
||||||
|
|
||||||
class StockItemSelectLabels(AjaxView):
|
class StockItemSelectLabels(AjaxView):
|
||||||
"""
|
"""
|
||||||
@ -417,8 +408,8 @@ class StockItemDeleteTestData(AjaxUpdateView):
|
|||||||
confirm = str2bool(request.POST.get('confirm', False))
|
confirm = str2bool(request.POST.get('confirm', False))
|
||||||
|
|
||||||
if confirm is not True:
|
if confirm is not True:
|
||||||
form.errors['confirm'] = [_('Confirm test data deletion')]
|
form.add_error('confirm', _('Confirm test data deletion'))
|
||||||
form.non_field_errors = [_('Check the confirmation box')]
|
form.add_error(None, _('Check the confirmation box'))
|
||||||
else:
|
else:
|
||||||
stock_item.test_results.all().delete()
|
stock_item.test_results.all().delete()
|
||||||
valid = True
|
valid = True
|
||||||
@ -918,7 +909,7 @@ class StockItemUninstall(AjaxView, FormMixin):
|
|||||||
|
|
||||||
if not confirmed:
|
if not confirmed:
|
||||||
valid = False
|
valid = False
|
||||||
form.errors['confirm'] = [_('Confirm stock adjustment')]
|
form.add_error('confirm', _('Confirm stock adjustment'))
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
'form_valid': valid,
|
'form_valid': valid,
|
||||||
@ -1116,7 +1107,7 @@ class StockAdjust(AjaxView, FormMixin):
|
|||||||
|
|
||||||
if not confirmed:
|
if not confirmed:
|
||||||
valid = False
|
valid = False
|
||||||
form.errors['confirm'] = [_('Confirm stock adjustment')]
|
form.add_error('confirm', _('Confirm stock adjustment'))
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
'form_valid': valid,
|
'form_valid': valid,
|
||||||
@ -1416,7 +1407,7 @@ class StockItemSerialize(AjaxUpdateView):
|
|||||||
try:
|
try:
|
||||||
numbers = ExtractSerialNumbers(serials, quantity)
|
numbers = ExtractSerialNumbers(serials, quantity)
|
||||||
except ValidationError as e:
|
except ValidationError as e:
|
||||||
form.errors['serial_numbers'] = e.messages
|
form.add_error('serial_numbers', e.messages)
|
||||||
valid = False
|
valid = False
|
||||||
numbers = []
|
numbers = []
|
||||||
|
|
||||||
@ -1428,9 +1419,9 @@ class StockItemSerialize(AjaxUpdateView):
|
|||||||
|
|
||||||
for k in messages.keys():
|
for k in messages.keys():
|
||||||
if k in ['quantity', 'destination', 'serial_numbers']:
|
if k in ['quantity', 'destination', 'serial_numbers']:
|
||||||
form.errors[k] = messages[k]
|
form.add_error(k, messages[k])
|
||||||
else:
|
else:
|
||||||
form.non_field_errors = [messages[k]]
|
form.add_error(None, messages[k])
|
||||||
|
|
||||||
valid = False
|
valid = False
|
||||||
|
|
||||||
@ -1622,14 +1613,14 @@ class StockItemCreate(AjaxCreateView):
|
|||||||
part = None
|
part = None
|
||||||
quantity = 1
|
quantity = 1
|
||||||
valid = False
|
valid = False
|
||||||
form.errors['quantity'] = [_('Invalid quantity')]
|
form.add_error('quantity', _('Invalid quantity'))
|
||||||
|
|
||||||
if quantity < 0:
|
if quantity < 0:
|
||||||
form.errors['quantity'] = [_('Quantity cannot be less than zero')]
|
form.add_error('quantity', _('Quantity cannot be less than zero'))
|
||||||
valid = False
|
valid = False
|
||||||
|
|
||||||
if part is None:
|
if part is None:
|
||||||
form.errors['part'] = [_('Invalid part selection')]
|
form.add_error('part', _('Invalid part selection'))
|
||||||
else:
|
else:
|
||||||
# A trackable part must provide serial numbesr
|
# A trackable part must provide serial numbesr
|
||||||
if part.trackable:
|
if part.trackable:
|
||||||
@ -1642,15 +1633,14 @@ class StockItemCreate(AjaxCreateView):
|
|||||||
try:
|
try:
|
||||||
serials = ExtractSerialNumbers(sn, quantity)
|
serials = ExtractSerialNumbers(sn, quantity)
|
||||||
|
|
||||||
existing = []
|
existing = part.find_conflicting_serial_numbers(serials)
|
||||||
|
|
||||||
for serial in serials:
|
|
||||||
if part.checkIfSerialNumberExists(serial):
|
|
||||||
existing.append(serial)
|
|
||||||
|
|
||||||
if len(existing) > 0:
|
if len(existing) > 0:
|
||||||
exists = ",".join([str(x) for x in existing])
|
exists = ",".join([str(x) for x in existing])
|
||||||
form.errors['serial_numbers'] = [_('The following serial numbers already exist: ({sn})'.format(sn=exists))]
|
form.add_error(
|
||||||
|
'serial_numbers',
|
||||||
|
_('Serial numbers already exist') + ': ' + exists
|
||||||
|
)
|
||||||
valid = False
|
valid = False
|
||||||
|
|
||||||
else:
|
else:
|
||||||
@ -1682,7 +1672,7 @@ class StockItemCreate(AjaxCreateView):
|
|||||||
valid = True
|
valid = True
|
||||||
|
|
||||||
except ValidationError as e:
|
except ValidationError as e:
|
||||||
form.errors['serial_numbers'] = e.messages
|
form.add_error('serial_numbers', e.messages)
|
||||||
valid = False
|
valid = False
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
Loading…
Reference in New Issue
Block a user