From 5da198c94598c4d2d786a4e85ecee72234ca4f01 Mon Sep 17 00:00:00 2001 From: Horilla Date: Wed, 23 Apr 2025 23:04:16 +0530 Subject: [PATCH] CBV code updates: 23rd April --- attendance/forms.py | 119 +- attendance/models.py | 24 +- .../attendance/attendance_account/form.html | 24 +- .../grace_time/grace_time_form.html | 6 +- attendance/views/requests.py | 1 - base/cbv/mail_log_tab.py | 25 + base/forms.py | 8 +- base/methods.py | 34 +- biometric/forms.py | 3 +- .../biometric/add_biometric_device.html | 2 +- .../biometric/edit_biometric_device.html | 2 +- docker-compose.yaml | 4 - employee/cbv/allocations.py | 24 +- helpdesk/models.py | 34 +- .../department_managers.html | 45 +- .../department_managers_form.html | 36 +- .../department_managers_view.html | 4 +- .../helpdesk/faq/faq_category_create.html | 5 +- .../helpdesk/faq/faq_category_list.html | 2 +- .../helpdesk/faq/faq_category_nav.html | 2 +- .../templates/helpdesk/faq/faq_create.html | 5 +- helpdesk/templates/helpdesk/faq/faq_nav.html | 2 +- .../ticket/forms/change_assinees.html | 38 +- .../ticket/forms/change_raised_on.html | 112 +- .../helpdesk/ticket/ticket_form.html | 187 +- helpdesk/views.py | 20 +- horilla/decorators.py | 18 + horilla/locale/pt_BR/LC_MESSAGES/django.po | 25610 ---------------- horilla/models.py | 3 + horilla/settings.py | 31 +- horilla_automations/forms.py | 4 +- horilla_views/generic/cbv/history.py | 11 +- .../templates/generic/group_by_table.html | 4 + .../generic/horilla_history_view.html | 5 +- .../templates/generic/horilla_list_table.html | 2 +- horilla_widgets/forms.py | 2 +- .../employee_available_leave_count.html | 7 +- .../leave_request/leave_request_form.html | 139 +- .../leave_request/leave_request_pdf.html | 56 +- .../leave/user_leave/request_form.html | 108 +- .../leave/user_leave/user_request_form.html | 1 + leave/views.py | 2 + offboarding/cbv/accessibility.py | 5 +- offboarding/cbv/exit_process.py | 34 +- offboarding/filters.py | 4 + offboarding/models.py | 20 - .../exit_process/detail_view_stage_col.html | 17 - .../cbv/exit_process/detail_view_tasks.html | 4 +- .../cbv/exit_process/detailed_page_empty.html | 2 +- .../cbv/exit_process/pipeline_action_col.html | 4 +- .../cbv/exit_process/pipeline_filter.html | 102 + .../cbv/exit_process/pipeline_stage_col.html | 2 +- .../cbv/exit_process/stage_order.html | 72 + .../cbv/exit_process/tasks_cols.html | 10 +- offboarding/urls.py | 5 + offboarding/views.py | 33 + onboarding/cbv/pipeline.py | 90 +- onboarding/filters.py | 40 +- .../cbv/pipeline/onboarding/filters.html | 64 +- .../cbv/pipeline/onboarding/stage_order.html | 70 + .../templates/onboarding/empty_task.html | 8 + onboarding/urls.py | 5 + onboarding/views.py | 66 +- package-lock.json | 370 +- pms/forms.py | 58 +- pms/models.py | 2 +- pms/sidebar.py | 4 +- pms/templates/dashboard/pms_dashboard.html | 8 +- .../feedback/feedback_detailed_view.html | 119 +- pms/templates/feedback/feedback_list.html | 4 +- pms/views.py | 89 +- project/cbv/projects.py | 23 +- project/cbv/timesheet.py | 2 +- project/templates/cbv/tasks/task_actions.html | 94 +- .../cbv/tasks/task_detail_actions.html | 65 +- .../templates/cbv/tasks/task_document.html | 20 +- project/templates/cbv/tasks/task_filter.html | 45 +- project/templates/cbv/tasks/task_form.html | 13 +- .../cbv/tasks/task_template_view.html | 5 +- project/templates/cbv/timesheet/actions.html | 122 +- .../cbv/timesheet/detail_actions.html | 57 +- .../cbv/timesheet/employee_field.html | 44 +- project/templates/cbv/timesheet/filter.html | 94 +- .../templates/cbv/timesheet/task_field.html | 23 +- .../cbv/timesheet/task_timesheet.html | 47 +- .../templates/cbv/timesheet/timesheet.html | 54 +- .../cbv/timesheet/timesheet_nav.html | 1 - .../templates/task/new/task_kanban_view.html | 4 +- recruitment/cbv/accessibility.py | 16 +- recruitment/cbv/candidate_mail_log.py | 13 + recruitment/cbv/candidate_profile.py | 106 +- recruitment/cbv/candidates.py | 10 + recruitment/cbv/pipeline.py | 13 + recruitment/cbv_decorators.py | 53 + recruitment/decorators.py | 50 + recruitment/methods.py | 12 + .../templates/cbv/pipeline/stage_order.html | 70 + .../pipeline_components/view_note.html | 2 +- .../templatetags/recruitmentfilters.py | 13 + recruitment/urls.py | 10 + recruitment/views/views.py | 42 +- report/templates/report/asset_report.html | 397 +- report/templates/report/payroll_report.html | 495 +- report/templates/report/pms_report.html | 458 +- report/views/asset_report.py | 79 +- report/views/payroll_report.py | 156 +- report/views/pms_report.py | 165 +- requirments.txt | Bin 0 -> 9338 bytes 108 files changed, 3513 insertions(+), 27442 deletions(-) delete mode 100644 horilla/locale/pt_BR/LC_MESSAGES/django.po delete mode 100644 offboarding/templates/cbv/exit_process/detail_view_stage_col.html create mode 100644 offboarding/templates/cbv/exit_process/pipeline_filter.html create mode 100644 offboarding/templates/cbv/exit_process/stage_order.html create mode 100644 onboarding/templates/cbv/pipeline/onboarding/stage_order.html create mode 100644 onboarding/templates/onboarding/empty_task.html create mode 100644 recruitment/templates/cbv/pipeline/stage_order.html create mode 100644 requirments.txt diff --git a/attendance/forms.py b/attendance/forms.py index 44927a9e6..edf124a71 100644 --- a/attendance/forms.py +++ b/attendance/forms.py @@ -1,3 +1,4 @@ +# pylint: disable=too-few-public-methods """ forms.py @@ -54,6 +55,7 @@ from attendance.models import ( strtime_seconds, validate_time_format, ) +from base.forms import ModelForm as BaseModelForm from base.forms import MultipleFileField from base.methods import ( filtersubordinatesemployeemodel, @@ -71,84 +73,7 @@ from horilla_widgets.widgets.select_widgets import HorillaMultiSelectWidget logger = logging.getLogger(__name__) -class ModelForm(forms.ModelForm): - """ - Overriding django default model form to apply some styles - """ - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - class_name = self.__class__.__name__ - self.container_id = f"{class_name[0].lower()}{class_name[1:]}Fields" - reload_queryset(self.fields) - request = getattr(horilla_middlewares._thread_locals, "request", None) - - for field_name, field in self.fields.items(): - widget = field.widget - if isinstance(widget, (forms.DateInput)): - field.initial = datetime.date.today() - - if isinstance( - widget, (forms.NumberInput, forms.EmailInput, forms.TextInput) - ): - label = _(field.label.title()) - - field.widget.attrs.update( - {"class": "oh-input w-100", "placeholder": label} - ) - elif isinstance(widget, (forms.Select,)): - label = "" - if field.label is not None: - label = _(field.label) - field.empty_label = _("---Choose {label}---").format(label=label) - self.fields[field_name].widget.attrs.update( - { - "class": "oh-select oh-select-2 w-100", - "id": uuid.uuid4(), - "style": "height:50px;border-radius:0;", - } - ) - elif isinstance(widget, (forms.Textarea)): - label = _(field.label) - field.widget.attrs.update( - { - "class": "oh-input w-100", - "placeholder": label, - "rows": 2, - "cols": 40, - } - ) - elif isinstance( - widget, - ( - forms.CheckboxInput, - forms.CheckboxSelectMultiple, - ), - ): - field.widget.attrs.update({"class": "oh-switch__checkbox"}) - if isinstance(widget, forms.DateInput): - field.widget = forms.DateInput( - attrs={"type": "date", "class": "oh-input w-100"} - ) - if isinstance(widget, forms.TimeInput): - field.widget = forms.DateInput( - attrs={"type": "time", "class": "oh-input w-100"} - ) - - try: - self.fields["employee_id"].initial = request.user.employee_get - except Exception: - pass - - try: - self.fields["company_id"].initial = ( - request.user.employee_get.get_company - ) - except Exception: - pass - - -class AttendanceUpdateForm(ModelForm): +class AttendanceUpdateForm(BaseModelForm): """ This model form is used to direct save the validated query dict to attendance model from AttendanceUpdateForm. This form can be used to update existing attendance. @@ -247,8 +172,6 @@ class AttendanceUpdateForm(ModelForm): ) self.fields["work_type_id"].widget.attrs.update({"id": str(uuid.uuid4())}) - self.fields["attendance_overtime_approve"].label = _("Approve overtime?") - self.fields["attendance_validated"].label = _("Validate Attendance?") if ( instance is not None and not instance.attendance_overtime_approve @@ -271,12 +194,13 @@ class AttendanceUpdateForm(ModelForm): """ Render the form fields as HTML table rows with Bootstrap styling. """ - context = {"form": self, "id": "attendanceUpdateFormFields"} + _ = args, kwargs # Explicitly mark as used for pylint + context = {"form": self} table_html = render_to_string("attendance_form.html", context) return table_html -class AttendanceForm(ModelForm): +class AttendanceForm(BaseModelForm): """ Model form for Attendance model """ @@ -427,6 +351,7 @@ class AttendanceForm(ModelForm): """ Render the form fields as HTML table rows with Bootstrap styling. """ + _ = args, kwargs # Explicitly mark as used for pylint context = {"form": self} table_html = render_to_string("attendance_form.html", context) return table_html @@ -478,7 +403,7 @@ class AttendanceForm(ModelForm): return employee.first() -class AttendanceActivityForm(ModelForm): +class AttendanceActivityForm(BaseModelForm): """ Model form for AttendanceActivity model """ @@ -526,7 +451,7 @@ class MonthSelectField(forms.ChoiceField): super().__init__(choices=choices, *args, **kwargs) -class AttendanceOverTimeForm(ModelForm): +class AttendanceOverTimeForm(BaseModelForm): """ Model form for AttendanceOverTime model """ @@ -563,12 +488,13 @@ class AttendanceOverTimeForm(ModelForm): """ Render the form fields as HTML table rows with Bootstrap styling. """ + _ = args, kwargs # Explicitly mark as used for pylint context = {"form": self} table_html = render_to_string("attendance_form.html", context) return table_html -class AttendanceLateComeEarlyOutForm(ModelForm): +class AttendanceLateComeEarlyOutForm(BaseModelForm): """ Model form for attendance AttendanceLateComeEarlyOut """ @@ -631,12 +557,16 @@ class AttendanceValidationConditionForm(forms.ModelForm): ) class Meta: + """ + Meta class for additional options + """ + model = AttendanceValidationCondition fields = "__all__" exclude = ["is_active"] -class AttendanceRequestForm(ModelForm): +class AttendanceRequestForm(BaseModelForm): """ AttendanceRequestForm """ @@ -735,6 +665,7 @@ class AttendanceRequestForm(ModelForm): """ Render the form fields as HTML table rows with Bootstrap styling. """ + _ = args, kwargs # Explicitly mark as used for pylint context = {"form": self} table_html = render_to_string("attendance_form.html", context) return table_html @@ -782,7 +713,6 @@ class NewRequestForm(AttendanceRequestForm): ), ), } - self.fields["request_description"].label = _("Request description") new_dict.update(old_dict) self.fields = new_dict kwargs["initial"] = view_initial @@ -791,6 +721,7 @@ class NewRequestForm(AttendanceRequestForm): """ Render the form fields as HTML table rows with Bootstrap styling. """ + _ = args, kwargs # Explicitly mark as used for pylint context = {"form": self} form_html = render_to_string( "requests/attendance/request_new_form.html", context @@ -997,7 +928,7 @@ class AttendanceOverTimeExportForm(forms.Form): ) -class GraceTimeForm(ModelForm): +class GraceTimeForm(BaseModelForm): """ Form for create or update Grace time """ @@ -1039,6 +970,7 @@ class GraceTimeAssignForm(forms.Form): """ Render the form fields as HTML table rows with Bootstrap styling. """ + _ = args, kwargs # Explicitly mark as used for pylint context = {"form": self} form_html = render_to_string("common_form.html", context) return form_html @@ -1048,7 +980,7 @@ class GraceTimeAssignForm(forms.Form): self.fields["shifts"].widget.attrs["class"] = "oh-select w-100 oh-select-2" -class AttendanceRequestCommentForm(ModelForm): +class AttendanceRequestCommentForm(BaseModelForm): """ AttendanceRequestComment form """ @@ -1112,7 +1044,7 @@ def get_date_list(employee_id, from_date, to_date): return date_list -class BulkAttendanceRequestForm(ModelForm): +class BulkAttendanceRequestForm(BaseModelForm): """ Bulk attendance request create form """ @@ -1314,7 +1246,7 @@ class BulkAttendanceRequestForm(ModelForm): return instance -class WorkRecordsForm(ModelForm): +class WorkRecordsForm(BaseModelForm): """ WorkRecordForm """ @@ -1328,7 +1260,7 @@ class WorkRecordsForm(ModelForm): model = WorkRecords -class BatchAttendanceForm(ModelForm): +class BatchAttendanceForm(BaseModelForm): """ BatchAttendanceForm """ @@ -1348,6 +1280,7 @@ class BatchAttendanceForm(ModelForm): """ Render the form fields as HTML table rows with Bootstrap styling. """ + _ = args, kwargs # Explicitly mark as used for pylint context = {"form": self} form_html = render_to_string("common_form.html", context) return form_html @@ -1356,4 +1289,4 @@ class BatchAttendanceForm(ModelForm): super().__init__(*args, **kwargs) if self.instance.pk: - self.verbose_name = _("Update batch attendance") + self.verbose_name = _("Update attendance batch") diff --git a/attendance/models.py b/attendance/models.py index 07e364f05..17dbfbd47 100644 --- a/attendance/models.py +++ b/attendance/models.py @@ -181,7 +181,7 @@ class BatchAttendance(HorillaModel): Batch attendance model """ - title = models.CharField(max_length=150) + title = models.CharField(max_length=150, verbose_name=_("Title")) def __str__(self): return f"{self.title}-{self.id}" @@ -268,10 +268,10 @@ class Attendance(HorillaModel): verbose_name=_("Overtime"), ) attendance_overtime_approve = models.BooleanField( - default=False, verbose_name=_("Overtime approved") + default=False, verbose_name=_("Overtime Approve") ) attendance_validated = models.BooleanField( - default=False, verbose_name=_("Attendance validated") + default=False, verbose_name=_("Attendance Validate") ) at_work_second = models.IntegerField(null=True, blank=True) overtime_second = models.IntegerField( @@ -285,7 +285,9 @@ class Attendance(HorillaModel): is_validate_request_approved = models.BooleanField( default=False, verbose_name=_("Is validate request approved") ) - request_description = models.TextField(null=True, max_length=255) + request_description = models.TextField( + null=True, max_length=255, verbose_name=_("Request Description") + ) request_type = models.CharField( max_length=18, null=True, choices=status, default="update_request" ) @@ -359,6 +361,8 @@ class Attendance(HorillaModel): "employee_id__employee_first_name", "attendance_clock_in", ] + verbose_name = _("Attendance") + verbose_name_plural = _("Attendances") def check_min_ot(self): """ @@ -923,6 +927,8 @@ class AttendanceOverTime(HorillaModel): unique_together = [("employee_id"), ("month"), ("year")] ordering = ["-year", "-month_sequence"] + verbose_name = _("Hour Account") + verbose_name_plural = _("Hour Accounts") def get_month_capitalized(self): """ @@ -1246,14 +1252,18 @@ class GraceTime(HorillaModel): default="00:00:00", validators=[validate_hh_mm_ss_format], max_length=10, - verbose_name=_("Allowed time"), + verbose_name=_("Allowed Time"), ) allowed_time_in_secs = models.IntegerField() allowed_clock_in = models.BooleanField( - default=True, help_text=_("Allcocate this grace time for Check-In Attendance") + default=True, + help_text=_("Allcocate this grace time for Check-In Attendance"), + verbose_name=_("Allowed Clock-In"), ) allowed_clock_out = models.BooleanField( - default=False, help_text=_("Allcocate this grace time for Check-Out Attendance") + default=False, + help_text=_("Allcocate this grace time for Check-Out Attendance"), + verbose_name=_("Allowed Clock-Out"), ) is_default = models.BooleanField(default=False) diff --git a/attendance/templates/attendance/attendance_account/form.html b/attendance/templates/attendance/attendance_account/form.html index 83f0191b5..74a10e044 100644 --- a/attendance/templates/attendance/attendance_account/form.html +++ b/attendance/templates/attendance/attendance_account/form.html @@ -1,20 +1,16 @@ {% load i18n %}
-

