Files
ihrm/horilla_theme/overrides.py

641 lines
19 KiB
Python

from django.apps import apps
from django.urls import reverse
from django.utils.html import format_html, format_html_join
from django.utils.translation import gettext_lazy as _
from horilla_views.cbv_methods import render_template
class DummyModel:
"""A dummy fallback class that behaves like a model placeholder."""
def __init__(self, *args, **kwargs):
pass
def __getattr__(self, name):
# if you try to access any attribute, just return another dummy
return DummyModel()
def __call__(self, *args, **kwargs):
# acts callable (like a model manager/queryset)
return DummyModel()
def get_horilla_model_class(app_label, model):
try:
return apps.get_model(app_label, model)
except LookupError:
return DummyModel
Recruitment = get_horilla_model_class(app_label="recruitment", model="recruitment")
AttendanceLateComeEarlyOut = get_horilla_model_class(
app_label="attendance", model="attendancelatecomeearlyOut"
)
Attendance = get_horilla_model_class(app_label="attendance", model="attendance")
LeaveRequest = get_horilla_model_class(app_label="leave", model="leaverequest")
Meetings = get_horilla_model_class(app_label="pms", model="Meetings")
ResignationLetter = get_horilla_model_class(
app_label="offboarding", model="ResignationLetter"
)
AssetAssignment = get_horilla_model_class(app_label="asset", model="AssetAssignment")
TimeSheet = get_horilla_model_class(app_label="project", model="TimeSheet")
Deduction = get_horilla_model_class(app_label="payroll", model="Deduction")
Task = get_horilla_model_class(app_label="project", model="Task")
def tot_hires(self):
"""
This method for get custom column for Total hires.
"""
col = f"""
{self.total_hires()} Hired of {self.candidate.all().count()} Candidates
"""
return col
def managers_detail(self):
"""
manager in detail view
"""
employees = self.recruitment_managers.all()
employee_names_string = ""
if employees:
employee_names_string = ",<br />".join(
[str(employee) for employee in employees]
)
return employee_names_string
def open_job_detail(self):
"""
open jobs in detail view
"""
jobs = self.open_positions.all()
jobs_names_string = ""
if jobs:
jobs_names_string = ",<br />".join([str(job) for job in jobs])
return jobs_names_string
def penalities_column(self):
"""
Returns an HTML snippet showing penalty status with Tailwind styling.
"""
penalties_count = self.get_penalties_count()
if penalties_count:
url = reverse("view-penalties") + f"?late_early_id={self.id}"
return format_html(
'<div class="bg-red-100/10 border-2 border-red-300 rounded-xl px-4 py-2 w-32 text-xs text-center text-red-700 font-semibold" '
'data-target="#penaltyViewModal" data-toggle="oh-modal-toggle" '
'onclick="event.stopPropagation();"'
'hx-get="{}" hx-target="#penaltyViewModalBody" align="center">'
"Penalties :{}</div>",
url,
penalties_count,
)
else:
return format_html(
'<div class="bg-green-100/10 border-2 border-green-300 rounded-xl px-4 py-2 w-32 text-xs text-center text-green-700 font-semibold">{}</div>',
"No Penalties",
)
def rejected_action(self):
if self.reject_reason:
if self.status == "cancelled":
label = _("Reason for Cancellation")
else:
label = _("Reason for Rejection")
return format_html(
"""
<div class="w-full p-4 rounded-lg bg-orange-100/20 border border-orange-300 rounded-md">
<div>
<span class="block text-xs font-medium text-gray-700 mb-1">{}</span>
<div class="text-sm text-gray-800 italic">{}</div>
</div>
</div>
""",
label,
self.reject_reason,
)
return ""
def attachment_action(self):
if self.attachment:
return format_html(
"""
<a href="{}" target="_blank" class="w-50 bg-gray-100 p-4 flex items-center text-gray-700 text-sm font-medium">
<ion-icon name="download-outline" class="me-1 text-lg"></ion-icon>
<span class="ml-1">{}</span>
</a>
""",
self.attachment.url,
_("View attachment"),
)
return ""
def attendance_detail_activity_col(self):
activity_count = self.activities().get("count", 0)
if activity_count == 0:
return ""
label = _("Activities:")
view_label = _("View Activities")
count_label = _("Activity") if activity_count == 1 else _("Activities")
url = reverse("get-attendance-activities", args=[self.id])
col = format_html(
"""
<div class="mb-2 flex gap-5 items-center">
<span class="font-medium text-xs text-[#565E6C] w-32">
{}
</span>
<p class="text-xs font-semibold flex items-center gap-5">
: <span>
<button
data-target="#activityViewModal"
data-toggle="oh-modal-toggle"
hx-get="{}"
hx-target="#activityViewModalBody"
title="{}"
class="flex items-center text-primary-600 text-sm font-semibold transition-colors"
>
<span>{} {}</span>
<ion-icon name="eye-outline" class="text-lg ml-2 mt-[2px]"></ion-icon>
</button>
</span>
</p>
</div>
""",
label,
url,
view_label,
activity_count,
count_label,
)
return col
def leave_clash_col(self):
count = self.leave_clashes_count
label = _("View Clashes")
url = reverse("view-clashes", args=[self.id])
col = format_html(
"""
<div onclick="event.stopPropagation();">
<div class="flex "
data-target="#clashModal"
data-toggle="oh-modal-toggle"
hx-get="{}"
hx-target="#clashModalBody"
title="{}">
<i class="material-icons text-4xl" >groups</i>
<span class="w-5 h-5 bg-[#e54f38] rounded-full text-white text-xs font-semibold flex items-center justify-center">{}</span>
</div>
</div>
""",
url,
label,
count,
)
return col
def mom_detail_col(self):
return render_template(
path="cbv/meetings/mom_detail_col.html",
context={"instance": self},
)
def detail_description_col(self):
return render_template(
path="cbv/exit_process/detail_page_description.html",
context={"instance": self},
)
def assign_condition_img(self):
images = self.assign_images.all()
if not images:
return ""
label = _("Assign Condition Images")
links_html = format_html_join(
"",
"""
<a href="{}" rel="noopener noreferrer" target="_blank">
<span
class="oh-file-icon oh-file-icon--pdf"
onmouseover="enlargeattachment('{}')"
style="width:40px;height:40px"
></span>
</a>
""",
((doc.image.url, doc.image.url) for doc in images),
)
col = format_html(
"""
<div class="mb-2">
<span class="font-medium text-xs text-[#565E6C] w-32">
{}
</span>
<div class="d-flex mt-2 mb-2 gap-2">
{}
</div>
</div>
""",
label,
links_html,
)
return col
def return_condition_img(self):
images = self.return_images.all()
if not images:
return ""
label = _("Return Condition Images")
links_html = format_html_join(
"",
"""
<a href="{}" rel="noopener noreferrer" target="_blank">
<span
class="oh-file-icon oh-file-icon--pdf"
onmouseover="enlargeattachment('{}')"
style="width:40px;height:40px"
></span>
</a>
""",
((doc.image.url, doc.image.url) for doc in images),
)
col = format_html(
"""
<div class="mb-2 ">
<span class="font-medium text-xs text-[#565E6C] w-32">
{}
</span>
<div class="d-flex mt-2 mb-2 gap-2">
{}
</div>
</div>
""",
label,
links_html,
)
return col
def detail_view_subtitle(self):
"""
for subtitle in detail view
"""
col = format_html(
"""
<div class="grid grid-cols-1 sm:grid-cols-2 gap-2 rounded-md bg-white text-sm text-gray-700">
<div>
<span class="text-gray-500 mb-2">Date</span><br />
<span class="font-semibold dateformat_changer">{}</span>
</div>
<div>
<span class="text-gray-500 mb-2">Time Spent</span><br />
<span class="font-semibold">{}</span>
</div>
<div>
<span class="text-gray-500 mb-2">Project</span><br />
<span class="font-semibold">{}</span>
</div>
<div>
<span class="text-gray-500 mb-2">Task</span><br />
<span class="font-semibold">{}</span>
</div>
</div>
""",
self.date,
self.time_spent,
self.project_id,
self.task_id,
)
return col
def detail_view_title(self):
col = format_html(
"""
<div class="flex items-center gap-5 mb-5">
<div>
<img src="{}" alt="" class="w-[50px] h-[50px] rounded-full">
</div>
<div>
<p class="mb-1 text-sm font-semibold">
{}
</p>
</div>
</div>
""",
self.employee_id.get_avatar(),
self.employee_id.get_full_name(),
)
return col
def tax_col(self):
if self.is_tax:
title = _("Tax")
value = "Yes" if self.is_tax else "No"
else:
title = _("Pretax")
value = "Yes" if self.is_pretax else "No"
col = format_html(
"""
<div class="col-span-1 md:col-span-6 mb-2 flex gap-5 items-center">
<span class="font-medium text-xs text-[#565E6C] w-32">
{}
</span>
<div class="text-xs font-semibold flex items-center gap-5">
: <span>
{}
</span>
</div>
</div>
""",
title,
value,
)
return col
def document_col(self):
"""
Task detail view document column
"""
if self.document:
document_url = self.document.url
title = _("Document")
col = format_html(
"""
<div class="col-span-1 md:col-span-6 mb-2 flex gap-5 items-center">
<span class="font-medium text-xs text-[#565E6C] w-32">
{1}
</span>
<div class="text-xs font-semibold flex items-center gap-5">
: <span onmouseover="enlargeattachment('{0}')">
<a href="{0}" style="text-decoration: none" rel="noopener noreferrer" class="oh-btn oh-btn--light" target="_blank" onclick="event.stopPropagation();">
<span class="oh-file-icon oh-file-icon--pdf"></span>
{1}
</a>
</span>
</div>
</div>
""",
document_url,
title,
)
return col
return ""
Recruitment.managers_detail = managers_detail
Recruitment.open_job_detail = open_job_detail
Recruitment.tot_hires = tot_hires
AttendanceLateComeEarlyOut.penalities_column = penalities_column
Attendance.attendance_detail_activity_col = attendance_detail_activity_col
LeaveRequest.rejected_action = rejected_action
LeaveRequest.attachment_action = attachment_action
LeaveRequest.penality_col = penalities_column
LeaveRequest.leave_clash_col = leave_clash_col
Meetings.mom_detail_col = mom_detail_col
ResignationLetter.detail_description_col = detail_description_col
AssetAssignment.assign_condition_img = assign_condition_img
AssetAssignment.return_condition_img = return_condition_img
TimeSheet.detail_view_subtitle = detail_view_subtitle
TimeSheet.detail_view_title = detail_view_title
Deduction.tax_col = tax_col
Task.document_col = document_col
from base.cbv.dashboard.dashboard import DashboardWorkTypeRequest, ShiftRequestToApprove
_shift_request_to_approve_init_orig = ShiftRequestToApprove.__init__
def _shift_request_to_approve_init(self, **kwargs):
_shift_request_to_approve_init_orig(self, **kwargs)
self.header_attrs = {}
_work_type_request_to_approve_init_orig = DashboardWorkTypeRequest.__init__
def _work_type_request_to_approve_init(self, **kwargs):
_work_type_request_to_approve_init_orig(self, **kwargs)
self.header_attrs = {}
ShiftRequestToApprove.__init__ = _shift_request_to_approve_init
DashboardWorkTypeRequest.__init__ = _work_type_request_to_approve_init
if apps.is_installed("pms"):
from pms.cbv.meetings import MeetingsDetailedView
_meeting_detailed_init_orig = MeetingsDetailedView.__init__
def _meeting_detailed_init(self, **kwargs):
_meeting_detailed_init_orig(self, **kwargs)
self.body = [
(_("Date"), "date"),
(_("Question Template"), "question_template"),
(_("Employees"), "employ_detail_col"),
(_("Managers"), "manager_detail_col"),
(_("Answerable employees"), "answerable_col"),
(_("Minutes of Meeting"), "mom_detail_col", True),
]
MeetingsDetailedView.__init__ = _meeting_detailed_init
if apps.is_installed("offboarding"):
from offboarding.cbv.resignation import ResignationLetterDetailView
_resignation_detailed_init_orig = ResignationLetterDetailView.__init__
def _resignation_detailed_init(self, **kwargs):
_resignation_detailed_init_orig(self, **kwargs)
self.body = [
(_("Planned To Leave"), "planned_to_leave_on"),
(_("Status"), "get_status"),
(_("Description"), "detail_description_col", True),
]
self.cols = {
"detail_description_col": 12,
}
ResignationLetterDetailView.__init__ = _resignation_detailed_init
if apps.is_installed("project"):
from project.cbv.timesheet import TimeSheetCardView, TimeSheetList
_timesheet_list_init_orig = TimeSheetList.__init__
def _timesheet_list_init(self, **kwargs):
_timesheet_list_init_orig(self, **kwargs)
self.row_attrs = """
hx-get='{detail_view}?instance_ids={ordered_ids}'
hx-target="#objectDetailsModalTarget"
data-target="#objectDetailsModal"
data-toggle="oh-modal-toggle"
"""
_timesheet_card_init_orig = TimeSheetCardView.__init__
def _timesheet_card_init(self, **kwargs):
_timesheet_card_init_orig(self, **kwargs)
self.card_attrs = """
hx-get='{detail_view}?instance_ids={ordered_ids}'
hx-target="#objectDetailsModalTarget"
data-target="#objectDetailsModal"
data-toggle="oh-modal-toggle"
"""
self.details = {
"title": "{detail_view_title}",
"subtitle": "{detail_view_subtitle}",
}
TimeSheetList.__init__ = _timesheet_list_init
TimeSheetCardView.__init__ = _timesheet_card_init
if apps.is_installed("attendance"):
from attendance.cbv.dashboard import (
DashboardaAttendanceOT,
DashboardAttendanceToValidate,
)
from biometric.cbv.biometric import BiometricCardView
_overtime_attendance_init_orig = DashboardaAttendanceOT.__init__
def _overtime_attendance_init(self, **kwargs):
_overtime_attendance_init_orig(self, **kwargs)
self.header_attrs = {}
_validate_attendance_init_orig = DashboardAttendanceToValidate.__init__
def _validate_attendance_init(self, **kwargs):
_validate_attendance_init_orig(self, **kwargs)
self.header_attrs = {}
_biometric_device_card_init_orig = BiometricCardView.__init__
def _biometric_card_init(self, **kwargs):
_biometric_device_card_init_orig(self, **kwargs)
self.custom_body_template = "cbv/biometric_card_body.html"
DashboardaAttendanceOT.__init__ = _overtime_attendance_init
DashboardAttendanceToValidate.__init__ = _validate_attendance_init
BiometricCardView.__init__ = _biometric_card_init
if apps.is_installed("leave"):
from leave.cbv.dashboard import LeaveRequestsToApprove
_leave_request_to_approve_init_orig = LeaveRequestsToApprove.__init__
def _leave_request_to_approve_init(self, **kwargs):
_leave_request_to_approve_init_orig(self, **kwargs)
self.header_attrs = {}
LeaveRequestsToApprove.__init__ = _leave_request_to_approve_init
if apps.is_installed("payroll"):
from payroll.cbv.allowance_deduction import AllowanceDetailView, DeductionDetailView
_allowance_detail_init_orig = AllowanceDetailView.__init__
_deduction_detail_init_orig = DeductionDetailView.__init__
def _allowance_detail_init(self, **kwargs):
_allowance_detail_init_orig(self, **kwargs)
self.body = [
(_("Taxable"), "get_is_taxable_display"),
(_("One Time Allowance"), "one_time_date_display"),
(_("Condition Based"), "condition_based_display"),
(_("Amount"), "based_on_amount"),
(_("Has Maximum Limit"), "cust_allowance_max_limit"),
(_("Allowance Eligibility"), "allowance_eligibility"),
(_("Specific Employees"), "get_specific_exclude_employees", True),
]
self.cols = {
"get_specific_exclude_employees": 12,
}
def _deduction_detail_init(self, **kwargs):
_deduction_detail_init_orig(self, **kwargs)
self.body = [
(_("Tax"), "tax_col", True),
(_("One Time deduction"), "get_one_time_deduction"),
(_("Condition Based"), "condition_based_col"),
(_("Amount"), "amount_col"),
(_("Has Maximum Limit"), "has_maximum_limit_col"),
(_("Deduction Eligibility"), "deduction_eligibility"),
(_("Specific Employees"), "get_specific_exclude_employees", True),
]
self.cols = {
"get_specific_exclude_employees": 12,
}
AllowanceDetailView.__init__ = _allowance_detail_init
DeductionDetailView.__init__ = _deduction_detail_init