2024-04-02 10:06:29 +05:30
|
|
|
"""
|
|
|
|
|
This module provides Horilla ModelForms for creating and managing leave-related data,
|
|
|
|
|
including leave type, leave request, leave allocation request, holidays and company leaves.
|
|
|
|
|
"""
|
|
|
|
|
|
2023-08-14 14:47:15 +05:30
|
|
|
import re
|
2023-11-24 16:59:15 +05:30
|
|
|
import uuid
|
2024-05-07 12:23:36 +05:30
|
|
|
from datetime import date, datetime
|
|
|
|
|
from typing import Any
|
|
|
|
|
|
2023-08-14 14:47:15 +05:30
|
|
|
from django import forms
|
2024-05-07 12:23:36 +05:30
|
|
|
from django.core.exceptions import ValidationError
|
2023-05-10 15:06:57 +05:30
|
|
|
from django.forms import ModelForm
|
|
|
|
|
from django.forms.widgets import TextInput
|
2023-10-09 16:13:10 +05:30
|
|
|
from django.template.loader import render_to_string
|
2024-05-07 12:23:36 +05:30
|
|
|
from django.utils.translation import gettext_lazy as _
|
|
|
|
|
|
2024-02-14 14:58:09 +05:30
|
|
|
from base import thread_local_middleware
|
2024-04-02 10:06:29 +05:30
|
|
|
from base.methods import reload_queryset
|
2024-04-03 10:26:31 +05:30
|
|
|
from base.models import Department
|
2023-11-24 16:59:15 +05:30
|
|
|
from employee.filters import EmployeeFilter
|
2024-01-30 19:09:18 +05:30
|
|
|
from employee.forms import MultipleFileField
|
2023-05-10 15:06:57 +05:30
|
|
|
from employee.models import Employee
|
2024-05-07 12:23:36 +05:30
|
|
|
from horilla_widgets.forms import HorillaForm, HorillaModelForm
|
|
|
|
|
from horilla_widgets.widgets.horilla_multi_select_field import HorillaMultiSelectField
|
|
|
|
|
from horilla_widgets.widgets.select_widgets import HorillaMultiSelectWidget
|
|
|
|
|
|
|
|
|
|
from .methods import (
|
|
|
|
|
calculate_requested_days,
|
|
|
|
|
company_leave_dates_list,
|
|
|
|
|
holiday_dates_list,
|
|
|
|
|
leave_requested_dates,
|
|
|
|
|
)
|
2023-11-13 12:34:15 +05:30
|
|
|
from .models import (
|
|
|
|
|
AvailableLeave,
|
|
|
|
|
CompanyLeave,
|
2024-05-07 12:23:36 +05:30
|
|
|
Holiday,
|
2023-11-13 12:34:15 +05:30
|
|
|
LeaveAllocationRequest,
|
2024-01-16 14:21:53 +05:30
|
|
|
LeaveallocationrequestComment,
|
2024-05-07 12:23:36 +05:30
|
|
|
LeaveRequest,
|
2024-01-15 10:15:58 +05:30
|
|
|
LeaverequestComment,
|
2024-01-30 19:09:18 +05:30
|
|
|
LeaverequestFile,
|
2024-05-07 12:23:36 +05:30
|
|
|
LeaveType,
|
2024-04-03 10:26:31 +05:30
|
|
|
RestrictLeave,
|
2023-11-13 12:34:15 +05:30
|
|
|
)
|
2023-05-10 15:06:57 +05:30
|
|
|
|
|
|
|
|
CHOICES = [("yes", _("Yes")), ("no", _("No"))]
|
|
|
|
|
|
2023-07-11 12:14:08 +05:30
|
|
|
|
2023-05-10 15:06:57 +05:30
|
|
|
class ModelForm(forms.ModelForm):
|
2024-04-02 10:06:29 +05:30
|
|
|
"""
|
|
|
|
|
Customized ModelForm class with additional functionality for field customization
|
|
|
|
|
based on the type of widget and setting initial values based on the current request.
|
|
|
|
|
"""
|
|
|
|
|
|
2023-05-10 15:06:57 +05:30
|
|
|
def __init__(self, *args, **kwargs):
|
2024-04-02 10:06:29 +05:30
|
|
|
"""
|
|
|
|
|
Initializes the ModelForm instance.
|
|
|
|
|
|
|
|
|
|
This method customizes field attributes such as CSS classes and placeholders
|
|
|
|
|
based on the type of widget. It also sets initial values for specific fields
|
|
|
|
|
based on the current request, particularly for 'employee_id' and 'company_id' fields.
|
|
|
|
|
"""
|
2023-05-10 15:06:57 +05:30
|
|
|
super().__init__(*args, **kwargs)
|
2024-02-14 14:58:09 +05:30
|
|
|
request = getattr(thread_local_middleware._thread_locals, "request", None)
|
2023-12-01 15:36:51 +05:30
|
|
|
reload_queryset(self.fields)
|
2023-05-10 15:06:57 +05:30
|
|
|
for field_name, field in self.fields.items():
|
|
|
|
|
widget = field.widget
|
|
|
|
|
|
|
|
|
|
if isinstance(widget, (forms.DateInput)):
|
2023-07-11 12:14:08 +05:30
|
|
|
field.widget.attrs.update({"class": "oh-input oh-calendar-input w-100"})
|
2024-02-14 14:58:09 +05:30
|
|
|
field.initial = date.today()
|
2023-07-11 12:14:08 +05:30
|
|
|
elif isinstance(
|
|
|
|
|
widget, (forms.NumberInput, forms.EmailInput, forms.TextInput)
|
2023-08-14 14:47:15 +05:30
|
|
|
):
|
2023-07-11 12:14:08 +05:30
|
|
|
field.widget.attrs.update(
|
2023-08-14 14:47:15 +05:30
|
|
|
{"class": "oh-input w-100", "placeholder": field.label}
|
2023-07-11 12:14:08 +05:30
|
|
|
)
|
|
|
|
|
elif isinstance(widget, (forms.Select,)):
|
|
|
|
|
field.widget.attrs.update(
|
|
|
|
|
{"class": "oh-select oh-select-2 select2-hidden-accessible"}
|
|
|
|
|
)
|
|
|
|
|
elif isinstance(widget, (forms.Textarea)):
|
|
|
|
|
field.widget.attrs.update(
|
|
|
|
|
{
|
|
|
|
|
"class": "oh-input w-100",
|
2023-08-14 14:47:15 +05:30
|
|
|
"placeholder": field.label,
|
2023-07-11 12:14:08 +05:30
|
|
|
"rows": 2,
|
|
|
|
|
"cols": 40,
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
elif isinstance(
|
|
|
|
|
widget,
|
|
|
|
|
(
|
|
|
|
|
forms.CheckboxInput,
|
|
|
|
|
forms.CheckboxSelectMultiple,
|
|
|
|
|
),
|
|
|
|
|
):
|
|
|
|
|
field.widget.attrs.update({"class": "oh-switch__checkbox"})
|
2024-02-23 12:06:56 +05:30
|
|
|
try:
|
|
|
|
|
self.fields["employee_id"].initial = request.user.employee_get
|
2024-02-14 14:58:09 +05:30
|
|
|
except:
|
|
|
|
|
pass
|
|
|
|
|
|
2024-02-23 12:06:56 +05:30
|
|
|
try:
|
2024-02-14 14:58:09 +05:30
|
|
|
self.fields["company_id"].initial = request.user.employee_get.get_company
|
|
|
|
|
except:
|
|
|
|
|
pass
|
2023-05-10 15:06:57 +05:30
|
|
|
|
|
|
|
|
|
|
|
|
|
class ConditionForm(forms.ModelForm):
|
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
|
super().__init__(*args, **kwargs)
|
2024-02-14 14:58:09 +05:30
|
|
|
request = getattr(thread_local_middleware._thread_locals, "request", None)
|
2023-12-01 15:36:51 +05:30
|
|
|
reload_queryset(self.fields)
|
2023-05-10 15:06:57 +05:30
|
|
|
for field_name, field in self.fields.items():
|
|
|
|
|
widget = field.widget
|
2023-07-11 12:14:08 +05:30
|
|
|
if isinstance(widget, (forms.Select,)):
|
2024-02-23 12:06:56 +05:30
|
|
|
field.widget.attrs["style"] = (
|
|
|
|
|
"width:100%; height:50px;border: 1px solid hsl(213deg,22%,84%);border-radius: 0rem;padding: 0.8rem 1.25rem;"
|
|
|
|
|
)
|
2023-05-10 15:06:57 +05:30
|
|
|
elif isinstance(widget, (forms.DateInput)):
|
2023-07-11 12:14:08 +05:30
|
|
|
field.widget.attrs.update({"class": "oh-input oh-calendar-input w-100"})
|
2024-02-14 14:58:09 +05:30
|
|
|
field.initial = date.today()
|
|
|
|
|
|
2023-07-11 12:14:08 +05:30
|
|
|
elif isinstance(
|
|
|
|
|
widget, (forms.NumberInput, forms.EmailInput, forms.TextInput)
|
|
|
|
|
):
|
|
|
|
|
field.widget.attrs.update(
|
|
|
|
|
{"class": "oh-input w-100", "placeholder": field.label}
|
|
|
|
|
)
|
|
|
|
|
elif isinstance(widget, (forms.Textarea)):
|
|
|
|
|
field.widget.attrs.update(
|
|
|
|
|
{
|
|
|
|
|
"class": "oh-input w-100",
|
|
|
|
|
"placeholder": field.label,
|
|
|
|
|
"rows": 2,
|
|
|
|
|
"cols": 40,
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
elif isinstance(
|
|
|
|
|
widget,
|
|
|
|
|
(
|
|
|
|
|
forms.CheckboxInput,
|
|
|
|
|
forms.CheckboxSelectMultiple,
|
|
|
|
|
),
|
|
|
|
|
):
|
|
|
|
|
field.widget.attrs.update({"class": "oh-switch__checkbox"})
|
2024-02-23 12:06:56 +05:30
|
|
|
try:
|
|
|
|
|
self.fields["employee_id"].initial = request.user.employee_get
|
2024-02-14 14:58:09 +05:30
|
|
|
except:
|
|
|
|
|
pass
|
2023-05-10 15:06:57 +05:30
|
|
|
|
2024-02-23 12:06:56 +05:30
|
|
|
try:
|
2024-02-14 14:58:09 +05:30
|
|
|
self.fields["company_id"].initial = request.user.employee_get.get_company
|
|
|
|
|
except:
|
|
|
|
|
pass
|
2023-05-10 15:06:57 +05:30
|
|
|
|
2024-02-23 12:06:56 +05:30
|
|
|
|
2023-05-10 15:06:57 +05:30
|
|
|
class LeaveTypeForm(ConditionForm):
|
2023-07-11 12:14:08 +05:30
|
|
|
|
2023-05-10 15:06:57 +05:30
|
|
|
class Meta:
|
|
|
|
|
model = LeaveType
|
2023-07-11 12:14:08 +05:30
|
|
|
fields = "__all__"
|
2024-03-25 10:08:53 +00:00
|
|
|
exclude = ["is_active"]
|
2023-05-10 15:06:57 +05:30
|
|
|
labels = {
|
2023-07-11 12:14:08 +05:30
|
|
|
"name": _("Name"),
|
2023-05-10 15:06:57 +05:30
|
|
|
}
|
|
|
|
|
widgets = {
|
2023-07-11 12:14:08 +05:30
|
|
|
"color": TextInput(attrs={"type": "color", "style": "height:40px;"}),
|
2023-10-09 11:58:52 +05:30
|
|
|
"period_in": forms.HiddenInput(),
|
|
|
|
|
"total_days": forms.HiddenInput(),
|
2023-05-10 15:06:57 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def clean(self):
|
|
|
|
|
cleaned_data = super().clean()
|
2023-07-11 12:14:08 +05:30
|
|
|
if "employee_id" in self.errors:
|
|
|
|
|
del self.errors["employee_id"]
|
|
|
|
|
if "exceed_days" in self.errors:
|
|
|
|
|
del self.errors["exceed_days"]
|
2023-05-10 15:06:57 +05:30
|
|
|
return cleaned_data
|
2023-07-11 12:14:08 +05:30
|
|
|
|
2023-05-10 15:06:57 +05:30
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
|
super().__init__(*args, **kwargs)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class UpdateLeaveTypeForm(ConditionForm):
|
|
|
|
|
|
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
|
super(UpdateLeaveTypeForm, self).__init__(*args, **kwargs)
|
|
|
|
|
|
|
|
|
|
empty_fields = []
|
|
|
|
|
for field_name, field_value in self.instance.__dict__.items():
|
2023-07-11 12:14:08 +05:30
|
|
|
if field_value is None or field_value == "":
|
|
|
|
|
if field_name.endswith("_id"):
|
|
|
|
|
foreign_key_field_name = re.sub("_id$", "", field_name)
|
2023-05-10 15:06:57 +05:30
|
|
|
empty_fields.append(foreign_key_field_name)
|
|
|
|
|
empty_fields.append(field_name)
|
2023-07-11 12:14:08 +05:30
|
|
|
|
2023-05-10 15:06:57 +05:30
|
|
|
for index, visible in enumerate(self.visible_fields()):
|
|
|
|
|
if list(self.fields.keys())[index] in empty_fields:
|
2024-02-23 12:06:56 +05:30
|
|
|
visible.field.widget.attrs["style"] = (
|
|
|
|
|
"display:none;width:100%; height:50px;border: 1px solid hsl(213deg,22%,84%);border-radius: 0rem;padding: 0.8rem 1.25rem;"
|
|
|
|
|
)
|
2023-07-11 12:14:08 +05:30
|
|
|
visible.field.widget.attrs["data-hidden"] = True
|
2023-05-10 15:06:57 +05:30
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
|
model = LeaveType
|
2023-07-11 12:14:08 +05:30
|
|
|
fields = "__all__"
|
2024-03-25 10:08:53 +00:00
|
|
|
exclude = ["period_in", "total_days", "is_active"]
|
2023-05-10 15:06:57 +05:30
|
|
|
widgets = {
|
2023-07-11 12:14:08 +05:30
|
|
|
"color": TextInput(attrs={"type": "color", "style": "height:40px;"}),
|
2023-05-10 15:06:57 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def clean(self):
|
|
|
|
|
cleaned_data = super().clean()
|
2023-07-11 12:14:08 +05:30
|
|
|
if "exceed_days" in self.errors:
|
|
|
|
|
del self.errors["exceed_days"]
|
2023-05-10 15:06:57 +05:30
|
|
|
return cleaned_data
|
|
|
|
|
|
2024-02-23 12:06:56 +05:30
|
|
|
|
|
|
|
|
def cal_effective_requested_days(start_date, end_date, leave_type_id, requested_days):
|
2023-11-20 14:22:43 +05:30
|
|
|
requested_dates = leave_requested_dates(start_date, end_date)
|
|
|
|
|
holidays = Holiday.objects.all()
|
|
|
|
|
holiday_dates = holiday_dates_list(holidays)
|
|
|
|
|
company_leaves = CompanyLeave.objects.all()
|
|
|
|
|
company_leave_dates = company_leave_dates_list(company_leaves, start_date)
|
|
|
|
|
if (
|
|
|
|
|
leave_type_id.exclude_company_leave == "yes"
|
|
|
|
|
and leave_type_id.exclude_holiday == "yes"
|
|
|
|
|
):
|
|
|
|
|
total_leaves = list(set(holiday_dates + company_leave_dates))
|
|
|
|
|
total_leave_count = sum(
|
|
|
|
|
requested_date in total_leaves for requested_date in requested_dates
|
|
|
|
|
)
|
|
|
|
|
requested_days = requested_days - total_leave_count
|
|
|
|
|
else:
|
|
|
|
|
holiday_count = 0
|
|
|
|
|
if leave_type_id.exclude_holiday == "yes":
|
|
|
|
|
for requested_date in requested_dates:
|
|
|
|
|
if requested_date in holiday_dates:
|
|
|
|
|
holiday_count += 1
|
|
|
|
|
requested_days = requested_days - holiday_count
|
|
|
|
|
if leave_type_id.exclude_company_leave == "yes":
|
|
|
|
|
company_leave_count = sum(
|
|
|
|
|
requested_date in company_leave_dates
|
|
|
|
|
for requested_date in requested_dates
|
|
|
|
|
)
|
|
|
|
|
requested_days = requested_days - company_leave_count
|
|
|
|
|
return requested_days
|
2023-05-10 15:06:57 +05:30
|
|
|
|
2024-02-23 12:06:56 +05:30
|
|
|
|
2023-05-10 15:06:57 +05:30
|
|
|
class LeaveRequestCreationForm(ModelForm):
|
2023-07-11 12:14:08 +05:30
|
|
|
start_date = forms.DateField(widget=forms.DateInput(attrs={"type": "date"}))
|
|
|
|
|
end_date = forms.DateField(widget=forms.DateInput(attrs={"type": "date"}))
|
|
|
|
|
|
2023-05-10 15:06:57 +05:30
|
|
|
def clean(self):
|
|
|
|
|
cleaned_data = super().clean()
|
2023-07-11 12:14:08 +05:30
|
|
|
start_date = cleaned_data.get("start_date")
|
|
|
|
|
end_date = cleaned_data.get("end_date")
|
|
|
|
|
employee_id = cleaned_data.get("employee_id")
|
|
|
|
|
leave_type_id = cleaned_data.get("leave_type_id")
|
|
|
|
|
start_date_breakdown = cleaned_data.get("start_date_breakdown")
|
|
|
|
|
end_date_breakdown = cleaned_data.get("end_date_breakdown")
|
2023-09-25 15:52:50 +05:30
|
|
|
attachment = cleaned_data.get("attachment")
|
2023-07-11 12:14:08 +05:30
|
|
|
overlapping_requests = LeaveRequest.objects.filter(
|
|
|
|
|
employee_id=employee_id, start_date__lte=end_date, end_date__gte=start_date
|
2024-02-23 12:06:56 +05:30
|
|
|
).exclude(
|
|
|
|
|
id=self.instance.id,
|
2023-05-10 15:06:57 +05:30
|
|
|
)
|
2023-09-25 15:52:50 +05:30
|
|
|
if leave_type_id.require_attachment == "yes":
|
|
|
|
|
if attachment is None:
|
|
|
|
|
raise forms.ValidationError(
|
2024-04-08 15:18:36 +05:30
|
|
|
{
|
|
|
|
|
"attachment": _(
|
|
|
|
|
"An attachment is required for this leave request"
|
|
|
|
|
)
|
|
|
|
|
}
|
2023-09-25 15:52:50 +05:30
|
|
|
)
|
2023-05-10 15:06:57 +05:30
|
|
|
if not start_date <= end_date:
|
2023-07-11 12:14:08 +05:30
|
|
|
raise forms.ValidationError(
|
|
|
|
|
_("End date should not be less than start date.")
|
|
|
|
|
)
|
2023-11-16 14:45:58 +05:30
|
|
|
if start_date == end_date:
|
|
|
|
|
if start_date_breakdown != end_date_breakdown:
|
|
|
|
|
raise forms.ValidationError(
|
2024-02-23 12:06:56 +05:30
|
|
|
_(
|
|
|
|
|
"There is a mismatch in the breakdown of the start date and end date."
|
|
|
|
|
)
|
2023-11-16 14:45:58 +05:30
|
|
|
)
|
2023-07-11 12:14:08 +05:30
|
|
|
if not AvailableLeave.objects.filter(
|
|
|
|
|
employee_id=employee_id, leave_type_id=leave_type_id
|
|
|
|
|
).exists():
|
2023-05-18 15:05:28 +05:30
|
|
|
raise forms.ValidationError(_("Employee has no leave type.."))
|
2023-05-10 15:06:57 +05:30
|
|
|
|
2024-03-10 19:37:46 +05:30
|
|
|
if overlapping_requests.exclude(status__in=["cancelled", "rejected"]).exists():
|
2023-07-11 12:14:08 +05:30
|
|
|
raise forms.ValidationError(
|
|
|
|
|
_("Employee has already a leave request for this date range..")
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
available_leave = AvailableLeave.objects.get(
|
|
|
|
|
employee_id=employee_id, leave_type_id=leave_type_id
|
|
|
|
|
)
|
|
|
|
|
total_leave_days = (
|
|
|
|
|
available_leave.available_days + available_leave.carryforward_days
|
|
|
|
|
)
|
|
|
|
|
requested_days = calculate_requested_days(
|
|
|
|
|
start_date, end_date, start_date_breakdown, end_date_breakdown
|
|
|
|
|
)
|
2024-02-23 12:06:56 +05:30
|
|
|
effective_requested_days = cal_effective_requested_days(
|
|
|
|
|
start_date=start_date,
|
|
|
|
|
end_date=end_date,
|
|
|
|
|
leave_type_id=leave_type_id,
|
|
|
|
|
requested_days=requested_days,
|
|
|
|
|
)
|
|
|
|
|
leave_dates = leave_requested_dates(start_date, end_date)
|
2024-02-03 13:58:16 +05:30
|
|
|
month_year = [f"{date.year}-{date.strftime('%m')}" for date in leave_dates]
|
|
|
|
|
today = datetime.today()
|
|
|
|
|
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()
|
2024-03-18 15:35:31 +05:30
|
|
|
if leave_type_id.reset_based == "monthly":
|
|
|
|
|
if f"{today.year}-{today.strftime('%m')}" not in unique_dates:
|
|
|
|
|
for item in unique_dates:
|
|
|
|
|
total_leave_days += forcasted_leaves[item]
|
2023-11-20 14:22:43 +05:30
|
|
|
if not effective_requested_days <= total_leave_days:
|
2023-05-18 15:05:28 +05:30
|
|
|
raise forms.ValidationError(_("Employee doesn't have enough leave days.."))
|
2024-03-25 10:08:53 +00:00
|
|
|
|
2023-05-10 15:06:57 +05:30
|
|
|
return cleaned_data
|
2023-07-11 12:14:08 +05:30
|
|
|
|
2024-01-04 16:52:12 +05:30
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
|
|
|
|
|
|
super().__init__(*args, **kwargs)
|
|
|
|
|
self.fields["leave_type_id"].widget.attrs.update(
|
|
|
|
|
{
|
|
|
|
|
"onchange": "empleavetypeChange($(this))",
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
self.fields["employee_id"].widget.attrs.update(
|
|
|
|
|
{
|
2024-04-15 16:22:49 +05:30
|
|
|
"hx-target": "#id_leave_type_id_parent_div",
|
2024-04-08 15:18:36 +05:30
|
|
|
"hx-trigger": "change",
|
|
|
|
|
"hx-get": "/leave/get-employee-leave-types",
|
2024-01-04 16:52:12 +05:30
|
|
|
}
|
|
|
|
|
)
|
2024-02-03 13:58:16 +05:30
|
|
|
self.fields["start_date"].widget.attrs.update(
|
|
|
|
|
{
|
|
|
|
|
"onchange": "dateChange($(this))",
|
|
|
|
|
}
|
|
|
|
|
)
|
2024-01-04 16:52:12 +05:30
|
|
|
|
2023-10-09 16:13:10 +05:30
|
|
|
def as_p(self, *args, **kwargs):
|
|
|
|
|
"""
|
|
|
|
|
Render the form fields as HTML table rows with Bootstrap styling.
|
|
|
|
|
"""
|
|
|
|
|
context = {"form": self}
|
|
|
|
|
table_html = render_to_string("attendance_form.html", context)
|
|
|
|
|
return table_html
|
|
|
|
|
|
2023-05-10 15:06:57 +05:30
|
|
|
class Meta:
|
|
|
|
|
model = LeaveRequest
|
2023-07-11 12:14:08 +05:30
|
|
|
fields = [
|
|
|
|
|
"employee_id",
|
2023-11-23 00:22:19 +05:30
|
|
|
"leave_type_id",
|
2023-07-11 12:14:08 +05:30
|
|
|
"start_date",
|
|
|
|
|
"start_date_breakdown",
|
|
|
|
|
"end_date",
|
|
|
|
|
"end_date_breakdown",
|
|
|
|
|
"attachment",
|
2024-04-08 15:18:36 +05:30
|
|
|
"description",
|
2023-07-11 12:14:08 +05:30
|
|
|
]
|
2023-05-10 15:06:57 +05:30
|
|
|
|
|
|
|
|
|
|
|
|
|
class LeaveRequestUpdationForm(ModelForm):
|
2023-07-11 12:14:08 +05:30
|
|
|
start_date = forms.DateField(widget=forms.DateInput(attrs={"type": "date"}))
|
|
|
|
|
end_date = forms.DateField(widget=forms.DateInput(attrs={"type": "date"}))
|
|
|
|
|
|
2023-05-10 15:06:57 +05:30
|
|
|
def clean(self):
|
|
|
|
|
cleaned_data = super().clean()
|
2023-07-11 12:14:08 +05:30
|
|
|
start_date = cleaned_data.get("start_date")
|
|
|
|
|
end_date = cleaned_data.get("end_date")
|
|
|
|
|
employee_id = cleaned_data.get("employee_id")
|
|
|
|
|
leave_type_id = cleaned_data.get("leave_type_id")
|
|
|
|
|
start_date_breakdown = cleaned_data.get("start_date_breakdown")
|
|
|
|
|
end_date_breakdown = cleaned_data.get("end_date_breakdown")
|
|
|
|
|
overlapping_requests = LeaveRequest.objects.filter(
|
|
|
|
|
employee_id=employee_id, start_date__lte=end_date, end_date__gte=start_date
|
|
|
|
|
).exclude(id=self.instance.id)
|
2023-05-10 15:06:57 +05:30
|
|
|
if not start_date <= end_date:
|
2023-07-11 12:14:08 +05:30
|
|
|
raise forms.ValidationError(
|
|
|
|
|
_("End date should not be less than start date.")
|
|
|
|
|
)
|
2023-11-16 14:45:58 +05:30
|
|
|
if start_date == end_date:
|
|
|
|
|
if start_date_breakdown != end_date_breakdown:
|
|
|
|
|
raise forms.ValidationError(
|
2024-02-23 12:06:56 +05:30
|
|
|
_(
|
|
|
|
|
"There is a mismatch in the breakdown of the start date and end date."
|
|
|
|
|
)
|
2023-11-16 14:45:58 +05:30
|
|
|
)
|
2023-07-11 12:14:08 +05:30
|
|
|
if not AvailableLeave.objects.filter(
|
|
|
|
|
employee_id=employee_id, leave_type_id=leave_type_id
|
|
|
|
|
).exists():
|
2023-05-18 15:05:28 +05:30
|
|
|
raise forms.ValidationError(_("Employee has no leave type.."))
|
2024-02-23 12:06:56 +05:30
|
|
|
if overlapping_requests.exclude(status__in=["cancelled", "rejected"]).exists():
|
2023-07-11 12:14:08 +05:30
|
|
|
raise forms.ValidationError(
|
|
|
|
|
_("Employee has already a leave request for this date range..")
|
|
|
|
|
)
|
|
|
|
|
available_leave = AvailableLeave.objects.get(
|
|
|
|
|
employee_id=employee_id, leave_type_id=leave_type_id
|
|
|
|
|
)
|
|
|
|
|
total_leave_days = (
|
|
|
|
|
available_leave.available_days + available_leave.carryforward_days
|
|
|
|
|
)
|
|
|
|
|
requested_days = calculate_requested_days(
|
|
|
|
|
start_date, end_date, start_date_breakdown, end_date_breakdown
|
|
|
|
|
)
|
2024-02-23 12:06:56 +05:30
|
|
|
effective_requested_days = cal_effective_requested_days(
|
|
|
|
|
start_date=start_date,
|
|
|
|
|
end_date=end_date,
|
|
|
|
|
leave_type_id=leave_type_id,
|
|
|
|
|
requested_days=requested_days,
|
|
|
|
|
)
|
|
|
|
|
leave_dates = leave_requested_dates(start_date, end_date)
|
2024-02-03 13:58:16 +05:30
|
|
|
month_year = [f"{date.year}-{date.strftime('%m')}" for date in leave_dates]
|
|
|
|
|
today = datetime.today()
|
|
|
|
|
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()
|
2024-03-18 16:43:26 +05:30
|
|
|
if leave_type_id.reset_based == "monthly":
|
|
|
|
|
if f"{today.year}-{today.strftime('%m')}" not in unique_dates:
|
|
|
|
|
for item in unique_dates:
|
|
|
|
|
total_leave_days += forcasted_leaves[item]
|
2023-11-21 17:12:00 +05:30
|
|
|
if not effective_requested_days <= total_leave_days:
|
2023-05-18 15:05:28 +05:30
|
|
|
raise forms.ValidationError(_("Employee doesn't have enough leave days.."))
|
2023-05-10 15:06:57 +05:30
|
|
|
|
|
|
|
|
return cleaned_data
|
2024-02-23 12:06:56 +05:30
|
|
|
|
2024-01-04 16:52:12 +05:30
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
|
|
|
|
|
|
super().__init__(*args, **kwargs)
|
|
|
|
|
self.fields["leave_type_id"].widget.attrs.update(
|
|
|
|
|
{
|
|
|
|
|
"onchange": "empleavetypeChange($(this))",
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
self.fields["employee_id"].widget.attrs.update(
|
|
|
|
|
{
|
2024-04-15 16:22:49 +05:30
|
|
|
"hx-target": "#id_leave_type_id_parent_div",
|
2024-04-08 15:18:36 +05:30
|
|
|
"hx-trigger": "change",
|
|
|
|
|
"hx-get": "/leave/get-employee-leave-types",
|
2024-01-04 16:52:12 +05:30
|
|
|
}
|
|
|
|
|
)
|
2024-02-03 13:58:16 +05:30
|
|
|
self.fields["start_date"].widget.attrs.update(
|
|
|
|
|
{
|
|
|
|
|
"onchange": "dateChange($(this))",
|
|
|
|
|
}
|
|
|
|
|
)
|
2023-07-11 12:14:08 +05:30
|
|
|
|
2023-10-09 16:13:10 +05:30
|
|
|
def as_p(self, *args, **kwargs):
|
|
|
|
|
"""
|
|
|
|
|
Render the form fields as HTML table rows with Bootstrap styling.
|
|
|
|
|
"""
|
|
|
|
|
context = {"form": self}
|
|
|
|
|
table_html = render_to_string("attendance_form.html", context)
|
|
|
|
|
return table_html
|
|
|
|
|
|
2023-05-10 15:06:57 +05:30
|
|
|
class Meta:
|
|
|
|
|
model = LeaveRequest
|
2023-07-11 12:14:08 +05:30
|
|
|
fields = [
|
|
|
|
|
"leave_type_id",
|
|
|
|
|
"employee_id",
|
|
|
|
|
"start_date",
|
|
|
|
|
"start_date_breakdown",
|
|
|
|
|
"end_date",
|
|
|
|
|
"end_date_breakdown",
|
|
|
|
|
"attachment",
|
2024-04-08 15:18:36 +05:30
|
|
|
"description",
|
2023-07-11 12:14:08 +05:30
|
|
|
]
|
2023-05-10 15:06:57 +05:30
|
|
|
|
|
|
|
|
|
|
|
|
|
class AvailableLeaveForm(ModelForm):
|
2024-04-02 10:06:29 +05:30
|
|
|
"""
|
|
|
|
|
Form for managing available leave data.
|
|
|
|
|
|
|
|
|
|
This form allows users to manage available leave data by specifying details such as
|
|
|
|
|
the leave type and employee.
|
|
|
|
|
|
|
|
|
|
Attributes:
|
|
|
|
|
- leave_type_id: A ModelChoiceField representing the leave type associated with the available leave.
|
|
|
|
|
- employee_id: A ModelChoiceField representing the employee associated with the available leave.
|
|
|
|
|
"""
|
|
|
|
|
|
2023-05-10 15:06:57 +05:30
|
|
|
leave_type_id = forms.ModelChoiceField(
|
|
|
|
|
queryset=LeaveType.objects.all(),
|
|
|
|
|
widget=forms.SelectMultiple,
|
|
|
|
|
empty_label=None,
|
|
|
|
|
)
|
|
|
|
|
employee_id = forms.ModelChoiceField(
|
|
|
|
|
queryset=Employee.objects.all(),
|
|
|
|
|
widget=forms.SelectMultiple,
|
|
|
|
|
empty_label=None,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
|
model = AvailableLeave
|
2024-03-25 10:08:53 +00:00
|
|
|
fields = ["leave_type_id", "employee_id", "is_active"]
|
2023-05-10 15:06:57 +05:30
|
|
|
|
|
|
|
|
|
|
|
|
|
class HolidayForm(ModelForm):
|
2024-04-02 10:06:29 +05:30
|
|
|
"""
|
|
|
|
|
Form for creating or updating a holiday.
|
|
|
|
|
|
|
|
|
|
This form allows users to create or update holiday data by specifying details such as
|
|
|
|
|
the start date and end date.
|
|
|
|
|
|
|
|
|
|
Attributes:
|
|
|
|
|
- start_date: A DateField representing the start date of the holiday.
|
|
|
|
|
- end_date: A DateField representing the end date of the holiday.
|
|
|
|
|
"""
|
|
|
|
|
|
2023-05-10 15:06:57 +05:30
|
|
|
start_date = forms.DateField(
|
2023-07-11 12:14:08 +05:30
|
|
|
widget=forms.DateInput(attrs={"type": "date"}),
|
2023-05-10 15:06:57 +05:30
|
|
|
)
|
|
|
|
|
end_date = forms.DateField(
|
2023-07-11 12:14:08 +05:30
|
|
|
widget=forms.DateInput(attrs={"type": "date"}),
|
2023-05-10 15:06:57 +05:30
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def clean_end_date(self):
|
2023-07-11 12:14:08 +05:30
|
|
|
start_date = self.cleaned_data.get("start_date")
|
|
|
|
|
end_date = self.cleaned_data.get("end_date")
|
2023-05-10 15:06:57 +05:30
|
|
|
|
|
|
|
|
if start_date and end_date and end_date < start_date:
|
|
|
|
|
raise ValidationError(
|
2023-07-11 12:14:08 +05:30
|
|
|
_("End date should not be earlier than the start date.")
|
|
|
|
|
)
|
2023-05-10 15:06:57 +05:30
|
|
|
|
|
|
|
|
return end_date
|
|
|
|
|
|
|
|
|
|
class Meta:
|
2024-04-02 10:06:29 +05:30
|
|
|
"""
|
|
|
|
|
Meta class for additional options
|
|
|
|
|
"""
|
|
|
|
|
|
2023-05-10 15:06:57 +05:30
|
|
|
model = Holiday
|
2023-07-11 12:14:08 +05:30
|
|
|
fields = "__all__"
|
2024-03-25 10:08:53 +00:00
|
|
|
exclude = ["is_active"]
|
2023-05-10 15:06:57 +05:30
|
|
|
labels = {
|
2023-07-11 12:14:08 +05:30
|
|
|
"name": _("Name"),
|
2023-05-10 15:06:57 +05:30
|
|
|
}
|
2024-02-23 12:06:56 +05:30
|
|
|
|
2024-01-29 15:16:39 +05:30
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
|
super(HolidayForm, self).__init__(*args, **kwargs)
|
2024-02-23 12:06:56 +05:30
|
|
|
self.fields["name"].widget.attrs["autocomplete"] = "name"
|
2023-05-10 15:06:57 +05:30
|
|
|
|
|
|
|
|
|
2024-02-15 12:12:55 +05:30
|
|
|
class LeaveOneAssignForm(HorillaModelForm):
|
2024-04-02 10:06:29 +05:30
|
|
|
"""
|
|
|
|
|
Form for assigning available leave to employees.
|
|
|
|
|
|
|
|
|
|
This form allows administrators to assign available leave to a single employee
|
|
|
|
|
by specifying the employee and setting the is_active flag.
|
|
|
|
|
|
|
|
|
|
Attributes:
|
|
|
|
|
- employee_id: A HorillaMultiSelectField representing the employee to assign leave to.
|
|
|
|
|
"""
|
|
|
|
|
|
2024-02-15 12:12:55 +05:30
|
|
|
employee_id = HorillaMultiSelectField(
|
2023-05-10 15:06:57 +05:30
|
|
|
queryset=Employee.objects.all(),
|
2024-02-15 12:12:55 +05:30
|
|
|
widget=HorillaMultiSelectWidget(
|
|
|
|
|
filter_route_name="employee-widget-filter",
|
|
|
|
|
filter_class=EmployeeFilter,
|
|
|
|
|
filter_instance_contex_name="f",
|
|
|
|
|
filter_template_path="employee_filters.html",
|
|
|
|
|
required=True,
|
|
|
|
|
),
|
|
|
|
|
label="Employee",
|
2023-05-10 15:06:57 +05:30
|
|
|
)
|
|
|
|
|
|
|
|
|
|
class Meta:
|
2024-04-02 10:06:29 +05:30
|
|
|
"""
|
|
|
|
|
Meta class for additional options
|
|
|
|
|
"""
|
|
|
|
|
|
2023-05-10 15:06:57 +05:30
|
|
|
model = AvailableLeave
|
2024-03-25 10:08:53 +00:00
|
|
|
fields = ["employee_id", "is_active"]
|
2024-02-23 12:06:56 +05:30
|
|
|
|
|
|
|
|
def __init__(self, *args, **kwargs):
|
2024-02-15 12:12:55 +05:30
|
|
|
super().__init__(*args, **kwargs)
|
|
|
|
|
reload_queryset(self.fields)
|
2023-05-10 15:06:57 +05:30
|
|
|
|
|
|
|
|
|
|
|
|
|
class AvailableLeaveUpdateForm(ModelForm):
|
2024-04-02 10:06:29 +05:30
|
|
|
"""
|
|
|
|
|
Form for updating available leave data.
|
|
|
|
|
|
|
|
|
|
This form allows users to update available leave data by modifying fields such as
|
|
|
|
|
available_days, carryforward_days, and is_active.
|
|
|
|
|
|
|
|
|
|
Attributes:
|
|
|
|
|
- Meta: Inner class defining metadata options.
|
|
|
|
|
- model: The model associated with the form (AvailableLeave).
|
|
|
|
|
- fields: A list of fields to include in the form.
|
|
|
|
|
"""
|
|
|
|
|
|
2023-05-10 15:06:57 +05:30
|
|
|
class Meta:
|
2024-04-02 10:06:29 +05:30
|
|
|
"""
|
|
|
|
|
Meta class for additional options
|
|
|
|
|
"""
|
|
|
|
|
|
2023-05-10 15:06:57 +05:30
|
|
|
model = AvailableLeave
|
2024-03-25 10:08:53 +00:00
|
|
|
fields = ["available_days", "carryforward_days", "is_active"]
|
2023-05-10 15:06:57 +05:30
|
|
|
|
|
|
|
|
|
|
|
|
|
class CompanyLeaveForm(ModelForm):
|
2024-04-02 10:06:29 +05:30
|
|
|
"""
|
|
|
|
|
Form for managing company leave data.
|
|
|
|
|
|
|
|
|
|
This form allows users to manage company leave data by including all fields from
|
|
|
|
|
the CompanyLeave model except for is_active.
|
|
|
|
|
|
|
|
|
|
Attributes:
|
|
|
|
|
- Meta: Inner class defining metadata options.
|
|
|
|
|
- model: The model associated with the form (CompanyLeave).
|
|
|
|
|
- fields: A special value indicating all fields should be included in the form.
|
|
|
|
|
- exclude: A list of fields to exclude from the form (is_active).
|
|
|
|
|
"""
|
|
|
|
|
|
2023-05-10 15:06:57 +05:30
|
|
|
class Meta:
|
2024-04-02 10:06:29 +05:30
|
|
|
"""
|
|
|
|
|
Meta class for additional options
|
|
|
|
|
"""
|
|
|
|
|
|
2023-05-10 15:06:57 +05:30
|
|
|
model = CompanyLeave
|
|
|
|
|
fields = "__all__"
|
2024-03-25 10:08:53 +00:00
|
|
|
exclude = ["is_active"]
|
2023-05-10 15:06:57 +05:30
|
|
|
|
|
|
|
|
|
|
|
|
|
class UserLeaveRequestForm(ModelForm):
|
2023-07-11 12:14:08 +05:30
|
|
|
start_date = forms.DateField(widget=forms.DateInput(attrs={"type": "date"}))
|
|
|
|
|
end_date = forms.DateField(widget=forms.DateInput(attrs={"type": "date"}))
|
|
|
|
|
description = forms.CharField(label=_("Description"), widget=forms.Textarea)
|
2023-05-10 15:06:57 +05:30
|
|
|
|
|
|
|
|
def clean(self):
|
|
|
|
|
cleaned_data = super().clean()
|
2023-07-11 12:14:08 +05:30
|
|
|
start_date = cleaned_data.get("start_date")
|
|
|
|
|
end_date = cleaned_data.get("end_date")
|
2023-11-21 17:12:00 +05:30
|
|
|
employee_id = cleaned_data.get("employee_id")
|
2023-11-16 14:45:58 +05:30
|
|
|
start_date_breakdown = cleaned_data.get("start_date_breakdown")
|
2024-02-23 12:06:56 +05:30
|
|
|
end_date_breakdown = cleaned_data.get("end_date_breakdown")
|
2023-11-21 17:12:00 +05:30
|
|
|
leave_type_id = cleaned_data.get("leave_type_id")
|
2023-11-23 12:21:13 +05:30
|
|
|
overlapping_requests = LeaveRequest.objects.filter(
|
2024-02-23 12:06:56 +05:30
|
|
|
employee_id=employee_id, start_date__lte=end_date, end_date__gte=start_date
|
|
|
|
|
).exclude(id=self.instance.id)
|
2024-04-08 15:18:36 +05:30
|
|
|
assigned_leave_types = AvailableLeave.objects.filter(employee_id=employee_id)
|
|
|
|
|
if not assigned_leave_types:
|
|
|
|
|
raise forms.ValidationError(
|
|
|
|
|
_("You dont have enough leave days to update the request.")
|
|
|
|
|
)
|
|
|
|
|
if leave_type_id.require_attachment == "yes":
|
|
|
|
|
attachment = cleaned_data.get("attachment")
|
|
|
|
|
if attachment is None:
|
|
|
|
|
raise forms.ValidationError(
|
|
|
|
|
{
|
|
|
|
|
"attachment": _(
|
|
|
|
|
"An attachment is required for this leave request"
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
)
|
2023-11-16 14:45:58 +05:30
|
|
|
if start_date == end_date:
|
|
|
|
|
if start_date_breakdown != end_date_breakdown:
|
|
|
|
|
raise forms.ValidationError(
|
2024-02-23 12:06:56 +05:30
|
|
|
_(
|
|
|
|
|
"There is a mismatch in the breakdown of the start date and end date."
|
|
|
|
|
)
|
2023-11-16 14:45:58 +05:30
|
|
|
)
|
2023-05-10 15:06:57 +05:30
|
|
|
if not start_date <= end_date:
|
2023-07-11 12:14:08 +05:30
|
|
|
raise forms.ValidationError(
|
|
|
|
|
_("End date should not be less than start date.")
|
|
|
|
|
)
|
2024-02-23 12:06:56 +05:30
|
|
|
if overlapping_requests.exclude(status__in=["cancelled", "rejected"]).exists():
|
2023-11-23 12:21:13 +05:30
|
|
|
raise forms.ValidationError(
|
2024-02-23 12:06:56 +05:30
|
|
|
_("Employee has already a leave request for this date range.....")
|
2023-11-23 12:21:13 +05:30
|
|
|
)
|
2023-11-21 17:12:00 +05:30
|
|
|
requested_days = calculate_requested_days(
|
|
|
|
|
start_date, end_date, start_date_breakdown, end_date_breakdown
|
|
|
|
|
)
|
|
|
|
|
available_leave = AvailableLeave.objects.get(
|
|
|
|
|
employee_id=employee_id, leave_type_id=leave_type_id
|
|
|
|
|
)
|
|
|
|
|
total_leave_days = (
|
|
|
|
|
available_leave.available_days + available_leave.carryforward_days
|
|
|
|
|
)
|
2024-02-23 12:06:56 +05:30
|
|
|
effective_requested_days = cal_effective_requested_days(
|
|
|
|
|
start_date=start_date,
|
|
|
|
|
end_date=end_date,
|
|
|
|
|
leave_type_id=leave_type_id,
|
|
|
|
|
requested_days=requested_days,
|
|
|
|
|
)
|
2023-11-21 17:12:00 +05:30
|
|
|
if not effective_requested_days <= total_leave_days:
|
2023-11-23 12:21:13 +05:30
|
|
|
raise forms.ValidationError(_("Employee doesn't have enough leave days.."))
|
2023-05-10 15:06:57 +05:30
|
|
|
return cleaned_data
|
|
|
|
|
|
2024-04-08 15:18:36 +05:30
|
|
|
def __init__(self, *args, **kwargs):
|
2024-02-23 12:06:56 +05:30
|
|
|
leave_type = kwargs.pop("initial", None)
|
2024-04-08 15:18:36 +05:30
|
|
|
employee = kwargs.pop("employee", None)
|
2023-05-10 15:06:57 +05:30
|
|
|
super(UserLeaveRequestForm, self).__init__(*args, **kwargs)
|
2024-04-08 15:18:36 +05:30
|
|
|
|
|
|
|
|
if employee:
|
|
|
|
|
available_leaves = employee.available_leave.all()
|
|
|
|
|
assigned_leave_types = LeaveType.objects.filter(
|
|
|
|
|
id__in=available_leaves.values_list("leave_type_id", flat=True)
|
|
|
|
|
)
|
|
|
|
|
self.fields["leave_type_id"].queryset = assigned_leave_types
|
2024-02-23 12:06:56 +05:30
|
|
|
if leave_type:
|
|
|
|
|
self.fields["leave_type_id"].queryset = LeaveType.objects.filter(
|
|
|
|
|
id=leave_type["leave_type_id"].id
|
|
|
|
|
)
|
|
|
|
|
self.fields["leave_type_id"].initial = leave_type["leave_type_id"].id
|
|
|
|
|
self.fields["leave_type_id"].empty_label = None
|
2023-05-10 15:06:57 +05:30
|
|
|
|
2023-10-18 11:04:27 +05:30
|
|
|
def as_p(self, *args, **kwargs):
|
|
|
|
|
"""
|
|
|
|
|
Render the form fields as HTML table rows with Bootstrap styling.
|
|
|
|
|
"""
|
|
|
|
|
context = {"form": self}
|
|
|
|
|
table_html = render_to_string("attendance_form.html", context)
|
|
|
|
|
return table_html
|
|
|
|
|
|
2023-05-10 15:06:57 +05:30
|
|
|
class Meta:
|
2024-04-02 10:06:29 +05:30
|
|
|
"""
|
|
|
|
|
Meta class for additional options
|
|
|
|
|
"""
|
|
|
|
|
|
2023-05-10 15:06:57 +05:30
|
|
|
model = LeaveRequest
|
2023-07-11 12:14:08 +05:30
|
|
|
fields = [
|
2023-11-21 17:12:00 +05:30
|
|
|
"employee_id",
|
|
|
|
|
"leave_type_id",
|
2023-07-11 12:14:08 +05:30
|
|
|
"start_date",
|
|
|
|
|
"start_date_breakdown",
|
|
|
|
|
"end_date",
|
|
|
|
|
"end_date_breakdown",
|
|
|
|
|
"attachment",
|
2024-04-08 15:18:36 +05:30
|
|
|
"description",
|
2023-07-11 12:14:08 +05:30
|
|
|
]
|
2023-11-21 17:12:00 +05:30
|
|
|
widgets = {
|
|
|
|
|
"employee_id": forms.HiddenInput(),
|
|
|
|
|
}
|
2023-10-13 14:42:38 +05:30
|
|
|
|
|
|
|
|
|
2023-11-16 09:07:14 +05:30
|
|
|
excluded_fields = [
|
|
|
|
|
"id",
|
|
|
|
|
"approved_available_days",
|
|
|
|
|
"approved_carryforward_days",
|
|
|
|
|
"created_at",
|
|
|
|
|
"attachment",
|
|
|
|
|
]
|
2023-10-13 14:42:38 +05:30
|
|
|
|
|
|
|
|
|
|
|
|
|
class AvailableLeaveColumnExportForm(forms.Form):
|
2024-04-02 10:06:29 +05:30
|
|
|
"""
|
|
|
|
|
Form for selecting columns to export in available leave data.
|
|
|
|
|
|
|
|
|
|
This form allows users to select specific columns from the AvailableLeave model
|
|
|
|
|
for export. The available columns are dynamically generated based on the
|
|
|
|
|
model's meta information, excluding specified excluded_fields.
|
|
|
|
|
|
|
|
|
|
Attributes:
|
|
|
|
|
- model_fields: A list of fields in the AvailableLeave model.
|
|
|
|
|
- field_choices: A list of field choices for the form, consisting of field names
|
|
|
|
|
and their verbose names, excluding specified excluded_fields.
|
|
|
|
|
- selected_fields: A MultipleChoiceField representing the selected columns
|
|
|
|
|
to be exported.
|
|
|
|
|
"""
|
|
|
|
|
|
2023-10-13 14:42:38 +05:30
|
|
|
model_fields = AvailableLeave._meta.get_fields()
|
|
|
|
|
field_choices = [
|
|
|
|
|
(field.name, field.verbose_name)
|
|
|
|
|
for field in model_fields
|
|
|
|
|
if hasattr(field, "verbose_name") and field.name not in excluded_fields
|
|
|
|
|
]
|
|
|
|
|
selected_fields = forms.MultipleChoiceField(
|
|
|
|
|
choices=field_choices,
|
|
|
|
|
widget=forms.CheckboxSelectMultiple,
|
|
|
|
|
initial=[
|
|
|
|
|
"employee_id",
|
|
|
|
|
"leave_type_id",
|
2023-11-13 12:34:15 +05:30
|
|
|
"available_days",
|
|
|
|
|
"carryforward_days",
|
2023-10-13 14:42:38 +05:30
|
|
|
"total_leave_days",
|
|
|
|
|
],
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class HolidaysColumnExportForm(forms.Form):
|
2024-04-02 10:06:29 +05:30
|
|
|
"""
|
|
|
|
|
Form for selecting columns to export in holiday data.
|
|
|
|
|
|
|
|
|
|
This form allows users to select specific columns from the Holiday model
|
|
|
|
|
for export. The available columns are dynamically generated based on the
|
|
|
|
|
model's meta information, excluding specified excluded_fields.
|
|
|
|
|
|
|
|
|
|
Attributes:
|
|
|
|
|
- model_fields: A list of fields in the Holiday model.
|
|
|
|
|
- field_choices: A list of field choices for the form, consisting of field names
|
|
|
|
|
and their verbose names, excluding specified excluded_fields.
|
|
|
|
|
- selected_fields: A MultipleChoiceField representing the selected columns
|
|
|
|
|
to be exported.
|
|
|
|
|
"""
|
|
|
|
|
|
2023-10-13 14:42:38 +05:30
|
|
|
model_fields = Holiday._meta.get_fields()
|
|
|
|
|
field_choices = [
|
|
|
|
|
(field.name, field.verbose_name)
|
|
|
|
|
for field in model_fields
|
|
|
|
|
if hasattr(field, "verbose_name") and field.name not in excluded_fields
|
|
|
|
|
]
|
|
|
|
|
selected_fields = forms.MultipleChoiceField(
|
|
|
|
|
choices=field_choices,
|
|
|
|
|
widget=forms.CheckboxSelectMultiple,
|
|
|
|
|
initial=[
|
|
|
|
|
"name",
|
|
|
|
|
"start_date",
|
|
|
|
|
"end_date",
|
|
|
|
|
"recurring",
|
|
|
|
|
],
|
|
|
|
|
)
|
2023-10-17 10:04:23 +05:30
|
|
|
|
2023-11-13 12:34:15 +05:30
|
|
|
|
2023-10-17 10:04:23 +05:30
|
|
|
class RejectForm(forms.Form):
|
2024-04-02 10:06:29 +05:30
|
|
|
"""
|
|
|
|
|
Form for rejecting a leave request.
|
|
|
|
|
|
|
|
|
|
This form allows administrators to provide a rejection reason when rejecting
|
|
|
|
|
a leave request.
|
|
|
|
|
|
|
|
|
|
Attributes:
|
|
|
|
|
- reason: A CharField representing the reason for rejecting the leave request.
|
|
|
|
|
"""
|
|
|
|
|
|
2023-11-13 12:34:15 +05:30
|
|
|
reason = forms.CharField(
|
|
|
|
|
label=_("Rejection Reason"),
|
|
|
|
|
widget=forms.Textarea(attrs={"rows": 4, "class": "p-4 oh-input w-100"}),
|
|
|
|
|
)
|
|
|
|
|
|
2023-10-18 11:04:27 +05:30
|
|
|
class Meta:
|
2024-04-02 10:06:29 +05:30
|
|
|
"""
|
|
|
|
|
Meta class for additional options
|
|
|
|
|
"""
|
|
|
|
|
|
2023-10-18 11:04:27 +05:30
|
|
|
model = LeaveRequest
|
|
|
|
|
fields = ["reject_reason"]
|
|
|
|
|
|
|
|
|
|
|
2023-11-13 12:34:15 +05:30
|
|
|
class UserLeaveRequestCreationForm(ModelForm):
|
|
|
|
|
start_date = forms.DateField(widget=forms.DateInput(attrs={"type": "date"}))
|
|
|
|
|
end_date = forms.DateField(widget=forms.DateInput(attrs={"type": "date"}))
|
2023-10-18 11:04:27 +05:30
|
|
|
|
2023-11-16 14:45:58 +05:30
|
|
|
def as_p(self, *args, **kwargs):
|
|
|
|
|
"""
|
|
|
|
|
Render the form fields as HTML table rows with Bootstrap styling.
|
|
|
|
|
"""
|
|
|
|
|
context = {"form": self}
|
|
|
|
|
table_html = render_to_string("attendance_form.html", context)
|
|
|
|
|
return table_html
|
2024-02-23 12:06:56 +05:30
|
|
|
|
2024-01-04 16:52:12 +05:30
|
|
|
def __init__(self, *args, **kwargs):
|
2024-04-08 15:18:36 +05:30
|
|
|
employee = kwargs.pop("employee", None)
|
2024-01-04 16:52:12 +05:30
|
|
|
super().__init__(*args, **kwargs)
|
2024-04-08 15:18:36 +05:30
|
|
|
if employee:
|
|
|
|
|
available_leaves = employee.available_leave.all()
|
|
|
|
|
assigned_leave_types = LeaveType.objects.filter(
|
|
|
|
|
id__in=available_leaves.values_list("leave_type_id", flat=True)
|
|
|
|
|
)
|
|
|
|
|
self.fields["leave_type_id"].queryset = assigned_leave_types
|
2024-01-04 16:52:12 +05:30
|
|
|
self.fields["leave_type_id"].widget.attrs.update(
|
|
|
|
|
{
|
|
|
|
|
"onchange": "typeChange($(this))",
|
|
|
|
|
}
|
|
|
|
|
)
|
2024-04-08 15:18:36 +05:30
|
|
|
self.fields["employee_id"].initial = employee
|
2024-02-23 12:06:56 +05:30
|
|
|
|
2023-10-18 11:04:27 +05:30
|
|
|
def clean(self):
|
|
|
|
|
cleaned_data = super().clean()
|
2023-11-13 12:34:15 +05:30
|
|
|
start_date = cleaned_data.get("start_date")
|
|
|
|
|
end_date = cleaned_data.get("end_date")
|
|
|
|
|
employee_id = cleaned_data.get("employee_id")
|
|
|
|
|
leave_type_id = cleaned_data.get("leave_type_id")
|
2023-11-16 14:45:58 +05:30
|
|
|
start_date_breakdown = cleaned_data.get("start_date_breakdown")
|
2024-02-23 12:06:56 +05:30
|
|
|
end_date_breakdown = cleaned_data.get("end_date_breakdown")
|
2023-11-13 12:34:15 +05:30
|
|
|
overlapping_requests = LeaveRequest.objects.filter(
|
|
|
|
|
employee_id=employee_id, start_date__lte=end_date, end_date__gte=start_date
|
2024-02-23 12:06:56 +05:30
|
|
|
).exclude(id=self.instance.id)
|
2023-10-18 11:04:27 +05:30
|
|
|
if not start_date <= end_date:
|
2023-11-13 12:34:15 +05:30
|
|
|
raise forms.ValidationError(
|
|
|
|
|
_("End date should not be less than start date.")
|
|
|
|
|
)
|
2024-04-08 15:18:36 +05:30
|
|
|
if leave_type_id.require_attachment == "yes":
|
|
|
|
|
attachment = cleaned_data.get("attachment")
|
|
|
|
|
if attachment is None:
|
|
|
|
|
raise forms.ValidationError(
|
|
|
|
|
{
|
|
|
|
|
"attachment": _(
|
|
|
|
|
"An attachment is required for this leave request"
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
)
|
2023-11-16 14:45:58 +05:30
|
|
|
if start_date == end_date:
|
|
|
|
|
if start_date_breakdown != end_date_breakdown:
|
|
|
|
|
raise forms.ValidationError(
|
2024-02-23 12:06:56 +05:30
|
|
|
_(
|
|
|
|
|
"There is a mismatch in the breakdown of the start date and end date."
|
|
|
|
|
)
|
2023-11-16 14:45:58 +05:30
|
|
|
)
|
2023-11-13 12:34:15 +05:30
|
|
|
if not AvailableLeave.objects.filter(
|
|
|
|
|
employee_id=employee_id, leave_type_id=leave_type_id
|
|
|
|
|
).exists():
|
2023-10-18 11:04:27 +05:30
|
|
|
raise forms.ValidationError(_("Employee has no leave type.."))
|
|
|
|
|
|
2024-03-10 19:37:46 +05:30
|
|
|
if overlapping_requests.exclude(status__in=["cancelled", "rejected"]).exists():
|
2023-11-13 12:34:15 +05:30
|
|
|
raise forms.ValidationError(
|
|
|
|
|
_("Employee has already a leave request for this date range..")
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
available_leave = AvailableLeave.objects.get(
|
|
|
|
|
employee_id=employee_id, leave_type_id=leave_type_id
|
|
|
|
|
)
|
|
|
|
|
total_leave_days = (
|
|
|
|
|
available_leave.available_days + available_leave.carryforward_days
|
|
|
|
|
)
|
|
|
|
|
requested_days = (end_date - start_date).days + 1
|
2023-11-16 09:07:14 +05:30
|
|
|
cleaned_data["requested_days"] = requested_days
|
2023-10-18 11:04:27 +05:30
|
|
|
|
|
|
|
|
if not requested_days <= total_leave_days:
|
2023-11-23 00:22:19 +05:30
|
|
|
raise forms.ValidationError(_("Employee doesn't have enough leave days.."))
|
2023-10-18 11:04:27 +05:30
|
|
|
|
|
|
|
|
return cleaned_data
|
2023-11-13 12:34:15 +05:30
|
|
|
|
2023-10-17 10:04:23 +05:30
|
|
|
class Meta:
|
2024-04-02 10:06:29 +05:30
|
|
|
"""
|
|
|
|
|
Meta class for additional options
|
|
|
|
|
"""
|
|
|
|
|
|
2023-10-17 10:04:23 +05:30
|
|
|
model = LeaveRequest
|
2023-11-13 12:34:15 +05:30
|
|
|
fields = [
|
|
|
|
|
"leave_type_id",
|
|
|
|
|
"employee_id",
|
|
|
|
|
"start_date",
|
|
|
|
|
"start_date_breakdown",
|
|
|
|
|
"end_date",
|
|
|
|
|
"end_date_breakdown",
|
|
|
|
|
"attachment",
|
2024-04-08 15:18:36 +05:30
|
|
|
"description",
|
2023-11-16 09:07:14 +05:30
|
|
|
"requested_days",
|
2023-11-13 12:34:15 +05:30
|
|
|
]
|
2023-11-16 14:45:58 +05:30
|
|
|
widgets = {
|
|
|
|
|
"employee_id": forms.HiddenInput(),
|
2024-02-23 12:06:56 +05:30
|
|
|
"requested_days": forms.HiddenInput(),
|
2023-11-16 14:45:58 +05:30
|
|
|
}
|
2023-11-13 12:34:15 +05:30
|
|
|
|
|
|
|
|
|
2023-11-02 12:25:48 +05:30
|
|
|
class LeaveAllocationRequestForm(ModelForm):
|
2024-04-02 10:06:29 +05:30
|
|
|
"""
|
|
|
|
|
Form for creating a leave allocation request.
|
|
|
|
|
|
|
|
|
|
This form allows users to create a leave allocation request by specifying
|
|
|
|
|
details such as leave type, employee, requested days, description, and attachment.
|
|
|
|
|
|
|
|
|
|
Methods:
|
|
|
|
|
- as_p: Render the form fields as HTML table rows with Bootstrap styling.
|
|
|
|
|
"""
|
|
|
|
|
|
2023-11-02 12:25:48 +05:30
|
|
|
def as_p(self, *args, **kwargs):
|
|
|
|
|
"""
|
|
|
|
|
Render the form fields as HTML table rows with Bootstrap styling.
|
|
|
|
|
"""
|
|
|
|
|
context = {"form": self}
|
|
|
|
|
table_html = render_to_string("attendance_form.html", context)
|
2023-11-03 10:27:03 +05:30
|
|
|
return table_html
|
2023-11-13 12:34:15 +05:30
|
|
|
|
2023-11-03 10:27:03 +05:30
|
|
|
class Meta:
|
2024-04-02 10:06:29 +05:30
|
|
|
"""
|
|
|
|
|
Meta class for additional options
|
|
|
|
|
"""
|
|
|
|
|
|
2023-11-03 10:27:03 +05:30
|
|
|
model = LeaveAllocationRequest
|
|
|
|
|
fields = [
|
2023-11-13 12:34:15 +05:30
|
|
|
"leave_type_id",
|
|
|
|
|
"employee_id",
|
|
|
|
|
"requested_days",
|
|
|
|
|
"description",
|
|
|
|
|
"attachment",
|
2023-11-08 16:58:23 +05:30
|
|
|
]
|
|
|
|
|
|
2023-11-13 12:34:15 +05:30
|
|
|
|
2023-11-08 16:58:23 +05:30
|
|
|
class LeaveAllocationRequestRejectForm(forms.Form):
|
2024-04-02 10:06:29 +05:30
|
|
|
"""
|
|
|
|
|
Form for rejecting a leave allocation request.
|
|
|
|
|
|
|
|
|
|
This form allows administrators to provide a rejection reason when rejecting
|
|
|
|
|
a leave allocation request.
|
|
|
|
|
|
|
|
|
|
Attributes:
|
|
|
|
|
- reason: A CharField representing the reason for rejecting the leave allocation request.
|
|
|
|
|
"""
|
|
|
|
|
|
2023-11-13 12:34:15 +05:30
|
|
|
reason = forms.CharField(
|
|
|
|
|
label=_("Rejection Reason"),
|
|
|
|
|
widget=forms.Textarea(attrs={"rows": 4, "class": "p-4 oh-input w-100"}),
|
|
|
|
|
)
|
|
|
|
|
|
2023-11-08 16:58:23 +05:30
|
|
|
class Meta:
|
|
|
|
|
model = LeaveAllocationRequest
|
2023-11-13 12:34:15 +05:30
|
|
|
fields = ["reject_reason"]
|
2023-11-16 09:07:14 +05:30
|
|
|
|
|
|
|
|
|
|
|
|
|
class LeaveRequestExportForm(forms.Form):
|
2024-04-02 10:06:29 +05:30
|
|
|
"""
|
|
|
|
|
Form for selecting fields to export in a leave request export.
|
|
|
|
|
|
|
|
|
|
This form allows users to select specific fields from the LeaveRequest model
|
|
|
|
|
for export. The available fields are dynamically generated based on the
|
|
|
|
|
model's meta information, excluding certain fields specified in 'excluded_fields'.
|
|
|
|
|
|
|
|
|
|
Attributes:
|
|
|
|
|
- model_fields: A list of fields in the LeaveRequest model.
|
|
|
|
|
- field_choices: A list of field choices for the form, consisting of field names
|
|
|
|
|
and their verbose names, excluding specified excluded_fields.
|
|
|
|
|
- selected_fields: A MultipleChoiceField representing the selected fields
|
|
|
|
|
to be exported.
|
|
|
|
|
"""
|
|
|
|
|
|
2023-11-16 09:07:14 +05:30
|
|
|
model_fields = LeaveRequest._meta.get_fields()
|
|
|
|
|
field_choices = [
|
|
|
|
|
(field.name, field.verbose_name)
|
|
|
|
|
for field in model_fields
|
|
|
|
|
if hasattr(field, "verbose_name") and field.name not in excluded_fields
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
selected_fields = forms.MultipleChoiceField(
|
|
|
|
|
choices=field_choices,
|
|
|
|
|
widget=forms.CheckboxSelectMultiple,
|
|
|
|
|
initial=[
|
|
|
|
|
"employee_id",
|
2023-11-24 16:59:15 +05:30
|
|
|
"leave_type_Assignid",
|
2023-11-16 09:07:14 +05:30
|
|
|
"start_date",
|
|
|
|
|
"start_date_breakdown",
|
|
|
|
|
"end_date",
|
|
|
|
|
"end_date_breakdown",
|
|
|
|
|
"requested_days",
|
|
|
|
|
"description",
|
|
|
|
|
"status",
|
|
|
|
|
],
|
|
|
|
|
)
|
2023-11-24 16:59:15 +05:30
|
|
|
|
|
|
|
|
|
|
|
|
|
class AssignLeaveForm(HorillaForm):
|
|
|
|
|
"""
|
|
|
|
|
Form for Payslip
|
|
|
|
|
"""
|
2024-02-23 12:06:56 +05:30
|
|
|
|
2023-11-24 16:59:15 +05:30
|
|
|
leave_type_id = forms.ModelChoiceField(
|
|
|
|
|
queryset=LeaveType.objects.all(),
|
2024-02-23 12:06:56 +05:30
|
|
|
widget=forms.SelectMultiple(
|
|
|
|
|
attrs={"class": "oh-select oh-select-2 mb-2", "required": True}
|
|
|
|
|
),
|
2023-11-24 16:59:15 +05:30
|
|
|
empty_label=None,
|
|
|
|
|
label="Leave Type",
|
|
|
|
|
required=False,
|
|
|
|
|
)
|
|
|
|
|
employee_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",
|
2024-02-23 12:06:56 +05:30
|
|
|
required=True,
|
2023-11-24 16:59:15 +05:30
|
|
|
),
|
|
|
|
|
label="Employee",
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def clean(self):
|
|
|
|
|
cleaned_data = super().clean()
|
|
|
|
|
employee_id = cleaned_data.get("employee_id")
|
|
|
|
|
leave_type_id = cleaned_data.get("leave_type_id")
|
|
|
|
|
|
2024-02-23 12:06:56 +05:30
|
|
|
if not employee_id:
|
|
|
|
|
raise forms.ValidationError({"employee_id": "This field is required"})
|
|
|
|
|
if not leave_type_id:
|
|
|
|
|
raise forms.ValidationError({"leave_type_id": "This field is required"})
|
2023-11-24 16:59:15 +05:30
|
|
|
return cleaned_data
|
|
|
|
|
|
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
|
super().__init__(*args, **kwargs)
|
2023-12-01 15:36:51 +05:30
|
|
|
reload_queryset(self.fields)
|
2023-11-24 16:59:15 +05:30
|
|
|
self.fields["employee_id"].widget.attrs.update(
|
|
|
|
|
{"required": True, "id": uuid.uuid4()}
|
|
|
|
|
),
|
2024-02-23 12:06:56 +05:30
|
|
|
self.fields["leave_type_id"].label = "Leave Type"
|
2023-11-24 16:59:15 +05:30
|
|
|
|
2024-01-15 10:15:58 +05:30
|
|
|
|
|
|
|
|
class LeaverequestcommentForm(ModelForm):
|
|
|
|
|
"""
|
|
|
|
|
LeaverequestComment form
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
|
"""
|
|
|
|
|
Meta class for additional options
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
model = LeaverequestComment
|
2024-02-23 12:06:56 +05:30
|
|
|
fields = ("comment",)
|
2024-01-16 14:21:53 +05:30
|
|
|
|
2024-01-30 19:09:18 +05:30
|
|
|
|
|
|
|
|
class LeaveCommentForm(ModelForm):
|
|
|
|
|
"""
|
|
|
|
|
Leave request comment model form
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
verbose_name = "Add Comment"
|
|
|
|
|
|
|
|
|
|
class Meta:
|
2024-04-02 10:06:29 +05:30
|
|
|
"""
|
|
|
|
|
Meta class for additional options
|
|
|
|
|
"""
|
|
|
|
|
|
2024-01-30 19:09:18 +05:30
|
|
|
model = LeaverequestComment
|
|
|
|
|
fields = "__all__"
|
|
|
|
|
|
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
|
super().__init__(*args, **kwargs)
|
|
|
|
|
self.fields["files"] = MultipleFileField(label="files")
|
|
|
|
|
self.fields["files"].required = False
|
|
|
|
|
|
|
|
|
|
def as_p(self):
|
|
|
|
|
"""
|
|
|
|
|
Render the form fields as HTML table rows with Bootstrap styling.
|
|
|
|
|
"""
|
|
|
|
|
context = {"form": self}
|
|
|
|
|
table_html = render_to_string("common_form.html", context)
|
|
|
|
|
return table_html
|
|
|
|
|
|
|
|
|
|
def save(self, commit: bool = ...) -> Any:
|
|
|
|
|
multiple_files_ids = []
|
|
|
|
|
files = None
|
|
|
|
|
if self.files.getlist("files"):
|
|
|
|
|
files = self.files.getlist("files")
|
|
|
|
|
self.instance.attachemnt = files[0]
|
|
|
|
|
multiple_files_ids = []
|
|
|
|
|
for attachemnt in files:
|
|
|
|
|
file_instance = LeaverequestFile()
|
|
|
|
|
file_instance.file = attachemnt
|
|
|
|
|
file_instance.save()
|
|
|
|
|
multiple_files_ids.append(file_instance.pk)
|
|
|
|
|
instance = super().save(commit)
|
|
|
|
|
if commit:
|
|
|
|
|
instance.files.add(*multiple_files_ids)
|
|
|
|
|
return instance, files
|
|
|
|
|
|
|
|
|
|
|
2024-01-16 14:21:53 +05:30
|
|
|
class LeaveallocationrequestcommentForm(ModelForm):
|
|
|
|
|
"""
|
|
|
|
|
Leave Allocation Requestcomment form
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
|
"""
|
|
|
|
|
Meta class for additional options
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
model = LeaveallocationrequestComment
|
2024-02-23 12:06:56 +05:30
|
|
|
fields = ("comment",)
|
2024-01-30 19:09:18 +05:30
|
|
|
|
|
|
|
|
|
|
|
|
|
class LeaveAllocationCommentForm(ModelForm):
|
|
|
|
|
"""
|
|
|
|
|
Leave request comment model form
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
verbose_name = "Add Comment"
|
|
|
|
|
|
|
|
|
|
class Meta:
|
2024-04-02 10:06:29 +05:30
|
|
|
"""
|
|
|
|
|
Meta class for additional options
|
|
|
|
|
"""
|
|
|
|
|
|
2024-01-30 19:09:18 +05:30
|
|
|
model = LeaveallocationrequestComment
|
|
|
|
|
fields = "__all__"
|
|
|
|
|
|
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
|
super().__init__(*args, **kwargs)
|
|
|
|
|
self.fields["files"] = MultipleFileField(label="files")
|
|
|
|
|
self.fields["files"].required = False
|
|
|
|
|
|
|
|
|
|
def as_p(self):
|
|
|
|
|
"""
|
|
|
|
|
Render the form fields as HTML table rows with Bootstrap styling.
|
|
|
|
|
"""
|
|
|
|
|
context = {"form": self}
|
|
|
|
|
table_html = render_to_string("common_form.html", context)
|
|
|
|
|
return table_html
|
|
|
|
|
|
|
|
|
|
def save(self, commit: bool = ...) -> Any:
|
|
|
|
|
multiple_files_ids = []
|
|
|
|
|
files = None
|
|
|
|
|
if self.files.getlist("files"):
|
|
|
|
|
files = self.files.getlist("files")
|
|
|
|
|
self.instance.attachemnt = files[0]
|
|
|
|
|
multiple_files_ids = []
|
|
|
|
|
for attachemnt in files:
|
|
|
|
|
file_instance = LeaverequestFile()
|
|
|
|
|
file_instance.file = attachemnt
|
|
|
|
|
file_instance.save()
|
|
|
|
|
multiple_files_ids.append(file_instance.pk)
|
|
|
|
|
instance = super().save(commit)
|
|
|
|
|
if commit:
|
|
|
|
|
instance.files.add(*multiple_files_ids)
|
|
|
|
|
return instance, files
|
2024-04-03 10:26:31 +05:30
|
|
|
|
|
|
|
|
|
|
|
|
|
class RestrictLeaveForm(ModelForm):
|
|
|
|
|
start_date = forms.DateField(
|
|
|
|
|
widget=forms.DateInput(attrs={"type": "date"}),
|
|
|
|
|
)
|
|
|
|
|
end_date = forms.DateField(
|
|
|
|
|
widget=forms.DateInput(attrs={"type": "date"}),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def clean_end_date(self):
|
|
|
|
|
start_date = self.cleaned_data.get("start_date")
|
|
|
|
|
end_date = self.cleaned_data.get("end_date")
|
|
|
|
|
|
|
|
|
|
if start_date and end_date and end_date < start_date:
|
|
|
|
|
raise ValidationError(
|
|
|
|
|
_("End date should not be earlier than the start date.")
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
return end_date
|
|
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
|
model = RestrictLeave
|
|
|
|
|
fields = "__all__"
|
|
|
|
|
exclude = ["is_active"]
|
|
|
|
|
labels = {
|
|
|
|
|
"title": _("Title"),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
|
super(RestrictLeaveForm, self).__init__(*args, **kwargs)
|
|
|
|
|
self.fields["title"].widget.attrs["autocomplete"] = "title"
|