diff --git a/asset/sidebar.py b/asset/sidebar.py new file mode 100644 index 000000000..847420d05 --- /dev/null +++ b/asset/sidebar.py @@ -0,0 +1,35 @@ +""" +assets/sidebar.py +""" + +from django.urls import reverse +from django.utils.translation import gettext_lazy as trans + + +MENU = trans("Assets") +IMG_SRC = "images/ui/assets.svg" + +SUBMENUS = [ + { + "menu": trans("Dashboard"), + "redirect": reverse("asset-dashboard"), + "accessability": "asset.sidebar.dashboard_accessability", + }, + { + "menu": trans("Asset View"), + "redirect": reverse("asset-category-view"), + "accessability": "asset.sidebar.dashboard_accessability", + }, + { + "menu": trans("Request and Allocation"), + "redirect": reverse("asset-request-allocation-view"), + }, + { + "menu": trans("Asset History"), + "redirect": reverse("asset-history"), + }, +] + + +def dashboard_accessability(request, submenu, user_perms, *args, **kwargs): + return request.user.has_perm("asset.view_assetcategory") diff --git a/attendance/sidebar.py b/attendance/sidebar.py new file mode 100644 index 000000000..c53430650 --- /dev/null +++ b/attendance/sidebar.py @@ -0,0 +1,73 @@ +""" +attendance/sidebar.py +""" + +from datetime import datetime +from django.urls import reverse +from base.context_processors import biometric_app_exists +from base.templatetags.basefilters import is_reportingmanager +from django.utils.translation import gettext_lazy as trans + + + +MENU = trans("Attendance") +IMG_SRC = "images/ui/attendances.svg" + + +SUBMENUS = [ + { + "menu": trans("Dashboard"), + "redirect": reverse("attendance-dashboard"), + }, + { + "menu": trans("Attendance View"), + "redirect": reverse("attendance-view"), + "accessibility": "attendance.sidebar.attendances_accessibility", + }, + { + "menu": trans("Attendance View"), + "redirect": reverse("attendance-view"), + "accessibility": "attendance.sidebar.attendances_accessibility", + }, + { + "menu": trans("Attendance Requests"), + "redirect": reverse("request-attendance-view"), + }, + { + "menu": trans("Hour Account"), + "redirect": reverse("attendance-overtime-view"), + "accessibility": "attendance.sidebar.hour_account_accessibility", + }, + { + "menu": trans("Work Records"), + "redirect": reverse("work-records"), + "accessibility": "attendance.sidebar.work_record_accessibility", + }, + { + "menu": trans("Attendance Activities"), + "redirect": reverse("attendance-activity-view"), + }, + { + "menu": trans("Late Come Early Out"), + "redirect": reverse("late-come-early-out-view"), + }, + { + "menu": trans("My Attendances"), + "redirect": reverse("view-my-attendance"), + }, +] + + +def attendances_accessibility(request, submenu, user_perms, *args, **kwargs): + return request.user.has_perm("attendance.view_attendance") or is_reportingmanager( + request.user + ) + + +def hour_account_accessibility(request, submenu, user_perms, *args, **kwargs): + submenu["redirect"] = submenu["redirect"] + f"?year={datetime.now().year}" + return True + + +def work_record_accessibility(request, submenu, user_perms, *args, **kwargs): + return request.user.has_perm("view_attendance") or is_reportingmanager(request.user) diff --git a/helpdesk/sidebar.py b/helpdesk/sidebar.py new file mode 100644 index 000000000..7e6985b12 --- /dev/null +++ b/helpdesk/sidebar.py @@ -0,0 +1,21 @@ +""" +helpdesk/sidebar.py +""" + +from django.urls import reverse +from django.utils.translation import gettext_lazy as trans + + +MENU = trans("Help Desk") +IMG_SRC = "images/ui/headset-solid.svg" + +SUBMENUS = [ + { + "menu": trans("FAQa"), + "redirect": reverse("faq-category-view"), + }, + { + "menu": trans("Tickets"), + "redirect": reverse("ticket-view"), + }, +] diff --git a/horilla/config.py b/horilla/config.py new file mode 100644 index 000000000..30959be0c --- /dev/null +++ b/horilla/config.py @@ -0,0 +1,90 @@ +""" +horilla/config.py + +Horilla app configurations +""" + +import os, importlib, logging +from django.conf import settings +from django.contrib.auth.context_processors import PermWrapper +from horilla.horilla_apps import SIDEBARS + +logger = logging.getLogger(__name__) + + +def get_apps_in_base_dir(): + base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + base_dir_apps = [] + + for app_name in settings.INSTALLED_APPS: + app_module = __import__(app_name) + app_path = os.path.dirname(os.path.abspath(app_module.__file__)) + if app_path.startswith(base_dir): + base_dir_apps.append(app_name) + + return SIDEBARS + + +def import_method(accessibility): + module_path, method_name = accessibility.rsplit(".", 1) + module = __import__(module_path, fromlist=[method_name]) + accessibility_method = getattr(module, method_name) + return accessibility_method + + +ALL_MENUS = {} + + +def sidebar(request): + + base_dir_apps = get_apps_in_base_dir() + + if not request.user.is_anonymous: + request.MENUS = [] + MENUS = request.MENUS + + for app in base_dir_apps: + + try: + sidebar = importlib.import_module(app + ".sidebar") + + except Exception as e: + logger.error(e) + continue + + if sidebar: + accessibility = None + if getattr(sidebar, "ACCESSIBILITY", None): + accessibility = import_method(sidebar.ACCESSIBILITY) + + if not accessibility or accessibility( + request, + sidebar.MENU, + PermWrapper(request.user), + ): + MENU = {} + MENU["menu"] = sidebar.MENU + MENU["app"] = app + MENU["img_src"] = sidebar.IMG_SRC + MENU["submenu"] = [] + MENUS.append(MENU) + for submenu in sidebar.SUBMENUS: + + accessibility = None + + if submenu.get("accessibility"): + accessibility = import_method(submenu["accessibility"]) + + if not accessibility or accessibility( + request, + submenu, + PermWrapper(request.user), + ): + MENU["submenu"].append(submenu) + ALL_MENUS[request.session.session_key] = MENUS + + +def get_MENUS(request): + ALL_MENUS[request.session.session_key] = [] + sidebar(request) + return {"sidebar": ALL_MENUS.get(request.session.session_key)} diff --git a/horilla/horilla_apps.py b/horilla/horilla_apps.py index 2a7fe7501..aa8d48e82 100644 --- a/horilla/horilla_apps.py +++ b/horilla/horilla_apps.py @@ -20,3 +20,17 @@ SETTINGS_EMAIL_BACKEND = getattr(settings, "EMAIL_BACKEND", False) setattr(settings, "EMAIL_BACKEND", "base.backends.ConfiguredEmailBackend") if SETTINGS_EMAIL_BACKEND: setattr(settings, "EMAIL_BACKEND", SETTINGS_EMAIL_BACKEND) + + +SIDEBARS = [ + "recruitment", + "onboarding", + "employee", + "attendance", + "leave", + "payroll", + "pms", + "offboarding", + "asset", + "helpdesk", +] diff --git a/horilla/horilla_context_processors.py b/horilla/horilla_context_processors.py index 649048e46..af7ab5779 100644 --- a/horilla/horilla_context_processors.py +++ b/horilla/horilla_context_processors.py @@ -6,6 +6,9 @@ This module is used to register context processors without effecting the horilla from horilla.settings import TEMPLATES +TEMPLATES[0]["OPTIONS"]["context_processors"].append( + "horilla.config.get_MENUS", +) TEMPLATES[0]["OPTIONS"]["context_processors"].append( "base.context_processors.get_companies", ) diff --git a/leave/sidebar.py b/leave/sidebar.py new file mode 100644 index 000000000..2e4daecba --- /dev/null +++ b/leave/sidebar.py @@ -0,0 +1,95 @@ +""" +leave/sidebar.py +""" + +from django.urls import reverse +from base.templatetags.basefilters import is_leave_approval_manager, is_reportingmanager +from leave.templatetags.leavefilters import is_compensatory +from django.utils.translation import gettext_lazy as trans + + +MENU = trans("Leave") +IMG_SRC = "images/ui/leave.svg" + +SUBMENUS = [ + { + "menu": trans("Dashboard"), + "redirect": reverse("leave-dashboard"), + "accessibility": "leave.sidebar.dashboard_accessibility", + }, + { + "menu": trans("My Leave Requests"), + "redirect": reverse("user-request-view"), + }, + { + "menu": trans("Leave Requests"), + "redirect": reverse("request-view"), + "accessibility": "leave.sidebar.leave_request_accessibility", + }, + { + "menu": trans("Leave Types"), + "redirect": reverse("type-view"), + "accessibility": "leave.sidebar.type_accessibility", + }, + { + "menu": trans("Assigned Leave"), + "redirect": reverse("assign-view"), + "accessibility": "leave.sidebar.assign_accessibility", + }, + { + "menu": trans("Leave Allocation Request"), + "redirect": reverse("leave-allocation-request-view"), + }, + { + "menu": trans("Compensatory Leave Requests"), + "redirect": reverse("view-compensatory-leave"), + "accessibility": "leave.sidebar.componstory_accessibility", + }, + { + "menu": trans("Holidays"), + "redirect": reverse("holiday-view"), + "accessibility": "leave.sidebar.holiday_accessibility", + }, + { + "menu": trans("Company Leaves"), + "redirect": reverse("holiday-view"), + "accessibility": "leave.sidebar.company_leave_accessibility", + }, +] + + +def dashboard_accessibility(request, submenu, user_perms, *args, **kwargs): + have_perm = request.user.has_perm("leave.view_leaverequest") + if not have_perm: + submenu["redirect"] = reverse("leave-employee-dashboard") + "?dashboard=true" + return True + + +def leave_request_accessibility(request, submenu, user_perms, *args, **kwargs): + return ( + request.user.has_perm("leave.view_leavereqeust") + or is_leave_approval_manager(request.user) + or is_reportingmanager(request.user) + ) + + +def type_accessibility(request, submenu, user_perms, *args, **kwargs): + return request.user.has_perm("leave.view_leawvetype") + + +def assign_accessibility(request, submenu, user_perm, *args, **kwargs): + return request.user.has_perm("leave.view_assignedleave") or is_reportingmanager( + request.user + ) + + +def holiday_accessibility(request, submenu, user_perms, *args, **kwargs): + return not request.user.has_perm("leave.add_holiday") + + +def company_leave_accessibility(request, submenu, user_perms, *args, **kwargs): + return not request.user.has_perm("leave.add_companyleave") + + +def componstory_accessibility(request, submenu, user_perms, *args, **kwargs): + return is_compensatory(request.user) diff --git a/offboarding/sidebar.py b/offboarding/sidebar.py new file mode 100644 index 000000000..fedf61389 --- /dev/null +++ b/offboarding/sidebar.py @@ -0,0 +1,43 @@ +""" +offboarding/sidebar.py +""" + +from django.urls import reverse +from django.utils.translation import gettext_lazy as trans +from base.context_processors import resignation_request_enabled +from offboarding.templatetags.offboarding_filter import ( + any_manager, + is_offboarding_employee, +) + + +MENU = trans("Offboarding") +IMG_SRC = "images/ui/exit-outline.svg" +ACCESSIBILITY = "offboarding.sidebar.offboarding_accessibility" + + +SUBMENUS = [ + { + "menu": trans("Exit Process"), + "redirect": reverse("offboarding-pipeline"), + }, + { + "menu": trans("Resignation Letters"), + "redirect": reverse("resignation-request-view"), + "accessability": "resignation_letter_accessibility", + }, +] + + +def offboarding_accessibility(request, menu, user_perms, *args, **kwargs): + return ( + request.user.has_perm("offboarding.view_offboarding") + or any_manager(request.user.employee_get) + or is_offboarding_employee(request.user.employee_get) + ) + + +def resignation_letter_accessibility(request, menu, user_perms, *args, **kwargs): + return resignation_request_enabled(request)[ + "enabled_resignation_request" + ] and request.user.has_perm("offboarding.view_resignationletter") diff --git a/onboarding/sidebar.py b/onboarding/sidebar.py new file mode 100644 index 000000000..113252bfc --- /dev/null +++ b/onboarding/sidebar.py @@ -0,0 +1,51 @@ +""" +onboarding/sidebar.py + +To set Horilla sidebar for onboarding +""" + +from django.urls import reverse +from django.contrib.auth.context_processors import PermWrapper +from onboarding.templatetags.onboardingfilters import is_taskmanager + + +MENU = "Onboarding" +ACCESSIBILITY = "onboarding.sidebar.menu_accessibilty" +IMG_SRC = "images/ui/rocket.svg" + +SUBMENUS = [ + { + "menu": "Onboarding view", + "redirect": reverse("onboarding-view") + "?closed=false", + "accessibility": "onboarding.sidebar.onboarding_view_accessibility", + }, + { + "menu": "Candidates view", + "redirect": reverse("candidates-view"), + "accessibility": "onboarding.sidebar.candidates_view_accessibility", + }, +] + + +def menu_accessibilty( + request, _menu: str = "", user_perms: PermWrapper = [], *args, **kwargs +) -> bool: + return ( + is_taskmanager(request.user) + or "recruitment" in user_perms + or "onboarding" in user_perms + ) + + +def onboarding_view_accessibility( + request, _submenu: dict = {}, user_perms: PermWrapper = [], *args, **kwargs +) -> bool: + return is_taskmanager(request.user) or request.user.has_perm( + "onboarding.view_onboarding" + ) + + +def candidates_view_accessibility( + request, _submenu: dict = {}, user_perms: PermWrapper = [], * args, **kwargs +) -> bool: + return request.user.has_perm("recruitment.view_candidate") diff --git a/payroll/sidebar.py b/payroll/sidebar.py new file mode 100644 index 000000000..d70d93ee5 --- /dev/null +++ b/payroll/sidebar.py @@ -0,0 +1,72 @@ +""" +payroll/sidebar.py + +""" + +from django.urls import reverse +from django.utils.translation import gettext_lazy as trans + + +MENU = trans("Payroll") +IMG_SRC = "images/ui/wallet-outline.svg" + +SUBMENUS = [ + { + "menu": trans("Dashboard"), + "redirect": reverse("view-payroll-dashboard"), + "accessibility": "payroll.sidebar.dasbhoard_accessibility", + }, + { + "menu": trans("Contract"), + "redirect": reverse("view-contract"), + "accessibility": "payroll.sidebar.dasbhoard_accessibility", + }, + { + "menu": trans("Allowances"), + "redirect": reverse("view-allowance"), + "accessibility": "payroll.sidebar.allowance_accessibility", + }, + { + "menu": trans("Deductions"), + "redirect": reverse("view-deduction"), + "accessibility": "payroll.sidebar.deduction_accessibility", + }, + { + "menu": trans("Payslips"), + "redirect": reverse("view-payslip"), + }, + { + "menu": trans("Loan / Advanced Salary"), + "redirect": reverse("view-loan"), + "accessibility": "payroll.sidebar.loan_accessibility", + }, + { + "menu": trans("Encashments & Reimbursements"), + "redirect": reverse("view-reimbursement"), + }, + { + "menu": trans("Federal Tax"), + "redirect": reverse("filing-status-view"), + "accessibility": "payroll.sidebar.federal_tax_accessibility", + }, +] + + +def dasbhoard_accessibility(request, submenu, user_perms, *args, **kwargs): + return request.user.has_perm("payroll.view_contract") + + +def allowance_accessibility(request, submenu, user_perms, *args, **kwargs): + return request.user.has_perm("payroll.view_allowance") + + +def deduction_accessibility(request, submenu, user_perms, *args, **kwargs): + return request.user.has_perm("payroll.view_deduction") + + +def loan_accessibility(request, submenu, user_perms, *args, **kwargs): + return request.user.has_perm("payroll.view_loanaccount") + + +def federal_tax_accessibility(request, submenu, user_perms, *args, **kwargs): + return request.user.has_perm("payroll.view_filingstatus") diff --git a/payroll/views/views.py b/payroll/views/views.py index b86ef519e..fa1310ef8 100644 --- a/payroll/views/views.py +++ b/payroll/views/views.py @@ -1329,6 +1329,31 @@ def contract_bulk_delete(request): return JsonResponse({"message": "Success"}) +def equalize_lists_length(allowances, deductions): + """ + Equalize the lengths of two lists by appending empty dictionaries to the shorter list. + + Args: + deductions (list): List of dictionaries representing deductions. + allowances (list): List of dictionaries representing allowances. + + Returns: + tuple: Tuple containing two lists with equal lengths. + """ + num_deductions = len(deductions) + num_allowances = len(allowances) + + while num_deductions < num_allowances: + deductions.append({"title": "", "amount": ""}) + num_deductions += 1 + + while num_allowances < num_deductions: + allowances.append({"title": "", "amount": ""}) + num_allowances += 1 + + return deductions, allowances + + def payslip_pdf(request, id): payslip = Payslip.objects.get(id=id) if ( @@ -1396,6 +1421,18 @@ def payslip_pdf(request, id): data["json_data"]["payslip"] = payslip.id data["instance"] = payslip data["currency"] = PayrollSettings.objects.first().currency_symbol + data["all_deductions"] = [] + for deduction_list in [ + data["basic_pay_deductions"], + data["gross_pay_deductions"], + data["pretax_deductions"], + data["post_tax_deductions"], + data["tax_deductions"], + data["net_deductions"], + ]: + data["all_deductions"].extend(deduction_list) + equalize_lists_length(data["allowances"], data["all_deductions"]) + data["zipped_data"] = zip(data["allowances"], data["all_deductions"]) return generate_pdf("payroll/payslip/individual_pdf.html", context=data) diff --git a/pms/sidebar.py b/pms/sidebar.py new file mode 100644 index 000000000..565bc975a --- /dev/null +++ b/pms/sidebar.py @@ -0,0 +1,54 @@ +""" +pms/sidebar.py +""" + +from django.urls import reverse +from base.templatetags.basefilters import is_reportingmanager +from django.utils.translation import gettext_lazy as trans + + +MENU = trans("Performance") +IMG_SRC = "images/ui/pms.svg" + + +SUBMENUS = [ + { + "menu": trans("Dashboard"), + "redirect": reverse("dashboard-view"), + }, + { + "menu": trans("Objectives"), + "redirect": reverse("objective-list-view"), + }, + { + "menu": trans("360 Feedback"), + "redirect": reverse("feedback-view"), + }, + { + "menu": trans("Meetings"), + "redirect": reverse("view-meetings"), + }, + { + "menu": trans("Key Results"), + "redirect": reverse("view-key-result"), + "accessibility": "pms.sidebar.key_result_accessibility", + }, + { + "menu": trans("Period"), + "redirect": reverse("period-view"), + "accessibility": "pms.sidebar.key_result_accessibility", + }, + { + "menu": trans("Question Template"), + "redirect": reverse("question-template-view"), + "accessibility": "pms.sidebar.question_template_accessibility", + }, +] + + +def key_result_accessibility(request, submenu, user_perms, *args, **kwargs): + return request.user.has_perm("pms.view_period") or is_reportingmanager(request.user) + + +def question_template_accessibility(request, submenu, user_perms, *args, **kwargs): + return request.user.has_perm("pms.view_questiontemplate") or is_reportingmanager(request.user) diff --git a/recruitment/sidebar.py b/recruitment/sidebar.py new file mode 100644 index 000000000..8dee799b6 --- /dev/null +++ b/recruitment/sidebar.py @@ -0,0 +1,112 @@ +""" +recruitment/sidebar.py + +To set Horilla sidebar for onboarding +""" + +from django.urls import reverse +from django.contrib.auth.context_processors import PermWrapper +from recruitment.models import InterviewSchedule +from recruitment.templatetags.recruitmentfilters import is_stagemanager +from django.utils.translation import gettext_lazy as trans + + +MENU = trans("Recruitment") +ACCESSIBILITY = "recruitment.sidebar.menu_accessibilty" +IMG_SRC = "images/ui/recruitment.svg" + +SUBMENUS = [ + { + "menu": trans("Dashboard"), + "redirect": reverse("recruitment-dashboard"), + }, + { + "menu": trans("Recruitment Pipeline"), + "redirect": reverse("pipeline") + "?closed=false", + "accessibility": "recruitment.sidebar.pipeline_accessibility", + }, + { + "menu": trans("Recruitment Survey"), + "redirect": reverse("recruitment-survey-question-template-view"), + "accessibility": "recruitment.sidebar.survey_accessibility", + }, + { + "menu": trans("Candidates"), + "redirect": reverse("candidates-view"), + "accessibility": "recruitment.sidebar.candidates_accessibility", + }, + { + "menu": trans("Interview"), + "redirect": reverse("interview-view"), + "accessibility": "recruitment.sidebar.interview_accessibility", + }, + { + "menu": trans("Recruitment"), + "redirect": reverse("recruitment-view"), + "accessibility": "recruitment.sidebar.recruitment_accessibility", + }, + { + "menu": trans("Open Jobs"), + "redirect": reverse("open-recruitments"), + "accessibility": "recruitment.sidebar.recruitment_accessibility", + }, + { + "menu": trans("Stages"), + "redirect": reverse("rec-stage-view"), + "accessibility": "recruitment.sidebar.stage_accessibility", + }, +] + + +def menu_accessibilty( + request, _menu: str = "", user_perms: PermWrapper = [], *args, **kwargs +) -> bool: + return is_stagemanager(request.user) or "recruitment" in user_perms + + +def pipeline_accessibility( + request, _submenu: dict = {}, user_perms: PermWrapper = [], *args, **kwargs +) -> bool: + return is_stagemanager(request.user) or request.user.has_perm( + "recruitment.view_recruitment" + ) + + +def candidates_accessibility( + request, _submenu: dict = {}, user_perms: PermWrapper = [], *args, **kwargs +) -> bool: + return request.user.has_perm("recruitment.view_candidate") + + +def survey_accessibility( + request, _submenu: dict = {}, user_perms: PermWrapper = [], *args, **kwargs +) -> bool: + return request.user.has_perm("recruitment.view_recruitmentsurvey") + + +def recruitment_accessibility( + request, _submenu: dict = {}, user_perms: PermWrapper = [], *args, **kwargs +) -> bool: + return request.user.has_perm("recruitment.view_recruitment") + + +def interview_accessibility( + request, _submenu: dict = {}, user_perms: PermWrapper = [], *args, **kwargs +) -> bool: + interviews = InterviewSchedule.objects.all() + interviewers = [] + for interview in interviews: + for emp in interview.employee_id.all(): + interviewers.append(emp) + if request.user.employee_get in interviewers: + view_interview = True + else: + view_interview = False + + return request.user.has_perm("recruitment.view_interviewschedule") or view_interview + + +def stage_accessibility( + request, _submenu: dict = {}, user_perms: PermWrapper = [], *args, **kwargs +) -> bool: + return request.user.has_perm("recruitment.view_stage") diff --git a/recruitment/views/views.py b/recruitment/views/views.py index 112d69ffa..b0115018c 100644 --- a/recruitment/views/views.py +++ b/recruitment/views/views.py @@ -1288,6 +1288,7 @@ def interview_filter_view(request): ) +@login_required def interview_view(request): """ This method render all interviews to the template @@ -1321,6 +1322,7 @@ def interview_view(request): @login_required +@manager_can_enter(perm="recruitment.change_interviewschedule") def interview_employee_remove(request, interview_id, employee_id): """ This view is used to remove the employees from the meeting , diff --git a/static/css/style.css b/static/css/style.css index 71d9092ea..f52c76373 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -506,4 +506,5 @@ form label { top: 0; left: 0; backdrop-filter: blur(8px); + z-index: 9 !important; } diff --git a/templates/sidebar.html b/templates/sidebar.html index f95a5e86d..150477449 100755 --- a/templates/sidebar.html +++ b/templates/sidebar.html @@ -82,878 +82,54 @@ {% trans "Dashboard" %} - -{% comment %} -
  • - -
    - Dashboard -
    - {% trans "Announcement" %} -
    -
  • - - {% endcomment %} - {% if request.user|is_stagemanager or 'recruitment' in perms %} + {% for menues in sidebar %}
  • -
    - Dashboard -
    - {% trans "Recruitments" %} +
    + Dashboard +
    + + {{menues.menu}} +
    -
  • - {% endif %} {% if request.user|is_taskmanager or 'recruitment' in perms or 'onboarding' in perms %} -
  • - -
    - Dashboard -
    - {% trans "Onboarding" %} -
    - -
  • - {% endif %} -
  • - -
    - Dashboard -
    - {% trans "Employees" %} -
    - -
  • -
  • - -
    - Dashboard -
    - {% trans "Attendances" %} -
    - -
  • -
  • - -
    - Dashboard -
    - {% trans "Leave" %} -
    -
  • -
  • - -
    - Dashboard -
    - {% trans "Payroll" %} -
    - -
  • -
  • - -
    - Dashboard -
    - {% trans "Performance" %} -
    - -
  • - {% if perms.offboarding.view_offboarding or request.user.employee_get|any_manager or request.user.employee_get|is_offboarding_employee %} -
  • - -
    - Dashboard -
    - {% trans "Offboarding" %} -
    - -
  • - {% endif %} + {% endfor %} -
  • - -
    - Dashboard -
    - {% trans "Assets" %} -
    - -
  • -
  • - -
    - Dashboard -
    - {% trans "Help Desk" %} -
    - -
  • {% if request.user|config_perms %}