mirror of
https://github.com/inventree/InvenTree.git
synced 2025-06-20 13:56:30 +00:00
More serial number validation and unit testing
-
This commit is contained in:
@ -266,6 +266,48 @@ class Part(MPTTModel):
|
||||
def __str__(self):
|
||||
return "{n} - {d}".format(n=self.full_name, d=self.description)
|
||||
|
||||
def check_if_serial_number_exists(self, sn):
|
||||
"""
|
||||
Check if a serial number exists for this Part.
|
||||
|
||||
Note: Serial numbers must be unique across an entire Part "tree",
|
||||
so here we filter by the entire tree.
|
||||
"""
|
||||
|
||||
parts = Part.objects.filter(tree_id=self.tree_id)
|
||||
stock = StockModels.StockItem.objects.filter(part__in=parts, serial=sn)
|
||||
|
||||
return stock.exists()
|
||||
|
||||
def get_highest_serial_number(self):
|
||||
"""
|
||||
Return the highest serial number for this Part.
|
||||
|
||||
Note: Serial numbers must be unique across an entire Part "tree",
|
||||
so we filter by the entire tree.
|
||||
"""
|
||||
|
||||
parts = Part.objects.filter(tree_id=self.tree_id)
|
||||
stock = StockModels.StockItem.objects.filter(part__in=parts).exclude(serial=None).order_by('-serial')
|
||||
|
||||
if stock.count() > 0:
|
||||
return stock.first().serial
|
||||
|
||||
# No serial numbers found
|
||||
return None
|
||||
|
||||
def get_next_serial_number(self):
|
||||
"""
|
||||
Return the next-available serial number for this Part.
|
||||
"""
|
||||
|
||||
n = self.get_highest_serial_number()
|
||||
|
||||
if n is None:
|
||||
return 1
|
||||
else:
|
||||
return n + 1
|
||||
|
||||
@property
|
||||
def full_name(self):
|
||||
""" Format a 'full name' for this Part.
|
||||
@ -642,32 +684,40 @@ class Part(MPTTModel):
|
||||
self.sales_order_allocation_count(),
|
||||
])
|
||||
|
||||
@property
|
||||
def stock_entries(self):
|
||||
""" Return all 'in stock' items. To be in stock:
|
||||
def stock_entries(self, include_variants=True, in_stock=None):
|
||||
""" Return all stock entries for this Part.
|
||||
|
||||
- build_order is None
|
||||
- sales_order is None
|
||||
- belongs_to is None
|
||||
- If this is a template part, include variants underneath this.
|
||||
|
||||
Note: To return all stock-entries for all part variants under this one,
|
||||
we need to be creative with the filtering.
|
||||
"""
|
||||
|
||||
return self.stock_items.filter(StockModels.StockItem.IN_STOCK_FILTER)
|
||||
if include_variants:
|
||||
query = StockModels.StockItem.objects.filter(part__in=self.get_descendants(include_self=True))
|
||||
else:
|
||||
query = self.stock_items
|
||||
|
||||
if in_stock is True:
|
||||
query = query.filter(StockModels.StockItem.IN_STOCK_FILTER)
|
||||
elif in_stock is False:
|
||||
query = query.exclude(StockModels.StockItem.IN_STOCK_FILTER)
|
||||
|
||||
return query
|
||||
|
||||
@property
|
||||
def total_stock(self):
|
||||
""" Return the total stock quantity for this part.
|
||||
Part may be stored in multiple locations
|
||||
|
||||
- Part may be stored in multiple locations
|
||||
- If this part is a "template" (variants exist) then these are counted too
|
||||
"""
|
||||
|
||||
if self.is_template:
|
||||
total = sum([variant.total_stock for variant in self.variants.all()])
|
||||
else:
|
||||
total = self.stock_entries.filter(status__in=StockStatus.AVAILABLE_CODES).aggregate(total=Sum('quantity'))['total']
|
||||
entries = self.stock_entries(in_stock=True)
|
||||
|
||||
if total:
|
||||
return total
|
||||
else:
|
||||
return Decimal(0)
|
||||
query = entries.aggregate(t=Coalesce(Sum('quantity'), Decimal(0)))
|
||||
|
||||
return query['t']
|
||||
|
||||
@property
|
||||
def has_bom(self):
|
||||
|
@ -85,7 +85,7 @@ class PartAPITest(APITestCase):
|
||||
data = {'cascade': True}
|
||||
response = self.client.get(url, data, format='json')
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(len(response.data), 8)
|
||||
self.assertEqual(len(response.data), 13)
|
||||
|
||||
def test_get_parts_by_cat(self):
|
||||
url = reverse('api-part-list')
|
||||
|
@ -88,9 +88,9 @@ class CategoryTest(TestCase):
|
||||
|
||||
self.assertEqual(self.electronics.partcount(), 3)
|
||||
|
||||
self.assertEqual(self.mechanical.partcount(), 4)
|
||||
self.assertEqual(self.mechanical.partcount(active=True), 3)
|
||||
self.assertEqual(self.mechanical.partcount(False), 2)
|
||||
self.assertEqual(self.mechanical.partcount(), 8)
|
||||
self.assertEqual(self.mechanical.partcount(active=True), 7)
|
||||
self.assertEqual(self.mechanical.partcount(False), 6)
|
||||
|
||||
self.assertEqual(self.electronics.item_count, self.electronics.partcount())
|
||||
|
||||
|
@ -63,6 +63,8 @@ class PartTest(TestCase):
|
||||
green = Part.objects.get(pk=10004)
|
||||
self.assertEqual(green.get_ancestors().count(), 2)
|
||||
self.assertEqual(green.get_root(), chair)
|
||||
self.assertEqual(green.get_family().count(), 3)
|
||||
self.assertEqual(Part.objects.filter(tree_id=chair.tree_id).count(), 5)
|
||||
|
||||
def test_str(self):
|
||||
p = Part.objects.get(pk=100)
|
||||
|
Reference in New Issue
Block a user