Improve clean() function for BuildItem model

- BuildItemDelete now handled by API forms
- BuildItemEdit now handled by API forms
This commit is contained in:
Oliver 2021-10-05 00:14:31 +11:00
parent 8f298f71ef
commit 8a90b9df6d
5 changed files with 41 additions and 93 deletions

View File

@ -4,6 +4,7 @@ Build database model definitions
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import decimal
import os
from datetime import datetime
@ -1185,13 +1186,13 @@ class BuildItem(models.Model):
def save(self, *args, **kwargs):
self.validate_unique()
self.clean()
super().save()
def clean(self):
""" Check validity of the BuildItem model.
"""
Check validity of this BuildItem instance.
The following checks are performed:
- StockItem.part must be in the BOM of the Part object referenced by Build
@ -1202,8 +1203,6 @@ class BuildItem(models.Model):
super().clean()
errors = {}
try:
# If the 'part' is trackable, then the 'install_into' field must be set!
@ -1212,29 +1211,39 @@ class BuildItem(models.Model):
# Allocated quantity cannot exceed available stock quantity
if self.quantity > self.stock_item.quantity:
errors['quantity'] = [_("Allocated quantity ({n}) must not exceed available quantity ({q})").format(
n=normalize(self.quantity),
q=normalize(self.stock_item.quantity)
)]
q = normalize(self.quantity)
a = normalize(self.stock_item.quantity)
raise ValidationError({
'quantity': _(f'Allocated quantity ({q}) must not execed available stock quantity ({a})')
})
# Allocated quantity cannot cause the stock item to be over-allocated
if self.stock_item.quantity - self.stock_item.allocation_count() + self.quantity < self.quantity:
errors['quantity'] = _('StockItem is over-allocated')
available = decimal.Decimal(self.stock_item.quantity)
allocated = decimal.Decimal(self.stock_item.allocation_count())
quantity = decimal.Decimal(self.quantity)
if available - allocated + quantity < quantity:
raise ValidationError({
'quantity': _('Stock item is over-allocated')
})
# Allocated quantity must be positive
if self.quantity <= 0:
errors['quantity'] = _('Allocation quantity must be greater than zero')
raise ValidationError({
'quantity': _('Allocation quantity must be greater than zero'),
})
# Quantity must be 1 for serialized stock
if self.stock_item.serialized and not self.quantity == 1:
errors['quantity'] = _('Quantity must be 1 for serialized stock')
raise ValidationError({
'quantity': _('Quantity must be 1 for serialized stock')
})
except (StockModels.StockItem.DoesNotExist, PartModels.Part.DoesNotExist):
pass
if len(errors) > 0:
raise ValidationError(errors)
"""
Attempt to find the "BomItem" which links this BuildItem to the build.
@ -1247,7 +1256,7 @@ class BuildItem(models.Model):
"""
A BomItem object has already been assigned. This is valid if:
a) It points to the same "part" as the referened build
a) It points to the same "part" as the referenced build
b) Either:
i) The sub_part points to the same part as the referenced StockItem
ii) The BomItem allows variants and the part referenced by the StockItem
@ -1287,7 +1296,7 @@ class BuildItem(models.Model):
if not bom_item_valid:
raise ValidationError({
'stock_item': _("Selected stock item not found in BOM for part '{p}'").format(p=self.build.part.full_name)
'stock_item': _("Selected stock item not found in BOM")
})
@transaction.atomic

View File

@ -1,14 +0,0 @@
{% extends "modal_delete_form.html" %}
{% load i18n %}
{% load inventree_extras %}
{% block pre_form_content %}
<div class='alert alert-block alert-danger'>
<p>
{% trans "Are you sure you want to unallocate this stock?" %}
</p>
<p>
{% trans "The selected stock will be unallocated from the build output" %}
</p>
</div>
{% endblock %}

View File

@ -20,12 +20,6 @@ build_detail_urls = [
]
build_urls = [
url(r'item/', include([
url(r'^(?P<pk>\d+)/', include([
url('^edit/', views.BuildItemEdit.as_view(), name='build-item-edit'),
url('^delete/', views.BuildItemDelete.as_view(), name='build-item-delete'),
])),
])),
url(r'^(?P<pk>\d+)/', include(build_detail_urls)),

View File

@ -628,21 +628,6 @@ class BuildDelete(AjaxDeleteView):
ajax_form_title = _('Delete Build Order')
class BuildItemDelete(AjaxDeleteView):
""" View to 'unallocate' a BuildItem.
Really we are deleting the BuildItem object from the database.
"""
model = BuildItem
ajax_template_name = 'build/delete_build_item.html'
ajax_form_title = _('Unallocate Stock')
context_object_name = 'item'
def get_data(self):
return {
'danger': _('Removed parts from build allocation')
}
class BuildItemCreate(AjaxCreateView):
"""
@ -859,35 +844,3 @@ class BuildItemCreate(AjaxCreateView):
initials['quantity'] = quantity
return initials
class BuildItemEdit(AjaxUpdateView):
""" View to edit a BuildItem object """
model = BuildItem
ajax_template_name = 'build/edit_build_item.html'
form_class = forms.EditBuildItemForm
ajax_form_title = _('Edit Stock Allocation')
def get_data(self):
return {
'info': _('Updated Build Item'),
}
def get_form(self):
"""
Create form for editing a BuildItem.
- Limit the StockItem options to items that match the part
"""
form = super(BuildItemEdit, self).get_form()
# Hide fields which we do not wish the user to edit
for field in ['build', 'stock_item']:
if form[field].value():
form.fields[field].widget = HiddenInput()
form.fields['install_into'].widget = HiddenInput()
return form

View File

@ -596,11 +596,9 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
text = `{% trans "Quantity" %}: ${row.quantity}`;
}
{% if build.status == BuildStatus.COMPLETE %}
url = `/stock/item/${row.pk}/`;
{% else %}
url = `/stock/item/${row.stock_item}/`;
{% endif %}
var pk = row.stock_item || row.pk;
url = `/stock/item/${pk}/`;
return renderLink(text, url);
}
@ -647,15 +645,23 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
// Assign button callbacks to the newly created allocation buttons
subTable.find('.button-allocation-edit').click(function() {
var pk = $(this).attr('pk');
launchModalForm(`/build/item/${pk}/edit/`, {
success: reloadTable,
constructForm(`/api/build/item/${pk}/`, {
fields: {
quantity: {},
},
title: '{% trans "Edit Allocation" %}',
onSuccess: reloadTable,
});
});
subTable.find('.button-allocation-delete').click(function() {
var pk = $(this).attr('pk');
launchModalForm(`/build/item/${pk}/delete/`, {
success: reloadTable,
constructForm(`/api/build/item/${pk}/`, {
method: 'DELETE',
title: '{% trans "Remove Allocation" %}',
onSuccess: reloadTable,
});
});
},