InvenTree/InvenTree/build/test_build.py
2020-04-27 21:44:59 +10:00

228 lines
7.5 KiB
Python

# -*- coding: utf-8 -*-
from django.test import TestCase
from django.core.exceptions import ValidationError
from django.db import transaction
from django.db.utils import IntegrityError
from build.models import Build, BuildItem
from stock.models import StockItem
from part.models import Part, BomItem
from InvenTree import status_codes as status
from InvenTree.helpers import ExtractSerialNumbers
class BuildTest(TestCase):
"""
Run some tests to ensure that the Build model is working properly.
"""
def setUp(self):
"""
Initialize data to use for these tests.
"""
# Create a base "Part"
self.assembly = Part.objects.create(
name="An assembled part",
description="Why does it matter what my description is?",
assembly=True,
trackable=True,
)
self.sub_part_1 = Part.objects.create(
name="Widget A",
description="A widget",
component=True
)
self.sub_part_2 = Part.objects.create(
name="Widget B",
description="A widget",
component=True
)
# Create BOM item links for the parts
BomItem.objects.create(
part=self.assembly,
sub_part=self.sub_part_1,
quantity=10
)
BomItem.objects.create(
part=self.assembly,
sub_part=self.sub_part_2,
quantity=25
)
# Create a "Build" object to make 10x objects
self.build = Build.objects.create(
title="This is a build",
part=self.assembly,
quantity=10
)
# Create some stock items to assign to the build
self.stock_1_1 = StockItem.objects.create(part=self.sub_part_1, quantity=1000)
self.stock_1_2 = StockItem.objects.create(part=self.sub_part_1, quantity=100)
self.stock_2_1 = StockItem.objects.create(part=self.sub_part_2, quantity=5000)
def test_init(self):
# Perform some basic tests before we start the ball rolling
self.assertEqual(StockItem.objects.count(), 3)
self.assertEqual(self.build.status, status.BuildStatus.PENDING)
self.assertFalse(self.build.isFullyAllocated())
self.assertFalse(self.build.isPartFullyAllocated(self.sub_part_1))
self.assertFalse(self.build.isPartFullyAllocated(self.sub_part_2))
self.assertEqual(self.build.getRequiredQuantity(self.sub_part_1), 100)
self.assertEqual(self.build.getRequiredQuantity(self.sub_part_2), 250)
self.assertTrue(self.build.can_build)
self.assertFalse(self.build.is_complete)
# Delete some stock and see if the build can still be completed
self.stock_2_1.delete()
self.assertFalse(self.build.can_build)
def test_build_item_clean(self):
# Ensure that dodgy BuildItem objects cannot be created
stock = StockItem.objects.create(part=self.assembly, quantity=99)
# Create a BuiltItem which points to an invalid StockItem
b = BuildItem(stock_item=stock, build=self.build, quantity=10)
with self.assertRaises(ValidationError):
b.clean()
# Create a BuildItem which has too much stock assigned
b = BuildItem(stock_item=self.stock_1_1, build=self.build, quantity=9999999)
with self.assertRaises(ValidationError):
b.clean()
# Negative stock? Not on my watch!
b = BuildItem(stock_item=self.stock_1_1, build=self.build, quantity=-99)
with self.assertRaises(ValidationError):
b.clean()
def test_duplicate_bom_line(self):
# Try to add a duplicate BOM item - it should fail!
with self.assertRaises(IntegrityError):
BomItem.objects.create(
part=self.assembly,
sub_part=self.sub_part_1,
quantity=99
)
def allocate_stock(self, q11, q12, q21):
# Assign stock to this build
BuildItem.objects.create(
build=self.build,
stock_item=self.stock_1_1,
quantity=q11
)
BuildItem.objects.create(
build=self.build,
stock_item=self.stock_1_2,
quantity=q12
)
BuildItem.objects.create(
build=self.build,
stock_item=self.stock_2_1,
quantity=q21
)
with transaction.atomic():
with self.assertRaises(IntegrityError):
BuildItem.objects.create(
build=self.build,
stock_item=self.stock_2_1,
quantity=99
)
self.assertEqual(BuildItem.objects.count(), 3)
def test_partial_allocation(self):
self.allocate_stock(50, 50, 200)
self.assertFalse(self.build.isFullyAllocated())
self.assertTrue(self.build.isPartFullyAllocated(self.sub_part_1))
self.assertFalse(self.build.isPartFullyAllocated(self.sub_part_2))
self.build.unallocateStock()
self.assertEqual(BuildItem.objects.count(), 0)
def test_auto_allocate(self):
allocations = self.build.getAutoAllocations()
self.assertEqual(len(allocations), 1)
self.build.autoAllocate()
self.assertEqual(BuildItem.objects.count(), 1)
self.assertTrue(self.build.isPartFullyAllocated(self.sub_part_2))
def test_cancel(self):
self.allocate_stock(50, 50, 200)
self.build.cancelBuild(None)
self.assertEqual(BuildItem.objects.count(), 0)
def test_complete(self):
self.allocate_stock(50, 50, 250)
self.assertTrue(self.build.isFullyAllocated())
# Generate some serial numbers!
serials = ExtractSerialNumbers("1-10", 10)
self.build.completeBuild(None, serials, None)
self.assertEqual(self.build.status, status.BuildStatus.COMPLETE)
# the original BuildItem objects should have been deleted!
self.assertEqual(BuildItem.objects.count(), 0)
# New stock items should have been created!
# - Ten for the build output (as the part was serialized)
# - Three for the split items assigned to the build
self.assertEqual(StockItem.objects.count(), 16)
# Stock should have been subtracted from the original items
self.assertEqual(StockItem.objects.get(pk=1).quantity, 950)
self.assertEqual(StockItem.objects.get(pk=2).quantity, 50)
self.assertEqual(StockItem.objects.get(pk=3).quantity, 4750)
# New stock items created and assigned to the build
self.assertEqual(StockItem.objects.get(pk=4).quantity, 50)
self.assertEqual(StockItem.objects.get(pk=4).build_order, self.build)
self.assertEqual(StockItem.objects.get(pk=4).status, status.StockStatus.ASSIGNED_TO_BUILD)
self.assertEqual(StockItem.objects.get(pk=5).quantity, 50)
self.assertEqual(StockItem.objects.get(pk=5).build_order, self.build)
self.assertEqual(StockItem.objects.get(pk=5).status, status.StockStatus.ASSIGNED_TO_BUILD)
self.assertEqual(StockItem.objects.get(pk=6).quantity, 250)
self.assertEqual(StockItem.objects.get(pk=6).build_order, self.build)
self.assertEqual(StockItem.objects.get(pk=6).status, status.StockStatus.ASSIGNED_TO_BUILD)
# And a new stock item created for the build output
self.assertEqual(StockItem.objects.get(pk=7).quantity, 1)
self.assertEqual(StockItem.objects.get(pk=7).serial, 1)
self.assertEqual(StockItem.objects.get(pk=7).build, self.build)