Coverage for src/backend/InvenTree/InvenTree/helpers_email.py: 53%
53 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-07 17:47 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-07 17:47 +0000
1"""Code for managing email functionality in InvenTree."""
3from typing import Optional
5from django.conf import settings
7import structlog
8from allauth.account.models import EmailAddress
10import InvenTree.ready
11import InvenTree.tasks as tasks
12from common.models import Priority, issue_mail
14logger = structlog.get_logger('inventree')
17def is_email_configured() -> bool:
18 """Check if email backend is configured.
20 Fails on tests nominally, if no bypassed via settings.TESTING_BYPASS_MAILCHECK.
22 NOTE: This does not check if the configuration is valid!
23 """
24 configured = True
25 testing = settings.TESTING
27 if InvenTree.ready.isInTestMode(): 27 ↛ 28line 27 didn't jump to line 28 because the condition on line 27 was never true
28 return settings.TESTING_BYPASS_MAILCHECK
30 if InvenTree.ready.isImportingData(): 30 ↛ 31line 30 didn't jump to line 31 because the condition on line 30 was never true
31 return False
33 # Might be using a different INTERNAL_EMAIL_BACKEND
34 if settings.INTERNAL_EMAIL_BACKEND != 'django.core.mail.backends.smtp.EmailBackend': 34 ↛ 37line 34 didn't jump to line 37 because the condition on line 34 was never true
35 # If we are using a different email backend, we don't need to check
36 # the SMTP settings
37 return True
39 if not settings.EMAIL_HOST: 39 ↛ 47line 39 didn't jump to line 47 because the condition on line 39 was always true
40 configured = False
42 # Display warning unless in test mode
43 if not testing: # pragma: no cover 43 ↛ 47line 43 didn't jump to line 47 because the condition on line 43 was always true
44 logger.debug('INVE-W7: EMAIL_HOST is not configured')
46 # Display warning unless in test mode
47 if not settings.EMAIL_HOST_USER and not testing: # pragma: no cover 47 ↛ 51line 47 didn't jump to line 51 because the condition on line 47 was always true
48 logger.debug('INVE-W7: EMAIL_HOST_USER is not configured')
50 # Display warning unless in test mode
51 if not settings.EMAIL_HOST_PASSWORD and testing: # pragma: no cover 51 ↛ 52line 51 didn't jump to line 52 because the condition on line 51 was never true
52 logger.debug('INVE-W7: EMAIL_HOST_PASSWORD is not configured')
54 # Email sender must be configured
55 if not settings.DEFAULT_FROM_EMAIL: 55 ↛ 61line 55 didn't jump to line 61 because the condition on line 55 was always true
56 configured = False
58 if not testing: # pragma: no cover 58 ↛ 61line 58 didn't jump to line 61 because the condition on line 58 was always true
59 logger.debug('DEFAULT_FROM_EMAIL is not configured')
61 return configured
64def send_email(
65 subject: str,
66 body: str,
67 recipients: str | list,
68 from_email: Optional[str] = None,
69 html_message=None,
70 prio: Priority = Priority.NORMAL,
71 headers: Optional[dict] = None,
72) -> tuple[bool, Optional[str]]:
73 """Send an email with the specified subject and body, to the specified recipients list."""
74 if isinstance(recipients, str): 74 ↛ 75line 74 didn't jump to line 75 because the condition on line 74 was never true
75 recipients = [recipients]
77 import InvenTree.ready
79 if InvenTree.ready.isImportingData(): 79 ↛ 81line 79 didn't jump to line 81 because the condition on line 79 was never true
80 # If we are importing data, don't send emails
81 return False, 'Data import in progress'
83 if not is_email_configured() and not settings.TESTING: 83 ↛ 89line 83 didn't jump to line 89 because the condition on line 83 was always true
84 # Email is not configured / enabled
85 logger.info('INVE-W7: Email will not be send, no mail server configured')
86 return False, 'INVE-W7: Email server not configured'
88 # If a *from_email* is not specified, ensure that the default is set
89 if not from_email:
90 from_email = settings.DEFAULT_FROM_EMAIL
92 # If we still don't have a valid from_email, then we can't send emails
93 if not from_email:
94 if settings.TESTING:
95 from_email = 'test@test.inventree.org'
96 else:
97 logger.error(
98 'INVE-W7: send_email failed: DEFAULT_FROM_EMAIL not specified'
99 )
100 return False, 'INVE-W7: no from_email or DEFAULT_FROM_EMAIL specified'
102 tasks.offload_task(
103 issue_mail,
104 subject=subject,
105 body=body,
106 from_email=from_email,
107 recipients=recipients,
108 fail_silently=False,
109 html_message=html_message,
110 prio=prio,
111 headers=headers,
112 group='notification',
113 )
114 return True, None
117def get_email_for_user(user) -> Optional[str]:
118 """Find an email address for the specified user."""
119 # First check if the user has an associated email address
120 if user.email:
121 return user.email
123 # Otherwise, find first matching email
124 # Priority is given to primary or verified email addresses
125 if (
126 email := EmailAddress.objects
127 .filter(user=user)
128 .order_by('-primary', '-verified')
129 .first()
130 ):
131 return email.email