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
|
|
|
|
|
"""
|
2024-03-08 23:49:22 +05:30
|
|
|
|
2023-08-01 16:48:48 +05:30
|
|
|
import calendar
|
2024-05-07 12:23:36 +05:30
|
|
|
import datetime
|
2023-09-28 14:26:22 +05:30
|
|
|
import os
|
2023-08-01 16:48:48 +05:30
|
|
|
import uuid
|
2024-02-14 14:58:09 +05:30
|
|
|
from datetime import date, timedelta
|
2024-05-07 12:23:36 +05:30
|
|
|
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
|
2023-12-06 11:28:25 +05:30
|
|
|
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
|
2024-07-03 10:45:47 +05:30
|
|
|
from django.core.validators import validate_ipv46_address
|
2024-05-07 12:23:36 +05:30
|
|
|
from django.forms import DateInput, HiddenInput, TextInput
|
2024-05-14 11:20:38 +05:30
|
|
|
from django.template import loader
|
2024-05-07 12:23:36 +05:30
|
|
|
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
|
2024-05-07 12:23:36 +05:30
|
|
|
|
|
|
|
|
from base.methods import reload_queryset
|
2023-08-01 16:48:48 +05:30
|
|
|
from base.models import (
|
2024-01-24 15:34:01 +05:30
|
|
|
Announcement,
|
|
|
|
|
AnnouncementComment,
|
2024-01-25 15:46:12 +05:30
|
|
|
AnnouncementExpire,
|
2024-01-24 15:34:01 +05:30
|
|
|
Attachment,
|
2024-07-03 10:45:47 +05:30
|
|
|
AttendanceAllowedIP,
|
2024-01-29 15:08:36 +05:30
|
|
|
BaserequestFile,
|
2023-08-01 16:48:48 +05:30
|
|
|
Company,
|
|
|
|
|
Department,
|
2024-03-08 23:49:22 +05:30
|
|
|
DriverViewed,
|
2024-01-04 11:05:11 +05:30
|
|
|
DynamicEmailConfiguration,
|
2024-01-20 16:37:12 +05:30
|
|
|
DynamicPagination,
|
2024-05-07 12:23:36 +05:30
|
|
|
EmployeeShift,
|
|
|
|
|
EmployeeShiftDay,
|
|
|
|
|
EmployeeShiftSchedule,
|
|
|
|
|
EmployeeType,
|
2023-08-01 16:48:48 +05:30
|
|
|
JobPosition,
|
|
|
|
|
JobRole,
|
2024-01-12 10:38:53 +05:30
|
|
|
MultipleApprovalCondition,
|
2023-08-01 16:48:48 +05:30
|
|
|
RotatingShift,
|
|
|
|
|
RotatingShiftAssign,
|
|
|
|
|
RotatingWorkType,
|
|
|
|
|
RotatingWorkTypeAssign,
|
|
|
|
|
ShiftRequest,
|
2024-05-07 12:23:36 +05:30
|
|
|
ShiftRequestComment,
|
2024-01-08 13:54:00 +05:30
|
|
|
Tags,
|
2024-05-07 12:23:36 +05:30
|
|
|
WorkType,
|
|
|
|
|
WorkTypeRequest,
|
2024-03-28 14:28:49 +05:30
|
|
|
WorkTypeRequestComment,
|
2023-08-01 16:48:48 +05:30
|
|
|
)
|
2024-05-07 12:23:36 +05:30
|
|
|
from employee.filters import EmployeeFilter
|
|
|
|
|
from employee.forms import MultipleFileField
|
|
|
|
|
from employee.models import Employee, EmployeeTag
|
2024-06-20 11:45:55 +05:30
|
|
|
from horilla import horilla_middlewares
|
|
|
|
|
from horilla.horilla_middlewares import _thread_locals
|
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)
|
2024-06-20 11:45:55 +05:30
|
|
|
request = getattr(horilla_middlewares._thread_locals, "request", None)
|
2023-05-10 15:06:57 +05:30
|
|
|
for field_name, field in self.fields.items():
|
|
|
|
|
widget = field.widget
|
2024-02-14 14:58:09 +05:30
|
|
|
if isinstance(widget, (forms.DateInput)):
|
|
|
|
|
field.initial = date.today()
|
2024-03-08 23:49:22 +05:30
|
|
|
|
2023-08-01 16:48:48 +05:30
|
|
|
if isinstance(
|
|
|
|
|
widget,
|
|
|
|
|
(forms.NumberInput, forms.EmailInput, forms.TextInput, forms.FileInput),
|
|
|
|
|
):
|
2023-12-06 11:28:25 +05:30
|
|
|
label = ""
|
2023-05-18 15:05:28 +05:30
|
|
|
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,)):
|
2024-02-08 12:50:01 +05:30
|
|
|
field.empty_label = None
|
2024-03-08 23:49:22 +05:30
|
|
|
if not isinstance(field, forms.ModelMultipleChoiceField):
|
2024-02-08 12:50:01 +05:30
|
|
|
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,
|
|
|
|
|
),
|
|
|
|
|
):
|
2023-05-18 15:05:28 +05:30
|
|
|
field.widget.attrs.update({"class": "oh-switch__checkbox"})
|
2023-05-10 15:06:57 +05:30
|
|
|
|
2024-03-08 23:49:22 +05:30
|
|
|
try:
|
|
|
|
|
self.fields["employee_id"].initial = request.user.employee_get
|
2024-02-14 14:58:09 +05:30
|
|
|
except:
|
|
|
|
|
pass
|
|
|
|
|
|
2024-03-08 23:49:22 +05:30
|
|
|
try:
|
|
|
|
|
self.fields["company_id"].initial = (
|
|
|
|
|
request.user.employee_get.get_company
|
|
|
|
|
)
|
2024-02-14 14:58:09 +05:30
|
|
|
except:
|
|
|
|
|
pass
|
2023-05-10 15:06:57 +05:30
|
|
|
|
2024-03-08 23:49:22 +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,)):
|
2023-05-18 15:05:28 +05:30
|
|
|
label = ""
|
2023-05-10 15:06:57 +05:30
|
|
|
if field.label is not None:
|
2023-05-18 15:05:28 +05:30
|
|
|
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)):
|
2023-05-18 15:05:28 +05:30
|
|
|
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,
|
|
|
|
|
),
|
|
|
|
|
):
|
2023-05-18 15:05:28 +05:30
|
|
|
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
|
|
|
|
2023-12-14 10:35:59 +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-12-06 11:28:25 +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 = Group
|
2023-12-06 14:13:03 +05:30
|
|
|
fields = ["name", "permissions"]
|
2023-12-06 11:28:25 +05:30
|
|
|
|
|
|
|
|
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
|
|
|
|
|
)
|
2023-12-06 11:28:25 +05:30
|
|
|
group = forms.ModelChoiceField(queryset=Group.objects.all())
|
2023-05-10 15:06:57 +05:30
|
|
|
|
2023-12-06 14:13:03 +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"]
|
2023-12-06 14:13:03 +05:30
|
|
|
group.user_set.clear()
|
2023-05-10 15:06:57 +05:30
|
|
|
for employee in employees:
|
2023-12-06 11:28:25 +05:30
|
|
|
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",
|
|
|
|
|
)
|
2023-12-14 10:35:59 +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
|
2024-01-03 16:21:50 +05:30
|
|
|
|
2023-12-06 14:13:03 +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
|
|
|
|
|
"""
|
2024-03-08 23:49:22 +05:30
|
|
|
user_ids = Employee.objects.filter(
|
|
|
|
|
id__in=self.data.getlist("employee")
|
|
|
|
|
).values_list("employee_user_id", flat=True)
|
2023-12-06 11:28:25 +05:30
|
|
|
permissions = self.cleaned_data["permissions"]
|
|
|
|
|
permissions = Permission.objects.filter(codename__in=permissions)
|
2024-01-31 11:48:09 +05:30
|
|
|
users = User.objects.filter(id__in=user_ids)
|
2023-12-06 11:28:25 +05:30
|
|
|
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__"
|
2024-04-15 16:23:21 +05:30
|
|
|
excluded_fields = ["date_format", "time_format", "is_active"]
|
2023-05-10 15:06:57 +05:30
|
|
|
|
2023-11-01 12:05:41 +05:30
|
|
|
def validate_image(self, file):
|
2023-09-28 14:26:22 +05:30
|
|
|
max_size = 5 * 1024 * 1024
|
|
|
|
|
|
|
|
|
|
if file.size > max_size:
|
|
|
|
|
raise ValidationError("File size should be less than 5MB.")
|
|
|
|
|
|
|
|
|
|
# Check file extension
|
2023-11-01 12:05:41 +05:30
|
|
|
valid_extensions = [".jpg", ".jpeg", ".png", ".webp", ".svg"]
|
2023-09-28 14:26:22 +05:30
|
|
|
ext = os.path.splitext(file.name)[1].lower()
|
|
|
|
|
if ext not in valid_extensions:
|
|
|
|
|
raise ValidationError("Unsupported file extension.")
|
2023-11-01 12:05:41 +05:30
|
|
|
|
2023-09-28 14:26:22 +05:30
|
|
|
def clean_icon(self):
|
2023-11-01 12:05:41 +05:30
|
|
|
icon = self.cleaned_data.get("icon")
|
2023-09-28 14:26:22 +05:30
|
|
|
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__"
|
2024-03-25 10:08:53 +00:00
|
|
|
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__"
|
2024-03-25 10:08:53 +00:00
|
|
|
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
|
|
|
|
|
"""
|
|
|
|
|
|
2024-06-28 16:43:21 +05:30
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
|
super().__init__(*args, **kwargs)
|
|
|
|
|
if not self.instance.pk:
|
|
|
|
|
self.fields["job_position_id"] = forms.ModelMultipleChoiceField(
|
|
|
|
|
queryset=self.fields["job_position_id"].queryset
|
|
|
|
|
)
|
|
|
|
|
attrs = self.fields["job_position_id"].widget.attrs
|
|
|
|
|
attrs["class"] = "oh-select oh-select2 w-100"
|
|
|
|
|
attrs["style"] = "height:45px;"
|
|
|
|
|
|
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__"
|
2024-03-25 10:08:53 +00:00
|
|
|
exclude = ["is_active"]
|
2023-05-10 15:06:57 +05:30
|
|
|
|
2024-06-28 16:43:21 +05:30
|
|
|
def save(self, commit, *args, **kwargs) -> Any:
|
|
|
|
|
if not self.instance.pk:
|
|
|
|
|
request = getattr(_thread_locals, "request")
|
|
|
|
|
job_positions = JobPosition.objects.filter(
|
|
|
|
|
id__in=self.data.getlist("job_position_id")
|
|
|
|
|
)
|
|
|
|
|
roles = []
|
|
|
|
|
for position in job_positions:
|
|
|
|
|
role = JobRole()
|
|
|
|
|
role.job_position_id = position
|
|
|
|
|
role.job_role = self.data["job_role"]
|
|
|
|
|
try:
|
|
|
|
|
role.save()
|
|
|
|
|
except:
|
|
|
|
|
messages.info(request, f"Role already exists under {position}")
|
|
|
|
|
roles.append(role.pk)
|
|
|
|
|
return JobRole.objects.filter(id__in=roles)
|
|
|
|
|
super().save(commit, *args, **kwargs)
|
|
|
|
|
|
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__"
|
2024-03-25 10:08:53 +00:00
|
|
|
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__"
|
2024-03-25 10:08:53 +00:00
|
|
|
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"}),
|
2024-06-18 14:24:11 +05:30
|
|
|
"additional_data": forms.HiddenInput(),
|
2023-05-10 15:06:57 +05:30
|
|
|
}
|
|
|
|
|
|
2024-06-18 14:24:11 +05:30
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
|
super().__init__(*args, **kwargs)
|
|
|
|
|
work_type_counts = 0
|
|
|
|
|
|
|
|
|
|
def create_work_type_field(work_type_key, required, initial=None):
|
|
|
|
|
self.fields[work_type_key] = forms.ModelChoiceField(
|
|
|
|
|
queryset=WorkType.objects.all(),
|
|
|
|
|
widget=forms.Select(
|
|
|
|
|
attrs={
|
|
|
|
|
"class": "oh-select oh-select-2 mb-3",
|
|
|
|
|
"name": work_type_key,
|
|
|
|
|
"id": f"id_{work_type_key}",
|
|
|
|
|
}
|
|
|
|
|
),
|
|
|
|
|
required=required,
|
|
|
|
|
empty_label=_("---Choose Work Type---"),
|
|
|
|
|
initial=initial,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
for key in self.data.keys():
|
|
|
|
|
if key.startswith("work_type"):
|
|
|
|
|
work_type_counts += 1
|
|
|
|
|
create_work_type_field(key, work_type_counts <= 2)
|
|
|
|
|
|
|
|
|
|
additional_data = self.initial.get("additional_data")
|
|
|
|
|
additional_work_types = (
|
|
|
|
|
additional_data.get("additional_work_types") if additional_data else None
|
|
|
|
|
)
|
|
|
|
|
if additional_work_types:
|
|
|
|
|
work_type_counts = 3
|
|
|
|
|
for work_type_id in additional_work_types:
|
|
|
|
|
create_work_type_field(
|
|
|
|
|
f"work_type{work_type_counts}",
|
|
|
|
|
work_type_counts <= 2,
|
|
|
|
|
initial=work_type_id,
|
|
|
|
|
)
|
|
|
|
|
work_type_counts += 1
|
|
|
|
|
|
|
|
|
|
self.work_type_counts = work_type_counts
|
|
|
|
|
|
|
|
|
|
def as_p(self, *args, **kwargs):
|
|
|
|
|
context = {"form": self}
|
|
|
|
|
return render_to_string(
|
|
|
|
|
"base/rotating_work_type/htmx/rotating_work_type_as_p.html", context
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def clean(self):
|
|
|
|
|
cleaned_data = super().clean()
|
|
|
|
|
additional_work_types = []
|
|
|
|
|
model_fields = list(self.instance.__dict__.keys())
|
|
|
|
|
|
|
|
|
|
for key, value in self.data.items():
|
|
|
|
|
if (
|
|
|
|
|
f"{key}_id" not in model_fields
|
|
|
|
|
and key.startswith("work_type")
|
|
|
|
|
and value
|
|
|
|
|
):
|
|
|
|
|
additional_work_types.append(value)
|
|
|
|
|
|
|
|
|
|
if additional_work_types:
|
|
|
|
|
if (
|
|
|
|
|
"additional_data" not in cleaned_data
|
|
|
|
|
or cleaned_data["additional_data"] is None
|
|
|
|
|
):
|
|
|
|
|
cleaned_data["additional_data"] = {}
|
|
|
|
|
cleaned_data["additional_data"][
|
|
|
|
|
"additional_work_types"
|
|
|
|
|
] = additional_work_types
|
|
|
|
|
|
|
|
|
|
return cleaned_data
|
|
|
|
|
|
|
|
|
|
def save(self, commit=True):
|
|
|
|
|
instance = super().save(commit=False)
|
|
|
|
|
if self.cleaned_data.get("additional_data"):
|
|
|
|
|
if instance.additional_data is None:
|
|
|
|
|
instance.additional_data = {}
|
|
|
|
|
instance.additional_data["additional_work_types"] = self.cleaned_data[
|
|
|
|
|
"additional_data"
|
|
|
|
|
].get("additional_work_types")
|
|
|
|
|
else:
|
|
|
|
|
instance.additional_data = None
|
|
|
|
|
|
|
|
|
|
if commit:
|
|
|
|
|
instance.save()
|
|
|
|
|
self.save_m2m()
|
|
|
|
|
return instance
|
|
|
|
|
|
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
|
|
|
|
|
"""
|
|
|
|
|
|
2024-02-07 10:35:00 +05:30
|
|
|
employee_id = HorillaMultiSelectField(
|
2023-08-01 16:48:48 +05:30
|
|
|
queryset=Employee.objects.filter(employee_work_info__isnull=False),
|
2024-02-07 10:35:00 +05:30
|
|
|
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__"
|
2024-04-02 10:03:17 +05:30
|
|
|
exclude = [
|
|
|
|
|
"next_change_date",
|
|
|
|
|
"current_work_type",
|
|
|
|
|
"next_work_type",
|
|
|
|
|
"is_active",
|
2024-06-18 14:53:23 +05:30
|
|
|
"additional_data",
|
2024-04-02 10:03:17 +05:30
|
|
|
]
|
2023-05-10 15:06:57 +05:30
|
|
|
widgets = {
|
2023-08-01 16:48:48 +05:30
|
|
|
"start_date": DateInput(attrs={"type": "date"}),
|
2024-03-08 23:49:22 +05:30
|
|
|
"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)
|
2024-02-07 10:35:00 +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_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):
|
2024-02-07 10:35:00 +05:30
|
|
|
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
|
2024-06-18 14:53:23 +05:30
|
|
|
rotating_work_type_assign.additional_data["next_shift_index"] = 1
|
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
|
|
|
|
|
"""
|
|
|
|
|
|
2024-07-03 13:05:39 +05:30
|
|
|
based_on = forms.ChoiceField(
|
|
|
|
|
choices=BASED_ON, initial="daily", label=_trans("Based on")
|
|
|
|
|
)
|
|
|
|
|
|
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__"
|
2024-03-28 14:28:49 +05:30
|
|
|
exclude = [
|
|
|
|
|
"next_change_date",
|
|
|
|
|
"current_work_type",
|
|
|
|
|
"next_work_type",
|
|
|
|
|
"is_active",
|
2024-06-18 14:53:23 +05:30
|
|
|
"additional_data",
|
2024-03-28 14:28:49 +05:30
|
|
|
]
|
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__"
|
2024-03-25 10:08:53 +00:00
|
|
|
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__"
|
2024-03-25 10:08:53 +00:00
|
|
|
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__"
|
2024-03-25 10:08:53 +00:00
|
|
|
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__"
|
2024-03-25 10:08:53 +00:00
|
|
|
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__"
|
2024-03-25 10:08:53 +00:00
|
|
|
exclude = ["employee_id", "is_active"]
|
2024-06-18 14:24:11 +05:30
|
|
|
widgets = {"additional_data": forms.HiddenInput()}
|
|
|
|
|
|
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
|
super().__init__(*args, **kwargs)
|
|
|
|
|
|
|
|
|
|
shift_counts = 0
|
|
|
|
|
|
|
|
|
|
def create_shift_field(shift_key, required, initial=None):
|
|
|
|
|
self.fields[shift_key] = forms.ModelChoiceField(
|
|
|
|
|
queryset=EmployeeShift.objects.all(),
|
|
|
|
|
widget=forms.Select(
|
|
|
|
|
attrs={
|
|
|
|
|
"class": "oh-select oh-select-2 mb-3",
|
|
|
|
|
"name": shift_key,
|
|
|
|
|
"id": f"id_{shift_key}",
|
|
|
|
|
}
|
|
|
|
|
),
|
|
|
|
|
required=required,
|
|
|
|
|
empty_label=_("---Choose Shift---"),
|
|
|
|
|
initial=initial,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
for key in self.data.keys():
|
|
|
|
|
if key.startswith("shift") and self.data[key]:
|
|
|
|
|
shift_counts += 1
|
|
|
|
|
create_shift_field(key, shift_counts <= 2)
|
|
|
|
|
|
|
|
|
|
additional_data = self.initial.get("additional_data")
|
|
|
|
|
additional_shifts = (
|
|
|
|
|
additional_data.get("additional_shifts") if additional_data else None
|
|
|
|
|
)
|
|
|
|
|
if additional_shifts:
|
|
|
|
|
shift_counts = 3
|
|
|
|
|
for shift_id in additional_shifts:
|
|
|
|
|
if shift_id:
|
|
|
|
|
create_shift_field(
|
|
|
|
|
f"shift{shift_counts}", shift_counts <= 2, initial=shift_id
|
|
|
|
|
)
|
|
|
|
|
shift_counts += 1
|
|
|
|
|
|
|
|
|
|
self.shift_counts = shift_counts
|
|
|
|
|
|
|
|
|
|
def as_p(self, *args, **kwargs):
|
|
|
|
|
context = {"form": self}
|
|
|
|
|
return render_to_string(
|
|
|
|
|
"base/rotating_shift/htmx/rotating_shift_as_p.html", context
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def clean(self):
|
|
|
|
|
cleaned_data = super().clean()
|
|
|
|
|
additional_shifts = []
|
|
|
|
|
model_fields = list(self.instance.__dict__.keys())
|
|
|
|
|
|
|
|
|
|
for key, value in self.data.items():
|
|
|
|
|
if f"{key}_id" not in model_fields and key.startswith("shift") and value:
|
|
|
|
|
additional_shifts.append(value)
|
|
|
|
|
|
|
|
|
|
if additional_shifts:
|
|
|
|
|
if (
|
|
|
|
|
"additional_data" not in cleaned_data
|
|
|
|
|
or cleaned_data["additional_data"] is None
|
|
|
|
|
):
|
|
|
|
|
cleaned_data["additional_data"] = {}
|
|
|
|
|
cleaned_data["additional_data"]["additional_shifts"] = additional_shifts
|
|
|
|
|
|
|
|
|
|
return cleaned_data
|
|
|
|
|
|
|
|
|
|
def save(self, commit=True):
|
|
|
|
|
instance = super().save(commit=False)
|
|
|
|
|
if self.cleaned_data.get("additional_data"):
|
|
|
|
|
if instance.additional_data is None:
|
|
|
|
|
instance.additional_data = {}
|
|
|
|
|
instance.additional_data["additional_shifts"] = self.cleaned_data[
|
|
|
|
|
"additional_data"
|
|
|
|
|
].get("additional_shifts")
|
|
|
|
|
else:
|
|
|
|
|
instance.additional_data = None
|
|
|
|
|
|
|
|
|
|
if commit:
|
|
|
|
|
instance.save()
|
|
|
|
|
self.save_m2m()
|
|
|
|
|
return instance
|
2023-05-10 15:06:57 +05:30
|
|
|
|
|
|
|
|
|
|
|
|
|
class RotatingShiftAssignForm(forms.ModelForm):
|
2023-08-01 16:48:48 +05:30
|
|
|
"""
|
|
|
|
|
RotatingShiftAssign model's form
|
|
|
|
|
"""
|
|
|
|
|
|
2024-02-07 10:35:00 +05:30
|
|
|
employee_id = HorillaMultiSelectField(
|
2023-08-01 16:48:48 +05:30
|
|
|
queryset=Employee.objects.filter(employee_work_info__isnull=False),
|
2024-02-07 10:35:00 +05:30
|
|
|
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__"
|
2024-06-18 14:53:23 +05:30
|
|
|
exclude = [
|
|
|
|
|
"next_change_date",
|
|
|
|
|
"current_shift",
|
|
|
|
|
"next_shift",
|
|
|
|
|
"is_active",
|
|
|
|
|
"additional_data",
|
|
|
|
|
]
|
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):
|
2024-02-07 10:35:00 +05:30
|
|
|
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
|
2024-06-18 14:53:23 +05:30
|
|
|
rotating_shift_assign.next_shift = rotating_shift.shift1
|
|
|
|
|
rotating_shift_assign.additional_data["next_shift_index"] = 1
|
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
|
|
|
|
2024-07-03 13:05:39 +05:30
|
|
|
based_on = forms.ChoiceField(
|
|
|
|
|
choices=BASED_ON, initial="daily", label=_trans("Based on")
|
|
|
|
|
)
|
|
|
|
|
|
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__"
|
2024-06-18 14:53:23 +05:30
|
|
|
exclude = [
|
|
|
|
|
"next_change_date",
|
|
|
|
|
"current_shift",
|
|
|
|
|
"next_shift",
|
|
|
|
|
"is_active",
|
|
|
|
|
"additional_data",
|
|
|
|
|
]
|
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__"
|
2024-03-28 14:28:49 +05:30
|
|
|
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",
|
2024-03-28 14:28:49 +05:30
|
|
|
]
|
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
|
|
|
|
2023-11-01 12:05:41 +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
|
2023-11-01 12:05:41 +05:30
|
|
|
if hasattr(employee, "employee_work_info"):
|
|
|
|
|
self.instance.previous_shift_id = employee.employee_work_info.shift_id
|
2024-02-03 14:06:04 +05:30
|
|
|
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"),
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-06 15:23:35 +05:30
|
|
|
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
|
|
|
|
2023-11-01 12:05:41 +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
|
2023-11-01 12:05:41 +05:30
|
|
|
if hasattr(employee, "employee_work_info"):
|
|
|
|
|
self.instance.previous_work_type_id = (
|
|
|
|
|
employee.employee_work_info.work_type_id
|
|
|
|
|
)
|
2024-02-06 10:18:48 +05:30
|
|
|
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
|
|
|
|
2023-11-14 16:27:54 +05:30
|
|
|
|
2023-11-21 16:37:08 +05:30
|
|
|
excluded_fields = ["id", "is_active", "shift_changed", "work_type_changed"]
|
2023-11-14 16:27:54 +05:30
|
|
|
|
|
|
|
|
|
|
|
|
|
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",
|
|
|
|
|
],
|
|
|
|
|
)
|
2023-11-21 16:37:08 +05:30
|
|
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
2024-01-31 11:48:09 +05:30
|
|
|
|
2024-01-08 13:54:00 +05:30
|
|
|
class TagsForm(ModelForm):
|
|
|
|
|
"""
|
|
|
|
|
Tags form
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
|
"""
|
|
|
|
|
Meta class for additional options
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
model = Tags
|
|
|
|
|
fields = "__all__"
|
2024-01-31 11:48:09 +05:30
|
|
|
widgets = {"color": TextInput(attrs={"type": "color", "style": "height:50px"})}
|
2024-03-28 14:28:49 +05:30
|
|
|
exclude = ["objects", "is_active"]
|
2024-01-08 13:54:00 +05:30
|
|
|
|
2024-05-15 14:33:47 +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-01-10 15:54:26 +05:30
|
|
|
|
|
|
|
|
class EmployeeTagForm(ModelForm):
|
2024-01-08 13:54:00 +05:30
|
|
|
"""
|
2024-01-10 15:54:26 +05:30
|
|
|
Employee Tags form
|
2024-01-08 13:54:00 +05:30
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
|
"""
|
|
|
|
|
Meta class for additional options
|
|
|
|
|
"""
|
|
|
|
|
|
2024-01-10 15:54:26 +05:30
|
|
|
model = EmployeeTag
|
2024-01-08 13:54:00 +05:30
|
|
|
fields = "__all__"
|
2024-03-28 14:28:49 +05:30
|
|
|
exclude = ["is_active"]
|
2024-01-31 11:48:09 +05:30
|
|
|
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-08 13:54:00 +05:30
|
|
|
|
2024-01-04 11:05:11 +05:30
|
|
|
|
2024-03-28 14:28:49 +05:30
|
|
|
class ShiftRequestCommentForm(ModelForm):
|
2024-01-12 21:08:37 +05:30
|
|
|
"""
|
2024-01-16 11:36:34 +05:30
|
|
|
Shift request comment form
|
2024-01-12 21:08:37 +05:30
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
|
"""
|
|
|
|
|
Meta class for additional options
|
|
|
|
|
"""
|
|
|
|
|
|
2024-03-28 14:28:49 +05:30
|
|
|
model = ShiftRequestComment
|
2024-01-31 11:48:09 +05:30
|
|
|
fields = ("comment",)
|
|
|
|
|
|
2024-01-29 15:08:36 +05:30
|
|
|
|
2024-03-28 14:28:49 +05:30
|
|
|
class WorkTypeRequestCommentForm(ModelForm):
|
2024-01-29 15:08:36 +05:30
|
|
|
"""
|
2024-03-28 14:28:49 +05:30
|
|
|
WorkType request comment form
|
2024-01-12 21:08:37 +05:30
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
|
"""
|
|
|
|
|
Meta class for additional options
|
|
|
|
|
"""
|
|
|
|
|
|
2024-03-28 14:28:49 +05:30
|
|
|
model = WorkTypeRequestComment
|
2024-01-31 11:48:09 +05:30
|
|
|
fields = ("comment",)
|
2024-01-12 21:08:37 +05:30
|
|
|
|
2024-01-04 11:05:11 +05:30
|
|
|
|
|
|
|
|
class DynamicMailConfForm(ModelForm):
|
|
|
|
|
"""
|
|
|
|
|
DynamicEmailConfiguration
|
|
|
|
|
"""
|
2024-01-31 11:48:09 +05:30
|
|
|
|
2024-01-04 11:05:11 +05:30
|
|
|
class Meta:
|
|
|
|
|
model = DynamicEmailConfiguration
|
|
|
|
|
fields = "__all__"
|
2024-04-15 16:23:21 +05:30
|
|
|
exclude = ["is_active"]
|
2024-05-07 12:23:36 +05:30
|
|
|
|
2024-04-16 10:06:32 +05:30
|
|
|
# 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)
|
2024-01-12 10:38:53 +05:30
|
|
|
return table_html
|
2024-01-31 11:48:09 +05:30
|
|
|
|
|
|
|
|
|
2024-07-06 08:41:17 +02:00
|
|
|
class DynamicMailTestForm(forms.Form):
|
|
|
|
|
"""
|
|
|
|
|
DynamicEmailTest
|
|
|
|
|
"""
|
2024-07-08 11:08:30 +05:30
|
|
|
|
2024-07-06 08:41:17 +02:00
|
|
|
to_email = forms.EmailField(label="To email", required=True)
|
|
|
|
|
|
2024-07-08 11:08:30 +05:30
|
|
|
|
2024-01-12 10:38:53 +05:30
|
|
|
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",
|
2024-06-05 16:24:34 +05:30
|
|
|
"hx-trigger": "change",
|
|
|
|
|
"hx-target": "#conditionValueDiv",
|
|
|
|
|
"hx-get": "condition-value-fields",
|
2024-01-12 10:38:53 +05:30
|
|
|
},
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
class Meta:
|
2024-01-31 11:48:09 +05:30
|
|
|
model = MultipleApprovalCondition
|
2024-01-20 16:37:12 +05:30
|
|
|
fields = "__all__"
|
2024-03-28 14:28:49 +05:30
|
|
|
exclude = [
|
|
|
|
|
"is_active",
|
|
|
|
|
]
|
2024-01-20 16:37:12 +05:30
|
|
|
|
2024-01-31 11:48:09 +05:30
|
|
|
|
2024-01-20 16:37:12 +05:30
|
|
|
class DynamicPaginationForm(ModelForm):
|
|
|
|
|
"""
|
|
|
|
|
Form for setting default pagination
|
|
|
|
|
"""
|
2024-01-31 11:48:09 +05:30
|
|
|
|
2024-01-20 16:37:12 +05:30
|
|
|
class Meta:
|
|
|
|
|
model = DynamicPagination
|
|
|
|
|
fields = "__all__"
|
2024-01-31 11:48:09 +05:30
|
|
|
exclude = ("user_id",)
|
2024-01-24 15:34:01 +05:30
|
|
|
|
|
|
|
|
|
|
|
|
|
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:
|
2024-01-31 11:48:09 +05:30
|
|
|
result = [
|
|
|
|
|
single_file_clean(data, initial),
|
|
|
|
|
]
|
2024-01-24 15:34:01 +05:30
|
|
|
return result[0] if result else None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class AnnouncementForm(ModelForm):
|
|
|
|
|
"""
|
|
|
|
|
Announcement Form
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
|
"""
|
|
|
|
|
Meta class for additional options
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
model = Announcement
|
2024-01-31 11:48:09 +05:30
|
|
|
fields = "__all__"
|
2024-04-15 16:23:21 +05:30
|
|
|
exclude = ["is_active"]
|
2024-01-24 15:34:01 +05:30
|
|
|
widgets = {
|
|
|
|
|
"description": forms.Textarea(attrs={"data-summernote": ""}),
|
2024-01-25 15:46:12 +05:30
|
|
|
"expire_date": DateInput(attrs={"type": "date"}),
|
2024-01-24 15:34:01 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
|
super().__init__(*args, **kwargs)
|
|
|
|
|
self.fields["attachments"] = MultipleFileField(label="Attachments ")
|
2024-01-31 11:48:09 +05:30
|
|
|
self.fields["attachments"].required = False
|
|
|
|
|
|
2024-01-24 15:34:01 +05:30
|
|
|
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
|
|
|
|
|
|
2024-01-31 11:48:09 +05:30
|
|
|
|
2024-04-15 16:23:21 +05:30
|
|
|
class AnnouncementCommentForm(ModelForm):
|
2024-01-24 15:34:01 +05:30
|
|
|
"""
|
|
|
|
|
Announcement comment form
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
|
"""
|
|
|
|
|
Meta class for additional options
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
model = AnnouncementComment
|
2024-04-15 16:23:21 +05:30
|
|
|
fields = ["comment"]
|
2024-01-31 11:48:09 +05:30
|
|
|
|
2024-01-24 15:34:01 +05:30
|
|
|
|
2024-01-25 15:46:12 +05:30
|
|
|
class AnnouncementExpireForm(ModelForm):
|
|
|
|
|
"""
|
|
|
|
|
Announcement Expire form
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
|
"""
|
|
|
|
|
Meta class for additional options
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
model = AnnouncementExpire
|
2024-01-31 11:48:09 +05:30
|
|
|
fields = ("days",)
|
2024-03-08 23:49:22 +05:30
|
|
|
|
|
|
|
|
|
|
|
|
|
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,
|
|
|
|
|
)
|
2024-07-03 10:45:47 +05:30
|
|
|
|
|
|
|
|
|
|
|
|
|
class AttendanceAllowedIPForm(ModelForm):
|
|
|
|
|
ip_address = forms.CharField(max_length=30, label="IP Address")
|
|
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
|
model = AttendanceAllowedIP
|
|
|
|
|
fields = ["ip_address"]
|
|
|
|
|
|
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
|
super().__init__(*args, **kwargs)
|
|
|
|
|
|
|
|
|
|
ip_counts = 1
|
|
|
|
|
|
|
|
|
|
def create_ip_field(ip_key, initial=None):
|
|
|
|
|
self.fields[ip_key] = forms.ModelChoiceField(
|
|
|
|
|
widget=forms.TextInput(
|
|
|
|
|
attrs={
|
|
|
|
|
"class": "oh-input w-100 mb-3",
|
|
|
|
|
"name": ip_key,
|
|
|
|
|
"id": f"id_{ip_key}",
|
|
|
|
|
}
|
|
|
|
|
),
|
|
|
|
|
required=False,
|
|
|
|
|
initial=initial,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
additional_data = self.initial.get("additional_data")
|
|
|
|
|
additional_ips = (
|
|
|
|
|
additional_data.get("additional_ips") if additional_data else None
|
|
|
|
|
)
|
|
|
|
|
if additional_ips:
|
|
|
|
|
ip_counts = 1
|
|
|
|
|
for ip_id in additional_ips:
|
|
|
|
|
if ip_id:
|
|
|
|
|
create_ip_field(f"ip{ip_counts}", initial=ip_id)
|
|
|
|
|
ip_counts += 1
|
|
|
|
|
|
|
|
|
|
self.ip_counts = ip_counts
|
|
|
|
|
|
|
|
|
|
def as_p(self, *args, **kwargs):
|
|
|
|
|
context = {"form": self}
|
|
|
|
|
return render_to_string("attendance/ip_restriction/restrict_form.html", context)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class AttendanceAllowedIPUpdateForm(ModelForm):
|
|
|
|
|
ip_address = forms.CharField(max_length=30, label="IP Address")
|
|
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
|
model = AttendanceAllowedIP
|
|
|
|
|
fields = ["ip_address"]
|
|
|
|
|
|
|
|
|
|
def validate_ip_address(self, value):
|
|
|
|
|
try:
|
|
|
|
|
validate_ipv46_address(value)
|
|
|
|
|
except ValidationError:
|
|
|
|
|
raise ValidationError("Enter a valid IPv4 or IPv6 address.")
|
|
|
|
|
return value
|
|
|
|
|
|
|
|
|
|
def clean(self):
|
|
|
|
|
cleaned_data = super().clean()
|
|
|
|
|
|
|
|
|
|
for field_name, value in self.data.items():
|
|
|
|
|
cleaned_data[field_name] = self.validate_ip_address(value)
|
|
|
|
|
|
|
|
|
|
return cleaned_data
|