Coverage for src/backend/InvenTree/stock/filters.py: 86%

12 statements  

« prev     ^ index     » next       coverage.py v7.15.2, created at 2026-10-07 17:47 +0000

1"""Custom query filters for the Stock models.""" 

2 

3from typing import Optional 

4 

5from django.db.models import F, Func, IntegerField, OuterRef, Q, Subquery 

6from django.db.models.functions import Coalesce 

7 

8import stock.models 

9 

10 

11def annotate_location_items(filter: Optional[Q] = None): 

12 """Construct a queryset annotation which returns the number of stock items in a particular location. 

13 

14 - Includes items in subcategories also 

15 - Requires subquery to perform annotation 

16 """ 

17 # Construct a subquery to provide all items in this location and any sublocations 

18 subquery = stock.models.StockItem.objects.exclude(location=None).filter( 

19 location__tree_id=OuterRef('tree_id'), 

20 location__lft__gte=OuterRef('lft'), 

21 location__rght__lte=OuterRef('rght'), 

22 location__level__gte=OuterRef('level'), 

23 ) 

24 

25 # Optionally apply extra filter to returned results 

26 if filter is not None: 26 ↛ 27line 26 didn't jump to line 27 because the condition on line 26 was never true

27 subquery = subquery.filter(filter) 

28 

29 return Coalesce( 

30 Subquery( 

31 subquery 

32 .annotate( 

33 total=Func(F('pk'), function='COUNT', output_field=IntegerField()) 

34 ) 

35 .values('total') 

36 .order_by() 

37 ), 

38 0, 

39 output_field=IntegerField(), 

40 ) 

41 

42 

43def annotate_sub_locations(): 

44 """Construct a queryset annotation which returns the number of sub-locations below a certain StockLocation node in a StockLocation tree.""" 

45 subquery = stock.models.StockLocation.objects.filter( 

46 tree_id=OuterRef('tree_id'), 

47 lft__gt=OuterRef('lft'), 

48 rght__lt=OuterRef('rght'), 

49 level__gt=OuterRef('level'), 

50 ) 

51 

52 return Coalesce( 

53 Subquery( 

54 subquery 

55 .annotate( 

56 count=Func(F('pk'), function='COUNT', output_field=IntegerField()) 

57 ) 

58 .values('count') 

59 .order_by() 

60 ), 

61 0, 

62 output_field=IntegerField(), 

63 )