This commit introduces significant changes to the architecture of the Horilla HRMS system by decoupling interdependent modules. The following modifications were made: 1. **Module Independence**: Each module has been refactored to eliminate reliance on other modules, promoting a more modular and maintainable codebase. 2. **Refactored Imports and Dependencies**: Adjusted import statements and dependency injections to support independent module operation. 3. **Compatibility and Functionality**: Ensured that all modules are compatible with existing systems and maintain their intended functionality both independently and when integrated with other modules. These changes enhance the modularity, maintainability, and scalability of the Horilla HRMS, allowing developers to work on individual modules without affecting the entire system. Future development and deployment will be more efficient and less prone to issues arising from tightly coupled code. **NOTE** For existing Horilla users, if you face any issues during the migrations, please run the following command and try again the migrations. - `python3 manage.py makemigrations` - `python3 manage.py migrate base` - `python3 manage.py migrate` * [IMP] ASSET: Asset module dependency removal from other Horilla apps * [IMP] ATTENDANCE: Attendance module dependency removal from other Horilla apps * [IMP] BASE: Base module dependency removal from other Horilla apps * [IMP] EMPLOYEE: Employee module dependency removal from other Horilla apps * [IMP] HELPDESK: Helpdesk module dependency removal from other Horilla apps * [IMP] HORILLA AUDIT: Horilla Audit module dependency removal from other Horilla apps * [IMP] HORILLA CRUMBS: Horilla Crumbs module dependency removal from other Horilla apps * [IMP] HORILLA AUTOMATIONS: Horilla Automations module dependency removal from other Horilla apps * [IMP] HORILLA VIEWS: Horilla Views module dependency removal from other Horilla apps * [IMP] LEAVE: Leave module dependency removal from other Horilla apps * [IMP] OFFBOARDING: Offboarding module dependency removal from other Horilla apps * [IMP] ONBOARDING: Onboarding module dependency removal from other Horilla apps * [IMP] PMS: PMS module dependency removal from other Horilla apps * [IMP] PAYROLL: Payroll module dependency removal from other Horilla apps * [IMP] RECRUITMENT: Recruitment module dependency removal from other Horilla apps * [IMP] HORILLA: Dependency removal updates * [IMP] TEMPLATES: Dependency removal updates * [IMP] STATIC: Dependency removal updates * [IMP] HORILLA DOCUMENTS: Horilla Documents module dependency removal from other Horilla apps * [ADD] HORILLA: methods.py * [UPDT] HORILLA: Settings.py * [FIX] EMPLOYEE: About tab issue * Update horilla_settings.py * Remove dummy db init password
348 lines
11 KiB
Python
348 lines
11 KiB
Python
from collections.abc import Iterable
|
|
from datetime import date, timedelta
|
|
|
|
from django.apps import apps
|
|
from django.contrib.auth.models import User
|
|
from django.db import models
|
|
from django.db.models.signals import post_save
|
|
from django.dispatch import receiver
|
|
from django.utils.translation import gettext_lazy as _
|
|
|
|
from base.horilla_company_manager import HorillaCompanyManager
|
|
from base.models import Company
|
|
from employee.models import Employee
|
|
from horilla import horilla_middlewares
|
|
from horilla.horilla_middlewares import _thread_locals
|
|
from horilla.methods import get_horilla_model_class
|
|
from horilla.models import HorillaModel
|
|
from horilla_audit.models import HorillaAuditInfo, HorillaAuditLog
|
|
from notifications.signals import notify
|
|
|
|
# Create your models here.
|
|
|
|
|
|
class Offboarding(HorillaModel):
|
|
"""
|
|
Offboarding model
|
|
"""
|
|
|
|
statuses = [("ongoing", _("Ongoing")), ("completed", _("Completed"))]
|
|
title = models.CharField(max_length=20)
|
|
description = models.TextField(max_length=255)
|
|
managers = models.ManyToManyField(Employee)
|
|
status = models.CharField(max_length=10, default="ongoing", choices=statuses)
|
|
company_id = models.ForeignKey(
|
|
Company, on_delete=models.CASCADE, null=True, editable=False
|
|
)
|
|
objects = HorillaCompanyManager()
|
|
|
|
def __str__(self):
|
|
return self.title
|
|
|
|
def save(self, *args, **kwargs):
|
|
is_new = not self.pk
|
|
super().save(*args, **kwargs)
|
|
if is_new:
|
|
stages = [
|
|
("Exit interview", "interview"),
|
|
("Work Handover", "handover"),
|
|
("FNF", "fnf"),
|
|
("Farewell", "other"),
|
|
("Archived", "archived"),
|
|
]
|
|
for stage in stages:
|
|
new = OffboardingStage()
|
|
new.offboarding_id = self
|
|
new.title = stage[0]
|
|
new.type = stage[1]
|
|
new.save()
|
|
|
|
return
|
|
|
|
|
|
class OffboardingStage(HorillaModel):
|
|
"""
|
|
Offboarding model
|
|
"""
|
|
|
|
types = [
|
|
("notice_period", _("Notice period")),
|
|
("fnf", _("FnF Settlement")),
|
|
("other", _("Other")),
|
|
("interview", _("Interview")),
|
|
("handover", _("Work handover")),
|
|
("archived", _("Archived")),
|
|
]
|
|
|
|
title = models.CharField(max_length=20)
|
|
type = models.CharField(max_length=13, choices=types)
|
|
offboarding_id = models.ForeignKey(Offboarding, on_delete=models.CASCADE)
|
|
managers = models.ManyToManyField(Employee)
|
|
sequence = models.IntegerField(default=0, editable=False)
|
|
|
|
def __str__(self) -> str:
|
|
return str(self.title)
|
|
|
|
def is_archived_stage(self):
|
|
"""
|
|
This method is to check the stage is archived or not
|
|
"""
|
|
return self.type == "archived"
|
|
|
|
|
|
@receiver(post_save, sender=Offboarding)
|
|
def create_initial_stage(sender, instance, created, **kwargs):
|
|
"""
|
|
This is post save method, used to create initial stage for the recruitment
|
|
"""
|
|
if created:
|
|
initial_stage = OffboardingStage()
|
|
initial_stage.title = "Notice Period"
|
|
initial_stage.offboarding_id = instance
|
|
initial_stage.type = "notice_period"
|
|
initial_stage.save()
|
|
|
|
|
|
class OffboardingStageMultipleFile(HorillaModel):
|
|
"""
|
|
OffboardingStageMultipleFile
|
|
"""
|
|
|
|
attachment = models.FileField(upload_to="offboarding/attachments")
|
|
|
|
|
|
class OffboardingEmployee(HorillaModel):
|
|
"""
|
|
OffboardingEmployee model / Employee on stage
|
|
"""
|
|
|
|
units = [("day", "days"), ("month", "Month")]
|
|
employee_id = models.OneToOneField(
|
|
Employee, on_delete=models.CASCADE, verbose_name="Employee"
|
|
)
|
|
stage_id = models.ForeignKey(
|
|
OffboardingStage, on_delete=models.CASCADE, verbose_name="Stage", null=True
|
|
)
|
|
notice_period = models.IntegerField(null=True)
|
|
unit = models.CharField(max_length=10, choices=units, default="month", null=True)
|
|
notice_period_starts = models.DateField(null=True)
|
|
notice_period_ends = models.DateField(null=True, blank=True)
|
|
objects = HorillaCompanyManager(
|
|
related_company_field="employee_id__employee_work_info__company_id"
|
|
)
|
|
|
|
def __str__(self) -> str:
|
|
return self.employee_id.get_full_name()
|
|
|
|
|
|
class ResignationLetter(HorillaModel):
|
|
"""
|
|
Resignation Request Employee model
|
|
"""
|
|
|
|
statuses = [
|
|
("requested", _("Requested")),
|
|
("approved", _("Approved")),
|
|
("rejected", _("Rejected")),
|
|
]
|
|
employee_id = models.ForeignKey(
|
|
Employee, on_delete=models.CASCADE, verbose_name="Employee"
|
|
)
|
|
title = models.CharField(max_length=100, null=True)
|
|
description = models.TextField(null=True, max_length=255)
|
|
planned_to_leave_on = models.DateField()
|
|
status = models.CharField(max_length=10, choices=statuses, default="requested")
|
|
offboarding_employee_id = models.ForeignKey(
|
|
OffboardingEmployee, on_delete=models.CASCADE, editable=False, null=True
|
|
)
|
|
objects = HorillaCompanyManager(
|
|
related_company_field="employee_id__employee_work_info__company_id"
|
|
)
|
|
|
|
def save(self, *args, **kwargs):
|
|
super().save(*args, **kwargs)
|
|
if self.status == "approved":
|
|
pass
|
|
|
|
return
|
|
|
|
def to_offboarding_employee(
|
|
self, offboarding, notice_period_starts, notice_period_ends
|
|
):
|
|
"""
|
|
This method is used to convert/add employee to offboarding
|
|
"""
|
|
offboarding_employee = OffboardingEmployee.objects.filter(
|
|
employee_id=self.employee_id
|
|
).first()
|
|
offboarding_employee = (
|
|
offboarding_employee if offboarding_employee else OffboardingEmployee()
|
|
)
|
|
offboarding_employee.employee_id = self.employee_id
|
|
offboarding_employee.stage_id = (
|
|
OffboardingStage.objects.order_by("created_at")
|
|
.filter(offboarding_id=offboarding)
|
|
.first()
|
|
)
|
|
contract_notice_end_date = (
|
|
get_horilla_model_class(app_label="payroll", model="contract")
|
|
.objects.filter(employee_id=self.employee_id, contract_status="active")
|
|
.first()
|
|
if apps.is_installed("payroll")
|
|
else None
|
|
)
|
|
|
|
try:
|
|
notice_period_ends = (
|
|
notice_period_starts
|
|
+ timedelta(contract_notice_end_date.notice_period_in_days)
|
|
if contract_notice_end_date
|
|
else notice_period_ends
|
|
)
|
|
except:
|
|
notice_period_ends = notice_period_ends
|
|
|
|
offboarding_employee.notice_period_starts = notice_period_starts
|
|
offboarding_employee.notice_period_ends = notice_period_ends
|
|
if (
|
|
notice_period_starts
|
|
and notice_period_ends
|
|
and not isinstance(notice_period_starts, str)
|
|
and not isinstance(notice_period_ends, str)
|
|
):
|
|
diffs = date(
|
|
day=notice_period_ends.day,
|
|
month=notice_period_ends.month,
|
|
year=notice_period_ends.year,
|
|
) - date(
|
|
day=notice_period_starts.day,
|
|
month=notice_period_starts.month,
|
|
year=notice_period_starts.year,
|
|
)
|
|
diffs = diffs.days
|
|
offboarding_employee.notice_period = diffs if diffs > 0 else None
|
|
offboarding_employee.unit = "day" if diffs > 0 else None
|
|
offboarding_employee.save()
|
|
|
|
|
|
class OffboardingTask(HorillaModel):
|
|
"""
|
|
OffboardingTask model
|
|
"""
|
|
|
|
title = models.CharField(max_length=30)
|
|
managers = models.ManyToManyField(Employee)
|
|
stage_id = models.ForeignKey(
|
|
OffboardingStage,
|
|
on_delete=models.PROTECT,
|
|
verbose_name="Stage",
|
|
null=True,
|
|
blank=True,
|
|
)
|
|
|
|
class Meta:
|
|
unique_together = ["title", "stage_id"]
|
|
|
|
def __str__(self) -> str:
|
|
return self.title
|
|
|
|
|
|
class EmployeeTask(HorillaModel):
|
|
"""
|
|
EmployeeTask model
|
|
"""
|
|
|
|
statuses = [
|
|
("todo", _("Todo")),
|
|
("in_progress", _("In progress")),
|
|
("stuck", _("Stuck")),
|
|
("completed", _("Completed")),
|
|
]
|
|
employee_id = models.ForeignKey(
|
|
OffboardingEmployee,
|
|
on_delete=models.CASCADE,
|
|
verbose_name="Employee",
|
|
null=True,
|
|
)
|
|
status = models.CharField(max_length=20, choices=statuses, default="todo")
|
|
task_id = models.ForeignKey(OffboardingTask, on_delete=models.CASCADE)
|
|
description = models.TextField(null=True, editable=False, max_length=255)
|
|
history = HorillaAuditLog(
|
|
related_name="history_set",
|
|
bases=[
|
|
HorillaAuditInfo,
|
|
],
|
|
)
|
|
|
|
class Meta:
|
|
unique_together = ["employee_id", "task_id"]
|
|
|
|
def save(self, *args, **kwargs):
|
|
super().save(*args, **kwargs)
|
|
request = getattr(_thread_locals, "request", None)
|
|
notify.send(
|
|
request.user.employee_get,
|
|
recipient=self.employee_id.employee_id.employee_user_id,
|
|
verb=f'Offboarding task "{self.task_id.title}" has been assigned',
|
|
verb_ar=f"",
|
|
verb_de=f"",
|
|
verb_es=f"",
|
|
verb_fr=f"",
|
|
redirect="offboarding/offboarding-pipeline",
|
|
icon="information",
|
|
)
|
|
|
|
|
|
class ExitReason(HorillaModel):
|
|
"""
|
|
ExitReason model
|
|
"""
|
|
|
|
title = models.CharField(max_length=50)
|
|
description = models.TextField(max_length=255)
|
|
offboarding_employee_id = models.ForeignKey(
|
|
OffboardingEmployee, on_delete=models.PROTECT
|
|
)
|
|
attachments = models.ManyToManyField(OffboardingStageMultipleFile)
|
|
|
|
|
|
class OffboardingNote(HorillaModel):
|
|
"""
|
|
OffboardingNote
|
|
"""
|
|
|
|
attachments = models.ManyToManyField(
|
|
OffboardingStageMultipleFile, blank=True, editable=False
|
|
)
|
|
description = models.TextField(null=True, blank=True, max_length=255)
|
|
note_by = models.ForeignKey(
|
|
Employee, on_delete=models.SET_NULL, null=True, editable=False
|
|
)
|
|
employee_id = models.ForeignKey(
|
|
OffboardingEmployee, on_delete=models.PROTECT, null=True, editable=False
|
|
)
|
|
stage_id = models.ForeignKey(
|
|
OffboardingStage, on_delete=models.PROTECT, null=True, editable=False
|
|
)
|
|
|
|
class Meta:
|
|
ordering = ["-created_at"]
|
|
|
|
def save(self, *args, **kwargs):
|
|
request = getattr(horilla_middlewares._thread_locals, "request", None)
|
|
if request:
|
|
updated_by = request.user.employee_get
|
|
self.note_by = updated_by
|
|
if self.employee_id:
|
|
self.stage_id = self.employee_id.stage_id
|
|
return super().save(*args, **kwargs)
|
|
|
|
|
|
class OffboardingGeneralSetting(HorillaModel):
|
|
"""
|
|
OffboardingGeneralSettings
|
|
"""
|
|
|
|
resignation_request = models.BooleanField(default=False)
|
|
company_id = models.ForeignKey(Company, on_delete=models.CASCADE, null=True)
|