diff --git a/InvenTree/part/fixtures/part.yaml b/InvenTree/part/fixtures/part.yaml index f9d7c9f4b4..632a265e23 100644 --- a/InvenTree/part/fixtures/part.yaml +++ b/InvenTree/part/fixtures/part.yaml @@ -1,12 +1,14 @@ # Create some fasteners - model: part.part + pk: 1 fields: name: 'M2x4 LPHS' description: 'M2x4 low profile head screw' category: 8 - model: part.part + pk: 2 fields: name: 'M3x12 SHCS' description: 'M3x12 socket head cap screw' @@ -15,6 +17,7 @@ # Create some resistors - model: part.part + pk: 3 fields: name: 'R_2K2_0805' description: '2.2kOhm resistor in 0805 package' @@ -35,6 +38,7 @@ category: 3 - model: part.part + pk: 25 fields: name: 'Widget' description: 'A watchamacallit' diff --git a/InvenTree/part/test_category.py b/InvenTree/part/test_category.py index ddbcf3babc..ea52396342 100644 --- a/InvenTree/part/test_category.py +++ b/InvenTree/part/test_category.py @@ -111,11 +111,11 @@ class CategoryTest(TestCase): def test_default_locations(self): """ Test traversal for default locations """ - self.assertEqual(str(self.fasteners.default_location), 'Office/Drawer') + self.assertEqual(str(self.fasteners.default_location), 'Office/Drawer_1') # Test that parts in this location return the same default location, too for p in self.fasteners.children.all(): - self.assert_equal(p.get_default_location(), 'Office/Drawer') + self.assert_equal(p.get_default_location(), 'Office/Drawer_1') # Any part under electronics should default to 'Home' R1 = Part.objects.get(name='R_2K2_0805') diff --git a/InvenTree/stock/fixtures/location.yaml b/InvenTree/stock/fixtures/location.yaml index f04b192a7d..fdc11f8fa1 100644 --- a/InvenTree/stock/fixtures/location.yaml +++ b/InvenTree/stock/fixtures/location.yaml @@ -26,6 +26,18 @@ - model: stock.stocklocation pk: 5 fields: - name: 'Drawer' + name: 'Drawer_1' description: 'In my desk' + parent: 4 +- model: stock.stocklocation + pk: 6 + fields: + name: 'Drawer_2' + description: 'Also in my desk' + parent: 4 +- model: stock.stocklocation + pk: 7 + fields: + name: 'Drawer_3' + description: 'Again, in my desk' parent: 4 \ No newline at end of file diff --git a/InvenTree/stock/fixtures/stock.yaml b/InvenTree/stock/fixtures/stock.yaml new file mode 100644 index 0000000000..18e0260e27 --- /dev/null +++ b/InvenTree/stock/fixtures/stock.yaml @@ -0,0 +1,42 @@ +# Create some sample stock items + +# 4,000 screws in the dining room +- model: stock.stockitem + fields: + part: 1 + location: 3 + batch: 'B123' + quantity: 4000 + +# 5,000 screws in the bathroom +- model: stock.stockitem + fields: + part: 1 + location: 2 + quantity: 5000 + +# 1234 2K2 resistors in 'Drawer_1' +- model: stock.stockitem + fields: + part: 3 + location: 5 + quantity: 1234 + +# Some widgets in drawer 3 +- model: stock.stockitem + fields: + part: 25 + location: 7 + quantity: 10 + +- model: stock.stockitem + fields: + part: 25 + location: 7 + quantity: 5 + +- model: stock.stockitem + fields: + part: 25 + location: 7 + quantity: 3 \ No newline at end of file diff --git a/InvenTree/stock/tests.py b/InvenTree/stock/tests.py index d5b1f8c3f1..5ca6c29e40 100644 --- a/InvenTree/stock/tests.py +++ b/InvenTree/stock/tests.py @@ -1,4 +1,5 @@ from django.test import TestCase +from django.db.models import Sum from .models import StockLocation, StockItem, StockItemTracking from part.models import Part @@ -9,100 +10,117 @@ class StockTest(TestCase): Tests to ensure that the stock location tree functions correcly """ + fixtures = [ + 'category', + 'part', + 'location', + 'stock', + ] + def setUp(self): - # Initialize some categories - self.loc1 = StockLocation.objects.create(name='L0', - description='Top level category', - parent=None) + # Extract some shortcuts from the fixtures + self.home = StockLocation.objects.get(name='Home') + self.bathroom = StockLocation.objects.get(name='Bathroom') + self.diningroom = StockLocation.objects.get(name='Dining Room') - self.loc2 = StockLocation.objects.create(name='L1.1', - description='Second level 1/2', - parent=self.loc1) + self.office = StockLocation.objects.get(name='Office') + self.drawer1 = StockLocation.objects.get(name='Drawer_1') + self.drawer2 = StockLocation.objects.get(name='Drawer_2') + self.drawer3 = StockLocation.objects.get(name='Drawer_3') - self.loc3 = StockLocation.objects.create(name='L1.2', - description='Second level 2/2', - parent=self.loc1) + def test_loc_count(self): + self.assertEqual(StockLocation.objects.count(), 7) - self.loc4 = StockLocation.objects.create(name='L2.1', - description='Third level 1/2', - parent=self.loc2) - - self.loc5 = StockLocation.objects.create(name='L2.2', - description='Third level 2/2', - parent=self.loc3) - - # Add some items to loc4 (all copies of a single part) - p = Part.objects.create(name='ACME Part', description='This is a part!') - - StockItem.objects.create(part=p, location=self.loc4, quantity=1000) - StockItem.objects.create(part=p, location=self.loc4, quantity=250) - StockItem.objects.create(part=p, location=self.loc4, quantity=12) - - def test_simple(self): + def test_url(self): it = StockItem.objects.get(pk=2) self.assertEqual(it.get_absolute_url(), '/stock/item/2/') - self.assertEqual(self.loc4.get_absolute_url(), '/stock/location/4/') + + self.assertEqual(self.home.get_absolute_url(), '/stock/location/1/') def test_strings(self): - it = StockItem.objects.get(pk=2) - self.assertEqual(str(it), '250 x ACME Part @ L2.1') + it = StockItem.objects.get(pk=1) + self.assertEqual(str(it), '4000 x M2x4 LPHS @ Dining Room') - def test_parent(self): - self.assertEqual(StockLocation.objects.count(), 5) - self.assertEqual(self.loc1.parent, None) - self.assertEqual(self.loc2.parent, self.loc1) - self.assertEqual(self.loc5.parent, self.loc3) + def test_parent_locations(self): + + self.assertEqual(self.office.parent, None) + self.assertEqual(self.drawer1.parent, self.office) + self.assertEqual(self.drawer2.parent, self.office) + self.assertEqual(self.drawer3.parent, self.office) + + self.assertEqual(self.drawer3.pathstring, 'Office/Drawer_3') + + # Move one of the drawers + self.drawer3.parent = self.home + self.assertNotEqual(self.drawer3.parent, self.office) + + self.assertEqual(self.drawer3.pathstring, 'Home/Drawer_3') def test_children(self): - self.assertTrue(self.loc1.has_children) - self.assertFalse(self.loc5.has_children) + self.assertTrue(self.office.has_children) + self.assertFalse(self.drawer2.has_children) - childs = self.loc1.getUniqueChildren() + childs = self.office.getUniqueChildren() - self.assertIn(self.loc2.id, childs) - self.assertIn(self.loc4.id, childs) + self.assertIn(self.drawer1.id, childs) + self.assertIn(self.drawer2.id, childs) - def test_paths(self): - self.assertEqual(self.loc5.pathstring, 'L0/L1.2/L2.2') + self.assertNotIn(self.bathroom.id, childs) def test_items(self): - # Location 5 should have no items - self.assertFalse(self.loc5.has_items()) - self.assertFalse(self.loc3.has_items()) + self.assertTrue(self.drawer1.has_items()) + self.assertTrue(self.drawer3.has_items()) + self.assertFalse(self.drawer2.has_items()) - # Location 4 should have three stock items - self.assertEqual(self.loc4.stock_items.count(), 3) + # Drawer 3 should have three stock items + self.assertEqual(self.drawer3.stock_items.count(), 3) def test_stock_count(self): part = Part.objects.get(pk=1) - # There should be 1262 items in stock - self.assertEqual(part.total_stock, 1262) + # There should be 5000 screws in stock + self.assertEqual(part.total_stock, 9000) + + # There should be 18 widgets in stock + self.assertEqual(StockItem.objects.filter(part=25).aggregate(Sum('quantity'))['quantity__sum'], 18) def test_delete_location(self): + + # How many stock items are there? + n_stock = StockItem.objects.count() + + # What parts are in drawer 3? + stock_ids = [part.id for part in StockItem.objects.filter(location=self.drawer3.id)] + # Delete location - parts should move to parent location - self.loc4.delete() + self.drawer3.delete() - # There should still be 3 stock items - self.assertEqual(StockItem.objects.count(), 3) + # There should still be the same number of parts + self.assertEqual(StockItem.objects.count(), n_stock) - # Parent location should have moved up to loc2 - for it in StockItem.objects.all(): - self.assertEqual(it.location, self.loc2) + # stock should have moved + for s_id in stock_ids: + s_item = StockItem.objects.get(id=s_id) + self.assertEqual(s_item.location, self.office) def test_move(self): - # Move the first stock item to loc5 + """ Test stock movement functions """ + + # Move 4,000 screws to the bathroom it = StockItem.objects.get(pk=1) - self.assertNotEqual(it.location, self.loc5) - self.assertTrue(it.move(self.loc5, 'Moved to another place', None)) - self.assertEqual(it.location, self.loc5) + self.assertNotEqual(it.location, self.bathroom) + self.assertTrue(it.move(self.bathroom, 'Moved to the bathroom', None)) + self.assertEqual(it.location, self.bathroom) + + # There now should be 2 lots of screws in the bathroom + self.assertEqual(StockItem.objects.filter(part=1, location=self.bathroom).count(), 2) # Check that a tracking item was added track = StockItemTracking.objects.filter(item=it).latest('id') self.assertEqual(track.item, it) self.assertIn('Moved to', track.title) - self.assertEqual(track.notes, 'Moved to another place') + self.assertEqual(track.notes, 'Moved to the bathroom') def test_self_move(self): # Try to move an item to its current location (should fail) @@ -114,10 +132,13 @@ class StockTest(TestCase): # Ensure tracking info was not added self.assertEqual(it.tracking_info.count(), n) + def test_partial_move(self): + pass + def test_stocktake(self): # Perform stocktake it = StockItem.objects.get(pk=2) - self.assertEqual(it.quantity, 250) + self.assertEqual(it.quantity, 5000) it.stocktake(255, None, notes='Counted items!') self.assertEqual(it.quantity, 255)