Files
ihrm/base/forms.py

1962 lines
60 KiB
Python
Raw Normal View History

2023-08-01 16:48:48 +05:30
"""
forms.py
2023-05-10 15:06:57 +05:30
2023-08-01 16:48:48 +05:30
This module is used to register forms for base module
"""
2023-08-01 16:48:48 +05:30
import calendar
import datetime
import os
2023-08-01 16:48:48 +05:30
import uuid
from datetime import date, timedelta
from typing import Any
2023-05-10 15:06:57 +05:30
from django import forms
2024-05-14 11:20:38 +05:30
from django.contrib import messages
from django.contrib.auth import get_user_model
from django.contrib.auth.forms import SetPasswordForm, _unicode_ci_compare
from django.contrib.auth.models import Group, Permission, User
2024-05-14 11:20:38 +05:30
from django.contrib.auth.tokens import default_token_generator
from django.contrib.sites.shortcuts import get_current_site
2023-05-10 15:06:57 +05:30
from django.core.exceptions import ValidationError
2024-05-14 11:20:38 +05:30
from django.core.mail import EmailMultiAlternatives
from django.forms import DateInput, HiddenInput, TextInput
2024-05-14 11:20:38 +05:30
from django.template import loader
from django.template.loader import render_to_string
2024-05-14 11:20:38 +05:30
from django.utils.encoding import force_bytes
from django.utils.http import urlsafe_base64_encode
2023-05-10 15:06:57 +05:30
from django.utils.translation import gettext as _
2023-08-07 13:02:24 +05:30
from django.utils.translation import gettext_lazy as _trans
from base import thread_local_middleware
from base.methods import reload_queryset
2023-08-01 16:48:48 +05:30
from base.models import (
Announcement,
AnnouncementComment,
AnnouncementExpire,
Attachment,
BaserequestFile,
2023-08-01 16:48:48 +05:30
Company,
Department,
DriverViewed,
2024-01-04 11:05:11 +05:30
DynamicEmailConfiguration,
DynamicPagination,
EmployeeShift,
EmployeeShiftDay,
EmployeeShiftSchedule,
EmployeeType,
2023-08-01 16:48:48 +05:30
JobPosition,
JobRole,
MultipleApprovalCondition,
2023-08-01 16:48:48 +05:30
RotatingShift,
RotatingShiftAssign,
RotatingWorkType,
RotatingWorkTypeAssign,
ShiftRequest,
ShiftRequestComment,
Tags,
WorkType,
WorkTypeRequest,
WorkTypeRequestComment,
2023-08-01 16:48:48 +05:30
)
2024-05-14 11:20:38 +05:30
from base.thread_local_middleware import _thread_locals
from employee.filters import EmployeeFilter
from employee.forms import MultipleFileField
from employee.models import Employee, EmployeeTag
2024-01-10 15:54:26 +05:30
from horilla_audit.models import AuditTag
2024-01-03 16:21:50 +05:30
from horilla_widgets.widgets.horilla_multi_select_field import HorillaMultiSelectField
from horilla_widgets.widgets.select_widgets import HorillaMultiSelectWidget
2023-05-10 15:06:57 +05:30
# your form here
def validate_time_format(value):
2023-08-01 16:48:48 +05:30
"""
2023-05-10 15:06:57 +05:30
this method is used to validate the format of duration like fields.
2023-08-01 16:48:48 +05:30
"""
2023-05-10 15:06:57 +05:30
if len(value) > 6:
2023-08-07 13:02:24 +05:30
raise ValidationError(_("Invalid format, it should be HH:MM format"))
try:
2023-05-10 15:06:57 +05:30
hour, minute = value.split(":")
hour = int(hour)
minute = int(minute)
if len(str(hour)) > 3 or minute not in range(60):
2023-08-07 13:02:24 +05:30
raise ValidationError(_("Invalid format, it should be HH:MM format"))
2023-08-01 16:48:48 +05:30
except ValueError as error:
2023-08-07 13:02:24 +05:30
raise ValidationError(_("Invalid format, it should be HH:MM format")) from error
2023-05-10 15:06:57 +05:30
BASED_ON = [
2023-08-07 13:02:24 +05:30
("after", _trans("After")),
("weekly", _trans("Weekend")),
("monthly", _trans("Monthly")),
2023-05-10 15:06:57 +05:30
]
def get_next_week_date(target_day, start_date):
"""
Calculates the date of the next occurrence of the target day within the next week.
Parameters:
target_day (int): The target day of the week (0-6, where Monday is 0 and Sunday is 6).
start_date (datetime.date): The starting date.
Returns:
datetime.date: The date of the next occurrence of the target day within the next week.
2023-08-01 16:48:48 +05:30
"""
2023-05-10 15:06:57 +05:30
if start_date.weekday() == target_day:
return start_date
days_until_target_day = (target_day - start_date.weekday()) % 7
if days_until_target_day == 0:
days_until_target_day = 7
return start_date + timedelta(days=days_until_target_day)
def get_next_monthly_date(start_date, rotate_every):
"""
2023-08-01 16:48:48 +05:30
Given a start date and a rotation day (specified as an integer between 1 and 31, or
the string 'last'),calculates the next rotation date for a monthly rotation schedule.
2023-05-10 15:06:57 +05:30
2023-08-01 16:48:48 +05:30
If the rotation day has not yet occurred in the current month, the next rotation date
will be on the rotation day of the current month. If the rotation day has already
occurred in the current month, the next rotation date will be on the rotation day of
the next month.
2023-05-10 15:06:57 +05:30
2023-08-01 16:48:48 +05:30
If 'last' is specified as the rotation day, the next rotation date will be on the
last day of the current month.
2023-05-10 15:06:57 +05:30
Parameters:
- start_date: The start date of the rotation schedule, as a datetime.date object.
2023-08-01 16:48:48 +05:30
- rotate_every: The rotation day, specified as an integer between 1 and 31, or the
string 'last'.
2023-05-10 15:06:57 +05:30
Returns:
- A datetime.date object representing the next rotation date.
"""
2023-08-01 16:48:48 +05:30
if rotate_every == "last":
2023-05-10 15:06:57 +05:30
# Set rotate_every to the last day of the current month
last_day = calendar.monthrange(start_date.year, start_date.month)[1]
rotate_every = str(last_day)
rotate_every = int(rotate_every)
# Calculate the next change date
if start_date.day <= rotate_every or rotate_every == 0:
2023-08-01 16:48:48 +05:30
# If the rotation day has not occurred yet this month, or if it's the last-
# day of the month, set the next change date to the rotation day of this month
2023-05-10 15:06:57 +05:30
try:
2023-08-01 16:48:48 +05:30
next_change = datetime.date(start_date.year, start_date.month, rotate_every)
2023-05-10 15:06:57 +05:30
except ValueError:
next_change = datetime.date(
2023-08-01 16:48:48 +05:30
start_date.year, start_date.month + 1, 1
) # Advance to next month
2023-05-10 15:06:57 +05:30
# Set day to rotate_every
next_change = datetime.date(
2023-08-01 16:48:48 +05:30
next_change.year, next_change.month, rotate_every
)
2023-05-10 15:06:57 +05:30
else:
2023-08-01 16:48:48 +05:30
# If the rotation day has already occurred this month, set the next change
# date to the rotation day of the next month
2023-05-10 15:06:57 +05:30
last_day = calendar.monthrange(start_date.year, start_date.month)[1]
next_month_start = start_date.replace(day=last_day) + timedelta(days=1)
try:
next_change = next_month_start.replace(day=rotate_every)
except ValueError:
2023-08-01 16:48:48 +05:30
next_change = (
next_month_start.replace(month=next_month_start.month + 1)
+ timedelta(days=1)
).replace(day=rotate_every)
2023-05-10 15:06:57 +05:30
return next_change
class ModelForm(forms.ModelForm):
2023-08-01 16:48:48 +05:30
"""
Override django model's form to add initial styling
"""
2023-05-10 15:06:57 +05:30
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
2023-12-01 15:36:51 +05:30
reload_queryset(self.fields)
request = getattr(thread_local_middleware._thread_locals, "request", None)
2023-05-10 15:06:57 +05:30
for field_name, field in self.fields.items():
widget = field.widget
if isinstance(widget, (forms.DateInput)):
field.initial = date.today()
2023-08-01 16:48:48 +05:30
if isinstance(
widget,
(forms.NumberInput, forms.EmailInput, forms.TextInput, forms.FileInput),
):
label = ""
if field.label is not None:
label = _(field.label.title())
2023-05-10 15:06:57 +05:30
field.widget.attrs.update(
2023-08-01 16:48:48 +05:30
{"class": "oh-input w-100", "placeholder": label}
)
2023-05-10 15:06:57 +05:30
elif isinstance(widget, (forms.Select,)):
field.empty_label = None
if not isinstance(field, forms.ModelMultipleChoiceField):
label = ""
if field.label is not None:
label = _(field.label)
field.empty_label = _("---Choose {label}---").format(label=label)
2023-05-10 15:06:57 +05:30
field.widget.attrs.update(
2023-08-01 16:48:48 +05:30
{"class": "oh-select oh-select-2 select2-hidden-accessible"}
)
2023-05-10 15:06:57 +05:30
elif isinstance(widget, (forms.Textarea)):
2023-08-01 16:48:48 +05:30
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"})
2023-05-10 15:06:57 +05:30
try:
self.fields["employee_id"].initial = request.user.employee_get
except:
pass
try:
self.fields["company_id"].initial = (
request.user.employee_get.get_company
)
except:
pass
2023-05-10 15:06:57 +05:30
2023-05-10 15:06:57 +05:30
class Form(forms.Form):
2023-08-01 16:48:48 +05:30
"""
Overrides to add initial styling to the django Form instance
"""
2023-05-10 15:06:57 +05:30
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for field_name, field in self.fields.items():
widget = field.widget
2023-08-01 16:48:48 +05:30
if isinstance(
widget, (forms.NumberInput, forms.EmailInput, forms.TextInput)
):
if field.label is not None:
label = _(field.label)
field.widget.attrs.update(
{"class": "oh-input w-100", "placeholder": label}
)
2023-05-10 15:06:57 +05:30
elif isinstance(widget, (forms.Select,)):
label = ""
2023-05-10 15:06:57 +05:30
if field.label is not None:
label = field.label.replace("id", " ")
field.empty_label = _("---Choose {label}---").format(label=label)
2023-08-01 16:48:48 +05:30
field.widget.attrs.update(
{"class": "oh-select oh-select-2 select2-hidden-accessible"}
)
2023-05-10 15:06:57 +05:30
elif isinstance(widget, (forms.Textarea)):
label = _(field.label)
2023-08-01 16:48:48 +05:30
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"})
2023-05-10 15:06:57 +05:30
class UserGroupForm(ModelForm):
2023-08-01 16:48:48 +05:30
"""
Django user groups form
"""
2024-01-03 16:21:50 +05:30
try:
permissions = forms.MultipleChoiceField(
choices=[(perm.codename, perm.name) for perm in Permission.objects.all()],
error_messages={
"required": "Please choose a permission.",
},
)
except:
pass
2023-05-10 15:06:57 +05:30
class Meta:
2023-08-01 16:48:48 +05:30
"""
Meta class for additional options
"""
2023-05-10 15:06:57 +05:30
model = Group
fields = ["name", "permissions"]
def save(self, commit=True):
"""
ModelForm save override
"""
group = super().save(commit=False)
if self.instance:
group = self.instance
group.save()
# Convert the selected codenames back to Permission instances
permissions_codenames = self.cleaned_data["permissions"]
permissions = Permission.objects.filter(codename__in=permissions_codenames)
# Set the associated permissions
group.permissions.set(permissions)
if commit:
group.save()
return group
2023-05-10 15:06:57 +05:30
class AssignUserGroup(Form):
2023-08-01 16:48:48 +05:30
"""
Form to assign groups
"""
2024-01-03 16:21:50 +05:30
employee = forms.ModelMultipleChoiceField(
queryset=Employee.objects.all(), required=False
)
group = forms.ModelChoiceField(queryset=Group.objects.all())
2023-05-10 15:06:57 +05:30
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
reload_queryset(self.fields)
2023-05-10 15:06:57 +05:30
def save(self):
2023-08-01 16:48:48 +05:30
"""
Save method to assign group to employees
"""
employees = self.cleaned_data["employee"]
group = self.cleaned_data["group"]
group.user_set.clear()
2023-05-10 15:06:57 +05:30
for employee in employees:
employee.employee_user_id.groups.add(group)
2023-05-10 15:06:57 +05:30
return group
class AssignPermission(Form):
2023-08-01 16:48:48 +05:30
"""
Forms to assign user permision
"""
2024-01-03 16:21:50 +05:30
employee = 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",
required=True,
),
label="Employee",
)
try:
permissions = forms.MultipleChoiceField(
choices=[(perm.codename, perm.name) for perm in Permission.objects.all()],
error_messages={
"required": "Please choose a permission.",
},
)
except:
pass
2024-01-03 16:21:50 +05:30
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
reload_queryset(self.fields)
2023-05-10 15:06:57 +05:30
2024-01-03 16:21:50 +05:30
def clean(self):
emps = self.data.getlist("employee")
if emps:
self.errors.pop("employee", None)
super().clean()
return
2023-05-10 15:06:57 +05:30
def save(self):
2023-08-01 16:48:48 +05:30
"""
Save method to assign permission to employee
"""
user_ids = Employee.objects.filter(
id__in=self.data.getlist("employee")
).values_list("employee_user_id", flat=True)
permissions = self.cleaned_data["permissions"]
permissions = Permission.objects.filter(codename__in=permissions)
users = User.objects.filter(id__in=user_ids)
for user in users:
user.user_permissions.set(permissions)
2023-08-01 16:48:48 +05:30
return self
2023-05-10 15:06:57 +05:30
class CompanyForm(ModelForm):
2023-08-01 16:48:48 +05:30
"""
Company model's form
"""
2023-05-10 15:06:57 +05:30
class Meta:
2023-08-01 16:48:48 +05:30
"""
Meta class for additional options
"""
2023-05-10 15:06:57 +05:30
2023-08-01 16:48:48 +05:30
model = Company
fields = "__all__"
excluded_fields = ["date_format", "time_format", "is_active"]
2023-05-10 15:06:57 +05:30
def validate_image(self, file):
max_size = 5 * 1024 * 1024
if file.size > max_size:
raise ValidationError("File size should be less than 5MB.")
# Check file extension
valid_extensions = [".jpg", ".jpeg", ".png", ".webp", ".svg"]
ext = os.path.splitext(file.name)[1].lower()
if ext not in valid_extensions:
raise ValidationError("Unsupported file extension.")
def clean_icon(self):
icon = self.cleaned_data.get("icon")
if icon:
self.validate_image(icon)
return icon
2023-05-10 15:06:57 +05:30
class DepartmentForm(ModelForm):
2023-08-01 16:48:48 +05:30
"""
Department model's form
"""
2023-05-10 15:06:57 +05:30
class Meta:
2023-08-01 16:48:48 +05:30
"""
Meta class for additional options
"""
2023-05-10 15:06:57 +05:30
2023-08-01 16:48:48 +05:30
model = Department
fields = "__all__"
exclude = ["is_active"]
2023-05-10 15:06:57 +05:30
class JobPositionForm(ModelForm):
2023-08-01 16:48:48 +05:30
"""
JobPosition model's form
"""
2023-05-10 15:06:57 +05:30
class Meta:
2023-08-01 16:48:48 +05:30
"""
Meta class for additional options
"""
2023-05-10 15:06:57 +05:30
2023-08-01 16:48:48 +05:30
model = JobPosition
fields = "__all__"
exclude = ["is_active"]
2023-05-10 15:06:57 +05:30
class JobRoleForm(ModelForm):
2023-08-01 16:48:48 +05:30
"""
JobRole model's form
"""
2023-05-10 15:06:57 +05:30
class Meta:
2023-08-01 16:48:48 +05:30
"""
Meta class for additional options
"""
2023-05-10 15:06:57 +05:30
2023-08-01 16:48:48 +05:30
model = JobRole
fields = "__all__"
exclude = ["is_active"]
2023-05-10 15:06:57 +05:30
class WorkTypeForm(ModelForm):
2023-08-01 16:48:48 +05:30
"""
WorkType model's form
"""
2023-05-10 15:06:57 +05:30
class Meta:
2023-08-01 16:48:48 +05:30
"""
Meta class for additional options
"""
2023-05-10 15:06:57 +05:30
2023-08-01 16:48:48 +05:30
model = WorkType
fields = "__all__"
exclude = ["is_active"]
2023-05-10 15:06:57 +05:30
class RotatingWorkTypeForm(ModelForm):
2023-08-01 16:48:48 +05:30
"""
RotatingWorkType model's form
"""
2023-05-10 15:06:57 +05:30
class Meta:
2023-08-01 16:48:48 +05:30
"""
Meta class for additional options
"""
2023-05-10 15:06:57 +05:30
model = RotatingWorkType
2023-08-01 16:48:48 +05:30
fields = "__all__"
exclude = ["employee_id", "is_active"]
2023-05-10 15:06:57 +05:30
widgets = {
2023-08-01 16:48:48 +05:30
"start_date": DateInput(attrs={"type": "date"}),
2023-05-10 15:06:57 +05:30
}
2023-08-07 13:02:24 +05:30
class RotatingWorkTypeAssignForm(ModelForm):
2023-08-01 16:48:48 +05:30
"""
RotatingWorkTypeAssign model's form
"""
employee_id = HorillaMultiSelectField(
2023-08-01 16:48:48 +05:30
queryset=Employee.objects.filter(employee_work_info__isnull=False),
widget=HorillaMultiSelectWidget(
filter_route_name="employee-widget-filter",
filter_class=EmployeeFilter,
filter_instance_contex_name="f",
filter_template_path="employee_filters.html",
),
label=_trans("Employees"),
2023-08-01 16:48:48 +05:30
)
2023-08-07 13:02:24 +05:30
based_on = forms.ChoiceField(
choices=BASED_ON, initial="daily", label=_trans("Based on")
)
rotate_after_day = forms.IntegerField(initial=5, label=_trans("Rotate after day"))
start_date = forms.DateField(
initial=datetime.date.today, widget=forms.DateInput, label=_trans("Start date")
2023-08-01 16:48:48 +05:30
)
2023-05-10 15:06:57 +05:30
class Meta:
2023-08-01 16:48:48 +05:30
"""
Meta class for additional options
"""
2023-05-10 15:06:57 +05:30
model = RotatingWorkTypeAssign
2023-08-01 16:48:48 +05:30
fields = "__all__"
exclude = [
"next_change_date",
"current_work_type",
"next_work_type",
"is_active",
]
2023-05-10 15:06:57 +05:30
widgets = {
2023-08-01 16:48:48 +05:30
"start_date": DateInput(attrs={"type": "date"}),
"is_active": HiddenInput(),
2023-05-10 15:06:57 +05:30
}
labels = {
2023-08-07 13:02:24 +05:30
"is_active": _trans("Is Active"),
"rotate_every_weekend": _trans("Rotate every weekend"),
"rotate_every": _trans("Rotate every"),
2023-05-10 15:06:57 +05:30
}
def __init__(self, *args, **kwargs):
2023-08-01 16:48:48 +05:30
super().__init__(*args, **kwargs)
reload_queryset(self.fields)
2023-08-01 16:48:48 +05:30
self.fields["rotate_every_weekend"].widget.attrs.update(
{
"class": "w-100",
"style": "display:none; height:50px; border-radius:0;border:1px \
solid hsl(213deg,22%,84%);",
"data-hidden": True,
}
)
self.fields["rotate_every"].widget.attrs.update(
{
"class": "w-100",
"style": "display:none; height:50px; border-radius:0;border:1px \
solid hsl(213deg,22%,84%);",
"data-hidden": True,
}
)
self.fields["rotate_after_day"].widget.attrs.update(
{
"class": "w-100 oh-input",
"style": " height:50px; border-radius:0;",
}
)
self.fields["based_on"].widget.attrs.update(
{
"class": "w-100",
"style": " height:50px; border-radius:0;border:1px solid hsl(213deg,22%,84%);",
}
)
self.fields["start_date"].widget = forms.DateInput(
attrs={
"class": "w-100 oh-input",
"type": "date",
"style": " height:50px; border-radius:0;",
}
)
self.fields["rotating_work_type_id"].widget.attrs.update(
{
"class": "oh-select oh-select-2",
}
)
self.fields["employee_id"].widget.attrs.update(
{
"class": "oh-select oh-select-2",
}
)
2023-05-10 15:06:57 +05:30
def clean_employee_id(self):
2023-08-01 16:48:48 +05:30
employee_ids = self.cleaned_data.get("employee_id")
2023-05-10 15:06:57 +05:30
if employee_ids:
return employee_ids[0]
else:
2023-08-07 13:02:24 +05:30
return ValidationError(_("This field is required"))
2023-05-10 15:06:57 +05:30
def clean(self):
super().clean()
self.instance.employee_id = Employee.objects.filter(
id=self.data.get("employee_id")
).first()
self.errors.pop("employee_id", None)
if self.instance.employee_id is None:
raise ValidationError({"employee_id": _("This field is required")})
super().clean()
2023-05-10 15:06:57 +05:30
cleaned_data = super().clean()
2023-08-01 16:48:48 +05:30
if "rotate_after_day" in self.errors:
del self.errors["rotate_after_day"]
2023-05-10 15:06:57 +05:30
return cleaned_data
def save(self, commit=False, manager=None):
2023-08-01 16:48:48 +05:30
employee_ids = self.data.getlist("employee_id")
2023-05-10 15:06:57 +05:30
rotating_work_type = RotatingWorkType.objects.get(
2023-08-01 16:48:48 +05:30
id=self.data["rotating_work_type_id"]
)
day_name = self.cleaned_data["rotate_every_weekend"]
day_names = [
"monday",
"tuesday",
"wednesday",
"thursday",
"friday",
"saturday",
"sunday",
]
2023-05-10 15:06:57 +05:30
target_day = day_names.index(day_name.lower())
for employee_id in employee_ids:
employee = Employee.objects.filter(id=employee_id).first()
rotating_work_type_assign = RotatingWorkTypeAssign()
rotating_work_type_assign.rotating_work_type_id = rotating_work_type
rotating_work_type_assign.employee_id = employee
2023-08-01 16:48:48 +05:30
rotating_work_type_assign.based_on = self.cleaned_data["based_on"]
rotating_work_type_assign.start_date = self.cleaned_data["start_date"]
rotating_work_type_assign.next_change_date = self.cleaned_data["start_date"]
2023-05-10 15:06:57 +05:30
rotating_work_type_assign.rotate_after_day = self.data.get(
2023-08-01 16:48:48 +05:30
"rotate_after_day"
)
rotating_work_type_assign.rotate_every = self.cleaned_data["rotate_every"]
2023-05-10 15:06:57 +05:30
rotating_work_type_assign.rotate_every_weekend = self.cleaned_data[
2023-08-01 16:48:48 +05:30
"rotate_every_weekend"
]
rotating_work_type_assign.next_change_date = self.cleaned_data["start_date"]
rotating_work_type_assign.current_work_type = (
employee.employee_work_info.work_type_id
)
2023-05-10 15:06:57 +05:30
rotating_work_type_assign.next_work_type = rotating_work_type.work_type2
2023-08-01 16:48:48 +05:30
based_on = self.cleaned_data["based_on"]
start_date = self.cleaned_data["start_date"]
2023-05-10 15:06:57 +05:30
if based_on == "weekly":
next_date = get_next_week_date(target_day, start_date)
rotating_work_type_assign.next_change_date = next_date
elif based_on == "monthly":
# 0, 1, 2, ..., 31, or "last"
2023-08-01 16:48:48 +05:30
rotate_every = self.cleaned_data["rotate_every"]
start_date = self.cleaned_data["start_date"]
2023-05-10 15:06:57 +05:30
next_date = get_next_monthly_date(start_date, rotate_every)
rotating_work_type_assign.next_change_date = next_date
elif based_on == "after":
2023-08-01 16:48:48 +05:30
rotating_work_type_assign.next_change_date = (
rotating_work_type_assign.start_date
+ datetime.timedelta(days=int(self.data.get("rotate_after_day")))
)
2023-05-10 15:06:57 +05:30
rotating_work_type_assign.save()
class RotatingWorkTypeAssignUpdateForm(forms.ModelForm):
2023-08-01 16:48:48 +05:30
"""
RotatingWorkTypeAssign model's form
"""
2023-05-10 15:06:57 +05:30
class Meta:
2023-08-01 16:48:48 +05:30
"""
Meta class for additional options
"""
2023-05-10 15:06:57 +05:30
model = RotatingWorkTypeAssign
2023-08-01 16:48:48 +05:30
fields = "__all__"
exclude = [
"next_change_date",
"current_work_type",
"next_work_type",
"is_active",
]
2023-05-10 15:06:57 +05:30
widgets = {
2023-08-01 16:48:48 +05:30
"start_date": DateInput(attrs={"type": "date"}),
2023-05-10 15:06:57 +05:30
}
2023-08-07 13:02:24 +05:30
labels = {
"start_date": _trans("Start date"),
"rotate_after_day": _trans("Rotate after day"),
"rotate_every_weekend": _trans("Rotate every weekend"),
"rotate_every": _trans("Rotate every"),
"based_on": _trans("Based on"),
"is_active": _trans("Is Active"),
}
2023-05-10 15:06:57 +05:30
def __init__(self, *args, **kwargs):
2023-08-01 16:48:48 +05:30
super().__init__(*args, **kwargs)
2023-12-01 15:36:51 +05:30
reload_queryset(self.fields)
2023-05-10 15:06:57 +05:30
2023-08-01 16:48:48 +05:30
self.fields["rotate_every_weekend"].widget.attrs.update(
{
"class": "w-100",
"style": "display:none; height:50px; border-radius:0;border:1px\
solid hsl(213deg,22%,84%);",
"data-hidden": True,
}
)
self.fields["rotate_every"].widget.attrs.update(
{
"class": "w-100",
"style": "display:none; height:50px; border-radius:0;border:1px \
solid hsl(213deg,22%,84%);",
"data-hidden": True,
}
)
self.fields["rotate_after_day"].widget.attrs.update(
{
"class": "w-100 oh-input",
"style": " height:50px; border-radius:0;",
}
)
self.fields["based_on"].widget.attrs.update(
{
"class": "w-100",
"style": " height:50px; border-radius:0; border:1px solid \
hsl(213deg,22%,84%);",
}
)
self.fields["start_date"].widget = forms.DateInput(
attrs={
"class": "w-100 oh-input",
"type": "date",
"style": " height:50px; border-radius:0;",
}
)
self.fields["rotating_work_type_id"].widget.attrs.update(
{
"class": "oh-select oh-select-2",
}
)
self.fields["employee_id"].widget.attrs.update(
{
"class": "oh-select oh-select-2",
}
)
2023-05-10 15:06:57 +05:30
2023-08-01 16:48:48 +05:30
def save(self, *args, **kwargs):
day_name = self.cleaned_data["rotate_every_weekend"]
day_names = [
"monday",
"tuesday",
"wednesday",
"thursday",
"friday",
"saturday",
"sunday",
]
2023-05-10 15:06:57 +05:30
target_day = day_names.index(day_name.lower())
2023-08-01 16:48:48 +05:30
based_on = self.cleaned_data["based_on"]
2023-05-10 15:06:57 +05:30
start_date = self.instance.start_date
if based_on == "weekly":
next_date = get_next_week_date(target_day, start_date)
self.instance.next_change_date = next_date
elif based_on == "monthly":
rotate_every = self.instance.rotate_every # 0, 1, 2, ..., 31, or "last"
start_date = self.instance.start_date
next_date = get_next_monthly_date(start_date, rotate_every)
self.instance.next_change_date = next_date
elif based_on == "after":
2023-08-01 16:48:48 +05:30
self.instance.next_change_date = (
self.instance.start_date
+ datetime.timedelta(days=int(self.data.get("rotate_after_day")))
)
2023-05-10 15:06:57 +05:30
return super().save()
class EmployeeTypeForm(ModelForm):
2023-08-01 16:48:48 +05:30
"""
EmployeeType form
"""
2023-05-10 15:06:57 +05:30
class Meta:
2023-08-01 16:48:48 +05:30
"""
Meta class for additional options
"""
2023-05-10 15:06:57 +05:30
2023-08-01 16:48:48 +05:30
model = EmployeeType
fields = "__all__"
exclude = ["is_active"]
2023-05-10 15:06:57 +05:30
class EmployeeShiftForm(ModelForm):
2023-08-01 16:48:48 +05:30
"""
EmployeeShift Form
"""
2023-05-10 15:06:57 +05:30
class Meta:
2023-08-01 16:48:48 +05:30
"""
Meta class for additional options
"""
2023-05-10 15:06:57 +05:30
2023-08-01 16:48:48 +05:30
model = EmployeeShift
fields = "__all__"
exclude = ["days", "is_active"]
2023-05-10 15:06:57 +05:30
2024-01-03 16:21:50 +05:30
def clean(self):
2023-08-01 16:48:48 +05:30
full_time = self.data["full_time"]
2023-05-10 15:06:57 +05:30
validate_time_format(full_time)
2023-08-01 16:48:48 +05:30
full_time = self.data["weekly_full_time"]
validate_time_format(full_time)
return super().clean()
2023-05-10 15:06:57 +05:30
2023-08-07 13:02:24 +05:30
2023-05-10 15:06:57 +05:30
class EmployeeShiftScheduleUpdateForm(ModelForm):
2023-08-01 16:48:48 +05:30
"""
EmployeeShiftSchedule model's form
"""
2023-05-10 15:06:57 +05:30
class Meta:
2023-08-01 16:48:48 +05:30
"""
Meta class for additional options
"""
2023-05-10 15:06:57 +05:30
2023-08-01 16:48:48 +05:30
fields = "__all__"
exclude = ["is_active"]
2023-08-01 16:48:48 +05:30
widgets = {
"start_time": DateInput(attrs={"type": "time"}),
"end_time": DateInput(attrs={"type": "time"}),
2023-05-10 15:06:57 +05:30
}
model = EmployeeShiftSchedule
def __init__(self, *args, **kwargs):
2023-08-01 16:48:48 +05:30
if instance := kwargs.get("instance"):
# """
# django forms not showing value inside the date, time html element.
# so here overriding default forms instance method to set initial value
# """
2023-05-10 15:06:57 +05:30
initial = {
2023-08-01 16:48:48 +05:30
"start_time": instance.start_time.strftime("%H:%M"),
"end_time": instance.end_time.strftime("%H:%M"),
2023-05-10 15:06:57 +05:30
}
2023-08-01 16:48:48 +05:30
kwargs["initial"] = initial
2023-05-10 15:06:57 +05:30
super().__init__(*args, **kwargs)
class EmployeeShiftScheduleForm(ModelForm):
2023-08-01 16:48:48 +05:30
"""
EmployeeShiftSchedule model's form
"""
2023-05-10 15:06:57 +05:30
day = forms.ModelMultipleChoiceField(
2023-08-01 16:48:48 +05:30
queryset=EmployeeShiftDay.objects.all(),
)
2023-05-10 15:06:57 +05:30
class Meta:
2023-08-01 16:48:48 +05:30
"""
Meta class for additional options
"""
2023-05-10 15:06:57 +05:30
model = EmployeeShiftSchedule
2023-08-01 16:48:48 +05:30
fields = "__all__"
exclude = ["is_night_shift", "is_active"]
2023-05-10 15:06:57 +05:30
widgets = {
2023-08-01 16:48:48 +05:30
"start_time": DateInput(attrs={"type": "time"}),
"end_time": DateInput(attrs={"type": "time"}),
2023-05-10 15:06:57 +05:30
}
def __init__(self, *args, **kwargs):
2023-08-01 16:48:48 +05:30
if instance := kwargs.get("instance"):
# """
# django forms not showing value inside the date, time html element.
# so here overriding default forms instance method to set initial value
# """
2023-05-10 15:06:57 +05:30
initial = {
2023-08-01 16:48:48 +05:30
"start_time": instance.start_time.strftime("%H:%M"),
"end_time": instance.end_time.strftime("%H:%M"),
2023-05-10 15:06:57 +05:30
}
2023-08-01 16:48:48 +05:30
kwargs["initial"] = initial
super().__init__(*args, **kwargs)
self.fields["day"].widget.attrs.update({"id": str(uuid.uuid4())})
self.fields["shift_id"].widget.attrs.update({"id": str(uuid.uuid4())})
2023-05-10 15:06:57 +05:30
def save(self, commit=True):
instance = super().save(commit=False)
2023-08-01 16:48:48 +05:30
for day in self.data.getlist("day"):
2023-05-10 15:06:57 +05:30
if int(day) != int(instance.day.id):
data_copy = self.data.copy()
2023-08-01 16:48:48 +05:30
data_copy.update({"day": str(day)})
shift_schedule = EmployeeShiftScheduleUpdateForm(data_copy).save(
commit=False
)
2023-05-10 15:06:57 +05:30
shift_schedule.save()
if commit:
instance.save()
return instance
def clean_day(self):
2023-08-01 16:48:48 +05:30
"""
Validation to day field
"""
days = self.cleaned_data["day"]
2023-05-10 15:06:57 +05:30
for day in days:
attendance = EmployeeShiftSchedule.objects.filter(
2023-08-01 16:48:48 +05:30
day=day, shift_id=self.data["shift_id"]
).first()
2023-05-10 15:06:57 +05:30
if attendance is not None:
2023-08-07 13:02:24 +05:30
raise ValidationError(
_("Shift schedule is already exist for {day}").format(
day=_(day.day)
)
)
2023-05-10 15:06:57 +05:30
if days.first() is None:
2023-08-07 13:02:24 +05:30
raise ValidationError(_("Employee not chosen"))
2023-05-10 15:06:57 +05:30
return days.first()
class RotatingShiftForm(ModelForm):
2023-08-01 16:48:48 +05:30
"""
RotatingShift model's form
"""
2023-05-10 15:06:57 +05:30
class Meta:
2023-08-01 16:48:48 +05:30
"""
Meta class for additional options
"""
2023-05-10 15:06:57 +05:30
2023-08-01 16:48:48 +05:30
model = RotatingShift
fields = "__all__"
exclude = ["employee_id", "is_active"]
2023-05-10 15:06:57 +05:30
class RotatingShiftAssignForm(forms.ModelForm):
2023-08-01 16:48:48 +05:30
"""
RotatingShiftAssign model's form
"""
employee_id = HorillaMultiSelectField(
2023-08-01 16:48:48 +05:30
queryset=Employee.objects.filter(employee_work_info__isnull=False),
widget=HorillaMultiSelectWidget(
filter_route_name="employee-widget-filter",
filter_class=EmployeeFilter,
filter_instance_contex_name="f",
filter_template_path="employee_filters.html",
),
label=_trans("Employees"),
2023-08-01 16:48:48 +05:30
)
2023-08-07 13:02:24 +05:30
based_on = forms.ChoiceField(
choices=BASED_ON, initial="daily", label=_trans("Based on")
)
rotate_after_day = forms.IntegerField(initial=5, label=_trans("Rotate after day"))
start_date = forms.DateField(
initial=datetime.date.today, widget=forms.DateInput, label=_trans("Start date")
2023-08-01 16:48:48 +05:30
)
2023-05-10 15:06:57 +05:30
class Meta:
2023-08-01 16:48:48 +05:30
"""
Meta class for additional options
"""
2023-05-10 15:06:57 +05:30
model = RotatingShiftAssign
2023-08-01 16:48:48 +05:30
fields = "__all__"
exclude = ["next_change_date", "current_shift", "next_shift", "is_active"]
2023-05-10 15:06:57 +05:30
widgets = {
2023-08-01 16:48:48 +05:30
"start_date": DateInput(attrs={"type": "date"}),
2023-05-10 15:06:57 +05:30
}
labels = {
2023-08-07 13:02:24 +05:30
"rotating_shift_id": _trans("Rotating Shift"),
"start_date": _("Start date"),
"is_active": _trans("Is Active"),
"rotate_every_weekend": _trans("Rotate every weekend"),
"rotate_every": _trans("Rotate every"),
2023-05-10 15:06:57 +05:30
}
def __init__(self, *args, **kwargs):
2023-08-01 16:48:48 +05:30
super().__init__(*args, **kwargs)
2023-12-01 15:36:51 +05:30
reload_queryset(self.fields)
2023-08-01 16:48:48 +05:30
self.fields["rotate_every_weekend"].widget.attrs.update(
{
"class": "w-100 ",
"style": "display:none; height:50px; border-radius:0;border:1px \
solid hsl(213deg,22%,84%);",
"data-hidden": True,
}
)
self.fields["rotate_every"].widget.attrs.update(
{
"class": "w-100 ",
"style": "display:none; height:50px; border-radius:0;border:1px \
solid hsl(213deg,22%,84%);",
"data-hidden": True,
}
)
self.fields["rotate_after_day"].widget.attrs.update(
{
"class": "w-100 oh-input",
"style": " height:50px; border-radius:0;",
}
)
self.fields["based_on"].widget.attrs.update(
{
"class": "w-100",
"style": " height:50px; border-radius:0;border:1px solid hsl(213deg,22%,84%);",
}
)
self.fields["start_date"].widget = forms.DateInput(
attrs={
"class": "w-100 oh-input",
"type": "date",
"style": " height:50px; border-radius:0;",
}
)
self.fields["rotating_shift_id"].widget.attrs.update(
{
"class": "oh-select oh-select-2",
}
)
self.fields["employee_id"].widget.attrs.update(
{
"class": "oh-select oh-select-2",
}
)
2023-05-10 15:06:57 +05:30
def clean_employee_id(self):
2023-08-01 16:48:48 +05:30
"""
Validation to employee_id field
"""
employee_ids = self.cleaned_data.get("employee_id")
2023-05-10 15:06:57 +05:30
if employee_ids:
return employee_ids[0]
else:
2023-08-07 13:02:24 +05:30
return ValidationError(_("This field is required"))
2023-05-10 15:06:57 +05:30
def clean(self):
super().clean()
self.instance.employee_id = Employee.objects.filter(
id=self.data.get("employee_id")
).first()
self.errors.pop("employee_id", None)
if self.instance.employee_id is None:
raise ValidationError({"employee_id": _("This field is required")})
super().clean()
2023-05-10 15:06:57 +05:30
cleaned_data = super().clean()
2023-08-01 16:48:48 +05:30
if "rotate_after_day" in self.errors:
del self.errors["rotate_after_day"]
2023-05-10 15:06:57 +05:30
return cleaned_data
2023-08-01 16:48:48 +05:30
def save(
self,
commit=False,
):
employee_ids = self.data.getlist("employee_id")
rotating_shift = RotatingShift.objects.get(id=self.data["rotating_shift_id"])
day_name = self.cleaned_data["rotate_every_weekend"]
day_names = [
"monday",
"tuesday",
"wednesday",
"thursday",
"friday",
"saturday",
"sunday",
]
2023-05-10 15:06:57 +05:30
target_day = day_names.index(day_name.lower())
for employee_id in employee_ids:
employee = Employee.objects.filter(id=employee_id).first()
rotating_shift_assign = RotatingShiftAssign()
rotating_shift_assign.rotating_shift_id = rotating_shift
rotating_shift_assign.employee_id = employee
2023-08-01 16:48:48 +05:30
rotating_shift_assign.based_on = self.cleaned_data["based_on"]
rotating_shift_assign.start_date = self.cleaned_data["start_date"]
rotating_shift_assign.next_change_date = self.cleaned_data["start_date"]
rotating_shift_assign.rotate_after_day = self.data.get("rotate_after_day")
rotating_shift_assign.rotate_every = self.cleaned_data["rotate_every"]
rotating_shift_assign.rotate_every_weekend = self.cleaned_data[
"rotate_every_weekend"
]
rotating_shift_assign.next_change_date = self.cleaned_data["start_date"]
2023-05-10 15:06:57 +05:30
rotating_shift_assign.current_shift = employee.employee_work_info.shift_id
rotating_shift_assign.next_shift = rotating_shift.shift2
2023-08-01 16:48:48 +05:30
based_on = self.cleaned_data["based_on"]
start_date = self.cleaned_data["start_date"]
2023-05-10 15:06:57 +05:30
if based_on == "weekly":
next_date = get_next_week_date(target_day, start_date)
rotating_shift_assign.next_change_date = next_date
elif based_on == "monthly":
# 0, 1, 2, ..., 31, or "last"
2023-08-01 16:48:48 +05:30
rotate_every = self.cleaned_data["rotate_every"]
start_date = self.cleaned_data["start_date"]
2023-05-10 15:06:57 +05:30
next_date = get_next_monthly_date(start_date, rotate_every)
rotating_shift_assign.next_change_date = next_date
elif based_on == "after":
2023-08-01 16:48:48 +05:30
rotating_shift_assign.next_change_date = (
rotating_shift_assign.start_date
+ datetime.timedelta(days=int(self.data.get("rotate_after_day")))
)
2023-05-10 15:06:57 +05:30
rotating_shift_assign.save()
2023-08-07 13:02:24 +05:30
class RotatingShiftAssignUpdateForm(ModelForm):
2023-08-01 16:48:48 +05:30
"""
RotatingShiftAssign model's form
"""
2023-05-10 15:06:57 +05:30
class Meta:
2023-08-01 16:48:48 +05:30
"""
Meta class for additional options
"""
2023-05-10 15:06:57 +05:30
model = RotatingShiftAssign
2023-08-01 16:48:48 +05:30
fields = "__all__"
exclude = ["next_change_date", "current_shift", "next_shift", "is_active"]
2023-05-10 15:06:57 +05:30
widgets = {
2023-08-01 16:48:48 +05:30
"start_date": DateInput(attrs={"type": "date"}),
2023-05-10 15:06:57 +05:30
}
2023-08-07 13:02:24 +05:30
labels = {
"start_date": _trans("Start date"),
"rotate_after_day": _trans("Rotate after day"),
"rotate_every_weekend": _trans("Rotate every weekend"),
"rotate_every": _trans("Rotate every"),
"based_on": _trans("Based on"),
"is_active": _trans("Is Active"),
}
2023-05-10 15:06:57 +05:30
def __init__(self, *args, **kwargs):
2023-08-01 16:48:48 +05:30
super().__init__(*args, **kwargs)
2023-12-01 15:36:51 +05:30
reload_queryset(self.fields)
2023-08-01 16:48:48 +05:30
self.fields["rotate_every_weekend"].widget.attrs.update(
{
"class": "w-100 ",
"style": "display:none; height:50px; border-radius:0; border:1px \
solid hsl(213deg,22%,84%);",
"data-hidden": True,
}
)
self.fields["rotate_every"].widget.attrs.update(
{
"class": "w-100 ",
"style": "display:none; height:50px; border-radius:0; border:1px \
solid hsl(213deg,22%,84%);",
"data-hidden": True,
}
)
self.fields["rotate_after_day"].widget.attrs.update(
{
"class": "w-100 oh-input",
"style": " height:50px; border-radius:0;",
}
)
self.fields["based_on"].widget.attrs.update(
{
"class": "w-100",
"style": " height:50px; border-radius:0; border:1px solid hsl(213deg,22%,84%);",
}
)
self.fields["start_date"].widget = forms.DateInput(
attrs={
"class": "w-100 oh-input",
"type": "date",
"style": " height:50px; border-radius:0;",
}
)
self.fields["rotating_shift_id"].widget.attrs.update(
{
"class": "oh-select oh-select-2",
}
)
self.fields["employee_id"].widget.attrs.update(
{
"class": "oh-select oh-select-2",
}
)
2023-05-10 15:06:57 +05:30
def save(self, *args, **kwargs):
2023-08-01 16:48:48 +05:30
day_name = self.cleaned_data["rotate_every_weekend"]
day_names = [
"monday",
"tuesday",
"wednesday",
"thursday",
"friday",
"saturday",
"sunday",
]
2023-05-10 15:06:57 +05:30
target_day = day_names.index(day_name.lower())
2023-08-01 16:48:48 +05:30
based_on = self.cleaned_data["based_on"]
2023-05-10 15:06:57 +05:30
start_date = self.instance.start_date
if based_on == "weekly":
next_date = get_next_week_date(target_day, start_date)
self.instance.next_change_date = next_date
elif based_on == "monthly":
rotate_every = self.instance.rotate_every # 0, 1, 2, ..., 31, or "last"
start_date = self.instance.start_date
next_date = get_next_monthly_date(start_date, rotate_every)
self.instance.next_change_date = next_date
elif based_on == "after":
2023-08-01 16:48:48 +05:30
self.instance.next_change_date = (
self.instance.start_date
+ datetime.timedelta(days=int(self.data.get("rotate_after_day")))
)
2023-05-10 15:06:57 +05:30
return super().save()
class ShiftRequestForm(ModelForm):
2023-08-01 16:48:48 +05:30
"""
ShiftRequest model's form
"""
2023-05-10 15:06:57 +05:30
class Meta:
2023-08-01 16:48:48 +05:30
"""
Meta class for additional options
"""
2023-05-10 15:06:57 +05:30
model = ShiftRequest
2023-08-01 16:48:48 +05:30
fields = "__all__"
exclude = [
2024-02-06 18:45:32 +05:30
"reallocate_to",
2023-08-01 16:48:48 +05:30
"approved",
"canceled",
2024-02-06 18:45:32 +05:30
"reallocate_approved",
"reallocate_canceled",
2023-08-01 16:48:48 +05:30
"previous_shift_id",
"is_active",
"shift_changed",
]
2023-05-10 15:06:57 +05:30
widgets = {
2023-08-01 16:48:48 +05:30
"requested_date": DateInput(attrs={"type": "date"}),
"requested_till": DateInput(attrs={"type": "date"}),
2023-05-10 15:06:57 +05:30
}
2023-08-07 13:02:24 +05:30
labels = {
"description": _trans("Description"),
"requested_date": _trans("Requested Date"),
"requested_till": _trans("Requested Till"),
}
2023-05-10 15:06:57 +05:30
def as_p(self):
"""
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
def save(self, commit: bool = ...):
if not self.instance.approved:
employee = self.instance.employee_id
if hasattr(employee, "employee_work_info"):
self.instance.previous_shift_id = employee.employee_work_info.shift_id
if self.instance.is_permanent_shift:
self.instance.requested_till = None
2023-05-10 15:06:57 +05:30
return super().save(commit)
# here set default filter for all the employees those have work information filled.
2024-02-06 18:45:32 +05:30
class ShiftAllocationForm(ModelForm):
"""
ShiftRequest model's form
"""
class Meta:
"""
Meta class for additional options
"""
model = ShiftRequest
fields = "__all__"
exclude = (
"is_permanent_shift",
"approved",
"canceled",
"reallocate_approved",
"reallocate_canceled",
"previous_shift_id",
"is_active",
"shift_changed",
)
widgets = {
"requested_date": DateInput(attrs={"type": "date"}),
"requested_till": DateInput(attrs={"type": "date", "required": "true"}),
}
labels = {
"description": _trans("Description"),
"requested_date": _trans("Requested Date"),
"requested_till": _trans("Requested Till"),
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["shift_id"].widget.attrs.update(
{
"hx-target": "#id_reallocate_to_parent_div",
"hx-trigger": "change",
"hx-get": "/update-employee-allocation",
}
)
2024-02-06 18:45:32 +05:30
def as_p(self):
"""
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
def save(self, commit: bool = ...):
if not self.instance.approved:
employee = self.instance.employee_id
if hasattr(employee, "employee_work_info"):
self.instance.previous_shift_id = employee.employee_work_info.shift_id
if not self.instance.requested_till:
self.instance.requested_till = (
employee.employee_work_info.contract_end_date
)
return super().save(commit)
2023-05-10 15:06:57 +05:30
class WorkTypeRequestForm(ModelForm):
2023-08-01 16:48:48 +05:30
"""
WorkTypeRequest model's form
"""
2023-05-10 15:06:57 +05:30
class Meta:
2023-08-01 16:48:48 +05:30
"""
Meta class for additional options
"""
2023-05-10 15:06:57 +05:30
model = WorkTypeRequest
2023-08-01 16:48:48 +05:30
fields = "__all__"
exclude = (
"approved",
"canceled",
"previous_work_type_id",
"is_active",
"work_type_changed",
)
2023-05-10 15:06:57 +05:30
widgets = {
2023-08-01 16:48:48 +05:30
"requested_date": DateInput(attrs={"type": "date"}),
"requested_till": DateInput(attrs={"type": "date"}),
2023-05-10 15:06:57 +05:30
}
2023-08-07 13:02:24 +05:30
labels = {
"requested_date": _trans("Requested Date"),
"requested_till": _trans("Requested Till"),
"description": _trans("Description"),
}
2023-05-10 15:06:57 +05:30
def as_p(self):
"""
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
def save(self, commit: bool = ...):
if not self.instance.approved:
employee = self.instance.employee_id
if hasattr(employee, "employee_work_info"):
self.instance.previous_work_type_id = (
employee.employee_work_info.work_type_id
)
if self.instance.is_permanent_work_type:
self.instance.requested_till = None
2023-05-10 15:06:57 +05:30
return super().save(commit)
2023-08-08 10:13:50 +05:30
class ChangePasswordForm(forms.Form):
old_password = forms.CharField(
label=_("Old password"),
strip=False,
widget=forms.PasswordInput(
attrs={
"autocomplete": "new-password",
"placeholder": _("Enter Old Password"),
"class": "oh-input oh-input--password w-100 mb-2",
}
),
help_text=_("Enter your old password."),
)
new_password = forms.CharField(
label=_("New password"),
strip=False,
widget=forms.PasswordInput(
attrs={
"autocomplete": "new-password",
"placeholder": _("Enter New Password"),
"class": "oh-input oh-input--password w-100 mb-2",
}
),
)
confirm_password = forms.CharField(
label=_("New password confirmation"),
strip=False,
widget=forms.PasswordInput(
attrs={
"autocomplete": "new-password",
"placeholder": _("Re-Enter Password"),
"class": "oh-input oh-input--password w-100 mb-2",
}
),
)
def __init__(self, user, *args, **kwargs):
self.user = user
super(ChangePasswordForm, self).__init__(*args, **kwargs)
def clean_old_password(self):
old_password = self.cleaned_data.get("old_password")
if not self.user.check_password(old_password):
raise forms.ValidationError("Incorrect old password.")
return old_password
def clean_new_password(self):
new_password = self.cleaned_data.get("new_password")
if self.user.check_password(new_password):
raise forms.ValidationError(
"New password must be different from the old password."
)
return new_password
def clean(self):
cleaned_data = super().clean()
new_password = cleaned_data.get("new_password")
confirm_password = cleaned_data.get("confirm_password")
if new_password and confirm_password and new_password != confirm_password:
raise ValidationError(
2023-09-08 14:38:51 +05:30
{"new_password": _("New password and confirm password do not match")}
2023-08-08 10:13:50 +05:30
)
return cleaned_data
2024-05-14 11:20:38 +05:30
class ResetPasswordForm(SetPasswordForm):
2023-08-01 16:48:48 +05:30
"""
ResetPasswordForm
"""
2024-05-14 11:20:38 +05:30
new_password1 = forms.CharField(
2023-08-07 13:02:24 +05:30
label=_("New password"),
2023-05-10 15:06:57 +05:30
strip=False,
2023-08-01 16:48:48 +05:30
widget=forms.PasswordInput(
attrs={
"autocomplete": "new-password",
2023-08-07 13:02:24 +05:30
"placeholder": _("Enter Strong Password"),
2023-08-01 16:48:48 +05:30
"class": "oh-input oh-input--password w-100 mb-2",
}
),
2023-08-07 13:02:24 +05:30
help_text=_("Enter your new password."),
2023-05-10 15:06:57 +05:30
)
2024-05-14 11:20:38 +05:30
new_password2 = forms.CharField(
2023-08-08 10:13:50 +05:30
label=_("New password confirmation"),
2023-05-10 15:06:57 +05:30
strip=False,
2023-08-01 16:48:48 +05:30
widget=forms.PasswordInput(
attrs={
"autocomplete": "new-password",
2023-08-07 13:02:24 +05:30
"placeholder": _("Re-Enter Password"),
2023-08-01 16:48:48 +05:30
"class": "oh-input oh-input--password w-100 mb-2",
}
),
2023-08-07 13:02:24 +05:30
help_text=_("Enter the same password as before, for verification."),
2023-05-10 15:06:57 +05:30
)
2024-05-14 11:20:38 +05:30
def save(self, commit=True):
if self.is_valid():
request = getattr(_thread_locals, "request", None)
if request:
messages.success(request, _("Password changed successfully"))
return super().save()
2023-05-10 15:06:57 +05:30
def clean_confirm_password(self):
2023-08-01 16:48:48 +05:30
"""
validation method for confirm password field
"""
password = self.cleaned_data.get("password")
confirm_password = self.cleaned_data.get("confirm_password")
2023-05-10 15:06:57 +05:30
if password == confirm_password:
return confirm_password
2023-08-07 13:02:24 +05:30
raise forms.ValidationError(_("Password must be same."))
2023-05-10 15:06:57 +05:30
excluded_fields = ["id", "is_active", "shift_changed", "work_type_changed"]
class ShiftRequestColumnForm(forms.Form):
model_fields = ShiftRequest._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",
"shift_id",
"requested_date",
"requested_till",
"previous_shift_id",
"approved",
],
)
class WorkTypeRequestColumnForm(forms.Form):
model_fields = WorkTypeRequest._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",
"work_type_id",
"requested_date",
"requested_till",
"previous_shift_id",
"approved",
],
)
class RotatingShiftAssignExportForm(forms.Form):
model_fields = RotatingShiftAssign._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",
"rotating_shift_id",
"start_date",
"next_change_date",
"current_shift",
"next_shift",
"based_on",
],
)
class RotatingWorkTypeAssignExportForm(forms.Form):
model_fields = RotatingWorkTypeAssign._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",
"rotating_work_type_id",
"start_date",
"next_change_date",
"current_work_type",
"next_work_type",
"based_on",
],
)
2024-01-04 11:05:11 +05:30
class TagsForm(ModelForm):
"""
Tags form
"""
class Meta:
"""
Meta class for additional options
"""
model = Tags
fields = "__all__"
widgets = {"color": TextInput(attrs={"type": "color", "style": "height:50px"})}
exclude = ["objects", "is_active"]
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-01-10 15:54:26 +05:30
class EmployeeTagForm(ModelForm):
"""
2024-01-10 15:54:26 +05:30
Employee Tags form
"""
class Meta:
"""
Meta class for additional options
"""
2024-01-10 15:54:26 +05:30
model = EmployeeTag
fields = "__all__"
exclude = ["is_active"]
widgets = {"color": TextInput(attrs={"type": "color", "style": "height:50px"})}
2024-01-10 15:54:26 +05:30
class AuditTagForm(ModelForm):
"""
Audit Tags form
"""
class Meta:
"""
Meta class for additional options
"""
model = AuditTag
fields = "__all__"
2024-01-04 11:05:11 +05:30
class ShiftRequestCommentForm(ModelForm):
"""
Shift request comment form
"""
class Meta:
"""
Meta class for additional options
"""
model = ShiftRequestComment
fields = ("comment",)
class WorkTypeRequestCommentForm(ModelForm):
"""
WorkType request comment form
"""
class Meta:
"""
Meta class for additional options
"""
model = WorkTypeRequestComment
fields = ("comment",)
2024-01-04 11:05:11 +05:30
class DynamicMailConfForm(ModelForm):
"""
DynamicEmailConfiguration
"""
2024-01-04 11:05:11 +05:30
class Meta:
model = DynamicEmailConfiguration
fields = "__all__"
exclude = ["is_active"]
# def clean(self):
# from_mail = self.from_email
# return super().clean()
2024-01-04 11:05:11 +05:30
def as_p(self):
"""
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
class MultipleApproveConditionForm(ModelForm):
CONDITION_CHOICE = [
("equal", _("Equal (==)")),
("notequal", _("Not Equal (!=)")),
("range", _("Range")),
("lt", _("Less Than (<)")),
("gt", _("Greater Than (>)")),
("le", _("Less Than or Equal To (<=)")),
("ge", _("Greater Than or Equal To (>=)")),
("icontains", _("Contains")),
]
multi_approval_manager = forms.ModelChoiceField(
queryset=Employee.objects.all(),
widget=forms.Select(attrs={"class": "oh-select oh-select-2 mb-2"}),
label=_("Approval Manager"),
required=True,
)
condition_operator = forms.ChoiceField(
choices=CONDITION_CHOICE,
widget=forms.Select(
attrs={
"class": "oh-select oh-select-2 mb-2",
"hx-trigger": "change",
"hx-target": "#conditionValueDiv",
"hx-get": "condition-value-fields",
},
),
)
class Meta:
model = MultipleApprovalCondition
fields = "__all__"
exclude = [
"is_active",
]
class DynamicPaginationForm(ModelForm):
"""
Form for setting default pagination
"""
class Meta:
model = DynamicPagination
fields = "__all__"
exclude = ("user_id",)
class MultipleFileInput(forms.ClearableFileInput):
allow_multiple_selected = True
class MultipleFileField(forms.FileField):
def __init__(self, *args, **kwargs):
kwargs.setdefault("widget", MultipleFileInput())
super().__init__(*args, **kwargs)
def clean(self, data, initial=None):
single_file_clean = super().clean
if isinstance(data, (list, tuple)):
result = [single_file_clean(d, initial) for d in data]
else:
result = [
single_file_clean(data, initial),
]
return result[0] if result else None
class AnnouncementForm(ModelForm):
"""
Announcement Form
"""
class Meta:
"""
Meta class for additional options
"""
model = Announcement
fields = "__all__"
exclude = ["is_active"]
widgets = {
"description": forms.Textarea(attrs={"data-summernote": ""}),
"expire_date": DateInput(attrs={"type": "date"}),
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["attachments"] = MultipleFileField(label="Attachments ")
self.fields["attachments"].required = False
def save(self, commit: bool = ...) -> Any:
attachement = []
multiple_attachment_ids = []
attachements = None
if self.files.getlist("attachments"):
attachements = self.files.getlist("attachments")
self.instance.attachement = attachements[0]
multiple_attachment_ids = []
for attachement in attachements:
file_instance = Attachment()
file_instance.file = attachement
file_instance.save()
multiple_attachment_ids.append(file_instance.pk)
instance = super().save(commit)
if commit:
instance.attachements.add(*multiple_attachment_ids)
return instance, multiple_attachment_ids
class AnnouncementCommentForm(ModelForm):
"""
Announcement comment form
"""
class Meta:
"""
Meta class for additional options
"""
model = AnnouncementComment
fields = ["comment"]
class AnnouncementExpireForm(ModelForm):
"""
Announcement Expire form
"""
class Meta:
"""
Meta class for additional options
"""
model = AnnouncementExpire
fields = ("days",)
class DriverForm(forms.ModelForm):
"""
DriverForm
"""
class Meta:
model = DriverViewed
fields = "__all__"
2024-05-14 11:20:38 +05:30
UserModel = get_user_model()
class PassWordResetForm(forms.Form):
email = forms.CharField()
def send_mail(
self,
subject_template_name,
email_template_name,
context,
from_email,
to_email,
html_email_template_name=None,
):
"""
Send a django.core.mail.EmailMultiAlternatives to `to_email`.
"""
subject = loader.render_to_string(subject_template_name, context)
# Email subject *must not* contain newlines
subject = "".join(subject.splitlines())
body = loader.render_to_string(email_template_name, context)
email_message = EmailMultiAlternatives(subject, body, from_email, [to_email])
if html_email_template_name is not None:
html_email = loader.render_to_string(html_email_template_name, context)
email_message.attach_alternative(html_email, "text/html")
email_message.send()
def get_users(self, email):
"""
Given an email, return matching user(s) who should receive a reset.
This allows subclasses to more easily customize the default policies
that prevent inactive users and users with unusable passwords from
resetting their password.
"""
email_field_name = UserModel.get_email_field_name()
active_users = UserModel._default_manager.filter(
**{
"%s__iexact" % email_field_name: email,
"is_active": True,
}
)
return (
u
for u in active_users
if u.has_usable_password()
and _unicode_ci_compare(email, getattr(u, email_field_name))
)
def save(
self,
domain_override=None,
subject_template_name="registration/password_reset_subject.txt",
email_template_name="registration/password_reset_email.html",
use_https=False,
token_generator=default_token_generator,
from_email=None,
request=None,
html_email_template_name=None,
extra_email_context=None,
):
"""
Generate a one-use only link for resetting password and send it to the
user.
"""
username = self.cleaned_data["email"]
user = User.objects.get(username=username)
employee = user.employee_get
email = employee.email
work_mail = None
try:
work_mail = employee.employee_work_info.email
except Exception as e:
pass
if work_mail:
email = work_mail
if not domain_override:
current_site = get_current_site(request)
site_name = current_site.name
domain = current_site.domain
else:
site_name = domain = domain_override
if email:
token = token_generator.make_token(user)
context = {
"email": email,
"domain": domain,
"site_name": site_name,
"uid": urlsafe_base64_encode(force_bytes(user.pk)),
"user": user,
"token": token,
"protocol": "https" if use_https else "http",
**(extra_email_context or {}),
}
self.send_mail(
subject_template_name,
email_template_name,
context,
from_email,
email,
html_email_template_name=html_email_template_name,
)