mirror of
https://github.com/inventree/InvenTree.git
synced 2025-06-17 04:25:42 +00:00
Adds ability to partially scrap build outputs (#4846)
* BuildOrder updates: - Use batch code generation when creating a new build output - Allow partial scrapping of build outputs * Fixes for stock table * Bump API version * Update unit tests
This commit is contained in:
@ -797,7 +797,7 @@ class Build(MPTTModel, InvenTree.models.InvenTreeBarcodeMixin, InvenTree.models.
|
||||
items.all().delete()
|
||||
|
||||
@transaction.atomic
|
||||
def scrap_build_output(self, output, location, **kwargs):
|
||||
def scrap_build_output(self, output, quantity, location, **kwargs):
|
||||
"""Mark a particular build output as scrapped / rejected
|
||||
|
||||
- Mark the output as "complete"
|
||||
@ -809,10 +809,25 @@ class Build(MPTTModel, InvenTree.models.InvenTreeBarcodeMixin, InvenTree.models.
|
||||
if not output:
|
||||
raise ValidationError(_("No build output specified"))
|
||||
|
||||
if quantity <= 0:
|
||||
raise ValidationError({
|
||||
'quantity': _("Quantity must be greater than zero")
|
||||
})
|
||||
|
||||
if quantity > output.quantity:
|
||||
raise ValidationError({
|
||||
'quantity': _("Quantity cannot be greater than the output quantity")
|
||||
})
|
||||
|
||||
user = kwargs.get('user', None)
|
||||
notes = kwargs.get('notes', '')
|
||||
discard_allocations = kwargs.get('discard_allocations', False)
|
||||
|
||||
if quantity < output.quantity:
|
||||
# Split output into two items
|
||||
output = output.splitStock(quantity, location=location, user=user)
|
||||
output.build = self
|
||||
|
||||
# Update build output item
|
||||
output.is_building = False
|
||||
output.status = StockStatus.REJECTED
|
||||
|
@ -17,7 +17,7 @@ import InvenTree.helpers
|
||||
from InvenTree.serializers import InvenTreeDecimalField
|
||||
from InvenTree.status_codes import StockStatus
|
||||
|
||||
from stock.models import StockItem, StockLocation
|
||||
from stock.models import generate_batch_code, StockItem, StockLocation
|
||||
from stock.serializers import StockItemSerializerBrief, LocationSerializer
|
||||
|
||||
from part.models import BomItem
|
||||
@ -181,6 +181,45 @@ class BuildOutputSerializer(serializers.Serializer):
|
||||
return output
|
||||
|
||||
|
||||
class BuildOutputQuantitySerializer(BuildOutputSerializer):
|
||||
"""Serializer for a single build output, with additional quantity field"""
|
||||
|
||||
class Meta:
|
||||
"""Serializer metaclass"""
|
||||
fields = BuildOutputSerializer.Meta.fields + [
|
||||
'quantity',
|
||||
]
|
||||
|
||||
quantity = serializers.DecimalField(
|
||||
max_digits=15,
|
||||
decimal_places=5,
|
||||
min_value=0,
|
||||
required=True,
|
||||
label=_('Quantity'),
|
||||
help_text=_('Enter quantity for build output'),
|
||||
)
|
||||
|
||||
def validate(self, data):
|
||||
"""Validate the serializer data"""
|
||||
|
||||
data = super().validate(data)
|
||||
|
||||
output = data.get('output')
|
||||
quantity = data.get('quantity')
|
||||
|
||||
if quantity <= 0:
|
||||
raise ValidationError({
|
||||
'quantity': _('Quantity must be greater than zero')
|
||||
})
|
||||
|
||||
if quantity > output.quantity:
|
||||
raise ValidationError({
|
||||
'quantity': _("Quantity cannot be greater than the output quantity")
|
||||
})
|
||||
|
||||
return data
|
||||
|
||||
|
||||
class BuildOutputCreateSerializer(serializers.Serializer):
|
||||
"""Serializer for creating a new BuildOutput against a BuildOrder.
|
||||
|
||||
@ -226,6 +265,7 @@ class BuildOutputCreateSerializer(serializers.Serializer):
|
||||
batch_code = serializers.CharField(
|
||||
required=False,
|
||||
allow_blank=True,
|
||||
default=generate_batch_code,
|
||||
label=_('Batch Code'),
|
||||
help_text=_('Batch code for this build output'),
|
||||
)
|
||||
@ -362,7 +402,7 @@ class BuildOutputScrapSerializer(serializers.Serializer):
|
||||
'notes',
|
||||
]
|
||||
|
||||
outputs = BuildOutputSerializer(
|
||||
outputs = BuildOutputQuantitySerializer(
|
||||
many=True,
|
||||
required=True,
|
||||
)
|
||||
@ -412,8 +452,10 @@ class BuildOutputScrapSerializer(serializers.Serializer):
|
||||
with transaction.atomic():
|
||||
for item in outputs:
|
||||
output = item['output']
|
||||
quantity = item['quantity']
|
||||
build.scrap_build_output(
|
||||
output,
|
||||
quantity,
|
||||
data.get('location', None),
|
||||
user=request.user,
|
||||
notes=data.get('notes', ''),
|
||||
|
@ -302,7 +302,7 @@
|
||||
</div>
|
||||
|
||||
<div class='panel-content'>
|
||||
{% include "stock_table.html" with read_only=True prefix="build-" %}
|
||||
{% include "stock_table.html" with prefix="build-" %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -343,6 +343,7 @@
|
||||
|
||||
onPanelLoad('consumed', function() {
|
||||
loadStockTable($('#consumed-stock-table'), {
|
||||
filterTarget: '#filter-list-consumed-stock',
|
||||
params: {
|
||||
location_detail: true,
|
||||
part_detail: true,
|
||||
@ -354,6 +355,7 @@ onPanelLoad('consumed', function() {
|
||||
|
||||
onPanelLoad('completed', function() {
|
||||
loadStockTable($("#build-stock-table"), {
|
||||
filterTarget: '#filter-list-build-stock',
|
||||
params: {
|
||||
location_detail: true,
|
||||
part_detail: true,
|
||||
|
@ -1027,12 +1027,15 @@ class BuildOutputScrapTest(BuildAPITest):
|
||||
'outputs': [
|
||||
{
|
||||
'output': outputs[0].pk,
|
||||
'quantity': outputs[0].quantity,
|
||||
},
|
||||
{
|
||||
'output': outputs[1].pk,
|
||||
'quantity': outputs[1].quantity,
|
||||
},
|
||||
{
|
||||
'output': outputs[2].pk,
|
||||
'quantity': outputs[2].quantity,
|
||||
},
|
||||
],
|
||||
'location': 1,
|
||||
|
Reference in New Issue
Block a user