- {% trans "Hour Account" %} -

- +

+ {{form.verbose_name}} +

+
-
- {{form.as_p}} -
+
+ {{form.as_p}} +
diff --git a/attendance/templates/attendance/grace_time/grace_time_form.html b/attendance/templates/attendance/grace_time/grace_time_form.html index 6f61217e0..b46d2aa2b 100644 --- a/attendance/templates/attendance/grace_time/grace_time_form.html +++ b/attendance/templates/attendance/grace_time/grace_time_form.html @@ -40,7 +40,7 @@
- + {{ form.allowed_time }} {{ form.allowed_time.errors }}
@@ -50,7 +50,7 @@
- +
@@ -62,7 +62,7 @@
- +
diff --git a/attendance/views/requests.py b/attendance/views/requests.py index 031b596a1..b9a5a9ac9 100644 --- a/attendance/views/requests.py +++ b/attendance/views/requests.py @@ -432,7 +432,6 @@ def validate_attendance_request(request, attendance_id): other_dict = first_dict first_dict = empty_data else: - print(attendance.requested_data) other_dict = json.loads(attendance.requested_data) requests_ids_json = request.GET.get("requests_ids") previous_instance_id = next_instance_id = attendance.pk diff --git a/base/cbv/mail_log_tab.py b/base/cbv/mail_log_tab.py index 1fdae9007..d04797f8d 100644 --- a/base/cbv/mail_log_tab.py +++ b/base/cbv/mail_log_tab.py @@ -6,14 +6,30 @@ from typing import Any from django.db.models import Q from django.urls import reverse +from django.utils.decorators import method_decorator from django.utils.translation import gettext_lazy as _ +from accessibility.cbv_decorators import enter_if_accessible from base.filters import MailLogFilter from base.models import EmailLog from employee.models import Employee +from horilla_views.cbv_methods import login_required from horilla_views.generic.cbv.views import HorillaDetailedView, HorillaListView +def _check_reporting_manager(request, *args, **kwargs): + return request.user.employee_get.reporting_manager.exists() + + +@method_decorator(login_required, name="dispatch") +@method_decorator( + enter_if_accessible( + feature="view_mail_log", + perm="employee.view_employee", + method=_check_reporting_manager, + ), + name="dispatch", +) class MailLogTabList(HorillaListView): """ list view for mail log tab @@ -68,6 +84,15 @@ class MailLogTabList(HorillaListView): """ +@method_decorator(login_required, name="dispatch") +@method_decorator( + enter_if_accessible( + feature="view_mail_log", + perm="employee.view_employee", + method=_check_reporting_manager, + ), + name="dispatch", +) class MailLogDetailView(HorillaDetailedView): """ detail view for mail log tab diff --git a/base/forms.py b/base/forms.py index 4154c74c6..8198e788c 100644 --- a/base/forms.py +++ b/base/forms.py @@ -245,8 +245,12 @@ class ModelForm(forms.ModelForm): pass try: - self.fields["company_id"].initial = ( - request.user.employee_get.get_company + company_field = self.fields["company_id"] + company = request.user.employee_get.get_company + company_queryset = company_field.queryset + + company_field.initial = ( + company if company in company_queryset else company_queryset.first() ) except: pass diff --git a/base/methods.py b/base/methods.py index 52eaafe63..d00639b60 100644 --- a/base/methods.py +++ b/base/methods.py @@ -608,28 +608,30 @@ def export_data(request, model, form_class, filter_class, file_name, perm=None): def reload_queryset(fields): """ - This method is used to reload the querysets in the form + Reloads querysets in the form based on active filters and selected company. """ + request = getattr(_thread_locals, "request", None) + selected_company = request.session.get("selected_company") if request else None + + recruitment_installed = apps.is_installed("recruitment") model_filters = { "Employee": {"is_active": True}, - "Candidate": {"is_active": True} if apps.is_installed("recruitment") else None, + "Candidate": {"is_active": True} if recruitment_installed else None, } - request = getattr(_thread_locals, "request", None) - selected_company = request.session.get("selected_company") if request else None for field in fields.values(): - if isinstance(field, ModelChoiceField): - model_name = field.queryset.model.__name__ - filter_criteria = model_filters.get(model_name) - if filter_criteria is not None: - field.queryset = field.queryset.model.objects.filter(**filter_criteria) - # Future updation for company select field options when select a comapany from navbar - elif selected_company and not selected_company == "all": - field.queryset = field.queryset.model.objects.filter( - id=selected_company - ) - else: - field.queryset = field.queryset.model.objects.all() + if not isinstance(field, ModelChoiceField): + continue + + model = field.queryset.model + model_name = model.__name__ + + if model_name == "Company" and selected_company and selected_company != "all": + field.queryset = model.objects.filter(id=selected_company) + elif (filters := model_filters.get(model_name)) is not None: + field.queryset = model.objects.filter(**filters) + else: + field.queryset = model.objects.all() return fields diff --git a/biometric/forms.py b/biometric/forms.py index 0fb352d2a..28f22bb4c 100644 --- a/biometric/forms.py +++ b/biometric/forms.py @@ -10,8 +10,7 @@ from django import forms from django.db.models import Q from django.utils.translation import gettext_lazy as _ -from attendance.forms import ModelForm -from base.forms import Form +from base.forms import Form, ModelForm from base.methods import reload_queryset from employee.models import Employee from horilla.horilla_middlewares import _thread_locals diff --git a/biometric/templates/biometric/add_biometric_device.html b/biometric/templates/biometric/add_biometric_device.html index 08a88a396..794d0d176 100644 --- a/biometric/templates/biometric/add_biometric_device.html +++ b/biometric/templates/biometric/add_biometric_device.html @@ -5,7 +5,7 @@ {% endif %}

- {% trans "Add Biometric Device" %} + {% trans "Add" %} {{biometric_form.verbose_name}}

- - - - - - - - - - diff --git a/helpdesk/templates/helpdesk/ticket/ticket_form.html b/helpdesk/templates/helpdesk/ticket/ticket_form.html index 73f2d00dc..91dfb0100 100644 --- a/helpdesk/templates/helpdesk/ticket/ticket_form.html +++ b/helpdesk/templates/helpdesk/ticket/ticket_form.html @@ -1,63 +1,50 @@ {% load i18n %}
-

- {% if ticket_id %} {% trans "Update Ticket" %} {% else %} {% trans "Create Ticket" %} {% endif %} -

+

+ {% if ticket_id %} {% trans "Update" %} {% else %} {% trans "Create" %} {% endif %} + {{form.verbose_name}} +

-
- {% if form.errors %} - -
-
- {% for error in form.non_field_errors %} -
- {{ error }} -
- {% endfor %} -
-
- {% endif %} - -
- {% csrf_token %} {{form.as_p}} -
+
+ {% if form.errors %} + +
+
+ {% for error in form.non_field_errors %} +
+ {{ error }} +
+ {% endfor %} +
+
+ {% endif %} + +
+ {% csrf_token %} {{form.as_p}} +
- + {% comment %} {% endcomment %} diff --git a/onboarding/templates/cbv/pipeline/onboarding/stage_order.html b/onboarding/templates/cbv/pipeline/onboarding/stage_order.html new file mode 100644 index 000000000..2f0e9bb8a --- /dev/null +++ b/onboarding/templates/cbv/pipeline/onboarding/stage_order.html @@ -0,0 +1,70 @@ +{% load static i18n %} +
+ + {% trans "Update Stage Order" %} + + +
+
+
+
    + {% for stage in stages %} +
  • + + {{stage.sequence}}. + {{stage}} +
  • + {% endfor %} +
+
+ +
+
+
+ diff --git a/onboarding/templates/onboarding/empty_task.html b/onboarding/templates/onboarding/empty_task.html new file mode 100644 index 000000000..2a6a4ebc1 --- /dev/null +++ b/onboarding/templates/onboarding/empty_task.html @@ -0,0 +1,8 @@ +{% load i18n %} +
+
+

+ {% trans "No Tasks found." %} +

+
+
\ No newline at end of file diff --git a/onboarding/urls.py b/onboarding/urls.py index 162d19466..f687467c8 100644 --- a/onboarding/urls.py +++ b/onboarding/urls.py @@ -22,6 +22,11 @@ urlpatterns = [ onboarding_view.StageCreateForm.as_view(), name="stage-update", ), + path( + "onboarding-stage-sequence-update//", + views.update_stage_order, + name="onboarding-stage-sequence-update", + ), path( "task-creation//", onboarding_view.TaskCreateForm.as_view(), diff --git a/onboarding/views.py b/onboarding/views.py index 1c0a8b1fa..7bb958341 100644 --- a/onboarding/views.py +++ b/onboarding/views.py @@ -199,6 +199,39 @@ def stage_update(request, stage_id, recruitment_id): ) +@login_required +@recruitment_manager_can_enter("onboarding.change_onboardingstage") +def update_stage_order(request, pk): + """ + This method is used to update the stage sequence of the onboarding + """ + recruitment = Recruitment.objects.get(id=pk) + + if request.method == "POST": + try: + order = json.loads(request.POST.get("order", "[]")) + for index, stage_id in enumerate(order): + stage = recruitment.onboarding_stage.get(id=stage_id) + stage.sequence = index + 1 + stage.save() + messages.success(request, "Sequence Updated Successfully") + return JsonResponse({"status": "success"}) + except Exception as e: + messages.error(request, "Error Updating Sequence..") + return JsonResponse({"status": "error", "message": str(e)}, status=400) + + stages = recruitment.onboarding_stage.order_by("sequence") + + return render( + request, + "cbv/pipeline/onboarding/stage_order.html", + { + "stages": stages, + "recruitment": recruitment, + }, + ) + + @login_required @permission_required("onboarding.delete_onboardingstage") @recruitment_manager_can_enter("onboarding.delete_onboardingstage") @@ -1716,20 +1749,31 @@ def update_offer_letter_status(request): """ This method is used to update the offer letter status """ - candidate_id = request.GET["candidate_id"] - status = request.GET["status"] - candidate = Candidate.objects.get(id=candidate_id) + candidate_id = request.GET.get("candidate_id") + status = request.GET.get("status") + candidate = None + if not candidate_id or not status: + messages.error(request, "candidate or status is missing") + return redirect("/onboarding/candidates-view/") + if not status in ["not_sent", "sent", "accepted", "rejected", "joined"]: + messages.error(request, "Please Pass valid status") + return redirect("/onboarding/candidates-view/") + try: + candidate = Candidate.objects.get(id=candidate_id) + except Candidate.DoesNotExist: + messages.error(request, "Candidate not found") + return redirect("/onboarding/candidates-view/") if status in ["not_sent", "sent", "accepted", "rejected", "joined"]: candidate.offer_letter_status = status candidate.save() - # return HttpResponse("Success") - return JsonResponse( - { - "type": "success", - "message": _("{candidate}'s Offer letter status updated").format( - candidate=candidate.name - ), - } + messages.success(request, "Status of offer letter updated successfully") + url = "/onboarding/candidates-view/" + return HttpResponse( + f""" + + """ ) diff --git a/package-lock.json b/package-lock.json index df3df2408..41b7b2b1f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -36,80 +36,17 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", - "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", "dev": true, "dependencies": { - "@babel/highlight": "^7.22.13", - "chalk": "^2.4.2" + "@babel/highlight": "^7.18.6" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/code-frame/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/code-frame/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/@babel/code-frame/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/@babel/compat-data": { "version": "7.20.10", "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.10.tgz", @@ -159,14 +96,13 @@ } }, "node_modules/@babel/generator": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", - "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.7.tgz", + "integrity": "sha512-7wqMOJq8doJMZmP4ApXTzLxSr7+oO2jroJURrVEp6XShrQUObV8Tq/D0NCcoYg2uHqUrjzO0zwBjoYzelxK+sw==", "dev": true, "dependencies": { - "@babel/types": "^7.23.0", + "@babel/types": "^7.20.7", "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" }, "engines": { @@ -305,9 +241,9 @@ } }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", - "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", + "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", "dev": true, "engines": { "node": ">=6.9.0" @@ -326,25 +262,25 @@ } }, "node_modules/@babel/helper-function-name": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", - "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", + "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", "dev": true, "dependencies": { - "@babel/template": "^7.22.15", - "@babel/types": "^7.23.0" + "@babel/template": "^7.18.10", + "@babel/types": "^7.19.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-hoist-variables": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", - "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", + "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", "dev": true, "dependencies": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.18.6" }, "engines": { "node": ">=6.9.0" @@ -474,30 +410,30 @@ } }, "node_modules/@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", + "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", "dev": true, "dependencies": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.18.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", - "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", + "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", "dev": true, "engines": { "node": ">=6.9.0" @@ -542,13 +478,13 @@ } }, "node_modules/@babel/highlight": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", - "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.22.20", - "chalk": "^2.4.2", + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", "js-tokens": "^4.0.0" }, "engines": { @@ -618,9 +554,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", - "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.7.tgz", + "integrity": "sha512-T3Z9oHybU+0vZlY9CiDSJQTD5ZapcW18ZctFMi0MOAl/4BjFF4ul7NVSARLdbGO5vDqy9eQiGTV0LtKfvCYvcg==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -1765,33 +1701,33 @@ } }, "node_modules/@babel/template": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", - "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", + "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/parser": "^7.22.15", - "@babel/types": "^7.22.15" + "@babel/code-frame": "^7.18.6", + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", - "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", + "version": "7.20.12", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.12.tgz", + "integrity": "sha512-MsIbFN0u+raeja38qboyF8TIT7K0BFzz/Yd/77ta4MsUsmP2RAnidIlwq7d5HFQrH/OZJecGV6B71C4zAgpoSQ==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.0", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.0", - "@babel/types": "^7.23.0", + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.20.7", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -1800,13 +1736,13 @@ } }, "node_modules/@babel/types": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", - "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.7.tgz", + "integrity": "sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg==", "dev": true, "dependencies": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.20", + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", "to-fast-properties": "^2.0.0" }, "engines": { @@ -4526,9 +4462,9 @@ } }, "node_modules/follow-redirects": { - "version": "1.15.6", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", - "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", "dev": true, "funding": [ { @@ -9037,65 +8973,12 @@ } }, "@babel/code-frame": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", - "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", "dev": true, "requires": { - "@babel/highlight": "^7.22.13", - "chalk": "^2.4.2" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } + "@babel/highlight": "^7.18.6" } }, "@babel/compat-data": { @@ -9136,14 +9019,13 @@ } }, "@babel/generator": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", - "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.7.tgz", + "integrity": "sha512-7wqMOJq8doJMZmP4ApXTzLxSr7+oO2jroJURrVEp6XShrQUObV8Tq/D0NCcoYg2uHqUrjzO0zwBjoYzelxK+sw==", "dev": true, "requires": { - "@babel/types": "^7.23.0", + "@babel/types": "^7.20.7", "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" }, "dependencies": { @@ -9249,9 +9131,9 @@ } }, "@babel/helper-environment-visitor": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", - "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", + "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", "dev": true }, "@babel/helper-explode-assignable-expression": { @@ -9264,22 +9146,22 @@ } }, "@babel/helper-function-name": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", - "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", + "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", "dev": true, "requires": { - "@babel/template": "^7.22.15", - "@babel/types": "^7.23.0" + "@babel/template": "^7.18.10", + "@babel/types": "^7.19.0" } }, "@babel/helper-hoist-variables": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", - "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", + "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", "dev": true, "requires": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.18.6" } }, "@babel/helper-member-expression-to-functions": { @@ -9376,24 +9258,24 @@ } }, "@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", + "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", "dev": true, "requires": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.18.6" } }, "@babel/helper-string-parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", - "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", + "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", "dev": true }, "@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", "dev": true }, "@babel/helper-validator-option": { @@ -9426,13 +9308,13 @@ } }, "@babel/highlight": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", - "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.22.20", - "chalk": "^2.4.2", + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", "js-tokens": "^4.0.0" }, "dependencies": { @@ -9489,9 +9371,9 @@ } }, "@babel/parser": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", - "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.7.tgz", + "integrity": "sha512-T3Z9oHybU+0vZlY9CiDSJQTD5ZapcW18ZctFMi0MOAl/4BjFF4ul7NVSARLdbGO5vDqy9eQiGTV0LtKfvCYvcg==", "dev": true }, "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { @@ -10259,42 +10141,42 @@ } }, "@babel/template": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", - "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", + "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", "dev": true, "requires": { - "@babel/code-frame": "^7.22.13", - "@babel/parser": "^7.22.15", - "@babel/types": "^7.22.15" + "@babel/code-frame": "^7.18.6", + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7" } }, "@babel/traverse": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", - "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", + "version": "7.20.12", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.12.tgz", + "integrity": "sha512-MsIbFN0u+raeja38qboyF8TIT7K0BFzz/Yd/77ta4MsUsmP2RAnidIlwq7d5HFQrH/OZJecGV6B71C4zAgpoSQ==", "dev": true, "requires": { - "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.0", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.0", - "@babel/types": "^7.23.0", + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.20.7", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", "debug": "^4.1.0", "globals": "^11.1.0" } }, "@babel/types": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", - "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.7.tgz", + "integrity": "sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg==", "dev": true, "requires": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.20", + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", "to-fast-properties": "^2.0.0" } }, @@ -12530,9 +12412,9 @@ } }, "follow-redirects": { - "version": "1.15.6", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", - "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", "dev": true }, "forwarded": { diff --git a/pms/forms.py b/pms/forms.py index a3dc97efa..f7e9ce22c 100644 --- a/pms/forms.py +++ b/pms/forms.py @@ -766,22 +766,52 @@ class FeedbackForm(HorillaModelForm): ) self.fields["employee_id"].widget.attrs["onchange"] = "get_collegues($(this))" - # Horilla multi-select filter for subordinates - self.fields["subordinate_id"] = HorillaMultiSelectField( - queryset=Employee.objects.all(), - widget=HorillaMultiSelectWidget( - filter_route_name="employee-widget-filter", - filter_class=EmployeeFilter, - filter_instance_contex_name="f", - filter_template_path="employee_filters.html", - instance=self.instance, - required=False, - ), - label=_("Subordinates"), - ) - reload_queryset(self.fields) + if self.instance and self.instance.pk: + employee = self.instance.employee_id + reporting_manager = ( + getattr(employee.employee_work_info, "reporting_manager_id", None) + if employee + and hasattr(employee, "employee_work_info") + and employee.employee_work_info + else None + ) + subordinates = Employee.objects.filter( + is_active=True, employee_work_info__reporting_manager_id=employee + ) + department = employee.get_department() + + exclude_ids = [employee.id] + if reporting_manager: + exclude_ids.append(reporting_manager.id) + + colleagues = Employee.objects.filter( + is_active=True, employee_work_info__department_id=department + ).exclude(id__in=exclude_ids) + + self.fields["colleague_id"].queryset = colleagues + self.fields["subordinate_id"].queryset = subordinates + self.fields["manager_id"].queryset = ( + Employee.objects.filter(id=reporting_manager.id) + if reporting_manager + else Employee.objects.none() + ) + + # # Horilla multi-select filter for subordinates + # self.fields["subordinate_id"] = HorillaMultiSelectField( + # queryset=Employee.objects.all(), + # widget=HorillaMultiSelectWidget( + # filter_route_name="employee-widget-filter", + # filter_class=EmployeeFilter, + # filter_instance_contex_name="f", + # filter_template_path="employee_filters.html", + # instance=self.instance, + # required=False, + # ), + # label=_("Subordinates"), + # ) + def clean(self): """ Cleans and validates the feedback form data. diff --git a/pms/models.py b/pms/models.py index b37080e13..2fa56cb6f 100644 --- a/pms/models.py +++ b/pms/models.py @@ -970,7 +970,7 @@ class Feedback(HorillaModel): related_name="feedback_manager", on_delete=models.DO_NOTHING, null=True, - blank=False, + blank=True, verbose_name=_("Manager"), ) employee_id = models.ForeignKey( diff --git a/pms/sidebar.py b/pms/sidebar.py index e213e8335..4b78a8f5e 100644 --- a/pms/sidebar.py +++ b/pms/sidebar.py @@ -51,9 +51,7 @@ SUBMENUS = [ def key_result_accessibility(request, submenu, user_perms, *args, **kwargs): - return request.user.has_perm("pms.view_keyresult") or is_reportingmanager( - request.user - ) + return request.user.has_perm("pms.view_keyresult") def period_accessibility(request, submenu, user_perms, *args, **kwargs): diff --git a/pms/templates/dashboard/pms_dashboard.html b/pms/templates/dashboard/pms_dashboard.html index 9ae9f429b..124218001 100644 --- a/pms/templates/dashboard/pms_dashboard.html +++ b/pms/templates/dashboard/pms_dashboard.html @@ -53,9 +53,13 @@ {% trans "Total key results" %}
- + {% if perms.pms.view_keyresult %} + + {{count_key_result}} + + {% else %} {{count_key_result}} - + {% endif %}
diff --git a/pms/templates/feedback/feedback_detailed_view.html b/pms/templates/feedback/feedback_detailed_view.html index 790b983d8..54fd72b6d 100644 --- a/pms/templates/feedback/feedback_detailed_view.html +++ b/pms/templates/feedback/feedback_detailed_view.html @@ -113,23 +113,25 @@ {% trans feedback.get_status_display %} {% endif %} - {% if perms.pms.change_feedback or perms.pms.delete_feedback or request.user|is_reportingmanager %} + {% if perms.pms.change_feedback or perms.pms.delete_feedback %}
{% if not feedback_started %} - {% if perms.pms.change_feedback or request.user|is_reportingmanager %} + {% if perms.pms.change_feedback %}
{% endif %} {% endif %} - {% if perms.pms.delete_feedback or request.user|is_reportingmanager %} + + {% if perms.pms.delete_feedback %}
-
-
-
    -
  • -
    -
    - {{feedback.employee_id}} -
    - {{feedback.manager_id}} + {% if feedback.manager_id %} +
    +
    +
      +
    • +
      +
      + {{feedback.employee_id}} +
      + {{feedback.manager_id}} +
      +
    • +
    +
    +
    +
    +
    -
  • -
+
+
-
-
- + +
+
+ + {% trans "Answers" %} +
+
+
    + {% for answer in manager_answers %} + {% include 'feedback/feedback_detailed_view_answer.html' %} + {%endfor %} +
-
+ {% endif %} +
- -
-
- - {% trans "Answers" %} -
-
-
    - {% for answer in manager_answers %} - {% include 'feedback/feedback_detailed_view_answer.html' %} - {%endfor %} -
-
-
-
diff --git a/pms/templates/feedback/feedback_list.html b/pms/templates/feedback/feedback_list.html index 0bf3050f1..bf6102bf2 100644 --- a/pms/templates/feedback/feedback_list.html +++ b/pms/templates/feedback/feedback_list.html @@ -156,7 +156,7 @@
- {% if perms.pms.delete_feedback or request.user|is_reportingmanager %} + {% if perms.pms.delete_feedback %} {% if feedback.archive == True %}
- {% if perms.pms.delete_feedback or request.user|is_reportingmanager %} + {% if perms.pms.delete_feedback %}
diff --git a/pms/views.py b/pms/views.py index 6c05117fd..d03398244 100644 --- a/pms/views.py +++ b/pms/views.py @@ -276,7 +276,7 @@ def objective_update(request, obj_id): # key result @login_required -@manager_can_enter("pms.view_keyresult") +@permission_required("pms.view_keyresult") def view_key_result(request): """ This method is used render template to view all the key result instances @@ -1553,7 +1553,7 @@ def feedback_creation(request): @login_required @hx_request_required -@manager_can_enter(perm="pms.change_feedback") +@permission_required(perm="pms.change_feedback") def feedback_update(request, id): """ This view is used to update the feedback. @@ -1978,7 +1978,7 @@ def feedback_answer_view(request, id, **kwargs): @login_required -@manager_can_enter(perm="pms.delete_feedback") +@permission_required(perm="pms.delete_feedback") def feedback_delete(request, id): """ This view is used to delete the feedback. @@ -2122,50 +2122,49 @@ def get_collegues(request): try: employee_id = request.GET.get("employee_id") employee = Employee.objects.get(id=int(employee_id)) if employee_id else None - - if employee: - employees_queryset = Employee.objects.none() - reporting_manager = ( - employee.employee_work_info.reporting_manager_id - if employee.employee_work_info - else None + employees_queryset = Employee.objects.none() + reporting_manager = ( + employee.employee_work_info.reporting_manager_id + if employee and employee.employee_work_info + else None + ) + if request.GET.get("data") == "keyresults": + employees_queryset = EmployeeKeyResult.objects.filter( + employee_objective_id__employee_id=employee ) - - if request.GET.get("data") == "colleagues": - department = employee.get_department() - # employee ids to exclude from collegue list - exclude_ids = [employee.id] - if reporting_manager: - exclude_ids.append(reporting_manager.id) - - # Get employees in the same department as the employee - employees_queryset = Employee.objects.filter( - is_active=True, employee_work_info__department_id=department - ).exclude(id__in=exclude_ids) - elif request.GET.get("data") == "manager": - if reporting_manager: - employees_queryset = Employee.objects.filter( - id=reporting_manager.id - ) - elif request.GET.get("data") == "subordinates": - employees_queryset = Employee.objects.filter( - is_active=True, employee_work_info__reporting_manager_id=employee - ) - elif request.GET.get("data") == "keyresults": - employees_queryset = EmployeeKeyResult.objects.filter( - employee_objective_id__employee_id=employee - ) - # Convert QuerySets to a list - employees = [(employee.id, employee) for employee in employees_queryset] - context = {"employees": employees} - employee_html = render_to_string("employee/employees_select.html", context) - return HttpResponse(employee_html) else: - return JsonResponse({"error": "Employee not found"}, status=404) - except Employee.DoesNotExist: - return JsonResponse({"error": "Invalid Employee ID"}, status=400) - except Exception as e: - return JsonResponse({"error": str(e)}, status=500) + if employee and employee.employee_work_info: + if request.GET.get("data") == "colleagues": + department = employee.get_department() + # employee ids to exclude from collegue list + exclude_ids = [employee.id] + if reporting_manager: + exclude_ids.append(reporting_manager.id) + + # Get employees in the same department as the employee + employees_queryset = Employee.objects.filter( + is_active=True, employee_work_info__department_id=department + ).exclude(id__in=exclude_ids) + elif request.GET.get("data") == "manager": + if reporting_manager: + employees_queryset = Employee.objects.filter( + id=reporting_manager.id + ) + elif request.GET.get("data") == "subordinates": + employees_queryset = Employee.objects.filter( + is_active=True, + employee_work_info__reporting_manager_id=employee, + ) + + # Convert QuerySets to a list + employees = [(employee.id, employee) for employee in employees_queryset] + context = {"employees": employees} + employee_html = render_to_string("employee/employees_select.html", context) + return HttpResponse(employee_html) + except: + context = {"employees": []} + employee_html = render_to_string("employee/employees_select.html", context) + return HttpResponse(employee_html) @login_required diff --git a/project/cbv/projects.py b/project/cbv/projects.py index 1584a5fbd..85f25e9d9 100644 --- a/project/cbv/projects.py +++ b/project/cbv/projects.py @@ -12,7 +12,6 @@ from django.utils.decorators import method_decorator from django.utils.translation import gettext_lazy as _ from django.views.generic import ListView -from employee.cbv.employee_profile import EmployeeProfileView from employee.models import Employee from horilla.horilla_middlewares import _thread_locals from horilla_views.cbv_methods import login_required, permission_required @@ -508,13 +507,15 @@ class ProjectsTabView(ListView): return context -EmployeeProfileView.add_tab( - tabs=[ - { - "title": "Projects", - # "view": projects_tab, - "view": ProjectsTabView.as_view(), - "accessibility": "employee.cbv.accessibility.workshift_accessibility", - }, - ] -) +# Remove the command lines after horilla converted into CBV +# from employee.cbv.employee_profile import EmployeeProfileView +# EmployeeProfileView.add_tab( +# tabs=[ +# { +# "title": "Projects", +# # "view": projects_tab, +# "view": ProjectsTabView.as_view(), +# "accessibility": "employee.cbv.accessibility.workshift_accessibility", +# }, +# ] +# ) diff --git a/project/cbv/timesheet.py b/project/cbv/timesheet.py index faecf8017..57d9634c3 100644 --- a/project/cbv/timesheet.py +++ b/project/cbv/timesheet.py @@ -56,7 +56,7 @@ class TimeSheetNavView(HorillaNavView): def __init__(self, **kwargs: Any) -> None: super().__init__(**kwargs) - self.search_url = reverse("project-list-view") + self.search_url = reverse("time-sheet-list") url = f"{reverse('personal-time-sheet-view',kwargs={'emp_id': self.request.user.employee_get.id})}" self.actions = [ { diff --git a/project/templates/cbv/tasks/task_actions.html b/project/templates/cbv/tasks/task_actions.html index 190000d89..54bf09d25 100644 --- a/project/templates/cbv/tasks/task_actions.html +++ b/project/templates/cbv/tasks/task_actions.html @@ -1,59 +1,51 @@ {% load i18n %} {% load taskfilters %}
- {% if request.user|task_crud_perm:instance or perms.project.view_task %} -
- - - - {% csrf_token %} - - - - {% if instance.is_active %} - - + {% if request.user|task_crud_perm:instance or perms.project.view_task %} +
+ + - {% else %} - - + {% if instance.is_active %} + + + + {% else %} + + + + {% endif %} +
+ {% csrf_token %} + +
+ + +
+ {% else %} +
+ + - {% endif %} -
- {% else %} -
- - - - + - - - -
-{% endif %} + + + +
+ {% endif %}
diff --git a/project/templates/cbv/tasks/task_detail_actions.html b/project/templates/cbv/tasks/task_detail_actions.html index f918e8acf..3fcded097 100644 --- a/project/templates/cbv/tasks/task_detail_actions.html +++ b/project/templates/cbv/tasks/task_detail_actions.html @@ -1,42 +1,27 @@ {% load i18n %} {% if request.user|task_crud_perm:instance or perms.project.view_task %} - - - {% trans "Edit" %} - - {% endif %} - - - {% trans " Time sheet" %} - - {% if request.user|task_crud_perm:instance or perms.project.view_task %} - - {% trans "Delete" %} - - {% endif %} + + + + {% trans "Edit" %} + +{% endif %} + + + + {% trans "Time sheet" %} + +{% if request.user|task_crud_perm:instance or perms.project.view_task %} + + + {% trans "Delete" %} + +{% endif %} diff --git a/project/templates/cbv/tasks/task_document.html b/project/templates/cbv/tasks/task_document.html index ed77d5f58..4ac9ac7ca 100644 --- a/project/templates/cbv/tasks/task_document.html +++ b/project/templates/cbv/tasks/task_document.html @@ -1,16 +1,10 @@ {% load i18n %} {% if instance.document %} -{% trans "Document" %} -
- -
+ {% trans "Document" %} +
+ + +
{% endif%} diff --git a/project/templates/cbv/tasks/task_filter.html b/project/templates/cbv/tasks/task_filter.html index 763ed2629..13d8f1ae3 100644 --- a/project/templates/cbv/tasks/task_filter.html +++ b/project/templates/cbv/tasks/task_filter.html @@ -1,40 +1,41 @@ {% load i18n %} {% load basefilters %} -
-
-
{% trans "Task" %}
-
-
-
-
+
+
+
+ {% trans "Task" %} +
+
+
+
+
{{form.task_manager}} -
-
+
+
{{form.stage}} -
-
-
+
+
+
{{form.project}} -
-
+
+
{{form.status}} -
-
-
+
+
+
{{form.end_till}} -
- -
+ +
+
diff --git a/project/templates/cbv/tasks/task_form.html b/project/templates/cbv/tasks/task_form.html index 7fa54dfac..335e82ebb 100644 --- a/project/templates/cbv/tasks/task_form.html +++ b/project/templates/cbv/tasks/task_form.html @@ -1,14 +1,9 @@
- - {% include 'generic/horilla_form.html' %} + {% include 'generic/horilla_form.html' %}
diff --git a/project/templates/cbv/tasks/task_template_view.html b/project/templates/cbv/tasks/task_template_view.html index 0c65ca75f..d01a9b275 100644 --- a/project/templates/cbv/tasks/task_template_view.html +++ b/project/templates/cbv/tasks/task_template_view.html @@ -42,8 +42,7 @@ .status-in_progress { - border-left: 3.4px solid orange; - !important; + border-left: 3.4px solid orange; !important; border-radius: 4px 0 0 4px; } @@ -52,7 +51,7 @@ -
+
diff --git a/project/templates/cbv/timesheet/actions.html b/project/templates/cbv/timesheet/actions.html index a0c9467ef..c68261776 100644 --- a/project/templates/cbv/timesheet/actions.html +++ b/project/templates/cbv/timesheet/actions.html @@ -1,74 +1,58 @@ -{% load i18n %} -{% load taskfilters %} +{% load i18n %} {% load taskfilters %} {% if request.user|time_sheet_crud_perm:instance or perms.project.change_timesheet or perms.project.delete_timesheet %} -
- - - - {% if request.task_id %} - - - - {% else %} - - - - {% endif %} -
- {% else %} -
- - +
+ + + + {% if request.task_id %} + + + + {% else %} + + + + {% endif %} +
+{% else %} + +
{% endif %} diff --git a/project/templates/cbv/timesheet/detail_actions.html b/project/templates/cbv/timesheet/detail_actions.html index 7232a4d10..c95cd8452 100644 --- a/project/templates/cbv/timesheet/detail_actions.html +++ b/project/templates/cbv/timesheet/detail_actions.html @@ -1,40 +1,23 @@ {% load i18n %} {% load basefilters %}
- - {% trans "Edit" %} - - {% if perms.project.view_timesheet or request.user|is_reportingmanager %} - - {% trans "View Timesheet Chart" %} - - {% endif %} - - {% trans "Delete" %} - -
+ + + {% trans "Edit" %} + + {% if perms.project.view_timesheet or request.user|is_reportingmanager %} + + + {% trans "View Timesheet Chart" %} + + {% endif %} + + + {% trans "Delete" %} + +
diff --git a/project/templates/cbv/timesheet/employee_field.html b/project/templates/cbv/timesheet/employee_field.html index c99cbec8c..80f3e70d2 100644 --- a/project/templates/cbv/timesheet/employee_field.html +++ b/project/templates/cbv/timesheet/employee_field.html @@ -1,33 +1,29 @@ {% load i18n %} - + {{form.employee_id}} diff --git a/project/templates/cbv/timesheet/filter.html b/project/templates/cbv/timesheet/filter.html index 0ac2d1832..abd72ebea 100644 --- a/project/templates/cbv/timesheet/filter.html +++ b/project/templates/cbv/timesheet/filter.html @@ -1,59 +1,59 @@ {% load i18n %}{% load basefilters%}
-
{% trans "Time Sheet" %}
-
-
-
-
- - {{form.project_id}} -
-
- - {{form.status}} -
-
+
{% trans "Time Sheet" %}
+
+
+
+
+ + {{form.project_id}} +
+
+ + {{form.status}} +
+
-
-
- - {{form.task}} +
+
+ + {{form.task}} +
+
+ + {{form.date}} +
+
+ {% if perms.project.view_timesheet or request.user|is_reportingmanager %} +
+
+ + {{form.employee_id}} +
+
+ {% endif %}
-
- - {{form.date}} -
-
- {% if perms.project.view_timesheet or request.user|is_reportingmanager %} -
-
- - {{form.employee_id}} -
-
- {% endif %}
-
-
{% trans "Advanced" %}
-
-
-
-
- - {{form.start_from}} +
{% trans "Advanced" %}
+
+
+
+
+ + {{form.start_from}} +
+
+
+
+ + {{form.end_till}} +
+
-
-
-
- - {{form.end_till}} -
-
-
-
+
diff --git a/project/templates/cbv/timesheet/task_field.html b/project/templates/cbv/timesheet/task_field.html index 2ec9766e6..418b90fb3 100644 --- a/project/templates/cbv/timesheet/task_field.html +++ b/project/templates/cbv/timesheet/task_field.html @@ -1,18 +1,15 @@ {% load i18n %} - -
-{{form.task_id}} + +
+ {{form.task_id}}
diff --git a/project/templates/cbv/timesheet/task_timesheet.html b/project/templates/cbv/timesheet/task_timesheet.html index fcbb7b7bb..5747c3505 100644 --- a/project/templates/cbv/timesheet/task_timesheet.html +++ b/project/templates/cbv/timesheet/task_timesheet.html @@ -1,32 +1,23 @@ - {% load i18n %} -
- {% include "generic/components.html" %}
- -
{% trans "Time sheet" %}
-
- {% comment %} for add timesheet {% endcomment %} + +
{% trans "Time sheet" %}
+
+ {% comment %} for add timesheet {% endcomment %}
- - - - {% include "generic/horilla_list_table.html" %} + onclick="event.stopPropagation(); $(this).parents().find('.oh-modal--show:last').toggleClass('oh-modal--show')" + id="close"> + + + {% include "generic/horilla_list_table.html" %}
diff --git a/project/templates/cbv/timesheet/timesheet.html b/project/templates/cbv/timesheet/timesheet.html index e91b3e715..51bdea458 100644 --- a/project/templates/cbv/timesheet/timesheet.html +++ b/project/templates/cbv/timesheet/timesheet.html @@ -4,53 +4,47 @@ {% block content %} -
+
{% include "generic/components.html" %} - + -
-
-
+
+
diff --git a/project/templates/cbv/timesheet/timesheet_nav.html b/project/templates/cbv/timesheet/timesheet_nav.html index ea0386458..b9a6ae4cc 100644 --- a/project/templates/cbv/timesheet/timesheet_nav.html +++ b/project/templates/cbv/timesheet/timesheet_nav.html @@ -2,7 +2,6 @@
{% include "generic/horilla_nav.html" %}
- ' + key = "HTTP_HX_REQUEST" + if key in request.META.keys(): + return render(request, "decorator_404.html") + return HttpResponse(script) + + return _function diff --git a/recruitment/decorators.py b/recruitment/decorators.py index e75f8171c..7cea0c536 100644 --- a/recruitment/decorators.py +++ b/recruitment/decorators.py @@ -105,6 +105,56 @@ def manager_can_enter(function, perm): return _function +@decorator_with_arguments +def all_manager_can_enter(function, perm): + """ + Decorator that checks if the user has the specified permission or is a manager. + + Args: + perm (str): The permission to check. + + Returns: + function: The decorated function. + + Raises: + None + + """ + + def _function(request, *args, **kwargs): + """ + Inner function that performs the permission and manager check. + + Args: + request (HttpRequest): The request object. + *args: Variable length argument list. + **kwargs: Arbitrary keyword arguments. + + Returns: + HttpResponse: The response from the decorated function. + + """ + user = request.user + employee = Employee.objects.filter(employee_user_id=user).first() + is_manager = ( + Stage.objects.filter(stage_managers=employee).exists() + or Recruitment.objects.filter(recruitment_managers=employee).exists() + or request.user.employee_get.onboardingstage_set.exists() + or request.user.employee_get.onboarding_task.exists() + ) + if user.has_perm(perm) or is_manager: + return function(request, *args, **kwargs) + messages.info(request, "You dont have permission.") + previous_url = request.META.get("HTTP_REFERER", "/") + script = f'' + key = "HTTP_HX_REQUEST" + if key in request.META.keys(): + return render(request, "decorator_404.html") + return HttpResponse(script) + + return _function + + @decorator_with_arguments def recruitment_manager_can_enter(function, perm): """ diff --git a/recruitment/methods.py b/recruitment/methods.py index 4c2497ebd..fd6952a9c 100644 --- a/recruitment/methods.py +++ b/recruitment/methods.py @@ -70,3 +70,15 @@ def update_rec_template_grp(upt_template_ids, template_groups, rec_id): ) for survey in rec_surveys_templates: survey.recruitment_ids.add(recruitment_obj) + + +def in_all_managers(request): + """ + Check the user in any recruitment/onboarding related managers + """ + return ( + request.user.employee_get.stage_set.exists() + or request.user.employee_get.recruitment_set.exists() + or request.user.employee_get.onboardingstage_set.exists() + or request.user.employee_get.onboarding_task.exists() + ) diff --git a/recruitment/templates/cbv/pipeline/stage_order.html b/recruitment/templates/cbv/pipeline/stage_order.html new file mode 100644 index 000000000..25398b4a6 --- /dev/null +++ b/recruitment/templates/cbv/pipeline/stage_order.html @@ -0,0 +1,70 @@ +{% load static i18n %} +
+ + {% trans "Update Stage Order" %} + + +
+
+
+
    + {% for stage in stages %} +
  • + + {{stage.sequence}}. + {{stage}} +
  • + {% endfor %} +
+
+ +
+
+
+ diff --git a/recruitment/templates/pipeline/pipeline_components/view_note.html b/recruitment/templates/pipeline/pipeline_components/view_note.html index 23df97b42..eba476d76 100644 --- a/recruitment/templates/pipeline/pipeline_components/view_note.html +++ b/recruitment/templates/pipeline/pipeline_components/view_note.html @@ -39,7 +39,7 @@ {{cand}}'s {% trans "Notes" %}
-{% if perms.recruitment.add_stagenote or request.user|is_stagemanager %} +{% if perms.recruitment.add_stagenote or request|is_any_manager %}
{% csrf_token %} diff --git a/recruitment/templatetags/recruitmentfilters.py b/recruitment/templatetags/recruitmentfilters.py index 1c4016d43..c6a154a0b 100644 --- a/recruitment/templatetags/recruitmentfilters.py +++ b/recruitment/templatetags/recruitmentfilters.py @@ -35,6 +35,19 @@ def is_stagemanager(user): return False +@register.filter(name="is_any_manager") +def is_any_manager(request): + """ + This method is used to check the employee is stage or recruitment manager + """ + return ( + request.user.employee_get.stage_set.exists() + or request.user.employee_get.recruitment_set.exists() + or request.user.employee_get.onboardingstage_set.exists() + or request.user.employee_get.onboarding_task.exists() + ) + + @register.filter(name="is_recruitmentmanager") def is_recruitmentmangers(user): """ diff --git a/recruitment/urls.py b/recruitment/urls.py index b3c6281dd..0b279755c 100644 --- a/recruitment/urls.py +++ b/recruitment/urls.py @@ -34,6 +34,11 @@ urlpatterns = [ path( "interview-view/", interview.InterviewViewPage.as_view(), name="interview-view" ), + path( + "get-cand-tasks//", + candidate_profile.CandidateProfileTasks.as_view(), + name="get-cand-task", + ), path( "interview-nav-view/", interview.InterviewNavView.as_view(), @@ -162,6 +167,11 @@ urlpatterns = [ ), # path("stage-create", views.stage, name="rec-stage-create"), path("stage-create", stage_view.StageFormView.as_view(), name="rec-stage-create"), + path( + "update-stage-seq//", + views.update_stage_order, + name="rec-update-stage-seq", + ), # path("stage-view", views.stage_view, name="rec-stage-view"), path("stage-data//", views.stage_data, name="stage-data"), path("stage-search", recruitment.views.search.stage_search, name="stage-search"), diff --git a/recruitment/views/views.py b/recruitment/views/views.py index a9ad84e2f..c622bc821 100644 --- a/recruitment/views/views.py +++ b/recruitment/views/views.py @@ -65,6 +65,7 @@ from horilla_documents.models import Document from notifications.signals import notify from recruitment.auth import CandidateAuthenticationBackend from recruitment.decorators import ( + all_manager_can_enter, candidate_login_required, manager_can_enter, recruitment_manager_can_enter, @@ -895,7 +896,7 @@ def candidate_stage_update(request, cand_id): @login_required @hx_request_required -@manager_can_enter(perm="recruitment.view_stagenote") +@all_manager_can_enter(perm="recruitment.view_stagenote") def view_note(request, cand_id): """ This method renders a template components to view candidate remark or note @@ -1252,6 +1253,39 @@ def stage_update(request, stage_id): return render(request, "stage/stage_update_form.html", {"form": form}) +@login_required +@recruitment_manager_can_enter("recruitment.change_stage") +def update_stage_order(request, pk): + """ + This method is used to update the stage sequence of the onboarding + """ + recruitment = Recruitment.objects.get(id=pk) + + if request.method == "POST": + try: + order = json.loads(request.POST.get("order", "[]")) + for index, stage_id in enumerate(order): + stage = recruitment.stage_set.get(id=stage_id) + stage.sequence = index + 1 + stage.save() + messages.success(request, "Sequence Updated Successfully") + return JsonResponse({"status": "success"}) + except Exception as e: + messages.error(request, "Error Updating Sequence..") + return JsonResponse({"status": "error", "message": str(e)}, status=400) + + stages = recruitment.stage_set.order_by("sequence") + + return render( + request, + "cbv/pipeline/stage_order.html", + { + "stages": stages, + "recruitment": recruitment, + }, + ) + + @login_required @hx_request_required @manager_can_enter("recruitment.add_candidate") @@ -1650,7 +1684,7 @@ def candidate_history_tab(request, pk, **kwargs): @login_required -@manager_can_enter(perm="recruitment.view_candidate") +@all_manager_can_enter(perm="recruitment.view_candidate") def candidate_onboarding_tab(request, pk, **kwargs): """ method for rendering onboarding tab @@ -1667,7 +1701,7 @@ def candidate_onboarding_tab(request, pk, **kwargs): @login_required -@manager_can_enter(perm="recruitment.view_candidate") +@all_manager_can_enter(perm="recruitment.view_candidate") def candidate_rating_tab(request, pk, **kwargs): """ method for rendering rating tab @@ -1716,7 +1750,7 @@ def scheduled_interview_tab(request, pk, **kwargs): @login_required -@manager_can_enter(perm="recruitment.view_candidate") +@all_manager_can_enter(perm="recruitment.view_candidate") def candidate_view_individual(request, cand_id, **kwargs): """ This method is used to view profile of candidate. diff --git a/report/templates/report/asset_report.html b/report/templates/report/asset_report.html index 08dd2440d..d6123eeae 100644 --- a/report/templates/report/asset_report.html +++ b/report/templates/report/asset_report.html @@ -2,6 +2,9 @@ {% block content %} {% load static %} {% load i18n %} +{% load widget_tweaks %} +{% load assets_custom_filter %} +
@@ -10,13 +13,105 @@

{% trans "Asset Reports" %}

- - + + +
+ + + +
+ +
+
+ + +
+
+
+ + + + + +
+
@@ -33,11 +128,6 @@ - - - - - @@ -45,15 +135,52 @@ - - // Initialize pivot table with Plotly enabled + + {% endblock content %} diff --git a/report/templates/report/payroll_report.html b/report/templates/report/payroll_report.html index 6bbf9c1a4..e4d996bc8 100644 --- a/report/templates/report/payroll_report.html +++ b/report/templates/report/payroll_report.html @@ -10,11 +10,231 @@

{% trans "Payroll Reports" %}

- - + + +
+ +
+ +
+ +
+
+ + +
+
+
+
+ + + + +
@@ -55,6 +275,10 @@ + + + + + + + + diff --git a/report/views/asset_report.py b/report/views/asset_report.py index 0e22d759d..71a8fbe00 100644 --- a/report/views/asset_report.py +++ b/report/views/asset_report.py @@ -1,7 +1,9 @@ from django.http import JsonResponse from django.shortcuts import render +from asset.filters import AssetFilter from asset.models import Asset +from base.models import Company from horilla_views.cbv_methods import login_required @@ -10,7 +12,21 @@ def asset_report(request): if not request.user.is_superuser: return render(request, "404.html") - return render(request, "report/asset_report.html") + company = "all" + selected_company = request.session.get("selected_company") + if selected_company != "all": + company = Company.objects.filter(id=selected_company).first() + + asset_filter_form = AssetFilter() + + return render( + request, + "report/asset_report.html", + { + "company": company, + "asset_filter_form": asset_filter_form.form, + }, + ) @login_required @@ -19,26 +35,45 @@ def asset_pivot(request): if not request.user.is_superuser: return render(request, "404.html") - data = Asset.objects.values( - "asset_name", - "asset_purchase_date", - "asset_tracking_id", - "asset_purchase_cost", - "asset_status", - "asset_category_id__asset_category_name", - "asset_lot_number_id__lot_number", - "expiry_date", - "assetassignment__assigned_by_employee_id__employee_work_info__department_id__department", - "assetassignment__assigned_by_employee_id__employee_work_info__job_position_id__job_position", - "assetassignment__assigned_by_employee_id__employee_work_info__job_role_id__job_role", - "assetassignment__assigned_by_employee_id__email", - "assetassignment__assigned_by_employee_id__phone", - "assetassignment__assigned_by_employee_id__gender", - "assetassignment__assigned_by_employee_id__employee_first_name", - "assetassignment__assigned_by_employee_id__employee_last_name", - "assetassignment__assigned_date", - "assetassignment__return_date", - "assetassignment__return_status", + qs = Asset.objects.all() + + if asset_name := request.GET.get("asset_name"): + qs = qs.filter(asset_name=asset_name) + if asset_tracking_id := request.GET.get("asset_tracking_id"): + qs = qs.filter(asset_tracking_id=asset_tracking_id) + if asset_purchase_cost := request.GET.get("asset_purchase_cost"): + qs = qs.filter(asset_purchase_cost=asset_purchase_cost) + if asset_lot_number_id := request.GET.get("asset_lot_number_id"): + qs = qs.filter(asset_lot_number_id=asset_lot_number_id) + if asset_category_id := request.GET.get("asset_category_id"): + qs = qs.filter(asset_category_id=asset_category_id) + if asset_status := request.GET.get("asset_status"): + qs = qs.filter(asset_status=asset_status) + if asset_purchase_date := request.GET.get("asset_purchase_date"): + qs = qs.filter(asset_purchase_date=asset_purchase_date) + + data = list( + qs.values( + "asset_name", + "asset_purchase_date", + "asset_tracking_id", + "asset_purchase_cost", + "asset_status", + "asset_category_id__asset_category_name", + "asset_lot_number_id__lot_number", + "expiry_date", + "assetassignment__assigned_by_employee_id__employee_work_info__department_id__department", + "assetassignment__assigned_by_employee_id__employee_work_info__job_position_id__job_position", + "assetassignment__assigned_by_employee_id__employee_work_info__job_role_id__job_role", + "assetassignment__assigned_by_employee_id__email", + "assetassignment__assigned_by_employee_id__phone", + "assetassignment__assigned_by_employee_id__gender", + "assetassignment__assigned_by_employee_id__employee_first_name", + "assetassignment__assigned_by_employee_id__employee_last_name", + "assetassignment__assigned_date", + "assetassignment__return_date", + "assetassignment__return_status", + ) ) data_list = [ { @@ -110,7 +145,7 @@ def asset_pivot(request): else "-" ), "Category": item["asset_category_id__asset_category_name"], - "Lot Number": item["asset_lot_number_id__lot_number"], + "Batch Number": item["asset_lot_number_id__lot_number"], "Tracking ID": item["asset_tracking_id"], "Expiry Date": item["expiry_date"] if item["expiry_date"] else "-", } diff --git a/report/views/payroll_report.py b/report/views/payroll_report.py index 4e7aaa783..f900137d4 100644 --- a/report/views/payroll_report.py +++ b/report/views/payroll_report.py @@ -1,7 +1,10 @@ from django.http import JsonResponse from django.shortcuts import render +from django.utils.dateparse import parse_date +from base.models import Company from horilla_views.cbv_methods import login_required +from payroll.filters import PayslipFilter from payroll.models.models import Payslip @@ -10,7 +13,21 @@ def payroll_report(request): if not request.user.is_superuser: return render(request, "404.html") - return render(request, "report/payroll_report.html") + company = "all" + selected_company = request.session.get("selected_company") + if selected_company != "all": + company = Company.objects.filter(id=selected_company).first() + + if request.user.has_perm("payroll.view_payslip"): + payslips = Payslip.objects.all() + else: + payslips = Payslip.objects.filter(employee_id__employee_user_id=request.user) + + filter_form = PayslipFilter(request.GET, payslips) + + return render( + request, "report/payroll_report.html", {"company": company, "f": filter_form} + ) @login_required @@ -22,28 +39,78 @@ def payroll_pivot(request): model_type = request.GET.get("model", "payslip") if model_type == "payslip": - data = Payslip.objects.values( - "id", # Include payslip ID to fetch pay_head_data later - "employee_id__employee_first_name", - "employee_id__employee_last_name", - "employee_id__gender", - "employee_id__email", - "employee_id__phone", - "start_date", - "end_date", - "contract_wage", - "basic_pay", - "gross_pay", - "deduction", - "net_pay", - "status", - "employee_id__employee_work_info__department_id__department", - "employee_id__employee_work_info__job_role_id__job_role", - "employee_id__employee_work_info__job_position_id__job_position", - "employee_id__employee_work_info__work_type_id__work_type", - "employee_id__employee_work_info__shift_id__employee_shift", - "employee_id__employee_work_info__employee_type_id__employee_type", - "employee_id__employee_work_info__experience", + qs = Payslip.objects.all() + + if employee_id := request.GET.getlist("employee_id"): + qs = qs.filter(employee_id__id__in=employee_id) + if status := request.GET.get("status"): + qs = qs.filter(status=status) + if group_name := request.GET.get("group_name"): + qs = qs.filter(group_name=group_name) + + start_date_from = parse_date(request.GET.get("start_date_from", "")) + start_date_to = parse_date(request.GET.get("start_date_till", "")) + if start_date_from: + qs = qs.filter(start_date__gte=start_date_from) + if start_date_to: + qs = qs.filter(start_date__lte=start_date_to) + + end_date_from = parse_date(request.GET.get("end_date_from", "")) + end_date_to = parse_date(request.GET.get("end_date_till", "")) + if end_date_from: + qs = qs.filter(end_date__gte=end_date_from) + if end_date_to: + qs = qs.filter(end_date__lte=end_date_to) + + # Gross Pay Range + gross_pay_gte = request.GET.get("gross_pay__gte") + gross_pay_lte = request.GET.get("gross_pay__lte") + if gross_pay_gte: + qs = qs.filter(gross_pay__gte=gross_pay_gte) + if gross_pay_lte: + qs = qs.filter(gross_pay__lte=gross_pay_lte) + + # Deduction Range + deduction_gte = request.GET.get("deduction__gte") + deduction_lte = request.GET.get("deduction__lte") + if deduction_gte: + qs = qs.filter(deduction__gte=deduction_gte) + if deduction_lte: + qs = qs.filter(deduction__lte=deduction_lte) + + # Net Pay Range + net_pay_gte = request.GET.get("net_pay__gte") + net_pay_lte = request.GET.get("net_pay__lte") + if net_pay_gte: + qs = qs.filter(net_pay__gte=net_pay_gte) + if net_pay_lte: + qs = qs.filter(net_pay__lte=net_pay_lte) + + data = list( + qs.values( + "id", # Include payslip ID to fetch pay_head_data later + "employee_id__employee_first_name", + "employee_id__employee_last_name", + "employee_id__gender", + "employee_id__email", + "employee_id__phone", + "start_date", + "end_date", + "contract_wage", + "basic_pay", + "gross_pay", + "deduction", + "net_pay", + "group_name", + "status", + "employee_id__employee_work_info__department_id__department", + "employee_id__employee_work_info__job_role_id__job_role", + "employee_id__employee_work_info__job_position_id__job_position", + "employee_id__employee_work_info__work_type_id__work_type", + "employee_id__employee_work_info__shift_id__employee_shift", + "employee_id__employee_work_info__employee_type_id__employee_type", + "employee_id__employee_work_info__experience", + ) ) choice_gender = { @@ -174,6 +241,7 @@ def payroll_pivot(request): ), "Payslip Start Date": item["start_date"], "Payslip End Date": item["end_date"], + "Batch Name": item["group_name"] if item["group_name"] else "-", "Contract Wage": round(float(item["contract_wage"] or 0), 2), "Basic Salary": round(float(item["basic_pay"] or 0), 2), "Gross Pay": round(float(item["gross_pay"] or 0), 2), @@ -193,21 +261,29 @@ def payroll_pivot(request): ) elif model_type == "allowance": - data = Payslip.objects.values( - "id", # Include payslip ID to fetch pay_head_data later - "employee_id__employee_first_name", - "employee_id__employee_last_name", - "employee_id__gender", - "employee_id__email", - "employee_id__phone", - "start_date", - "end_date", - "status", - "employee_id__employee_work_info__department_id__department", - "employee_id__employee_work_info__job_role_id__job_role", - "employee_id__employee_work_info__job_position_id__job_position", - "employee_id__employee_work_info__work_type_id__work_type", - "employee_id__employee_work_info__shift_id__employee_shift", + + payslips = Payslip.objects.all() + + payslip_filter = PayslipFilter(request.GET, queryset=payslips) + filtered_qs = payslip_filter.qs # This uses all custom filters you defined + + data = list( + filtered_qs.values( + "id", # Include payslip ID to fetch pay_head_data later + "employee_id__employee_first_name", + "employee_id__employee_last_name", + "employee_id__gender", + "employee_id__email", + "employee_id__phone", + "start_date", + "end_date", + "status", + "employee_id__employee_work_info__department_id__department", + "employee_id__employee_work_info__job_role_id__job_role", + "employee_id__employee_work_info__job_position_id__job_position", + "employee_id__employee_work_info__work_type_id__work_type", + "employee_id__employee_work_info__shift_id__employee_shift", + ) ) choice_gender = { @@ -245,7 +321,7 @@ def payroll_pivot(request): { "Pay Type": "Allowance", "Title": allowance["title"], - "Amount": allowance["amount"], + "Amount": round(float(allowance["amount"] or 0), 2), } ) @@ -257,7 +333,7 @@ def payroll_pivot(request): { "Pay Type": "Deduction", "Title": deduction["title"], - "Amount": deduction["amount"], + "Amount": round(float(deduction["amount"] or 0), 2), } ) diff --git a/report/views/pms_report.py b/report/views/pms_report.py index 39a0727ef..640a0b719 100644 --- a/report/views/pms_report.py +++ b/report/views/pms_report.py @@ -1,8 +1,11 @@ from django.http import JsonResponse from django.shortcuts import render +from base.models import Company from horilla_views.cbv_methods import login_required -from pms.models import EmployeeKeyResult, Feedback, Objective +from pms.filters import EmployeeObjectiveFilter, FeedbackFilter +from pms.models import EmployeeKeyResult, EmployeeObjective, Feedback, Objective +from pms.views import objective_filter_pagination @login_required @@ -10,7 +13,36 @@ def pms_report(request): if not request.user.is_superuser: return render(request, "404.html") - return render(request, "report/pms_report.html") + company = "all" + selected_company = request.session.get("selected_company") + if selected_company != "all": + company = Company.objects.filter(id=selected_company).first() + employee = request.user.employee_get + objective_own = EmployeeObjective.objects.filter( + employee_id=employee, archive=False + ) + objective_own = objective_own.distinct() + + feedback = request.GET.get("search") # if the search is none the filter will works + if feedback is None: + feedback = "" + self_feedback = Feedback.objects.filter(employee_id=employee).filter( + review_cycle__icontains=feedback + ) + initial_data = {"archive": False} + feedback_filter_own = FeedbackFilter( + request.GET or initial_data, queryset=self_feedback + ) + + context = objective_filter_pagination(request, objective_own) + cm = { + "company": company, + "feedback_filter_form": feedback_filter_own.form, + "emp_obj_form": EmployeeObjectiveFilter(), + } + context.update(cm) + + return render(request, "report/pms_report.html", context) @login_required @@ -21,22 +53,35 @@ def pms_pivot(request): model_type = request.GET.get("model", "objective") if model_type == "objective": - data = Objective.objects.values( - "title", - "managers__employee_first_name", - "managers__employee_last_name", - "assignees__employee_first_name", - "assignees__employee_last_name", - "key_result_id__title", - "key_result_id__target_value", - "duration_unit", - "duration", - "company_id__company", - "key_result_id__progress_type", - "key_result_id__duration", - "assignees__employee_work_info__department_id__department", - "assignees__employee_work_info__job_role_id__job_role", - "assignees__employee_work_info__job_position_id__job_position", + qs = Objective.objects.all() + + if managers := request.GET.getlist("managers"): + qs = qs.filter(managers__id__in=managers) + if assignees := request.GET.getlist("assignees"): + qs = qs.filter(assignees__id__in=assignees) + if duration := request.GET.get("duration"): + qs = qs.filter(duration=duration) + if key_result_id := request.GET.get("employee_objective__key_result_id"): + qs = qs.filter(key_result_id=key_result_id) + + data = list( + qs.values( + "title", + "managers__employee_first_name", + "managers__employee_last_name", + "assignees__employee_first_name", + "assignees__employee_last_name", + "key_result_id__title", + "key_result_id__target_value", + "duration_unit", + "duration", + "company_id__company", + "key_result_id__progress_type", + "key_result_id__duration", + "assignees__employee_work_info__department_id__department", + "assignees__employee_work_info__job_role_id__job_role", + "assignees__employee_work_info__job_position_id__job_position", + ) ) DURATION_UNIT = { "days": "Days", @@ -102,6 +147,24 @@ def pms_pivot(request): "feedback_answer__employee_id", ) + # ✅ FILTERS added here + if review_cycle := request.GET.get("review_cycle"): + feedbacks = feedbacks.filter(review_cycle=review_cycle) + if status := request.GET.get("status"): + feedbacks = feedbacks.filter(status=status) + if employee_id := request.GET.get("employee_id"): + feedbacks = feedbacks.filter(employee_id=employee_id) + if manager_id := request.GET.get("manager_id"): + feedbacks = feedbacks.filter(manager_id=manager_id) + if colleague_id := request.GET.get("colleague_id"): + feedbacks = feedbacks.filter(colleague_id=colleague_id) + if subordinate_id := request.GET.get("subordinate_id"): + feedbacks = feedbacks.filter(subordinate_id=subordinate_id) + if start_date := request.GET.get("start_date"): + feedbacks = feedbacks.filter(created_at__date__gte=start_date) + if end_date := request.GET.get("end_date"): + feedbacks = feedbacks.filter(created_at__date__lte=end_date) + for feedback in feedbacks: manager = ( f"{feedback.manager_id.employee_first_name} {feedback.manager_id.employee_last_name}" @@ -141,6 +204,7 @@ def pms_pivot(request): if not question_answers: data_list.append( { + "Title": feedback.review_cycle, "Manager": manager, "Employee": employee, "Answerable Employees": answerable_names, @@ -170,6 +234,7 @@ def pms_pivot(request): ) data_list.append( { + "Title": feedback.review_cycle, "Manager": manager, "Employee": employee, "Answerable Employees": answerable_names, @@ -190,23 +255,52 @@ def pms_pivot(request): } ) elif model_type == "employeeobjective": - data = EmployeeKeyResult.objects.values( - "key_result", - "employee_objective_id__employee_id__employee_first_name", - "employee_objective_id__employee_id__employee_last_name", - "employee_objective_id__objective_id__title", - "employee_objective_id__objective_id__duration_unit", - "employee_objective_id__objective_id__duration", - "start_value", - "current_value", - "target_value", - "start_date", - "end_date", - "status", - "progress_type", - "employee_objective_id__employee_id__employee_work_info__department_id__department", - "employee_objective_id__employee_id__employee_work_info__job_role_id__job_role", - "employee_objective_id__employee_id__employee_work_info__job_position_id__job_position", + + from django.utils.dateparse import parse_date + + qs = EmployeeKeyResult.objects.all() + + # Filter section + if assignees := request.GET.getlist("employee_id"): + qs = qs.filter(employee_objective_id__employee_id__id__in=assignees) + if key_result_id := request.GET.get("key_result_id"): + qs = qs.filter(key_result_id__id=key_result_id) + if status := request.GET.get("status"): + qs = qs.filter(status=status) + + start_date_from = parse_date(request.GET.get("start_date_from", "")) + start_date_to = parse_date(request.GET.get("start_date_till", "")) + if start_date_from: + qs = qs.filter(start_date__gte=start_date_from) + if start_date_to: + qs = qs.filter(start_date__lte=start_date_to) + + end_date_from = parse_date(request.GET.get("end_date_from", "")) + end_date_to = parse_date(request.GET.get("end_date_till", "")) + if end_date_from: + qs = qs.filter(end_date__gte=end_date_from) + if end_date_to: + qs = qs.filter(end_date__lte=end_date_to) + + data = list( + qs.values( + "key_result", + "employee_objective_id__employee_id__employee_first_name", + "employee_objective_id__employee_id__employee_last_name", + "employee_objective_id__objective_id__title", + "employee_objective_id__objective_id__duration_unit", + "employee_objective_id__objective_id__duration", + "start_value", + "current_value", + "target_value", + "start_date", + "end_date", + "status", + "progress_type", + "employee_objective_id__employee_id__employee_work_info__department_id__department", + "employee_objective_id__employee_id__employee_work_info__job_role_id__job_role", + "employee_objective_id__employee_id__employee_work_info__job_position_id__job_position", + ) ) DURATION_UNIT = { "days": "Days", @@ -270,4 +364,5 @@ def pms_pivot(request): else: data_list = [] + return JsonResponse(data_list, safe=False) diff --git a/requirments.txt b/requirments.txt new file mode 100644 index 0000000000000000000000000000000000000000..f2b931e9238c6b2f64727dbb335a646bd0562a3c GIT binary patch literal 9338 zcmeI1QEysD5QX=-Qhy3o04Gi!@=!IZs;HJMrBd3b2m^LV@P+u|*yP8zJ>MRNx!73h z54f^Ov3GXo%$c(@yW4;NnUtn%%A%~wZMi9L^)oCdWu(ur{9Jx2zm$vep-ju?GB2}o zSC;zkZM7R$n-}``v|3K|_er_bTGKMn_jzeGYf{z+Gq8WLvv13_tg*b>ulRcJd7}A^ zP)ud9)hZid416ySthTblCvn_pwUuygs_kj@c2dUG2aO*Q|vMsTGl7$1qJQN=_y zM%Cj~xMQ3=4a+P2Yd-No4rW@((c^)+hvRJ>ABZ#Od?G}P@}v64FL~t?9vPjMuN9NC zb5hQBXo!YYCgOckEv$zV;esKUX~gnIHh22XxU4QL&-U0d$JceewW8V96;F3Dq}Ns} z5(Qdh&C%ZT**@BtvO_d@F;n1y8#2%AS$PoZ<=%?9*8EHn!}FjDehX9rIz1Gn-^4B+!FHaCfWAiGQbpxm$GYrVIJ%VU1#iu=28-PVjJ^R6aO*GI)m zMG@5`l>XEbRd0oOvqQt$)@SCci<}y;77BLLK_;Jo=51c@LZatiWcUe1T(Lqo9Yt+@v6{oQjx2(Qo0>_+2iv#gB!v=F&-CG7e%n^Y)j)s244&RX2MzozD1 zbuxA>bd92Ng===K7+u6h^wjv5yZ9pp*ZJqU$r#xMjhJ92YH{aV=xEA z?%(%9*VLMC-mU)7?-wCsFS@Tdt5G$KZ`tWktO_cRoC^_qipqSk(=&6~mT{z0}^ z2Py?4R*mLUQCvTqTl$XIIF^4R0EIgfRqX0ue%8sA{*JO){Xt9xfA)g+m157mpJ%(= zlQ2X7nP}(1q`RFvg|&-CBNUt!@nCF$4+P!H;1gBE;7qcUm-<<$PFWq~u!|v7Naa|H zu~g&esNJP(e4d4kQPqQ0Pru(*y{;9DH34FF@tc~F#})(aSt2Lf&eCK1ZH>KA=0Okk zJQ2cuoT5L~eHAD8tfyIvvF5Dm9&RoIzg1@oIOhbBof|7F0*l`@-Y9F%D2U8KWNfRN z6c+4xpL@USZ`2ub>0TA_jA}$?@IvdqFCWXFHCnjfMCz3pI~VsCDyXS;~ptFl)P?aa|! zvHI+Qedq#joPOx|$$0wU9z|z{_wa-MUhnh=o*#82?;p( zv2M&+*2?*-EK~7}=iW!0#YepZdi*c$j70BHJOy-hP88@DJ?N%s- z?;x|6&|uhgZ`q-yT#0HAbK|-Ks^DXd$BuFg@P;BhmlH5A2%K0^v>C7^R+#}?l4rUX z=hRrw8e~V`s|q?M?%|x}9TB@KEBHrQ5e+XX#LIl|A=Y@%UwytADI-yfjF^br<>|h> z3dDTI9qnHlX|>A7J-^m6xlW|azn7n0W0+mZhtD^-EYkv9+S^ZnbA@0b1CQM}0aZ%5p_Z*?Q)HLQO->fesI@%L{> MzP->z{_oq-e?p90-2eap literal 0 HcmV?d00001