Files
ihrm/attendance/forms.py

1274 lines
44 KiB
Python
Raw Normal View History

# pylint: disable=too-few-public-methods
2023-05-30 15:21:53 +05:30
"""
forms.py
This module contains the form classes used in the application.
Each form represents a specific functionality or data input in the
2023-05-30 15:21:53 +05:30
application. They are responsible for validating
and processing user input data.
Classes:
- YourForm: Represents a form for handling specific data input.
Usage:
from django import forms
class YourForm(forms.Form):
field_name = forms.CharField()
def clean_field_name(self):
# Custom validation logic goes here
pass
"""
2024-02-06 13:30:32 +05:30
import datetime
2023-08-14 14:44:47 +05:30
import json
import logging
import uuid
from calendar import month_name
from collections import OrderedDict
from typing import Any, Dict
2023-05-10 15:06:57 +05:30
from django import forms
[IMP] Remove inter module dependency (#274) This commit introduces significant changes to the architecture of the Horilla HRMS system by decoupling interdependent modules. The following modifications were made: 1. **Module Independence**: Each module has been refactored to eliminate reliance on other modules, promoting a more modular and maintainable codebase. 2. **Refactored Imports and Dependencies**: Adjusted import statements and dependency injections to support independent module operation. 3. **Compatibility and Functionality**: Ensured that all modules are compatible with existing systems and maintain their intended functionality both independently and when integrated with other modules. These changes enhance the modularity, maintainability, and scalability of the Horilla HRMS, allowing developers to work on individual modules without affecting the entire system. Future development and deployment will be more efficient and less prone to issues arising from tightly coupled code. **NOTE** For existing Horilla users, if you face any issues during the migrations, please run the following command and try again the migrations. - `python3 manage.py makemigrations` - `python3 manage.py migrate base` - `python3 manage.py migrate` * [IMP] ASSET: Asset module dependency removal from other Horilla apps * [IMP] ATTENDANCE: Attendance module dependency removal from other Horilla apps * [IMP] BASE: Base module dependency removal from other Horilla apps * [IMP] EMPLOYEE: Employee module dependency removal from other Horilla apps * [IMP] HELPDESK: Helpdesk module dependency removal from other Horilla apps * [IMP] HORILLA AUDIT: Horilla Audit module dependency removal from other Horilla apps * [IMP] HORILLA CRUMBS: Horilla Crumbs module dependency removal from other Horilla apps * [IMP] HORILLA AUTOMATIONS: Horilla Automations module dependency removal from other Horilla apps * [IMP] HORILLA VIEWS: Horilla Views module dependency removal from other Horilla apps * [IMP] LEAVE: Leave module dependency removal from other Horilla apps * [IMP] OFFBOARDING: Offboarding module dependency removal from other Horilla apps * [IMP] ONBOARDING: Onboarding module dependency removal from other Horilla apps * [IMP] PMS: PMS module dependency removal from other Horilla apps * [IMP] PAYROLL: Payroll module dependency removal from other Horilla apps * [IMP] RECRUITMENT: Recruitment module dependency removal from other Horilla apps * [IMP] HORILLA: Dependency removal updates * [IMP] TEMPLATES: Dependency removal updates * [IMP] STATIC: Dependency removal updates * [IMP] HORILLA DOCUMENTS: Horilla Documents module dependency removal from other Horilla apps * [ADD] HORILLA: methods.py * [UPDT] HORILLA: Settings.py * [FIX] EMPLOYEE: About tab issue * Update horilla_settings.py * Remove dummy db init password
2024-08-05 14:22:44 +05:30
from django.apps import apps
2023-05-10 15:06:57 +05:30
from django.core.exceptions import ValidationError
[IMP] Remove inter module dependency (#274) This commit introduces significant changes to the architecture of the Horilla HRMS system by decoupling interdependent modules. The following modifications were made: 1. **Module Independence**: Each module has been refactored to eliminate reliance on other modules, promoting a more modular and maintainable codebase. 2. **Refactored Imports and Dependencies**: Adjusted import statements and dependency injections to support independent module operation. 3. **Compatibility and Functionality**: Ensured that all modules are compatible with existing systems and maintain their intended functionality both independently and when integrated with other modules. These changes enhance the modularity, maintainability, and scalability of the Horilla HRMS, allowing developers to work on individual modules without affecting the entire system. Future development and deployment will be more efficient and less prone to issues arising from tightly coupled code. **NOTE** For existing Horilla users, if you face any issues during the migrations, please run the following command and try again the migrations. - `python3 manage.py makemigrations` - `python3 manage.py migrate base` - `python3 manage.py migrate` * [IMP] ASSET: Asset module dependency removal from other Horilla apps * [IMP] ATTENDANCE: Attendance module dependency removal from other Horilla apps * [IMP] BASE: Base module dependency removal from other Horilla apps * [IMP] EMPLOYEE: Employee module dependency removal from other Horilla apps * [IMP] HELPDESK: Helpdesk module dependency removal from other Horilla apps * [IMP] HORILLA AUDIT: Horilla Audit module dependency removal from other Horilla apps * [IMP] HORILLA CRUMBS: Horilla Crumbs module dependency removal from other Horilla apps * [IMP] HORILLA AUTOMATIONS: Horilla Automations module dependency removal from other Horilla apps * [IMP] HORILLA VIEWS: Horilla Views module dependency removal from other Horilla apps * [IMP] LEAVE: Leave module dependency removal from other Horilla apps * [IMP] OFFBOARDING: Offboarding module dependency removal from other Horilla apps * [IMP] ONBOARDING: Onboarding module dependency removal from other Horilla apps * [IMP] PMS: PMS module dependency removal from other Horilla apps * [IMP] PAYROLL: Payroll module dependency removal from other Horilla apps * [IMP] RECRUITMENT: Recruitment module dependency removal from other Horilla apps * [IMP] HORILLA: Dependency removal updates * [IMP] TEMPLATES: Dependency removal updates * [IMP] STATIC: Dependency removal updates * [IMP] HORILLA DOCUMENTS: Horilla Documents module dependency removal from other Horilla apps * [ADD] HORILLA: methods.py * [UPDT] HORILLA: Settings.py * [FIX] EMPLOYEE: About tab issue * Update horilla_settings.py * Remove dummy db init password
2024-08-05 14:22:44 +05:30
from django.db.models.query import QuerySet
2023-05-10 15:06:57 +05:30
from django.forms import DateTimeInput
from django.template.loader import render_to_string
from django.utils.html import format_html
from django.utils.translation import gettext_lazy as _
from attendance.filters import AttendanceFilters
2023-05-30 15:21:53 +05:30
from attendance.models import (
Attendance,
AttendanceActivity,
AttendanceLateComeEarlyOut,
AttendanceOverTime,
AttendanceRequestComment,
AttendanceValidationCondition,
BatchAttendance,
GraceTime,
[IMP] Remove inter module dependency (#274) This commit introduces significant changes to the architecture of the Horilla HRMS system by decoupling interdependent modules. The following modifications were made: 1. **Module Independence**: Each module has been refactored to eliminate reliance on other modules, promoting a more modular and maintainable codebase. 2. **Refactored Imports and Dependencies**: Adjusted import statements and dependency injections to support independent module operation. 3. **Compatibility and Functionality**: Ensured that all modules are compatible with existing systems and maintain their intended functionality both independently and when integrated with other modules. These changes enhance the modularity, maintainability, and scalability of the Horilla HRMS, allowing developers to work on individual modules without affecting the entire system. Future development and deployment will be more efficient and less prone to issues arising from tightly coupled code. **NOTE** For existing Horilla users, if you face any issues during the migrations, please run the following command and try again the migrations. - `python3 manage.py makemigrations` - `python3 manage.py migrate base` - `python3 manage.py migrate` * [IMP] ASSET: Asset module dependency removal from other Horilla apps * [IMP] ATTENDANCE: Attendance module dependency removal from other Horilla apps * [IMP] BASE: Base module dependency removal from other Horilla apps * [IMP] EMPLOYEE: Employee module dependency removal from other Horilla apps * [IMP] HELPDESK: Helpdesk module dependency removal from other Horilla apps * [IMP] HORILLA AUDIT: Horilla Audit module dependency removal from other Horilla apps * [IMP] HORILLA CRUMBS: Horilla Crumbs module dependency removal from other Horilla apps * [IMP] HORILLA AUTOMATIONS: Horilla Automations module dependency removal from other Horilla apps * [IMP] HORILLA VIEWS: Horilla Views module dependency removal from other Horilla apps * [IMP] LEAVE: Leave module dependency removal from other Horilla apps * [IMP] OFFBOARDING: Offboarding module dependency removal from other Horilla apps * [IMP] ONBOARDING: Onboarding module dependency removal from other Horilla apps * [IMP] PMS: PMS module dependency removal from other Horilla apps * [IMP] PAYROLL: Payroll module dependency removal from other Horilla apps * [IMP] RECRUITMENT: Recruitment module dependency removal from other Horilla apps * [IMP] HORILLA: Dependency removal updates * [IMP] TEMPLATES: Dependency removal updates * [IMP] STATIC: Dependency removal updates * [IMP] HORILLA DOCUMENTS: Horilla Documents module dependency removal from other Horilla apps * [ADD] HORILLA: methods.py * [UPDT] HORILLA: Settings.py * [FIX] EMPLOYEE: About tab issue * Update horilla_settings.py * Remove dummy db init password
2024-08-05 14:22:44 +05:30
WorkRecords,
attendance_date_validate,
2023-07-11 12:12:25 +05:30
strtime_seconds,
validate_time_format,
2023-05-30 15:21:53 +05:30
)
from base.forms import ModelForm as BaseModelForm
from base.methods import (
filtersubordinatesemployeemodel,
get_working_days,
is_reportingmanager,
reload_queryset,
)
from base.models import Company, EmployeeShift
from employee.filters import EmployeeFilter
from employee.models import Employee
from horilla import horilla_middlewares
from horilla_widgets.widgets.horilla_multi_select_field import HorillaMultiSelectField
from horilla_widgets.widgets.select_widgets import HorillaMultiSelectWidget
logger = logging.getLogger(__name__)
2023-05-10 15:06:57 +05:30
class AttendanceUpdateForm(BaseModelForm):
2023-05-10 15:06:57 +05:30
"""
This model form is used to direct save the validated query dict to attendance model
from AttendanceUpdateForm. This form can be used to update existing attendance.
2023-05-10 15:06:57 +05:30
"""
2023-05-30 15:21:53 +05:30
2023-05-10 15:06:57 +05:30
class Meta:
2023-05-30 15:21:53 +05:30
"""
Meta class to add the additional info
"""
fields = "__all__"
exclude = [
"overtime_second",
"at_work_second",
"attendance_day",
"request_description",
2023-05-30 15:21:53 +05:30
"approved_overtime_second",
"request_type",
"requested_data",
"is_validate_request",
"is_validate_request_approved",
"attendance_overtime",
"is_active",
"is_holiday",
2023-05-30 15:21:53 +05:30
]
2023-05-10 15:06:57 +05:30
model = Attendance
widgets = {
2023-05-30 15:21:53 +05:30
"attendance_clock_in": DateTimeInput(attrs={"type": "time"}),
"attendance_clock_out": DateTimeInput(attrs={"type": "time"}),
"attendance_clock_out_date": DateTimeInput(attrs={"type": "date"}),
"attendance_date": DateTimeInput(attrs={"type": "date"}),
"attendance_clock_in_date": DateTimeInput(attrs={"type": "date"}),
}
2023-05-10 15:06:57 +05:30
def update_worked_hour_hx_fields(self, field_name):
"""Update the widget attributes for worked hour fields."""
self.fields[field_name].widget.attrs.update(
{
"id": str(uuid.uuid4()),
"hx-include": "#attendanceUpdateForm",
"hx-target": "#id_attendance_worked_hour_parent_div",
"hx-swap": "outerHTML",
"hx-select": "#id_attendance_worked_hour_parent_div",
"hx-get": "/attendance/update-worked-hour-field",
"hx-trigger": "change delay:300ms", # Delay added here for 500ms
}
)
2023-05-30 15:21:53 +05:30
def __init__(self, *args, **kwargs):
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
condition = AttendanceValidationCondition.objects.first()
condition = (
strtime_seconds(condition.minimum_overtime_to_approve)
if condition and condition.minimum_overtime_to_approve
else 0
)
2023-05-30 15:21:53 +05:30
initial = {
"attendance_date": instance.attendance_date.strftime("%Y-%m-%d"),
"attendance_clock_in": instance.attendance_clock_in.strftime("%H:%M"),
"attendance_clock_in_date": instance.attendance_clock_in_date.strftime(
"%Y-%m-%d"
),
2023-05-10 15:06:57 +05:30
}
if instance.attendance_clock_out_date is not None:
2024-02-06 13:30:32 +05:30
initial["attendance_clock_out"] = (
instance.attendance_clock_out.strftime("%H:%M")
)
initial["attendance_clock_out_date"] = (
instance.attendance_clock_out_date.strftime("%Y-%m-%d")
)
2023-05-30 15:21:53 +05:30
kwargs["initial"] = initial
super().__init__(*args, **kwargs)
2024-01-01 11:14:18 +05:30
self.fields["employee_id"].widget.attrs.update({"id": str(uuid.uuid4())})
self.fields["shift_id"].widget.attrs.update(
{
"id": str(uuid.uuid4()),
"hx-include": "#attendanceUpdateForm",
"hx-target": "#attendanceUpdateForm",
"hx-get": "/attendance/update-fields-based-shift",
2024-01-01 11:14:18 +05:30
}
)
for field in [
"attendance_clock_in_date",
"attendance_clock_in",
"attendance_clock_out_date",
"attendance_clock_out",
]:
self.update_worked_hour_hx_fields(field)
2024-01-01 11:14:18 +05:30
self.fields["attendance_date"].widget.attrs.update(
{
"onchange": "attendanceDateChange($(this))",
2024-01-01 11:14:18 +05:30
}
)
self.fields["work_type_id"].widget.attrs.update({"id": str(uuid.uuid4())})
2023-05-30 15:21:53 +05:30
if (
instance is not None
and not instance.attendance_overtime_approve
and (
strtime_seconds(instance.attendance_overtime) < condition
or not instance.attendance_validated
)
):
del self.fields["attendance_overtime_approve"]
self.fields["batch_attendance_id"].choices = list(
self.fields["batch_attendance_id"].choices
) + [("dynamic_create", "Dynamic create")]
self.fields["batch_attendance_id"].widget.attrs.update(
{
"onchange": "dynamicBatchAttendance($(this))",
}
)
2023-07-05 11:46:47 +05:30
def as_p(self, *args, **kwargs):
"""
Render the form fields as HTML table rows with Bootstrap styling.
"""
_ = args, kwargs # Explicitly mark as used for pylint
2023-07-05 11:46:47 +05:30
context = {"form": self}
table_html = render_to_string("attendance_form.html", context)
return table_html
2023-05-10 15:06:57 +05:30
class AttendanceForm(BaseModelForm):
2023-05-30 15:21:53 +05:30
"""
Model form for Attendance model
"""
employee_id = HorillaMultiSelectField(
2023-05-30 15:21:53 +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",
),
2023-08-02 14:27:23 +05:30
label=_("Employees"),
2023-05-30 15:21:53 +05:30
)
2023-05-10 15:06:57 +05:30
class Meta:
2023-05-30 15:21:53 +05:30
"""
Meta class to add the additional info
"""
model = Attendance
fields = "__all__"
exclude = [
2023-05-30 15:21:53 +05:30
"attendance_overtime_approve",
"attendance_overtime_calculation",
"at_work_second",
"overtime_second",
"attendance_day",
"request_description",
2023-05-30 15:21:53 +05:30
"approved_overtime_second",
"request_type",
"requested_data",
"is_validate_request",
"is_validate_request_approved",
"attendance_overtime",
"is_active",
"is_holiday",
]
2023-05-10 15:06:57 +05:30
widgets = {
2023-05-30 15:21:53 +05:30
"attendance_clock_in": DateTimeInput(attrs={"type": "time"}),
"attendance_clock_out": DateTimeInput(attrs={"type": "time"}),
"attendance_clock_out_date": DateTimeInput(attrs={"type": "date"}),
"attendance_date": DateTimeInput(attrs={"type": "date"}),
"attendance_clock_in_date": DateTimeInput(attrs={"type": "date"}),
2023-05-10 15:06:57 +05:30
}
def update_worked_hour_hx_fields(self, field_name):
"""Update the widget attributes for worked hour fields."""
self.fields[field_name].widget.attrs.update(
{
"id": str(uuid.uuid4()),
"hx-include": "#attendanceCreateForm",
"hx-target": "#id_attendance_worked_hour_parent_div",
"hx-swap": "outerHTML",
"hx-select": "#id_attendance_worked_hour_parent_div",
"hx-get": "/attendance/update-worked-hour-field",
"hx-trigger": "change delay:300ms", # Delay added here for 500ms
}
)
2023-05-30 15:21:53 +05:30
def __init__(self, *args, **kwargs):
# Get the initial data passed from the view
view_initial = kwargs.pop("initial", {})
# Default initial values
2023-07-05 11:46:47 +05:30
initial = {
"attendance_clock_out_date": datetime.datetime.today()
.date()
.strftime("%Y-%m-%d"),
2023-07-11 12:12:25 +05:30
"attendance_clock_out": datetime.datetime.today().time().strftime("%H:%M"),
2023-07-05 11:46:47 +05:30
}
# If an instance is provided, override the default initial values
2023-05-30 15:21:53 +05:30
if instance := kwargs.get("instance"):
initial.update(
{
"attendance_date": instance.attendance_date.strftime("%Y-%m-%d"),
"attendance_clock_in": instance.attendance_clock_in.strftime(
"%H:%M"
),
"attendance_clock_in_date": instance.attendance_clock_in_date.strftime(
"%Y-%m-%d"
),
}
)
2023-05-10 15:06:57 +05:30
if instance.attendance_clock_out_date is not None:
2024-02-06 13:30:32 +05:30
initial["attendance_clock_out"] = (
instance.attendance_clock_out.strftime("%H:%M")
)
initial["attendance_clock_out_date"] = (
instance.attendance_clock_out_date.strftime("%Y-%m-%d")
)
# Merge with initial values passed from the view
initial.update(view_initial)
2023-07-05 11:46:47 +05:30
kwargs["initial"] = initial
2023-05-30 15:21:53 +05:30
super().__init__(*args, **kwargs)
reload_queryset(self.fields)
2023-05-30 15:21:53 +05:30
self.fields["employee_id"].widget.attrs.update({"id": str(uuid.uuid4())})
self.fields["shift_id"].widget.attrs.update(
{
"id": str(uuid.uuid4()),
"hx-include": "#attendanceCreateForm",
"hx-target": "#attendanceCreateForm",
"hx-get": "/attendance/update-fields-based-shift",
}
)
# Update attributes for worked hour fields
for field in [
"attendance_clock_in_date",
"attendance_clock_in",
"attendance_clock_out_date",
"attendance_clock_out",
]:
self.update_worked_hour_hx_fields(field)
2024-01-01 11:14:18 +05:30
self.fields["attendance_date"].widget.attrs.update(
{
"onchange": "attendanceDateChange($(this))",
2024-01-01 11:14:18 +05:30
}
)
2023-05-30 15:21:53 +05:30
self.fields["work_type_id"].widget.attrs.update({"id": str(uuid.uuid4())})
self.fields["batch_attendance_id"].choices = list(
self.fields["batch_attendance_id"].choices
) + [("dynamic_create", "Dynamic create")]
self.fields["batch_attendance_id"].widget.attrs.update(
{
"onchange": "dynamicBatchAttendance($(this))",
}
)
2023-05-10 15:06:57 +05:30
def save(self, commit=True):
instance = super().save(commit=False)
2023-05-30 15:21:53 +05:30
for emp_id in self.data.getlist("employee_id"):
2023-05-10 15:06:57 +05:30
if int(emp_id) != int(instance.employee_id.id):
data_copy = self.data.copy()
2023-05-30 15:21:53 +05:30
data_copy.update({"employee_id": str(emp_id)})
2023-05-10 15:06:57 +05:30
attendance = AttendanceUpdateForm(data_copy).save(commit=False)
attendance.save()
if commit:
instance.save()
return instance
2023-07-05 11:46:47 +05:30
def as_p(self, *args, **kwargs):
"""
Render the form fields as HTML table rows with Bootstrap styling.
"""
_ = args, kwargs # Explicitly mark as used for pylint
2023-07-05 11:46:47 +05:30
context = {"form": self}
table_html = render_to_string("attendance_form.html", context)
return table_html
2023-07-11 12:12:25 +05:30
def clean(self) -> Dict[str, Any]:
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()
employee_ids = self.data.getlist("employee_id")
existing_attendance = Attendance.objects.filter(
attendance_date=self.data["attendance_date"]
).filter(employee_id__id__in=employee_ids)
if existing_attendance.exists():
employee_names = [
attendance.employee_id.__str__() for attendance in existing_attendance
]
raise ValidationError(
{
"employee_id": f"Already attendance exists for {', '.join(employee_names)} employees"
}
)
2023-07-11 12:12:25 +05:30
2023-05-10 15:06:57 +05:30
def clean_employee_id(self):
2023-05-30 15:21:53 +05:30
"""
Used to validate employee_id field
"""
employee = self.cleaned_data["employee_id"]
2023-05-10 15:06:57 +05:30
for emp in employee:
2023-05-30 15:21:53 +05:30
attendance = Attendance.objects.filter(
employee_id=emp, attendance_date=self.data["attendance_date"]
).first()
2023-05-10 15:06:57 +05:30
if attendance is not None:
2023-05-30 15:21:53 +05:30
raise ValidationError(
_(
("Attendance for the date already exists for {emp}").format(
emp=emp
)
2023-05-30 15:21:53 +05:30
)
)
2023-05-10 15:06:57 +05:30
if employee.first() is None:
2023-05-30 15:21:53 +05:30
raise ValidationError(_("Employee not chosen"))
2023-05-10 15:06:57 +05:30
return employee.first()
2023-05-30 15:21:53 +05:30
2023-05-10 15:06:57 +05:30
class AttendanceActivityForm(BaseModelForm):
2023-05-30 15:21:53 +05:30
"""
Model form for AttendanceActivity model
"""
2023-05-10 15:06:57 +05:30
class Meta:
2023-05-30 15:21:53 +05:30
"""
Meta class to add the additional info
"""
model = AttendanceActivity
fields = "__all__"
2023-05-10 15:06:57 +05:30
widgets = {
2023-05-30 15:21:53 +05:30
"clock_in": DateTimeInput(attrs={"type": "time"}),
"clock_out": DateTimeInput(attrs={"type": "time"}),
"clock_in_date": DateTimeInput(attrs={"type": "date"}),
"clock_out_date": DateTimeInput(attrs={"type": "date"}),
2023-05-10 15:06:57 +05:30
}
def __init__(self, *args, **kwargs):
2023-05-30 15:21:53 +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-05-30 15:21:53 +05:30
"attendance_date": instance.attendance_date.strftime("%Y-%m-%d"),
"clock_in_date": instance.clock_in_date.strftime("%Y-%m-%d"),
"clock_in": instance.clock_in.strftime("%H:%M"),
2023-05-10 15:06:57 +05:30
}
if instance.clock_out is not None:
2023-05-30 15:21:53 +05:30
initial["clock_out"] = instance.clock_out.strftime("%H:%M")
initial["clock_out_date"] = instance.clock_out_date.strftime("%Y-%m-%d")
kwargs["initial"] = initial
super().__init__(*args, **kwargs)
2023-05-10 15:06:57 +05:30
class MonthSelectField(forms.ChoiceField):
2023-05-30 15:21:53 +05:30
"""
Generate month choices
"""
2023-05-10 15:06:57 +05:30
def __init__(self, *args, **kwargs):
2023-05-30 15:21:53 +05:30
choices = [
(month_name[i].lower(), _(month_name[i].capitalize())) for i in range(1, 13)
]
super().__init__(choices=choices, *args, **kwargs)
2023-05-10 15:06:57 +05:30
class AttendanceOverTimeForm(BaseModelForm):
2023-05-30 15:21:53 +05:30
"""
Model form for AttendanceOverTime model
"""
2023-05-10 15:06:57 +05:30
month = MonthSelectField(label=_("Month"))
class Meta:
2023-05-30 15:21:53 +05:30
"""
Meta class to add the additional info
"""
2023-05-10 15:06:57 +05:30
model = AttendanceOverTime
2023-05-30 15:21:53 +05:30
fields = "__all__"
exclude = [
"hour_account_second",
"overtime_second",
"month_sequence",
"hour_pending_second",
"is_active",
]
2023-05-10 15:06:57 +05:30
labels = {
2023-05-30 15:21:53 +05:30
"employee_id": _("Employee"),
"year": _("Year"),
"worked_hours": _("Worked Hours"),
"pending_hours": _("Pending Hours"),
2023-05-30 15:21:53 +05:30
"overtime": _("Overtime"),
2023-05-10 15:06:57 +05:30
}
def __init__(self, *args, **kwargs):
2023-05-30 15:21:53 +05:30
super().__init__(*args, **kwargs)
self.fields["employee_id"].widget.attrs.update({"id": str(uuid.uuid4())})
2023-05-10 15:06:57 +05:30
def as_p(self, *args, **kwargs):
"""
Render the form fields as HTML table rows with Bootstrap styling.
"""
_ = args, kwargs # Explicitly mark as used for pylint
context = {"form": self}
table_html = render_to_string("attendance_form.html", context)
return table_html
2023-05-10 15:06:57 +05:30
class AttendanceLateComeEarlyOutForm(BaseModelForm):
2023-05-30 15:21:53 +05:30
"""
Model form for attendance AttendanceLateComeEarlyOut
"""
2023-05-10 15:06:57 +05:30
class Meta:
2023-05-30 15:21:53 +05:30
"""
Meta class to add the additional info
"""
2023-05-10 15:06:57 +05:30
model = AttendanceLateComeEarlyOut
2023-05-30 15:21:53 +05:30
fields = "__all__"
2023-05-10 15:06:57 +05:30
class AttendanceValidationConditionForm(forms.ModelForm):
2023-05-30 15:21:53 +05:30
"""
Model form for AttendanceValidationCondition
"""
validation_at_work = forms.CharField(
required=True,
initial="00:00",
widget=forms.TextInput(
attrs={"class": "oh-input w-100", "placeholder": "09:00"}
),
label=format_html(
_(
"<span title='Do not Auto Validate Attendance if an Employee Works More Than this Amount of Duration'>{}</span>"
),
_("Worked Hours(At Work) Auto Approve Till"),
),
)
minimum_overtime_to_approve = forms.CharField(
required=True,
initial="00:00",
widget=forms.TextInput(
attrs={"class": "oh-input w-100", "placeholder": "00:30"}
),
label=_("Minimum Hour to Approve Overtime"),
)
overtime_cutoff = forms.CharField(
required=True,
initial="00:00",
widget=forms.TextInput(
attrs={"class": "oh-input w-100", "placeholder": "02:00"}
),
label=_("Maximum Allowed Overtime Per Day"),
)
company_id = forms.ModelMultipleChoiceField(
label=_("Company"),
queryset=Company.objects.all(),
required=False,
widget=forms.SelectMultiple(attrs={"class": "oh-select oh-select-2 w-100"}),
)
2023-05-10 15:06:57 +05:30
class Meta:
"""
Meta class for additional options
"""
model = AttendanceValidationCondition
2023-05-30 15:21:53 +05:30
fields = "__all__"
exclude = ["is_active"]
class AttendanceRequestForm(BaseModelForm):
"""
AttendanceRequestForm
"""
def update_worked_hour_hx_fields(self, field_name):
"""Update the widget attributes for worked hour fields."""
self.fields[field_name].widget.attrs.update(
{
"id": str(uuid.uuid4()),
"hx-include": "#attendanceRequestForm",
"hx-target": "#id_attendance_worked_hour_parent_div",
"hx-swap": "outerHTML",
"hx-select": "#id_attendance_worked_hour_parent_div",
"hx-get": "/attendance/update-worked-hour-field",
"hx-trigger": "change delay:300ms", # Delay added here for 300ms
}
)
def __init__(self, *args, **kwargs):
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
initial = {
"attendance_date": instance.attendance_date.strftime("%Y-%m-%d"),
"attendance_clock_in": instance.attendance_clock_in.strftime("%H:%M"),
"attendance_clock_in_date": instance.attendance_clock_in_date.strftime(
"%Y-%m-%d"
),
}
if instance.attendance_clock_out_date is not None:
2024-02-06 13:30:32 +05:30
initial["attendance_clock_out"] = (
instance.attendance_clock_out.strftime("%H:%M")
)
initial["attendance_clock_out_date"] = (
instance.attendance_clock_out_date.strftime("%Y-%m-%d")
)
kwargs["initial"] = initial
super().__init__(*args, **kwargs)
2023-12-08 17:13:06 +05:30
self.fields["attendance_clock_out_date"].required = False
self.fields["attendance_clock_out"].required = False
2024-01-01 11:14:18 +05:30
self.fields["shift_id"].widget.attrs.update(
{
"id": str(uuid.uuid4()),
"hx-include": "#attendanceRequestForm",
"hx-target": "#attendanceRequestDiv",
"hx-swap": "outerHTML",
"hx-get": "/attendance/update-fields-based-shift",
2024-01-01 11:14:18 +05:30
}
)
for field in [
"attendance_clock_in_date",
"attendance_clock_in",
"attendance_clock_out_date",
"attendance_clock_out",
]:
self.update_worked_hour_hx_fields(field)
2024-01-01 11:14:18 +05:30
self.fields["attendance_date"].widget.attrs.update(
{
"onchange": "attendanceDateChange($(this))",
2024-01-01 11:14:18 +05:30
}
)
self.fields["work_type_id"].widget.attrs.update({"id": str(uuid.uuid4())})
self.fields["batch_attendance_id"].choices = list(
self.fields["batch_attendance_id"].choices
) + [("dynamic_create", "Dynamic create")]
self.fields["batch_attendance_id"].widget.attrs.update(
{
"onchange": "dynamicBatchAttendance($(this))",
}
)
class Meta:
"""
Meta class for additional options
"""
model = Attendance
fields = [
"attendance_date",
"shift_id",
"work_type_id",
"attendance_clock_in_date",
"attendance_clock_in",
"attendance_clock_out_date",
"attendance_clock_out",
"attendance_worked_hour",
2023-08-14 14:44:47 +05:30
"minimum_hour",
"request_description",
"batch_attendance_id",
]
def as_p(self, *args, **kwargs):
"""
Render the form fields as HTML table rows with Bootstrap styling.
"""
_ = args, kwargs # Explicitly mark as used for pylint
context = {"form": self}
table_html = render_to_string("attendance_form.html", context)
return table_html
def save(self, commit: bool = ...) -> Any:
# No need to save the changes to the actual modal instance
return super().save(False)
2023-08-14 14:44:47 +05:30
class NewRequestForm(AttendanceRequestForm):
"""
NewRequestForm class
"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Get the initial data passes from views.py file
view_initial = kwargs.pop("initial", {})
2023-08-14 14:44:47 +05:30
# Add the new model choice field to the form at the beginning
old_dict = self.fields
new_dict = {
"employee_id": forms.ModelChoiceField(
queryset=Employee.objects.filter(is_active=True),
2023-08-14 14:44:47 +05:30
label=_("Employee"),
widget=forms.Select(
attrs={
"class": "oh-select oh-select-2 w-100",
"hx-target": "#id_shift_id_div",
"hx-get": "/attendance/get-employee-shift?bulk=False",
}
),
initial=view_initial.get("employee_id"),
2023-08-14 14:44:47 +05:30
),
"create_bulk": forms.BooleanField(
required=False,
label=_("Create Bulk"),
widget=forms.CheckboxInput(
attrs={
"class": "oh-checkbox",
"hx-target": "#objectCreateModalTarget",
"hx-get": "/attendance/request-new-attendance?bulk=True",
}
),
),
2023-08-14 14:44:47 +05:30
}
new_dict.update(old_dict)
self.fields = new_dict
kwargs["initial"] = view_initial
2023-08-14 14:44:47 +05:30
def as_p(self, *args, **kwargs):
"""
Render the form fields as HTML table rows with Bootstrap styling.
"""
_ = args, kwargs # Explicitly mark as used for pylint
2023-08-14 14:44:47 +05:30
context = {"form": self}
form_html = render_to_string(
"requests/attendance/request_new_form.html", context
)
return form_html
def clean(self) -> Dict[str, Any]:
super().clean()
employee = self.cleaned_data["employee_id"]
attendance_date = self.cleaned_data["attendance_date"]
attendances = Attendance.objects.filter(
employee_id=employee, attendance_date=attendance_date
)
if employee and not hasattr(employee, "employee_work_info"):
raise ValidationError(_("Employee work info not found"))
2023-08-14 14:44:47 +05:30
data = {
"employee_id": employee,
"attendance_date": attendance_date,
"attendance_clock_in_date": self.cleaned_data["attendance_clock_in_date"],
"attendance_clock_in": self.cleaned_data["attendance_clock_in"],
"attendance_clock_out": self.cleaned_data["attendance_clock_out"],
"attendance_clock_out_date": self.cleaned_data["attendance_clock_out_date"],
"shift_id": self.cleaned_data["shift_id"],
"work_type_id": self.cleaned_data["work_type_id"],
"attendance_worked_hour": self.cleaned_data["attendance_worked_hour"],
"minimum_hour": self.data["minimum_hour"],
}
if attendances.exists():
data["employee_id"] = employee.id
data["attendance_date"] = str(attendance_date)
data["attendance_clock_in_date"] = self.data["attendance_clock_in_date"]
data["attendance_clock_in"] = self.data["attendance_clock_in"]
2023-12-08 17:13:06 +05:30
data["attendance_clock_out"] = (
None
if data["attendance_clock_out"] == "None"
else data["attendance_clock_out"]
)
data["attendance_clock_out_date"] = (
None
if data["attendance_clock_out_date"] == "None"
else data["attendance_clock_out_date"]
)
2023-08-14 14:44:47 +05:30
data["work_type_id"] = self.data["work_type_id"]
data["shift_id"] = self.data["shift_id"]
attendance = attendances.first()
2024-02-06 13:30:32 +05:30
for key, value in data.items():
data[key] = str(value)
2023-08-14 14:44:47 +05:30
attendance.requested_data = json.dumps(data)
attendance.is_validate_request = True
if attendance.request_type != "create_request":
attendance.request_type = "update_request"
attendance.request_description = self.data["request_description"]
attendance.save()
self.new_instance = None
return
new_instance = Attendance(**data)
new_instance.is_validate_request = True
new_instance.attendance_validated = False
new_instance.request_description = self.data["request_description"]
new_instance.request_type = "create_request"
self.new_instance = new_instance
return
excluded_fields = [
"id",
"attendance_id__employee_id",
"in_datetime",
"out_datetime",
"requested_data",
"at_work_second",
"approved_overtime_second",
"is_validate_request",
"is_validate_request_approved",
"request_description",
"request_type",
"month_sequence",
"objects",
"horilla_history",
]
class AttendanceExportForm(forms.Form):
"""
This form allows users to choose which fields of the `Attendance` model
they want to include in the export excel file as column. The fields are
presented as a list of checkboxes, and the user can select multiple fields.
"""
model_fields = Attendance._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",
"work_type_id",
"attendance_date",
"attendance_clock_in",
"attendance_clock_in_date",
"attendance_clock_out",
"attendance_clock_out_date",
"attendance_worked_hour",
"attendance_validated",
],
)
class LateComeEarlyOutExportForm(forms.Form):
"""
This form allows users to choose fields from both the `AttendanceLateComeEarlyOut`
model and the related `Attendance` model to include in the export excel file.
The fields are presented as checkboxes, and users can select multiple fields.
"""
model_fields = AttendanceLateComeEarlyOut._meta.get_fields()
field_choices_1 = [
(field.name, field.verbose_name)
for field in model_fields
if hasattr(field, "verbose_name") and field.name not in excluded_fields
]
model_fields_2 = Attendance._meta.get_fields()
field_choices_2 = [
("attendance_id__" + field.name, field.verbose_name)
for field in model_fields_2
if hasattr(field, "verbose_name") and field.name not in excluded_fields
]
field_choices = field_choices_1 + field_choices_2
field_choices = list(OrderedDict.fromkeys(field_choices))
selected_fields = forms.MultipleChoiceField(
choices=field_choices,
widget=forms.CheckboxSelectMultiple,
initial=[
"employee_id",
"type",
"attendance_id__attendance_date",
"attendance_id__attendance_clock_in_date",
"attendance_id__attendance_clock_in",
"attendance_id__attendance_clock_out_date",
"attendance_id__attendance_clock_out",
],
)
class AttendanceActivityExportForm(forms.Form):
"""
This form allows users to choose specific fields from the `AttendanceActivity`
model to include in the export excel file. The fields are presented as checkboxes,
enabling users to select multiple fields.
"""
model_fields = AttendanceActivity._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",
"attendance_date",
"clock_in_date",
"clock_in",
"clock_out_date",
"clock_out",
],
)
class AttendanceOverTimeExportForm(forms.Form):
"""
This form allows users to choose specific fields from the `AttendanceOverTime`
model to include in the export. The fields are presented as checkboxes,
enabling users to select multiple fields.
"""
model_fields = AttendanceOverTime._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",
"month",
"year",
"worked_hours",
"pending_hours",
"overtime",
],
)
2024-02-06 13:30:32 +05:30
class GraceTimeForm(BaseModelForm):
"""
Form for create or update Grace time
"""
shifts = forms.ModelMultipleChoiceField(
queryset=EmployeeShift.objects.all(),
required=False,
help_text=_("Allcocate this grace time for Check-In Attendance"),
)
class Meta:
"""
Meta class for additional options
"""
model = GraceTime
fields = "__all__"
2024-02-06 13:30:32 +05:30
widgets = {
2024-01-31 12:12:43 +05:30
"is_default": forms.HiddenInput(),
"allowed_time": forms.TextInput(attrs={"placeholder": "00:00:00 Hours"}),
2024-01-31 12:12:43 +05:30
}
exclude = ["objects", "allowed_time_in_secs", "is_active"]
class GraceTimeAssignForm(forms.Form):
"""
Form for create or update Grace time
"""
shifts = forms.ModelMultipleChoiceField(
queryset=EmployeeShift.objects.all(),
)
verbose_name = _("Assign Shifts")
def as_p(self, *args, **kwargs):
"""
Render the form fields as HTML table rows with Bootstrap styling.
"""
_ = args, kwargs # Explicitly mark as used for pylint
context = {"form": self}
form_html = render_to_string("common_form.html", context)
return form_html
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["shifts"].widget.attrs["class"] = "oh-select w-100 oh-select-2"
class AttendanceRequestCommentForm(BaseModelForm):
"""
AttendanceRequestComment form
"""
class Meta:
"""
Meta class for additional options
"""
model = AttendanceRequestComment
2024-02-06 13:30:32 +05:30
fields = ("comment",)
def get_date_list(employee_id, from_date, to_date):
"""
This method will return a list of company working dates
"""
working_dates = get_working_days(from_date, to_date)
working_date_list = working_dates["working_days_on"]
working_date_list.sort()
attendance_dates = []
if len(working_date_list) > 0:
# filter through approved leave of employee
[IMP] Remove inter module dependency (#274) This commit introduces significant changes to the architecture of the Horilla HRMS system by decoupling interdependent modules. The following modifications were made: 1. **Module Independence**: Each module has been refactored to eliminate reliance on other modules, promoting a more modular and maintainable codebase. 2. **Refactored Imports and Dependencies**: Adjusted import statements and dependency injections to support independent module operation. 3. **Compatibility and Functionality**: Ensured that all modules are compatible with existing systems and maintain their intended functionality both independently and when integrated with other modules. These changes enhance the modularity, maintainability, and scalability of the Horilla HRMS, allowing developers to work on individual modules without affecting the entire system. Future development and deployment will be more efficient and less prone to issues arising from tightly coupled code. **NOTE** For existing Horilla users, if you face any issues during the migrations, please run the following command and try again the migrations. - `python3 manage.py makemigrations` - `python3 manage.py migrate base` - `python3 manage.py migrate` * [IMP] ASSET: Asset module dependency removal from other Horilla apps * [IMP] ATTENDANCE: Attendance module dependency removal from other Horilla apps * [IMP] BASE: Base module dependency removal from other Horilla apps * [IMP] EMPLOYEE: Employee module dependency removal from other Horilla apps * [IMP] HELPDESK: Helpdesk module dependency removal from other Horilla apps * [IMP] HORILLA AUDIT: Horilla Audit module dependency removal from other Horilla apps * [IMP] HORILLA CRUMBS: Horilla Crumbs module dependency removal from other Horilla apps * [IMP] HORILLA AUTOMATIONS: Horilla Automations module dependency removal from other Horilla apps * [IMP] HORILLA VIEWS: Horilla Views module dependency removal from other Horilla apps * [IMP] LEAVE: Leave module dependency removal from other Horilla apps * [IMP] OFFBOARDING: Offboarding module dependency removal from other Horilla apps * [IMP] ONBOARDING: Onboarding module dependency removal from other Horilla apps * [IMP] PMS: PMS module dependency removal from other Horilla apps * [IMP] PAYROLL: Payroll module dependency removal from other Horilla apps * [IMP] RECRUITMENT: Recruitment module dependency removal from other Horilla apps * [IMP] HORILLA: Dependency removal updates * [IMP] TEMPLATES: Dependency removal updates * [IMP] STATIC: Dependency removal updates * [IMP] HORILLA DOCUMENTS: Horilla Documents module dependency removal from other Horilla apps * [ADD] HORILLA: methods.py * [UPDT] HORILLA: Settings.py * [FIX] EMPLOYEE: About tab issue * Update horilla_settings.py * Remove dummy db init password
2024-08-05 14:22:44 +05:30
if apps.is_installed("leave"):
from leave.filters import LeaveRequestFilter
approved_leave_dates_filtered = LeaveRequestFilter(
data={
"from_date": working_date_list[0],
"to_date": working_date_list[-1],
"status": "approved",
}
)
approved_leave_dates_filtered = approved_leave_dates_filtered.qs.filter(
employee_id=employee_id
)
[IMP] Remove inter module dependency (#274) This commit introduces significant changes to the architecture of the Horilla HRMS system by decoupling interdependent modules. The following modifications were made: 1. **Module Independence**: Each module has been refactored to eliminate reliance on other modules, promoting a more modular and maintainable codebase. 2. **Refactored Imports and Dependencies**: Adjusted import statements and dependency injections to support independent module operation. 3. **Compatibility and Functionality**: Ensured that all modules are compatible with existing systems and maintain their intended functionality both independently and when integrated with other modules. These changes enhance the modularity, maintainability, and scalability of the Horilla HRMS, allowing developers to work on individual modules without affecting the entire system. Future development and deployment will be more efficient and less prone to issues arising from tightly coupled code. **NOTE** For existing Horilla users, if you face any issues during the migrations, please run the following command and try again the migrations. - `python3 manage.py makemigrations` - `python3 manage.py migrate base` - `python3 manage.py migrate` * [IMP] ASSET: Asset module dependency removal from other Horilla apps * [IMP] ATTENDANCE: Attendance module dependency removal from other Horilla apps * [IMP] BASE: Base module dependency removal from other Horilla apps * [IMP] EMPLOYEE: Employee module dependency removal from other Horilla apps * [IMP] HELPDESK: Helpdesk module dependency removal from other Horilla apps * [IMP] HORILLA AUDIT: Horilla Audit module dependency removal from other Horilla apps * [IMP] HORILLA CRUMBS: Horilla Crumbs module dependency removal from other Horilla apps * [IMP] HORILLA AUTOMATIONS: Horilla Automations module dependency removal from other Horilla apps * [IMP] HORILLA VIEWS: Horilla Views module dependency removal from other Horilla apps * [IMP] LEAVE: Leave module dependency removal from other Horilla apps * [IMP] OFFBOARDING: Offboarding module dependency removal from other Horilla apps * [IMP] ONBOARDING: Onboarding module dependency removal from other Horilla apps * [IMP] PMS: PMS module dependency removal from other Horilla apps * [IMP] PAYROLL: Payroll module dependency removal from other Horilla apps * [IMP] RECRUITMENT: Recruitment module dependency removal from other Horilla apps * [IMP] HORILLA: Dependency removal updates * [IMP] TEMPLATES: Dependency removal updates * [IMP] STATIC: Dependency removal updates * [IMP] HORILLA DOCUMENTS: Horilla Documents module dependency removal from other Horilla apps * [ADD] HORILLA: methods.py * [UPDT] HORILLA: Settings.py * [FIX] EMPLOYEE: About tab issue * Update horilla_settings.py * Remove dummy db init password
2024-08-05 14:22:44 +05:30
else:
approved_leave_dates_filtered = QuerySet().none()
approved_leave_dates = []
# Extract the list of approved leave dates
if len(approved_leave_dates_filtered) > 0:
for leave in approved_leave_dates_filtered:
approved_leave_dates += leave.requested_dates()
attendance_filters = AttendanceFilters(
data={
"attendance_date__gte": working_date_list[0],
"attendance_date__lte": working_date_list[-1],
}
)
existing_attendance = attendance_filters.qs.filter(employee_id=employee_id)
# Extract the list of attendance dates
attendance_dates = list(
existing_attendance.values_list("attendance_date", flat=True)
)
# Calculate the dates that need new attendance records
date_list = [
date
for date in working_date_list
if date not in attendance_dates and date not in approved_leave_dates
]
return date_list
class BulkAttendanceRequestForm(BaseModelForm):
"""
Bulk attendance request create form
"""
employee_id = forms.ModelChoiceField(
queryset=Employee.objects.filter(is_active=True),
widget=forms.Select(
attrs={
"hx-target": "#id_shift_id_div",
"hx-get": "/attendance/get-employee-shift?bulk=True",
}
),
label=_("Employee"),
)
create_bulk = forms.BooleanField(
required=False,
initial=True,
label=_("Create Bulk"),
widget=forms.CheckboxInput(
attrs={
"class": "oh-checkbox",
"hx-target": "#objectCreateModalTarget",
"hx-get": "/attendance/request-new-attendance?bulk=False",
}
),
)
from_date = forms.DateField(
required=False,
label=_("From Date"),
widget=forms.DateInput(attrs={"type": "date", "class": "form-control"}),
)
to_date = forms.DateField(
required=False,
label=_("To Date"),
widget=forms.DateInput(attrs={"type": "date", "class": "form-control"}),
)
batch_attendance_id = forms.ModelChoiceField(
queryset=BatchAttendance.objects.all(),
required=False,
label="Batch",
widget=forms.Select(attrs={"onchange": "dynamicBatchAttendance($(this))"}),
)
class Meta:
"""
Meta class for additional options
"""
model = Attendance
fields = (
"employee_id",
"create_bulk",
"from_date",
"to_date",
"shift_id",
"work_type_id",
"attendance_clock_in",
"attendance_clock_out",
"minimum_hour",
"attendance_worked_hour",
"request_description",
)
def update_worked_hour_hx_fields(self, field_name):
"""Update the widget attributes for worked hour fields."""
self.fields[field_name].widget.attrs.update(
{
"id": str(uuid.uuid4()),
"hx-include": "#attendanceRequestForm",
"hx-target": "#id_attendance_worked_hour_parent_div",
"hx-swap": "outerHTML",
"hx-select": "#id_attendance_worked_hour_parent_div",
"hx-get": "/attendance/update-worked-hour-field",
"hx-trigger": "change delay:300ms",
}
)
def __init__(self, *args, **kwargs):
request = getattr(horilla_middlewares._thread_locals, "request", None)
employee = request.user.employee_get
super().__init__(*args, **kwargs)
if employee and hasattr(employee, "employee_work_info"):
shift = employee.employee_work_info.shift_id
self.fields["shift_id"].initial = shift
for field in [
"attendance_clock_in",
"attendance_clock_out",
]:
self.update_worked_hour_hx_fields(field)
if request.user.has_perm("attendance.add_attendance") or is_reportingmanager(
request
):
employees = filtersubordinatesemployeemodel(
request, Employee.objects.all(), perm="pms.add_feedback"
)
self.fields["employee_id"].queryset = employees | Employee.objects.filter(
employee_user_id=request.user
)
else:
self.fields["employee_id"].queryset = Employee.objects.filter(
employee_user_id=request.user
)
self.fields["batch_attendance_id"].choices = list(
self.fields["batch_attendance_id"].choices
) + [("dynamic_create", "Dynamic create")]
def clean(self):
cleaned_data = self.cleaned_data
from_date = cleaned_data.get("from_date")
to_date = cleaned_data.get("to_date")
attendance_worked_hour = cleaned_data.get("attendance_worked_hour")
minimum_hour = cleaned_data.get("minimum_hour")
attendance_clock_out = cleaned_data.get("attendance_clock_out")
employee_id = cleaned_data.get("employee_id")
now = datetime.datetime.now().time()
today = datetime.datetime.today().date()
validate_time_format(attendance_worked_hour)
validate_time_format(minimum_hour)
attendance_date_validate(from_date)
attendance_date_validate(to_date)
date_list = get_date_list(employee_id, from_date, to_date)
if from_date and to_date and from_date > to_date:
raise ValidationError({"to_date": _("To date should be after from date")})
if to_date == today and attendance_clock_out > now:
raise ValidationError(
{
"attendance_clock_out": (
f"Check out time is in the future for the date {to_date}."
)
}
)
if employee_id and not hasattr(employee_id, "employee_work_info"):
raise ValidationError(_("Employee work info not found"))
if len(date_list) <= 0:
raise ValidationError(
_(
"There is no valid date to create attendance request between this date range"
)
)
return cleaned_data
def save(self, commit=True):
# Access cleaned data
cleaned_data = self.cleaned_data
employee_id = cleaned_data.get("employee_id")
from_date = cleaned_data.get("from_date")
to_date = cleaned_data.get("to_date")
shift_id = cleaned_data.get("shift_id")
attendance_clock_in = cleaned_data.get("attendance_clock_in")
attendance_clock_out = cleaned_data.get("attendance_clock_out")
request_description = cleaned_data.get("request_description")
attendance_worked_hour = cleaned_data.get("attendance_worked_hour")
minimum_hour = cleaned_data.get("minimum_hour")
work_type_id = employee_id.employee_work_info.work_type_id
date_list = get_date_list(employee_id, from_date, to_date)
batch = (
cleaned_data.get("batch_attendance_id")
if cleaned_data.get("batch_attendance_id")
else None
)
# Prepare initial data for the form
initial_data = {
"employee_id": employee_id,
"shift_id": shift_id,
"work_type_id": work_type_id,
"attendance_clock_in": attendance_clock_in,
"attendance_clock_out": attendance_clock_out,
"attendance_worked_hour": attendance_worked_hour,
"is_validate_request": True,
"minimum_hour": minimum_hour,
"request_description": request_description,
}
for date in date_list:
initial_data.update(
{
"attendance_date": date,
"attendance_clock_in_date": date,
"attendance_clock_out_date": date,
}
)
form = NewRequestForm(data=initial_data)
if form.is_valid():
instance = form.save(commit=False)
instance.is_validate_request = True
instance.employee_id = employee_id
instance.request_type = "create_request"
instance.is_bulk_request = True
if batch:
instance.batch_attendance_id = batch
instance.save()
else:
logger(form.errors)
instance = super().save(commit=False)
if commit:
instance.save()
return instance
[IMP] Remove inter module dependency (#274) This commit introduces significant changes to the architecture of the Horilla HRMS system by decoupling interdependent modules. The following modifications were made: 1. **Module Independence**: Each module has been refactored to eliminate reliance on other modules, promoting a more modular and maintainable codebase. 2. **Refactored Imports and Dependencies**: Adjusted import statements and dependency injections to support independent module operation. 3. **Compatibility and Functionality**: Ensured that all modules are compatible with existing systems and maintain their intended functionality both independently and when integrated with other modules. These changes enhance the modularity, maintainability, and scalability of the Horilla HRMS, allowing developers to work on individual modules without affecting the entire system. Future development and deployment will be more efficient and less prone to issues arising from tightly coupled code. **NOTE** For existing Horilla users, if you face any issues during the migrations, please run the following command and try again the migrations. - `python3 manage.py makemigrations` - `python3 manage.py migrate base` - `python3 manage.py migrate` * [IMP] ASSET: Asset module dependency removal from other Horilla apps * [IMP] ATTENDANCE: Attendance module dependency removal from other Horilla apps * [IMP] BASE: Base module dependency removal from other Horilla apps * [IMP] EMPLOYEE: Employee module dependency removal from other Horilla apps * [IMP] HELPDESK: Helpdesk module dependency removal from other Horilla apps * [IMP] HORILLA AUDIT: Horilla Audit module dependency removal from other Horilla apps * [IMP] HORILLA CRUMBS: Horilla Crumbs module dependency removal from other Horilla apps * [IMP] HORILLA AUTOMATIONS: Horilla Automations module dependency removal from other Horilla apps * [IMP] HORILLA VIEWS: Horilla Views module dependency removal from other Horilla apps * [IMP] LEAVE: Leave module dependency removal from other Horilla apps * [IMP] OFFBOARDING: Offboarding module dependency removal from other Horilla apps * [IMP] ONBOARDING: Onboarding module dependency removal from other Horilla apps * [IMP] PMS: PMS module dependency removal from other Horilla apps * [IMP] PAYROLL: Payroll module dependency removal from other Horilla apps * [IMP] RECRUITMENT: Recruitment module dependency removal from other Horilla apps * [IMP] HORILLA: Dependency removal updates * [IMP] TEMPLATES: Dependency removal updates * [IMP] STATIC: Dependency removal updates * [IMP] HORILLA DOCUMENTS: Horilla Documents module dependency removal from other Horilla apps * [ADD] HORILLA: methods.py * [UPDT] HORILLA: Settings.py * [FIX] EMPLOYEE: About tab issue * Update horilla_settings.py * Remove dummy db init password
2024-08-05 14:22:44 +05:30
class WorkRecordsForm(BaseModelForm):
[IMP] Remove inter module dependency (#274) This commit introduces significant changes to the architecture of the Horilla HRMS system by decoupling interdependent modules. The following modifications were made: 1. **Module Independence**: Each module has been refactored to eliminate reliance on other modules, promoting a more modular and maintainable codebase. 2. **Refactored Imports and Dependencies**: Adjusted import statements and dependency injections to support independent module operation. 3. **Compatibility and Functionality**: Ensured that all modules are compatible with existing systems and maintain their intended functionality both independently and when integrated with other modules. These changes enhance the modularity, maintainability, and scalability of the Horilla HRMS, allowing developers to work on individual modules without affecting the entire system. Future development and deployment will be more efficient and less prone to issues arising from tightly coupled code. **NOTE** For existing Horilla users, if you face any issues during the migrations, please run the following command and try again the migrations. - `python3 manage.py makemigrations` - `python3 manage.py migrate base` - `python3 manage.py migrate` * [IMP] ASSET: Asset module dependency removal from other Horilla apps * [IMP] ATTENDANCE: Attendance module dependency removal from other Horilla apps * [IMP] BASE: Base module dependency removal from other Horilla apps * [IMP] EMPLOYEE: Employee module dependency removal from other Horilla apps * [IMP] HELPDESK: Helpdesk module dependency removal from other Horilla apps * [IMP] HORILLA AUDIT: Horilla Audit module dependency removal from other Horilla apps * [IMP] HORILLA CRUMBS: Horilla Crumbs module dependency removal from other Horilla apps * [IMP] HORILLA AUTOMATIONS: Horilla Automations module dependency removal from other Horilla apps * [IMP] HORILLA VIEWS: Horilla Views module dependency removal from other Horilla apps * [IMP] LEAVE: Leave module dependency removal from other Horilla apps * [IMP] OFFBOARDING: Offboarding module dependency removal from other Horilla apps * [IMP] ONBOARDING: Onboarding module dependency removal from other Horilla apps * [IMP] PMS: PMS module dependency removal from other Horilla apps * [IMP] PAYROLL: Payroll module dependency removal from other Horilla apps * [IMP] RECRUITMENT: Recruitment module dependency removal from other Horilla apps * [IMP] HORILLA: Dependency removal updates * [IMP] TEMPLATES: Dependency removal updates * [IMP] STATIC: Dependency removal updates * [IMP] HORILLA DOCUMENTS: Horilla Documents module dependency removal from other Horilla apps * [ADD] HORILLA: methods.py * [UPDT] HORILLA: Settings.py * [FIX] EMPLOYEE: About tab issue * Update horilla_settings.py * Remove dummy db init password
2024-08-05 14:22:44 +05:30
"""
WorkRecordForm
"""
class Meta:
"""
Meta class for additional options
"""
fields = "__all__"
model = WorkRecords
class BatchAttendanceForm(BaseModelForm):
"""
BatchAttendanceForm
"""
verbose_name = _("Create attendance batch")
class Meta:
"""
Meta class for additional options
"""
fields = "__all__"
model = BatchAttendance
exclude = ["is_active"]
def as_p(self, *args, **kwargs):
"""
Render the form fields as HTML table rows with Bootstrap styling.
"""
_ = args, kwargs # Explicitly mark as used for pylint
context = {"form": self}
form_html = render_to_string("common_form.html", context)
return form_html
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if self.instance.pk:
self.verbose_name = _("Update attendance batch")