From 25e41b3fa2cf1149d2cd1c3fbe1c53a17f871cfc Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sun, 12 May 2019 21:20:43 +1000 Subject: [PATCH 1/6] Allow null values for StockItem.updated - Just to make the tests run better --- .../migrations/0016_auto_20190512_2119.py | 18 ++++++++++++++++++ InvenTree/stock/models.py | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 InvenTree/stock/migrations/0016_auto_20190512_2119.py diff --git a/InvenTree/stock/migrations/0016_auto_20190512_2119.py b/InvenTree/stock/migrations/0016_auto_20190512_2119.py new file mode 100644 index 0000000000..582e68d277 --- /dev/null +++ b/InvenTree/stock/migrations/0016_auto_20190512_2119.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2 on 2019-05-12 11:19 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('stock', '0015_stockitem_delete_on_deplete'), + ] + + operations = [ + migrations.AlterField( + model_name='stockitem', + name='updated', + field=models.DateField(auto_now=True, null=True), + ), + ] diff --git a/InvenTree/stock/models.py b/InvenTree/stock/models.py index 09b9cafff4..f4956d32e6 100644 --- a/InvenTree/stock/models.py +++ b/InvenTree/stock/models.py @@ -211,7 +211,7 @@ class StockItem(models.Model): quantity = models.PositiveIntegerField(validators=[MinValueValidator(0)], default=1) - updated = models.DateField(auto_now=True) + updated = models.DateField(auto_now=True, null=True) # last time the stock was checked / counted stocktake_date = models.DateField(blank=True, null=True) From f410957d8d5e899665a630ef3dc015f5a763a2d8 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sun, 12 May 2019 21:46:38 +1000 Subject: [PATCH 2/6] Refactored Stock test to use fixtures --- InvenTree/part/fixtures/part.yaml | 4 + InvenTree/part/test_category.py | 4 +- InvenTree/stock/fixtures/location.yaml | 14 ++- InvenTree/stock/fixtures/stock.yaml | 42 ++++++++ InvenTree/stock/tests.py | 141 ++++++++++++++----------- 5 files changed, 142 insertions(+), 63 deletions(-) create mode 100644 InvenTree/stock/fixtures/stock.yaml 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) From 42bbf95c42031523c7a20498bd71d7d7800bef4f Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Mon, 13 May 2019 18:09:59 +1000 Subject: [PATCH 3/6] Include the sub_part ID in the BOM hash --- InvenTree/part/models.py | 1 + 1 file changed, 1 insertion(+) diff --git a/InvenTree/part/models.py b/InvenTree/part/models.py index b781287930..d119c3094e 100644 --- a/InvenTree/part/models.py +++ b/InvenTree/part/models.py @@ -493,6 +493,7 @@ class Part(models.Model): hash = hashlib.md5('bom seed'.encode()) for item in self.bom_items.all(): + hash.update(str(item.sub_part.id).encode()) hash.update(str(item.sub_part.full_name).encode()) hash.update(str(item.quantity).encode()) hash.update(str(item.note).encode()) From 0813f8cbd5fffe8e63cca13968fb000e1c9c6e21 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Mon, 13 May 2019 18:45:52 +1000 Subject: [PATCH 4/6] More test coverage for Stock --- InvenTree/InvenTree/views.py | 4 +- InvenTree/stock/fixtures/location.yaml | 5 ++ InvenTree/stock/fixtures/stock.yaml | 5 ++ InvenTree/stock/models.py | 10 +--- InvenTree/stock/tests.py | 68 +++++++++++++++++++++++++- 5 files changed, 80 insertions(+), 12 deletions(-) diff --git a/InvenTree/InvenTree/views.py b/InvenTree/InvenTree/views.py index b837b3da59..9331cdf215 100644 --- a/InvenTree/InvenTree/views.py +++ b/InvenTree/InvenTree/views.py @@ -43,7 +43,7 @@ class TreeSerializer(views.APIView): 'pk': item.id, 'text': item.name, 'href': item.get_absolute_url(), - 'tags': [item.item_count], + 'tags': [item.stock_item_count], } if item.has_children: @@ -66,7 +66,7 @@ class TreeSerializer(views.APIView): for item in top_items: nodes.append(self.itemToJson(item)) - top_count += item.item_count + top_count += item.stock_item_count top = { 'pk': None, diff --git a/InvenTree/stock/fixtures/location.yaml b/InvenTree/stock/fixtures/location.yaml index fdc11f8fa1..f34490af29 100644 --- a/InvenTree/stock/fixtures/location.yaml +++ b/InvenTree/stock/fixtures/location.yaml @@ -5,12 +5,14 @@ fields: name: 'Home' description: 'My house' + - model: stock.stocklocation pk: 2 fields: name: 'Bathroom' description: 'Where I keep my bath' parent: 1 + - model: stock.stocklocation pk: 3 fields: @@ -23,18 +25,21 @@ fields: name: 'Office' description: 'Place of work' + - model: stock.stocklocation pk: 5 fields: 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: diff --git a/InvenTree/stock/fixtures/stock.yaml b/InvenTree/stock/fixtures/stock.yaml index 18e0260e27..96e0a3ab72 100644 --- a/InvenTree/stock/fixtures/stock.yaml +++ b/InvenTree/stock/fixtures/stock.yaml @@ -17,6 +17,7 @@ # 1234 2K2 resistors in 'Drawer_1' - model: stock.stockitem + pk: 1234 fields: part: 3 location: 5 @@ -24,18 +25,22 @@ # Some widgets in drawer 3 - model: stock.stockitem + pk: 100 fields: part: 25 location: 7 quantity: 10 + delete_on_deplete: False - model: stock.stockitem + pk: 101 fields: part: 25 location: 7 quantity: 5 - model: stock.stockitem + pk: 102 fields: part: 25 location: 7 diff --git a/InvenTree/stock/models.py b/InvenTree/stock/models.py index f4956d32e6..cbc64f42ab 100644 --- a/InvenTree/stock/models.py +++ b/InvenTree/stock/models.py @@ -53,12 +53,7 @@ class StockLocation(InvenTreeTree): """ Return the number of StockItem objects which live in or under this category """ - return len(StockItem.objects.filter(location__in=self.getUniqueChildren())) - - @property - def item_count(self): - - return self.stock_item_count + return StockItem.objects.filter(location__in=self.getUniqueChildren()).count() @receiver(pre_delete, sender=StockLocation, dispatch_uid='stocklocation_delete_log') @@ -444,9 +439,6 @@ class StockItem(models.Model): """ Remove items from stock """ - if self.quantity == 0: - return False - quantity = int(quantity) if quantity <= 0 or self.infinite: diff --git a/InvenTree/stock/tests.py b/InvenTree/stock/tests.py index 5ca6c29e40..fb1434828a 100644 --- a/InvenTree/stock/tests.py +++ b/InvenTree/stock/tests.py @@ -37,6 +37,11 @@ class StockTest(TestCase): self.assertEqual(self.home.get_absolute_url(), '/stock/location/1/') + def test_barcode(self): + barcode = self.office.format_barcode() + + self.assertIn('"name": "Office"', barcode) + def test_strings(self): it = StockItem.objects.get(pk=1) self.assertEqual(str(it), '4000 x M2x4 LPHS @ Dining Room') @@ -74,6 +79,7 @@ class StockTest(TestCase): # Drawer 3 should have three stock items self.assertEqual(self.drawer3.stock_items.count(), 3) + self.assertEqual(self.drawer3.stock_item_count, 3) def test_stock_count(self): part = Part.objects.get(pk=1) @@ -133,7 +139,44 @@ class StockTest(TestCase): self.assertEqual(it.tracking_info.count(), n) def test_partial_move(self): - pass + w1 = StockItem.objects.get(pk=100) + w2 = StockItem.objects.get(pk=101) + + q = w1.quantity + + # Move 6 of the units + self.assertTrue(w1.move(self.diningroom, 'Moved', None, quantity=6)) + self.assertEqual(w1.quantity, 6) + + # There should also be a new object still in drawer3 + self.assertEqual(StockItem.objects.filter(part=25).count(), 4) + widget = StockItem.objects.get(location=self.drawer3.id, part=25, quantity=4) + + # Try to move negative units + self.assertFalse(widget.move(self.bathroom, 'Test', None, quantity=-100)) + self.assertEqual(StockItem.objects.filter(part=25).count(), 4) + + # Try to move to a blank location + self.assertFalse(widget.move(None, 'null', None)) + + def test_split_stock(self): + # Split the 1234 x 2K2 resistors in Drawer_1 + + N = StockItem.objects.filter(part=3).count() + + stock = StockItem.objects.get(id=1234) + stock.splitStock(1000, None) + self.assertEqual(stock.quantity, 234) + + # There should be a new stock item too! + self.assertEqual(StockItem.objects.filter(part=3).count(), N + 1) + + # Try to split a negative quantity + stock.splitStock(-10, None) + self.assertEqual(StockItem.objects.filter(part=3).count(), N + 1) + + stock.splitStock(stock.quantity, None) + self.assertEqual(StockItem.objects.filter(part=3).count(), N + 1) def test_stocktake(self): # Perform stocktake @@ -168,6 +211,8 @@ class StockTest(TestCase): self.assertIn('Added', track.title) self.assertIn('Added some items', track.notes) + self.assertFalse(it.add_stock(-10, None)) + def test_take_stock(self): it = StockItem.objects.get(pk=2) n = it.quantity @@ -181,3 +226,24 @@ class StockTest(TestCase): self.assertIn('Removed', track.title) self.assertIn('Removed some items', track.notes) self.assertTrue(it.has_tracking_info) + + # Test that negative quantity does nothing + self.assertFalse(it.take_stock(-10, None)) + + def test_deplete_stock(self): + + w1 = StockItem.objects.get(pk=100) + w2 = StockItem.objects.get(pk=101) + + # Take 25 units from w1 + w1.take_stock(30, None, notes='Took 30') + + # Get from database again + w1 = StockItem.objects.get(pk=100) + self.assertEqual(w1.quantity, 0) + + # Take 25 units from w2 (will be deleted) + w2.take_stock(30, None, notes='Took 30') + + with self.assertRaises(StockItem.DoesNotExist): + w2 = StockItem.objects.get(pk=101) From b7d3bbd836030b6d3b245775a0c5a983af801b75 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Mon, 13 May 2019 18:46:48 +1000 Subject: [PATCH 5/6] PEP --- InvenTree/stock/tests.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/InvenTree/stock/tests.py b/InvenTree/stock/tests.py index fb1434828a..21242aad9b 100644 --- a/InvenTree/stock/tests.py +++ b/InvenTree/stock/tests.py @@ -140,15 +140,12 @@ class StockTest(TestCase): def test_partial_move(self): w1 = StockItem.objects.get(pk=100) - w2 = StockItem.objects.get(pk=101) - - q = w1.quantity # Move 6 of the units self.assertTrue(w1.move(self.diningroom, 'Moved', None, quantity=6)) self.assertEqual(w1.quantity, 6) - # There should also be a new object still in drawer3 + # There should also be a new object still in drawer3 self.assertEqual(StockItem.objects.filter(part=25).count(), 4) widget = StockItem.objects.get(location=self.drawer3.id, part=25, quantity=4) @@ -246,4 +243,4 @@ class StockTest(TestCase): w2.take_stock(30, None, notes='Took 30') with self.assertRaises(StockItem.DoesNotExist): - w2 = StockItem.objects.get(pk=101) + w2 = StockItem.objects.get(pk=101) From 150c68e65b68f860e08d8c4a233c7cf7cca5c267 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Mon, 13 May 2019 18:52:54 +1000 Subject: [PATCH 6/6] Bug fix - Turns out 'item_count' was actually used for something (that I wrote... recently...) --- InvenTree/InvenTree/views.py | 4 ++-- InvenTree/stock/models.py | 7 +++++++ InvenTree/stock/tests.py | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/InvenTree/InvenTree/views.py b/InvenTree/InvenTree/views.py index 9331cdf215..b837b3da59 100644 --- a/InvenTree/InvenTree/views.py +++ b/InvenTree/InvenTree/views.py @@ -43,7 +43,7 @@ class TreeSerializer(views.APIView): 'pk': item.id, 'text': item.name, 'href': item.get_absolute_url(), - 'tags': [item.stock_item_count], + 'tags': [item.item_count], } if item.has_children: @@ -66,7 +66,7 @@ class TreeSerializer(views.APIView): for item in top_items: nodes.append(self.itemToJson(item)) - top_count += item.stock_item_count + top_count += item.item_count top = { 'pk': None, diff --git a/InvenTree/stock/models.py b/InvenTree/stock/models.py index cbc64f42ab..950d6237dd 100644 --- a/InvenTree/stock/models.py +++ b/InvenTree/stock/models.py @@ -55,6 +55,13 @@ class StockLocation(InvenTreeTree): return StockItem.objects.filter(location__in=self.getUniqueChildren()).count() + @property + def item_count(self): + """ Simply returns the number of stock items in this location. + Required for tree view serializer. + """ + return self.stock_item_count + @receiver(pre_delete, sender=StockLocation, dispatch_uid='stocklocation_delete_log') def before_delete_stock_location(sender, instance, using, **kwargs): diff --git a/InvenTree/stock/tests.py b/InvenTree/stock/tests.py index 21242aad9b..417c995b6c 100644 --- a/InvenTree/stock/tests.py +++ b/InvenTree/stock/tests.py @@ -79,7 +79,7 @@ class StockTest(TestCase): # Drawer 3 should have three stock items self.assertEqual(self.drawer3.stock_items.count(), 3) - self.assertEqual(self.drawer3.stock_item_count, 3) + self.assertEqual(self.drawer3.item_count, 3) def test_stock_count(self): part = Part.objects.get(pk=1)