diff --git a/base/admin.py b/base/admin.py index f71922c51..cec28c9c6 100644 --- a/base/admin.py +++ b/base/admin.py @@ -13,6 +13,7 @@ from base.models import ( Department, DynamicEmailConfiguration, DynamicPagination, + EmailLog, JobPosition, JobRole, EmployeeShiftSchedule, @@ -57,6 +58,7 @@ admin.site.register(WorktyperequestComment) admin.site.register(DynamicPagination) admin.site.register(Announcement) admin.site.register(Attachment) +admin.site.register(EmailLog) diff --git a/base/backends.py b/base/backends.py index b011fcff0..f6acb32d0 100644 --- a/base/backends.py +++ b/base/backends.py @@ -4,6 +4,7 @@ email_backend.py This module is used to write email backends """ from django.core.mail.backends.smtp import EmailBackend +from base.models import EmailLog from horilla import settings from base.thread_local_middleware import _thread_locals @@ -32,6 +33,10 @@ class ConfiguredEmailBackend(EmailBackend): configuration = DynamicEmailConfiguration.objects.filter( company_id=compay ).first() + if configuration is None: + configuration = DynamicEmailConfiguration.objects.filter( + company_id__isnull=True + ).first() # Use default settings if configuration is not available host = configuration.host if configuration else host or settings.EMAIL_HOST port = configuration.port if configuration else port or settings.EMAIL_PORT @@ -53,17 +58,17 @@ class ConfiguredEmailBackend(EmailBackend): fail_silently = ( configuration.fail_silently if configuration - else fail_silently or getattr(settings,"EMAIL_FAIL_SILENTLY",True) + else fail_silently or getattr(settings, "EMAIL_FAIL_SILENTLY", True) ) use_ssl = ( configuration.use_ssl if configuration - else use_ssl or getattr(settings,"EMAIL_USE_SSL",None) + else use_ssl or getattr(settings, "EMAIL_USE_SSL", None) ) timeout = ( configuration.timeout if configuration - else timeout or getattr(settings,"EMAIL_TIMEOUT",None) + else timeout or getattr(settings, "EMAIL_TIMEOUT", None) ) ssl_keyfile = ( getattr(configuration, "ssl_keyfile", None) @@ -75,6 +80,7 @@ class ConfiguredEmailBackend(EmailBackend): if configuration else ssl_keyfile or getattr(settings, "ssl_certfile", None) ) + self.mail_sent_from = username super(ConfiguredEmailBackend, self).__init__( host=host, port=port, @@ -89,5 +95,19 @@ class ConfiguredEmailBackend(EmailBackend): **kwargs ) + def send_messages(self, email_messages): + response = super(ConfiguredEmailBackend, self).send_messages(email_messages) + # Save the email status in the EmailLog model + for message in email_messages: + email_log = EmailLog( + subject=message.subject, + from_email=self.mail_sent_from, + to=message.to, + body=message.body, + status="sent" if response else "failed", + ) + email_log.save() + return response + __all__ = ["ConfiguredEmailBackend"] diff --git a/base/models.py b/base/models.py index 65b7f856a..6030555df 100644 --- a/base/models.py +++ b/base/models.py @@ -427,7 +427,7 @@ class EmployeeShift(models.Model): ) company_id = models.ManyToManyField(Company, blank=True, verbose_name=_("Company")) grace_time_id = models.ForeignKey( - 'attendance.GraceTime', + "attendance.GraceTime", null=True, blank=True, related_name="employee_shift", @@ -742,8 +742,9 @@ class WorktyperequestComment(models.Model): """ WorktyperequestComment Model """ + from employee.models import Employee - + request_id = models.ForeignKey(WorkTypeRequest, on_delete=models.CASCADE) employee_id = models.ForeignKey(Employee, on_delete=models.CASCADE) comment = models.TextField(null=True, verbose_name=_("Comment")) @@ -757,7 +758,6 @@ class WorktyperequestComment(models.Model): return f"{self.comment}" - class ShiftRequest(models.Model): """ ShiftRequest model @@ -873,13 +873,13 @@ class ShiftRequest(models.Model): return f"{self.employee_id}" - class ShiftrequestComment(models.Model): """ ShiftrequestComment Model """ + from employee.models import Employee - + request_id = models.ForeignKey(ShiftRequest, on_delete=models.CASCADE) employee_id = models.ForeignKey(Employee, on_delete=models.CASCADE) comment = models.TextField(null=True, verbose_name=_("Comment")) @@ -893,7 +893,6 @@ class ShiftrequestComment(models.Model): return f"{self.comment}" - class Tags(models.Model): title = models.CharField(max_length=30) color = models.CharField(max_length=30) @@ -1136,10 +1135,12 @@ class MultipleApprovalManagers(models.Model): sequence = models.IntegerField(null=False, blank=False) employee_id = models.IntegerField(null=False, blank=False) + class DynamicPagination(models.Model): """ model for storing pagination for employees """ + from django.contrib.auth.models import User user_id = models.OneToOneField( @@ -1150,22 +1151,24 @@ class DynamicPagination(models.Model): related_name="dynamic_pagination", verbose_name=_("User"), ) - pagination = models.IntegerField(default = 50) - + pagination = models.IntegerField(default=50) + def save(self, *args, **kwargs): - request = getattr(_thread_locals,"request",None) + request = getattr(_thread_locals, "request", None) user = request.user self.user_id = user super().save(*args, **kwargs) + def __str__(self): - return (f"{self.user_id}|{self.pagination}") + return f"{self.user_id}|{self.pagination}" class Attachment(models.Model): """ Attachment model for multiple attachments in announcemnts. """ - file = models.FileField(upload_to='attachments/') + + file = models.FileField(upload_to="attachments/") def __str__(self): return self.file.name @@ -1175,8 +1178,8 @@ class AnnouncementExpire(models.Model): """ This model for setting a expire days for announcement if no expire date for announcement """ - days = models.IntegerField(null=True, blank = True, default = 30) + days = models.IntegerField(null=True, blank=True, default=30) class Announcement(models.Model): @@ -1184,9 +1187,12 @@ class Announcement(models.Model): """ Anonuncement Model for stroing all announcements. """ + title = models.CharField(max_length=30) description = models.TextField(null=True) - attachments = models.ManyToManyField(Attachment, related_name='announcement_attachments', blank=True) + attachments = models.ManyToManyField( + Attachment, related_name="announcement_attachments", blank=True + ) created_on = models.DateTimeField(auto_now_add=True) expire_date = models.DateField(null=True, blank=True) department = models.ManyToManyField(Department, blank=True) @@ -1200,8 +1206,9 @@ class AnnouncementComment(models.Model): """ AnnouncementComment Model """ + from employee.models import Employee - + announcement_id = models.ForeignKey(Announcement, on_delete=models.CASCADE) employee_id = models.ForeignKey(Employee, on_delete=models.CASCADE) comment = models.TextField(null=True, verbose_name=_("Comment")) @@ -1210,3 +1217,20 @@ class AnnouncementComment(models.Model): verbose_name=_("Created At"), null=True, ) + + +class EmailLog(models.Model): + """ + EmailLog Keeping model + """ + + statuses = [("sent", "Sent"), ("failed", "Failed")] + subject = models.CharField(max_length=255) + body = models.TextField() + from_email = models.EmailField() + to = models.EmailField() + status = models.CharField(max_length=6, choices=statuses) + created_at = models.DateTimeField(auto_now_add=True) + company_id = models.ForeignKey( + Company, on_delete=models.CASCADE, null=True, editable=False + ) diff --git a/recruitment/models.py b/recruitment/models.py index ab57f1619..7d392a5aa 100644 --- a/recruitment/models.py +++ b/recruitment/models.py @@ -18,7 +18,7 @@ from django.dispatch import receiver from horilla_audit.models import HorillaAuditLog, HorillaAuditInfo from horilla_audit.methods import get_diff from employee.models import Employee -from base.models import JobPosition, Company +from base.models import EmailLog, JobPosition, Company from django.core.files.storage import default_storage from base.horilla_company_manager import HorillaCompanyManager from django.core.validators import MinValueValidator, MaxValueValidator @@ -382,6 +382,16 @@ class Candidate(models.Model): """ return get_diff(self) + def get_last_sent_mail(self): + """ + This method is used to get last send mail + """ + return ( + EmailLog.objects.filter(to__icontains=self.email) + .order_by("-created_at") + .first() + ) + def save(self, *args, **kwargs): # Check if the 'stage_id' attribute is not None if self.stage_id is not None: @@ -433,7 +443,7 @@ class StageNote(models.Model): objects = HorillaCompanyManager( related_company_field="candidate_id__recruitment_id__company_id" ) - created_at = models.DateTimeField(auto_now_add=True,null=True) + created_at = models.DateTimeField(auto_now_add=True, null=True) def __str__(self) -> str: return f"{self.description}" diff --git a/recruitment/templates/candidate/individual.html b/recruitment/templates/candidate/individual.html index 5f5018724..5a892b0aa 100644 --- a/recruitment/templates/candidate/individual.html +++ b/recruitment/templates/candidate/individual.html @@ -215,6 +215,16 @@ $(document).on('click', function (event) { > {% endif %} +