mirror of
https://github.com/inventree/InvenTree.git
synced 2025-04-30 20:46:47 +00:00
Improve clean() function for BuildItem model
- BuildItemDelete now handled by API forms - BuildItemEdit now handled by API forms
This commit is contained in:
parent
8f298f71ef
commit
8a90b9df6d
@ -4,6 +4,7 @@ Build database model definitions
|
|||||||
|
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
import decimal
|
||||||
|
|
||||||
import os
|
import os
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
@ -1185,13 +1186,13 @@ class BuildItem(models.Model):
|
|||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
|
|
||||||
self.validate_unique()
|
|
||||||
self.clean()
|
self.clean()
|
||||||
|
|
||||||
super().save()
|
super().save()
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
""" Check validity of the BuildItem model.
|
"""
|
||||||
|
Check validity of this BuildItem instance.
|
||||||
The following checks are performed:
|
The following checks are performed:
|
||||||
|
|
||||||
- StockItem.part must be in the BOM of the Part object referenced by Build
|
- StockItem.part must be in the BOM of the Part object referenced by Build
|
||||||
@ -1202,8 +1203,6 @@ class BuildItem(models.Model):
|
|||||||
|
|
||||||
super().clean()
|
super().clean()
|
||||||
|
|
||||||
errors = {}
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
||||||
# If the 'part' is trackable, then the 'install_into' field must be set!
|
# 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
|
# Allocated quantity cannot exceed available stock quantity
|
||||||
if self.quantity > self.stock_item.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.quantity)
|
||||||
q=normalize(self.stock_item.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
|
# 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:
|
available = decimal.Decimal(self.stock_item.quantity)
|
||||||
errors['quantity'] = _('StockItem is over-allocated')
|
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
|
# Allocated quantity must be positive
|
||||||
if self.quantity <= 0:
|
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
|
# Quantity must be 1 for serialized stock
|
||||||
if self.stock_item.serialized and not self.quantity == 1:
|
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):
|
except (StockModels.StockItem.DoesNotExist, PartModels.Part.DoesNotExist):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if len(errors) > 0:
|
|
||||||
raise ValidationError(errors)
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Attempt to find the "BomItem" which links this BuildItem to the build.
|
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 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:
|
b) Either:
|
||||||
i) The sub_part points to the same part as the referenced StockItem
|
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
|
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:
|
if not bom_item_valid:
|
||||||
|
|
||||||
raise ValidationError({
|
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
|
@transaction.atomic
|
||||||
|
@ -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 %}
|
|
@ -20,12 +20,6 @@ build_detail_urls = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
build_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)),
|
url(r'^(?P<pk>\d+)/', include(build_detail_urls)),
|
||||||
|
|
||||||
|
@ -628,21 +628,6 @@ class BuildDelete(AjaxDeleteView):
|
|||||||
ajax_form_title = _('Delete Build Order')
|
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):
|
class BuildItemCreate(AjaxCreateView):
|
||||||
"""
|
"""
|
||||||
@ -859,35 +844,3 @@ class BuildItemCreate(AjaxCreateView):
|
|||||||
initials['quantity'] = quantity
|
initials['quantity'] = quantity
|
||||||
|
|
||||||
return initials
|
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
|
|
||||||
|
@ -596,11 +596,9 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
|
|||||||
text = `{% trans "Quantity" %}: ${row.quantity}`;
|
text = `{% trans "Quantity" %}: ${row.quantity}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
{% if build.status == BuildStatus.COMPLETE %}
|
var pk = row.stock_item || row.pk;
|
||||||
url = `/stock/item/${row.pk}/`;
|
|
||||||
{% else %}
|
url = `/stock/item/${pk}/`;
|
||||||
url = `/stock/item/${row.stock_item}/`;
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
return renderLink(text, url);
|
return renderLink(text, url);
|
||||||
}
|
}
|
||||||
@ -647,15 +645,23 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
|
|||||||
// Assign button callbacks to the newly created allocation buttons
|
// Assign button callbacks to the newly created allocation buttons
|
||||||
subTable.find('.button-allocation-edit').click(function() {
|
subTable.find('.button-allocation-edit').click(function() {
|
||||||
var pk = $(this).attr('pk');
|
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() {
|
subTable.find('.button-allocation-delete').click(function() {
|
||||||
var pk = $(this).attr('pk');
|
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,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user