mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Unit test improvements
This commit is contained in:
parent
715d912137
commit
debc086f89
@ -916,7 +916,7 @@ class Build(MPTTModel):
|
|||||||
|
|
||||||
return self.unallocatedQuantity(part, output) == 0
|
return self.unallocatedQuantity(part, output) == 0
|
||||||
|
|
||||||
def isFullyAllocated(self, output):
|
def isFullyAllocated(self, output, verbose=False):
|
||||||
"""
|
"""
|
||||||
Returns True if the particular build output is fully allocated.
|
Returns True if the particular build output is fully allocated.
|
||||||
"""
|
"""
|
||||||
@ -927,14 +927,21 @@ class Build(MPTTModel):
|
|||||||
else:
|
else:
|
||||||
bom_items = self.tracked_bom_items
|
bom_items = self.tracked_bom_items
|
||||||
|
|
||||||
|
fully_allocated = True
|
||||||
|
|
||||||
for bom_item in bom_items:
|
for bom_item in bom_items:
|
||||||
part = bom_item.sub_part
|
part = bom_item.sub_part
|
||||||
|
|
||||||
if not self.isPartFullyAllocated(part, output):
|
if not self.isPartFullyAllocated(part, output):
|
||||||
return False
|
fully_allocated = False
|
||||||
|
|
||||||
|
if verbose:
|
||||||
|
print(f"Part {part} is not fully allocated for output {output}")
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
# All parts must be fully allocated!
|
# All parts must be fully allocated!
|
||||||
return True
|
return fully_allocated
|
||||||
|
|
||||||
def areUntrackedPartsFullyAllocated(self):
|
def areUntrackedPartsFullyAllocated(self):
|
||||||
"""
|
"""
|
||||||
|
@ -19,6 +19,18 @@ class BuildTest(TestCase):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""
|
"""
|
||||||
Initialize data to use for these tests.
|
Initialize data to use for these tests.
|
||||||
|
|
||||||
|
The base Part 'assembly' has a BOM consisting of three parts:
|
||||||
|
|
||||||
|
- 5 x sub_part_1
|
||||||
|
- 3 x sub_part_2
|
||||||
|
- 2 x sub_part_3 (trackable)
|
||||||
|
|
||||||
|
We will build 10x 'assembly' parts, in two build outputs:
|
||||||
|
|
||||||
|
- 3 x output_1
|
||||||
|
- 7 x output_2
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Create a base "Part"
|
# Create a base "Part"
|
||||||
@ -41,17 +53,31 @@ class BuildTest(TestCase):
|
|||||||
component=True
|
component=True
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.sub_part_3 = Part.objects.create(
|
||||||
|
name="Widget C",
|
||||||
|
description="A widget",
|
||||||
|
component=True,
|
||||||
|
trackable=True
|
||||||
|
)
|
||||||
|
|
||||||
# Create BOM item links for the parts
|
# Create BOM item links for the parts
|
||||||
BomItem.objects.create(
|
BomItem.objects.create(
|
||||||
part=self.assembly,
|
part=self.assembly,
|
||||||
sub_part=self.sub_part_1,
|
sub_part=self.sub_part_1,
|
||||||
quantity=10
|
quantity=5
|
||||||
)
|
)
|
||||||
|
|
||||||
BomItem.objects.create(
|
BomItem.objects.create(
|
||||||
part=self.assembly,
|
part=self.assembly,
|
||||||
sub_part=self.sub_part_2,
|
sub_part=self.sub_part_2,
|
||||||
quantity=25
|
quantity=3
|
||||||
|
)
|
||||||
|
|
||||||
|
# sub_part_3 is trackable!
|
||||||
|
BomItem.objects.create(
|
||||||
|
part=self.assembly,
|
||||||
|
sub_part=self.sub_part_3,
|
||||||
|
quantity=2
|
||||||
)
|
)
|
||||||
|
|
||||||
# Create a "Build" object to make 10x objects
|
# Create a "Build" object to make 10x objects
|
||||||
@ -64,14 +90,14 @@ class BuildTest(TestCase):
|
|||||||
# Create some build output (StockItem) objects
|
# Create some build output (StockItem) objects
|
||||||
self.output_1 = StockItem.objects.create(
|
self.output_1 = StockItem.objects.create(
|
||||||
part=self.assembly,
|
part=self.assembly,
|
||||||
quantity=5,
|
quantity=3,
|
||||||
is_building=True,
|
is_building=True,
|
||||||
build=self.build
|
build=self.build
|
||||||
)
|
)
|
||||||
|
|
||||||
self.output_2 = StockItem.objects.create(
|
self.output_2 = StockItem.objects.create(
|
||||||
part=self.assembly,
|
part=self.assembly,
|
||||||
quantity=5,
|
quantity=7,
|
||||||
is_building=True,
|
is_building=True,
|
||||||
build=self.build,
|
build=self.build,
|
||||||
)
|
)
|
||||||
@ -82,10 +108,12 @@ class BuildTest(TestCase):
|
|||||||
|
|
||||||
self.stock_2_1 = StockItem.objects.create(part=self.sub_part_2, quantity=5000)
|
self.stock_2_1 = StockItem.objects.create(part=self.sub_part_2, quantity=5000)
|
||||||
|
|
||||||
|
self.stock_3_1 = StockItem.objects.create(part=self.sub_part_3, quantity=1000)
|
||||||
|
|
||||||
def test_init(self):
|
def test_init(self):
|
||||||
# Perform some basic tests before we start the ball rolling
|
# Perform some basic tests before we start the ball rolling
|
||||||
|
|
||||||
self.assertEqual(StockItem.objects.count(), 5)
|
self.assertEqual(StockItem.objects.count(), 6)
|
||||||
|
|
||||||
# Build is PENDING
|
# Build is PENDING
|
||||||
self.assertEqual(self.build.status, status.BuildStatus.PENDING)
|
self.assertEqual(self.build.status, status.BuildStatus.PENDING)
|
||||||
@ -144,84 +172,113 @@ class BuildTest(TestCase):
|
|||||||
quantity=99
|
quantity=99
|
||||||
)
|
)
|
||||||
|
|
||||||
def allocate_stock(self, q11, q12, q21, output):
|
def allocate_stock(self, output, allocations):
|
||||||
# Assign stock to this build
|
"""
|
||||||
|
Allocate stock to this build, against a particular output
|
||||||
|
|
||||||
if q11 > 0:
|
Args:
|
||||||
|
output - StockItem object (or None)
|
||||||
|
allocations - Map of {StockItem: quantity}
|
||||||
|
"""
|
||||||
|
|
||||||
|
for item, quantity in allocations.items():
|
||||||
BuildItem.objects.create(
|
BuildItem.objects.create(
|
||||||
build=self.build,
|
build=self.build,
|
||||||
stock_item=self.stock_1_1,
|
stock_item=item,
|
||||||
quantity=q11,
|
quantity=quantity,
|
||||||
install_into=output
|
install_into=output
|
||||||
)
|
)
|
||||||
|
|
||||||
if q12 > 0:
|
|
||||||
BuildItem.objects.create(
|
|
||||||
build=self.build,
|
|
||||||
stock_item=self.stock_1_2,
|
|
||||||
quantity=q12,
|
|
||||||
install_into=output
|
|
||||||
)
|
|
||||||
|
|
||||||
if q21 > 0:
|
|
||||||
BuildItem.objects.create(
|
|
||||||
build=self.build,
|
|
||||||
stock_item=self.stock_2_1,
|
|
||||||
quantity=q21,
|
|
||||||
install_into=output,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Attempt to create another identical BuildItem
|
|
||||||
b = BuildItem(
|
|
||||||
build=self.build,
|
|
||||||
stock_item=self.stock_2_1,
|
|
||||||
quantity=q21
|
|
||||||
)
|
|
||||||
|
|
||||||
with self.assertRaises(ValidationError):
|
|
||||||
b.clean()
|
|
||||||
|
|
||||||
def test_partial_allocation(self):
|
def test_partial_allocation(self):
|
||||||
"""
|
"""
|
||||||
Partially allocate against output 1
|
Test partial allocation of stock
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.allocate_stock(50, 50, 200, self.output_1)
|
# Fully allocate tracked stock against build output 1
|
||||||
|
self.allocate_stock(
|
||||||
|
self.output_1,
|
||||||
|
{
|
||||||
|
self.stock_3_1: 6,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
self.assertTrue(self.build.isFullyAllocated(self.output_1))
|
self.assertTrue(self.build.isFullyAllocated(self.output_1))
|
||||||
|
|
||||||
|
# Partially allocate tracked stock against build output 2
|
||||||
|
self.allocate_stock(
|
||||||
|
self.output_2,
|
||||||
|
{
|
||||||
|
self.stock_3_1: 1,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
self.assertFalse(self.build.isFullyAllocated(self.output_2))
|
self.assertFalse(self.build.isFullyAllocated(self.output_2))
|
||||||
self.assertTrue(self.build.isPartFullyAllocated(self.sub_part_1, self.output_1))
|
|
||||||
self.assertTrue(self.build.isPartFullyAllocated(self.sub_part_2, self.output_1))
|
|
||||||
|
|
||||||
self.assertFalse(self.build.isPartFullyAllocated(self.sub_part_1, self.output_2))
|
|
||||||
self.assertFalse(self.build.isPartFullyAllocated(self.sub_part_2, self.output_2))
|
|
||||||
|
|
||||||
# Check that the part has been allocated
|
# Partially allocate untracked stock against build
|
||||||
self.assertEqual(self.build.allocatedQuantity(self.sub_part_1, self.output_1), 100)
|
self.allocate_stock(
|
||||||
|
None,
|
||||||
|
{
|
||||||
|
self.stock_1_1: 1,
|
||||||
|
self.stock_2_1: 1
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
self.build.unallocateStock(output=self.output_1)
|
self.assertFalse(self.build.isFullyAllocated(None, verbose=True))
|
||||||
self.assertEqual(BuildItem.objects.count(), 0)
|
|
||||||
|
|
||||||
# Check that the part has been unallocated
|
unallocated = self.build.unallocatedParts(None)
|
||||||
self.assertEqual(self.build.allocatedQuantity(self.sub_part_1, self.output_1), 0)
|
|
||||||
|
self.assertEqual(len(unallocated), 2)
|
||||||
|
|
||||||
|
self.allocate_stock(
|
||||||
|
None,
|
||||||
|
{
|
||||||
|
self.stock_1_2: 100,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertFalse(self.build.isFullyAllocated(None, verbose=True))
|
||||||
|
|
||||||
|
unallocated = self.build.unallocatedParts(None)
|
||||||
|
|
||||||
|
self.assertEqual(len(unallocated), 1)
|
||||||
|
|
||||||
|
self.build.unallocateUntracked()
|
||||||
|
|
||||||
|
unallocated = self.build.unallocatedParts(None)
|
||||||
|
|
||||||
|
self.assertEqual(len(unallocated), 2)
|
||||||
|
|
||||||
|
self.assertFalse(self.build.areUntrackedPartsFullyAllocated())
|
||||||
|
|
||||||
|
# Now we "fully" allocate the untracked untracked items
|
||||||
|
self.allocate_stock(
|
||||||
|
None,
|
||||||
|
{
|
||||||
|
self.stock_1_1: 50,
|
||||||
|
self.stock_2_1: 50,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertTrue(self.build.areUntrackedPartsFullyAllocated())
|
||||||
|
|
||||||
def test_auto_allocate(self):
|
def test_auto_allocate(self):
|
||||||
"""
|
"""
|
||||||
Test auto-allocation functionality against the build outputs
|
Test auto-allocation functionality against the build outputs.
|
||||||
|
|
||||||
|
Note: auto-allocations only work for un-tracked stock!
|
||||||
"""
|
"""
|
||||||
|
|
||||||
allocations = self.build.getAutoAllocations(self.output_1)
|
allocations = self.build.getAutoAllocations()
|
||||||
|
|
||||||
self.assertEqual(len(allocations), 1)
|
self.assertEqual(len(allocations), 1)
|
||||||
|
|
||||||
self.build.autoAllocate(self.output_1)
|
self.build.autoAllocate()
|
||||||
self.assertEqual(BuildItem.objects.count(), 1)
|
self.assertEqual(BuildItem.objects.count(), 1)
|
||||||
|
|
||||||
# Check that one part has been fully allocated to the build output
|
# Check that one un-tracked part has been fully allocated to the build
|
||||||
self.assertTrue(self.build.isPartFullyAllocated(self.sub_part_2, self.output_1))
|
self.assertTrue(self.build.isPartFullyAllocated(self.sub_part_2, None))
|
||||||
|
|
||||||
# But, the *other* build output has not been allocated against
|
self.assertFalse(self.build.isPartFullyAllocated(self.sub_part_1, None))
|
||||||
self.assertFalse(self.build.isPartFullyAllocated(self.sub_part_2, self.output_2))
|
|
||||||
|
|
||||||
def test_cancel(self):
|
def test_cancel(self):
|
||||||
"""
|
"""
|
||||||
|
Loading…
Reference in New Issue
Block a user