2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-07-10 23:14:13 +00:00

Check for (and reduce) overallocated stock when editing build orders. (#3237)

* Check for (and reduce) overallocated stock when editing build orders.

After a build order is edited, check whether any bom items are now
overallocated to the build, and if they are find appropriate build items to
reduce the quantity of or remove from the build.

Fixes: #3236

* Only trim overallocated stock if requested

Turns the complete with overallocated stock option into a choice, so the
user can reject (default), continue (existing option in the bool), or
now additionally instead choose to have the overallocated stock removed
before the build is completed.

* fix style errors

* Another style fix.

* Add tests for overallocation handling API logic

* Capitalize overallocation options.
This commit is contained in:
Matt Brown
2022-08-10 23:37:29 +12:00
committed by GitHub
parent 2d63122ebe
commit 139e059a45
4 changed files with 217 additions and 8 deletions

View File

@ -7,6 +7,7 @@ from django.test import TestCase
from django.contrib.auth import get_user_model
from django.contrib.auth.models import Group
from django.core.exceptions import ValidationError
from django.db.models import Sum
from InvenTree import status_codes as status
@ -17,6 +18,9 @@ from part.models import Part, BomItem, BomItemSubstitute
from stock.models import StockItem
from users.models import Owner
import logging
logger = logging.getLogger('inventree')
class BuildTestBase(TestCase):
"""Run some tests to ensure that the Build model is working properly."""
@ -120,9 +124,9 @@ class BuildTestBase(TestCase):
self.stock_2_1 = StockItem.objects.create(part=self.sub_part_2, quantity=5)
self.stock_2_2 = StockItem.objects.create(part=self.sub_part_2, quantity=5)
self.stock_2_2 = StockItem.objects.create(part=self.sub_part_2, quantity=5)
self.stock_2_2 = StockItem.objects.create(part=self.sub_part_2, quantity=5)
self.stock_2_2 = StockItem.objects.create(part=self.sub_part_2, quantity=5)
self.stock_2_3 = StockItem.objects.create(part=self.sub_part_2, quantity=5)
self.stock_2_4 = StockItem.objects.create(part=self.sub_part_2, quantity=5)
self.stock_2_5 = StockItem.objects.create(part=self.sub_part_2, quantity=5)
self.stock_3_1 = StockItem.objects.create(part=self.sub_part_3, quantity=1000)
@ -375,6 +379,65 @@ class BuildTest(BuildTestBase):
self.assertTrue(self.build.are_untracked_parts_allocated())
def test_overallocation_and_trim(self):
"""Test overallocation of stock and trim function"""
# Fully allocate tracked stock (not eligible for trimming)
self.allocate_stock(
self.output_1,
{
self.stock_3_1: 6,
}
)
self.allocate_stock(
self.output_2,
{
self.stock_3_1: 14,
}
)
# Fully allocate part 1 (should be left alone)
self.allocate_stock(
None,
{
self.stock_1_1: 3,
self.stock_1_2: 47,
}
)
extra_2_1 = StockItem.objects.create(part=self.sub_part_2, quantity=6)
extra_2_2 = StockItem.objects.create(part=self.sub_part_2, quantity=4)
# Overallocate part 2 (30 needed)
self.allocate_stock(
None,
{
self.stock_2_1: 5,
self.stock_2_2: 5,
self.stock_2_3: 5,
self.stock_2_4: 5,
self.stock_2_5: 5, # 25
extra_2_1: 6, # 31
extra_2_2: 4, # 35
}
)
self.assertTrue(self.build.has_overallocated_parts(None))
self.build.trim_allocated_stock()
self.assertFalse(self.build.has_overallocated_parts(None))
self.build.complete_build_output(self.output_1, None)
self.build.complete_build_output(self.output_2, None)
self.assertTrue(self.build.can_complete)
self.build.complete_build(None)
self.assertEqual(self.build.status, status.BuildStatus.COMPLETE)
# Check stock items are in expected state.
self.assertEqual(StockItem.objects.get(pk=self.stock_1_2.pk).quantity, 53)
self.assertEqual(StockItem.objects.filter(part=self.sub_part_2).aggregate(Sum('quantity'))['quantity__sum'], 5)
self.assertEqual(StockItem.objects.get(pk=self.stock_3_1.pk).quantity, 980)
def test_cancel(self):
"""Test cancellation of the build"""