Coverage for src/backend/InvenTree/part/apps.py: 62%

41 statements  

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

1"""part app specification.""" 

2 

3from django.apps import AppConfig 

4from django.db.utils import OperationalError, ProgrammingError 

5 

6import structlog 

7 

8import InvenTree.ready 

9from InvenTree.apps import InvenTreeConfig 

10 

11logger = structlog.get_logger('inventree') 

12 

13 

14class PartConfig(AppConfig): 

15 """Config class for the 'part' app.""" 

16 

17 name = 'part' 

18 

19 def ready(self): 

20 """This function is called whenever the Part app is loaded.""" 

21 # skip loading if plugin registry is not loaded or we run in a background thread 

22 if ( 

23 not InvenTree.ready.isPluginRegistryLoaded() 

24 or not InvenTree.ready.isInMainThread() 

25 ): 

26 return 

27 

28 if InvenTree.ready.isRunningMigrations(): 28 ↛ 29line 28 didn't jump to line 29 because the condition on line 28 was never true

29 return 

30 

31 if InvenTree.ready.canAppAccessDatabase(): 31 ↛ exitline 31 didn't return from function 'ready' because the condition on line 31 was always true

32 # Ensure there are no open migrations 

33 InvenTreeConfig.ensure_migrations_done() 

34 

35 self.update_trackable_status() 

36 self.reset_part_pricing_flags() 

37 

38 def update_trackable_status(self): 

39 """Check for any instances where a trackable part is used in the BOM for a non-trackable part. 

40 

41 In such a case, force the top-level part to be trackable too. 

42 """ 

43 from .models import BomItem 

44 

45 try: 

46 items = BomItem.objects.filter( 

47 part__trackable=False, sub_part__trackable=True 

48 ) 

49 

50 for item in items: 50 ↛ 51line 50 didn't jump to line 51 because the loop on line 50 never started

51 logger.info("Marking part '%s' as trackable", item.part.name) 

52 item.part.trackable = True 

53 item.part.clean() 

54 item.part.save() 

55 except (OperationalError, ProgrammingError): # pragma: no cover 

56 # Exception if the database has not been migrated yet 

57 pass 

58 

59 def reset_part_pricing_flags(self): 

60 """Performed on startup, to ensure that all pricing objects are in a "good" state. 

61 

62 Prevents issues with state machine if the server is restarted mid-update 

63 """ 

64 from .models import PartPricing 

65 

66 if InvenTree.ready.isImportingData(): 66 ↛ 67line 66 didn't jump to line 67 because the condition on line 66 was never true

67 return 

68 

69 try: 

70 items = PartPricing.objects.filter(scheduled_for_update=True) 

71 

72 if items.count() > 0: 72 ↛ 74line 72 didn't jump to line 74 because the condition on line 72 was never true

73 # Find any pricing objects which have the 'scheduled_for_update' flag set 

74 logger.info( 

75 'Resetting update flags for %s pricing objects...', items.count() 

76 ) 

77 

78 for pricing in items: 

79 pricing.scheduled_for_update = False 

80 pricing.save() 

81 except Exception: 

82 logger.exception('Failed to reset pricing flags - database not ready')