diff --git a/leave/forms.py b/leave/forms.py index 01e1cd83f..5b66d7750 100644 --- a/leave/forms.py +++ b/leave/forms.py @@ -320,14 +320,19 @@ class LeaveRequestCreationForm(ModelForm): unique_dates = list(set(month_year)) if f"{today.month}-{today.year}" in unique_dates: unique_dates.remove(f"{today.strftime('%m')}-{today.year}") - forcasted_leaves = available_leave.forcasted_leaves() - if leave_type_id.reset_based == "monthly": - if f"{today.year}-{today.strftime('%m')}" not in unique_dates: - for item in unique_dates: - try: - total_leave_days += forcasted_leaves[item] - except: - pass + + forcated_days = available_leave.forcasted_leaves(start_date) + total_leave_days = ( + available_leave.leave_type_id.carryforward_max + if available_leave.leave_type_id.carryforward_type + in ["carryforward", "carryforward expire"] + and available_leave.leave_type_id.carryforward_max < total_leave_days + else total_leave_days + ) + if available_leave.leave_type_id.carryforward_type == "no carryforward": + total_leave_days = 0 + total_leave_days += forcated_days + if not effective_requested_days <= total_leave_days: raise forms.ValidationError(_("Employee doesn't have enough leave days..")) @@ -338,19 +343,27 @@ class LeaveRequestCreationForm(ModelForm): super().__init__(*args, **kwargs) self.fields["leave_type_id"].widget.attrs.update( { - "onchange": "empleavetypeChange($(this))", + "hx-include": "#id_employee_id, #id_start_date", + "hx-target": "#availableLeaveCount", + "hx-swap": "outerHTML", + "hx-trigger": "change", + "hx-get": "/leave/employee-available-leave-count", } ) self.fields["employee_id"].widget.attrs.update( { "hx-target": "#id_leave_type_id_parent_div", "hx-trigger": "change", - "hx-get": "/leave/get-employee-leave-types", + "hx-get": "/leave/get-employee-leave-types?form=LeaveRequestCreationForm", } ) self.fields["start_date"].widget.attrs.update( { - "onchange": "dateChange($(this))", + "hx-include": "#id_employee_id, #id_leave_type_id", + "hx-target": "#availableLeaveCount", + "hx-swap": "outerHTML", + "hx-trigger": "change", + "hx-get": "/leave/employee-available-leave-count", } ) @@ -431,14 +444,19 @@ class LeaveRequestUpdationForm(ModelForm): unique_dates = list(set(month_year)) if f"{today.month}-{today.year}" in unique_dates: unique_dates.remove(f"{today.strftime('%m')}-{today.year}") - forcasted_leaves = available_leave.forcasted_leaves() - if leave_type_id.reset_based == "monthly": - if f"{today.year}-{today.strftime('%m')}" not in unique_dates: - for item in unique_dates: - try: - total_leave_days += forcasted_leaves[item] - except: - pass + + forcated_days = available_leave.forcasted_leaves(start_date) + total_leave_days = ( + available_leave.leave_type_id.carryforward_max + if available_leave.leave_type_id.carryforward_type + in ["carryforward", "carryforward expire"] + and available_leave.leave_type_id.carryforward_max < total_leave_days + else total_leave_days + ) + if available_leave.leave_type_id.carryforward_type == "no carryforward": + total_leave_days = 0 + total_leave_days += forcated_days + if not effective_requested_days <= total_leave_days: raise forms.ValidationError(_("Employee doesn't have enough leave days..")) @@ -449,19 +467,30 @@ class LeaveRequestUpdationForm(ModelForm): super().__init__(*args, **kwargs) self.fields["leave_type_id"].widget.attrs.update( { - "onchange": "empleavetypeChange($(this))", + "id": "id_request_update_leave_type_id", + "hx-include": "#id_request_update_employee_id, #id_request_udpate_start_date", + "hx-target": "#assinedLeaveAvailableCount", + "hx-swap": "outerHTML", + "hx-trigger": "change", + "hx-get": "/leave/employee-available-leave-count", } ) self.fields["employee_id"].widget.attrs.update( { + "id": "id_request_update_employee_id", "hx-target": "#id_leave_type_id_parent_div", "hx-trigger": "change", - "hx-get": "/leave/get-employee-leave-types", + "hx-get": "/leave/get-employee-leave-types?form=LeaveRequestUpdationForm", } ) self.fields["start_date"].widget.attrs.update( { - "onchange": "dateChange($(this))", + "id": "id_request_udpate_start_date", + "hx-include": "#id_request_update_employee_id, #id_request_update_leave_type_id", + "hx-target": "#assinedLeaveAvailableCount", + "hx-swap": "outerHTML", + "hx-trigger": "change", + "hx-get": "/leave/employee-available-leave-count", } ) diff --git a/leave/models.py b/leave/models.py index e59b94f39..e9545d369 100644 --- a/leave/models.py +++ b/leave/models.py @@ -223,6 +223,51 @@ class LeaveType(HorillaModel): url = self.icon.url return url + def leave_type_next_reset_date(self): + today = datetime.now().date() + + if not self.reset: + return None + + def get_reset_day(month, day): + return ( + calendar.monthrange(today.year, month)[1] + if day == "last day" + else int(day) + ) + + if self.reset_based == "yearly": + month, day = int(self.reset_month), get_reset_day( + int(self.reset_month), self.reset_day + ) + reset_date = datetime( + today.year + (datetime(today.year, month, day).date() < today), + month, + day, + ).date() + + elif self.reset_based == "monthly": + month = today.month + reset_date = datetime( + today.year, month, get_reset_day(month, self.reset_day) + ).date() + if reset_date < today: + month = (month % 12) + 1 + year = today.year + (month == 1) + reset_date = datetime( + year, month, get_reset_day(month, self.reset_day) + ).date() + + elif self.reset_based == "weekly": + target_weekday = WEEK_DAYS[self.reset_day] + days_until_reset = (target_weekday - today.weekday()) % 7 or 7 + reset_date = today + timedelta(days=days_until_reset) + + else: + reset_date = None + + return reset_date + def clean(self, *args, **kwargs): if self.is_compensatory_leave: if LeaveType.objects.filter(is_compensatory_leave=True).count() >= 1: @@ -321,25 +366,14 @@ class AvailableLeave(HorillaModel): def __str__(self): return f"{self.employee_id} | {self.leave_type_id}" - def forcasted_leaves(self): - forecasted_leave = {} - if self.leave_type_id.reset_based == "monthly": - today = datetime.now() - for i in range(1, 7): # Calculate for the next 6 months - next_month = today + relativedelta(months=i) - if self.leave_type_id.carryforward_max: - forecasted_leave[next_month.strftime("%Y-%m")] = ( - self.available_days - + min( - self.leave_type_id.carryforward_max, - (self.leave_type_id.total_days * i), - ) - ) - else: - forecasted_leave[next_month.strftime("%Y-%m")] = ( - self.available_days + (self.leave_type_id.total_days * i) - ) - return forecasted_leave + def forcasted_leaves(self, date): + if isinstance(date, str): + date = datetime.strptime(date, "%Y-%m-%d").date() + next_reset_date = self.leave_type_id.leave_type_next_reset_date() + if next_reset_date <= date: + return self.leave_type_id.total_days + + return 0 # Resetting carryforward days diff --git a/leave/templates/leave/leave_request/employee_available_leave_count.html b/leave/templates/leave/leave_request/employee_available_leave_count.html new file mode 100644 index 000000000..c117c6739 --- /dev/null +++ b/leave/templates/leave/leave_request/employee_available_leave_count.html @@ -0,0 +1,53 @@ +{% load i18n %} +{% load static %} + +
+ {% if not leave_type_id %} + + {% elif not available_leave %} +
+ {% trans "Leave type is not assigned for the selected employee." %} +
+ {% elif total_leave_days == 0.0 %} +
+ {% trans "Available Leaves" %} : {{ total_leave_days }} +
+ {% else %} +
+ {% trans "Available Leaves" %} : {{ total_leave_days }} + {% if forcated_days and forcated_days > 0 %} + + {% endif %} + + +
+ {% endif %} +
\ No newline at end of file diff --git a/leave/templates/leave/leave_request/leave_request_form.html b/leave/templates/leave/leave_request/leave_request_form.html index b161672b6..c503cd518 100644 --- a/leave/templates/leave/leave_request/leave_request_form.html +++ b/leave/templates/leave/leave_request/leave_request_form.html @@ -28,6 +28,7 @@ > +
{% trans form.leave_type_id.label %} {{form.leave_type_id}} + diff --git a/leave/templates/leave/leave_request/request_update_form.html b/leave/templates/leave/leave_request/request_update_form.html index ba4c76cb4..b6774553b 100644 --- a/leave/templates/leave/leave_request/request_update_form.html +++ b/leave/templates/leave/leave_request/request_update_form.html @@ -6,6 +6,7 @@ +
$(document).ready(function () { $("select").on("select2:select", function (e) { - $(".leave-message").hide(); $(this).closest("select")[0].dispatchEvent(new Event("change")); }); }); diff --git a/leave/templates/leave/user_leave/user_request_view.html b/leave/templates/leave/user_leave/user_request_view.html index 3152206f8..3f5bb9878 100644 --- a/leave/templates/leave/user_leave/user_request_view.html +++ b/leave/templates/leave/user_leave/user_request_view.html @@ -408,63 +408,6 @@ }); }); - function typeChange(selectElement) { - var selectedLeavetype =selectElement.val() - let parentForm = selectElement.parents().closest("form") - var employeeId = parentForm.find('[name = employee_id]').val() - $.ajax({ - type: "post", - url: "{% url 'employee-leave-details' %}", - data: { - csrfmiddlewaretoken: getCookie("csrftoken"), - "leave_type":selectedLeavetype, - "employee_id":employeeId, - }, - success: function (response) { - - // Assuming parentForm is a reference to the form containing the element to update - var messageDiv = parentForm.find(".leave-message"); - - // Check if the messageDiv already exists, if not create it - if (!messageDiv.length) { - messageDiv = $("
"); - parentForm.prepend(messageDiv); - } - // Checking leave type is selected in the form or not - if (response.leave_count === ''){ - messageDiv.hide() - } - else if (response.leave_count === 0.0){ - messageDiv.show() - messageDiv.text("Available Leaves : " + response.leave_count); - messageDiv.css({ - 'background-color': 'rgb(229 79 56 / 17%)', - 'border': '2px solid hsl(8,77%,56%)', - 'border-radius': '18px', - 'padding': '10px', - 'font-weight': 'bold', - 'margin-bottom': '15px', - 'width': '35%' - }); - } - else{ - messageDiv.show() - // Set the message content and apply styling - messageDiv.text("Available Leaves : " + response.leave_count); - messageDiv.css({ - 'background-color': '#dff0d8', - 'border': '2px solid #3c763d', - 'border-radius': '18px', - 'padding': '10px', - 'font-weight': 'bold', - 'margin-bottom': '15px', - 'width': '35%' - }); - } - - } - }); - } function enlargeImage(src,$element) { $(".enlargeImageContainer").empty() diff --git a/leave/threading.py b/leave/threading.py index 8433c92da..0accd46df 100644 --- a/leave/threading.py +++ b/leave/threading.py @@ -26,32 +26,33 @@ class LeaveMailSendThread(Thread): if leave_request_id != "#": link = int(leave_request_id) for recipient in recipients: - html_message = render_to_string( - "base/mail_templates/leave_request_template.html", - { - "link": link, - "instance": recipient, - "host": host, - "protocol": protocol, - "subject": subject, - "content": content, - }, - ) - - email = EmailMessage( - subject, - html_message, - email_backend.dynamic_from_email_with_display_name, - [recipient.email], - ) - email.content_subtype = "html" - try: - email.send() - except: - messages.error( - self.request, f"Mail not sent to {recipient.get_full_name()}" + if recipient: + html_message = render_to_string( + "base/mail_templates/leave_request_template.html", + { + "link": link, + "instance": recipient, + "host": host, + "protocol": protocol, + "subject": subject, + "content": content, + }, ) + email = EmailMessage( + subject, + html_message, + email_backend.dynamic_from_email_with_display_name, + [recipient.email], + ) + email.content_subtype = "html" + try: + email.send() + except: + messages.error( + self.request, f"Mail not sent to {recipient.get_full_name()}" + ) + def run(self) -> None: super().run() if self.type == "request": diff --git a/leave/urls.py b/leave/urls.py index 5312ce2e6..0d82f170b 100644 --- a/leave/urls.py +++ b/leave/urls.py @@ -267,9 +267,9 @@ urlpatterns = [ name="user-request-select-filter", ), path( - "employee-leave-details", - views.employee_leave_details, - name="employee-leave-details", + "employee-available-leave-count", + views.employee_available_leave_count, + name="employee-available-leave-count", ), path( "leave-request-add-comment//", diff --git a/static/images/ui/info.png b/static/images/ui/info.png new file mode 100644 index 000000000..a762a6c7a Binary files /dev/null and b/static/images/ui/info.png differ diff --git a/static/index/index.js b/static/index/index.js index f12d0e671..10e3c549e 100644 --- a/static/index/index.js +++ b/static/index/index.js @@ -107,231 +107,6 @@ nav.after( ); - -function empleavetypeChange(selectElement) { - var selectedLeavetype =selectElement.val() - let parentForm = selectElement.parents().closest("form") - var employeeId = parentForm.find('[name = employee_id]').val() - var start_date = parentForm.find('[name = start_date_id]').val() - $.ajax({ - type: "post", - url: "/leave/employee-leave-details", - data: { - csrfmiddlewaretoken: getCookie("csrftoken"), - "leave_type":selectedLeavetype, - "employee_id":employeeId, - "date":start_date, - }, - success: function (response) { - - // Assuming parentForm is a reference to the form containing the element to update - var messageDiv = parentForm.find(".leave-message"); - - // Check if the messageDiv already exists, if not create it - if (!messageDiv.length) { - messageDiv = $("
"); - parentForm.prepend(messageDiv); - } - // Checking leave type is selected in the form or not - if (response.leave_count != '' && response.employee != ''){ - messageDiv.show() - messageDiv.text("Available Leaves : " + response.leave_count); - messageDiv.css({ - 'background-color': '#dff0d8', - 'border': '2px solid #3c763d', - 'border-radius': '18px', - 'padding': '10px', - 'font-weight': 'bold', - 'margin-bottom': '15px', - 'width': '35%' - }); - } - else if ( selectedLeavetype === ''){ - messageDiv.hide() - } - else if (selectedLeavetype != '' && response.leave_count === '' && response.employee != ''){ - messageDiv.show() - messageDiv.text("Leave type is not assigned for selecetd employee."); - messageDiv.css({ - 'background-color': 'rgb(229 79 56 / 17%)', - 'border': '2px solid hsl(8,77%,56%)', - 'border-radius': '18px', - 'padding': '10px', - 'font-weight': 'bold', - 'margin-bottom': '15px', - 'width': 'auto' - }); - } - else if (response.leave_count === 0.0){ - messageDiv.show() - messageDiv.text("Available Leaves : " + response.leave_count); - messageDiv.css({ - 'background-color': 'rgb(229 79 56 / 17%)', - 'border': '2px solid hsl(8,77%,56%)', - 'border-radius': '18px', - 'padding': '10px', - 'font-weight': 'bold', - 'margin-bottom': '15px', - 'width': '35%' - }); - } - else{ - messageDiv.hide() - } - - } - }); -} - - -function employeeChange(selectElement) { - var employeeId =selectElement.val() - let parentForm = selectElement.parents().closest("form") - var leavetypeId = parentForm.find('[name = leave_type_id]').val() - var start_date = parentForm.find('[name = start_date_id]').val() - $.ajax({ - type: "post", - url: "/leave/employee-leave-details", - data: { - csrfmiddlewaretoken: getCookie("csrftoken"), - "leave_type":leavetypeId, - "employee_id":employeeId, - "date":start_date, - }, - success: function (response) { - - // Assuming parentForm is a reference to the form containing the element to update - var messageDiv = parentForm.find(".leave-message"); - - // Check if the messageDiv already exists, if not create it - if (!messageDiv.length) { - messageDiv = $("
"); - parentForm.prepend(messageDiv); - } - // Checking leave type is selected in the form or not - if (response.leave_count != '' && response.employee != ''){ - messageDiv.show() - messageDiv.text("Available Leaves : " + response.leave_count); - messageDiv.css({ - 'background-color': '#dff0d8', - 'border': '2px solid #3c763d', - 'border-radius': '18px', - 'padding': '10px', - 'font-weight': 'bold', - 'margin-bottom': '15px', - 'width': '35%' - }); - } - else if ( leavetypeId === ''){ - messageDiv.hide() - } - else if (leavetypeId != '' && response.leave_count === '' && response.employee != ''){ - messageDiv.show() - messageDiv.text("Leave type is not assigned for selecetd employee."); - messageDiv.css({ - 'background-color': 'rgb(229 79 56 / 17%)', - 'border': '2px solid hsl(8,77%,56%)', - 'border-radius': '18px', - 'padding': '10px', - 'font-weight': 'bold', - 'margin-bottom': '15px', - 'width': 'auto' - }); - } - else if (response.leave_count === 0.0){ - messageDiv.show() - messageDiv.text("Available Leaves : " + response.leave_count); - messageDiv.css({ - 'background-color': 'rgb(229 79 56 / 17%)', - 'border': '2px solid hsl(8,77%,56%)', - 'border-radius': '18px', - 'padding': '10px', - 'font-weight': 'bold', - 'margin-bottom': '15px', - 'width': '35%' - }); - } - else{ - messageDiv.hide() - } - - } - }); -} - -function dateChange(selectElement) { - let parentForm = selectElement.parents().closest("form") - var employeeId = parentForm.find('[name = employee_id]').val() - var leavetypeId = parentForm.find('[name = leave_type_id]').val() - var start_date = selectElement.val() - $.ajax({ - type: "post", - url: "/leave/employee-leave-details", - data: { - csrfmiddlewaretoken: getCookie("csrftoken"), - "leave_type":leavetypeId, - "employee_id":employeeId, - "date": start_date, - }, - success: function (response) { - // Assuming parentForm is a reference to the form containing the element to update - var messageDiv = parentForm.find(".leave-message"); - - // Check if the messageDiv already exists, if not create it - if (!messageDiv.length) { - messageDiv = $("
"); - parentForm.prepend(messageDiv); - } - // Checking leave type is selected in the form or not - if (response.leave_count != '' && response.employee != '') { - messageDiv.show() - messageDiv.text("Available Leaves : " + response.leave_count); - messageDiv.css({ - 'background-color': '#dff0d8', - 'border': '2px solid #3c763d', - 'border-radius': '18px', - 'padding': '10px', - 'font-weight': 'bold', - 'margin-bottom': '15px', - 'width': '35%' - }); - } - else if ( leavetypeId === ''){ - messageDiv.hide() - } - else if (leavetypeId != '' && response.leave_count === '' && response.employee != ''){ - messageDiv.show() - messageDiv.text("Leave type is not assigned for selecetd employee."); - messageDiv.css({ - 'background-color': 'rgb(229 79 56 / 17%)', - 'border': '2px solid hsl(8,77%,56%)', - 'border-radius': '18px', - 'padding': '10px', - 'font-weight': 'bold', - 'margin-bottom': '15px', - 'width': 'auto' - }); - } - else if (response.leave_count === 0.0){ - messageDiv.show() - messageDiv.text("Available Leaves : " + response.leave_count); - messageDiv.css({ - 'background-color': 'rgb(229 79 56 / 17%)', - 'border': '2px solid hsl(8,77%,56%)', - 'border-radius': '18px', - 'padding': '10px', - 'font-weight': 'bold', - 'margin-bottom': '15px', - 'width': '35%' - }); - } - else{ - messageDiv.hide() - } - } - }); -} - function shiftChange(selectElement) { var shiftId =selectElement.val() let parentForm = selectElement.parents().closest("form")