Files
ihrm/payroll/forms/component_forms.py
2024-01-10 09:48:59 +05:30

386 lines
12 KiB
Python

"""
These forms provide a convenient way to handle data input, validation, and customization
of form fields and widgets for the corresponding models in the payroll management system.
"""
import uuid
import datetime
from django import forms
from django.utils.translation import gettext_lazy as _
from django.template.loader import render_to_string
from horilla_widgets.forms import HorillaForm
from horilla_widgets.widgets.horilla_multi_select_field import HorillaMultiSelectField
from horilla_widgets.widgets.select_widgets import HorillaMultiSelectWidget
from base.forms import Form, ModelForm
from employee.models import Employee
from employee.filters import EmployeeFilter
from payroll.models import tax_models as models
from payroll.widgets import component_widgets as widget
from payroll.models.models import Allowance, Contract, LoanAccount, Payslip
import payroll.models.models
from base.methods import reload_queryset
class AllowanceForm(forms.ModelForm):
"""
Form for Allowance model
"""
load = forms.CharField(widget=widget.AllowanceConditionalVisibility, required=False)
style = forms.CharField(required=False)
verbose_name = _("Allowance")
class Meta:
"""
Meta class for additional options
"""
model = payroll.models.models.Allowance
fields = "__all__"
widgets = {
"one_time_date": forms.DateTimeInput(attrs={"type": "date"}),
}
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 = {}
if instance.one_time_date is not None:
initial = {
"one_time_date": instance.one_time_date.strftime("%Y-%m-%d"),
}
kwargs["initial"] = initial
super().__init__(*args, **kwargs)
reload_queryset(self.fields)
self.fields["style"].widget = widget.StyleWidget(form=self)
def as_p(self):
"""
Render the form fields as HTML table rows with Bootstrap styling.
"""
context = {"form": self}
table_html = render_to_string("common_form.html", context)
return table_html
class DeductionForm(forms.ModelForm):
"""
Form for Deduction model
"""
load = forms.CharField(widget=widget.DeductionConditionalVisibility, required=False)
style = forms.CharField(required=False)
verbose_name = _("Deduction")
class Meta:
"""
Meta class for additional options
"""
model = payroll.models.models.Deduction
fields = "__all__"
widgets = {
"one_time_date": forms.DateTimeInput(attrs={"type": "date"}),
}
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 = {}
if instance.one_time_date is not None:
initial = {
"one_time_date": instance.one_time_date.strftime("%Y-%m-%d"),
}
kwargs["initial"] = initial
super().__init__(*args, **kwargs)
reload_queryset(self.fields)
self.fields["style"].widget = widget.StyleWidget(form=self)
def clean(self):
if (
self.data.get("update_compensation") is not None
and self.data.get("update_compensation") != ""
):
if (
self.data.getlist("specific_employees") is None
and len(self.data.getlist("specific_employees")) == 0
):
raise forms.ValidationError(
{"specific_employees": "You need to choose the employee."}
)
if (
self.data.get("one_time_date") is None
and self.data.get("one_time_date") == ""
):
raise forms.ValidationError(
{"one_time_date": "This field is required."}
)
if self.data.get("amount") is None and self.data.get("amount") == "":
raise forms.ValidationError({"amount": "This field is required."})
return super().clean()
def as_p(self):
"""
Render the form fields as HTML table rows with Bootstrap styling.
"""
context = {"form": self}
table_html = render_to_string("common_form.html", context)
return table_html
class PayslipForm(ModelForm):
"""
Form for Payslip
"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
active_contracts = Contract.objects.filter(contract_status="active")
self.fields["employee_id"].choices = [
(contract.employee_id.id, contract.employee_id)
for contract in active_contracts
if contract.employee_id.is_active
]
class Meta:
"""
Meta class for additional options
"""
model = payroll.models.models.Payslip
fields = [
"employee_id",
"start_date",
"end_date",
]
widgets = {
"start_date": forms.DateInput(attrs={"type": "date"}),
"end_date": forms.DateInput(attrs={"type": "date"}),
}
class GeneratePayslipForm(HorillaForm):
"""
Form for Payslip
"""
group_name = forms.CharField(
label="Batch name",
required=False,
# help_text="Enter +-something if you want to generate payslips by batches",
)
employee_id = HorillaMultiSelectField(
queryset=Employee.objects.none(),
widget=HorillaMultiSelectWidget(
filter_route_name="employee-widget-filter",
filter_class=EmployeeFilter,
filter_instance_contex_name="f",
filter_template_path="employee_filters.html",
),
label="Employee",
)
start_date = forms.DateField(widget=forms.DateInput(attrs={"type": "date"}))
end_date = forms.DateField(widget=forms.DateInput(attrs={"type": "date"}))
def clean(self):
cleaned_data = super().clean()
start_date = cleaned_data.get("start_date")
end_date = cleaned_data.get("end_date")
today = datetime.date.today()
if end_date < start_date:
raise forms.ValidationError(
{
"end_date": "The end date must be greater than or equal to the start date."
}
)
if start_date > today:
raise forms.ValidationError(
{"end_date": "The start date cannot be in the future."}
)
if end_date > today:
raise forms.ValidationError(
{"end_date": "The end date cannot be in the future."}
)
return cleaned_data
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["employee_id"].queryset = Employee.objects.filter(
is_active=True,
contract_set__isnull=False,
contract_set__contract_status="active",
)
self.fields["employee_id"].widget.attrs.update(
{"class": "oh-select oh-select-2", "id": uuid.uuid4()}
)
self.fields["start_date"].widget.attrs.update({"class": "oh-input w-100"})
self.fields["group_name"].widget.attrs.update({"class": "oh-input w-100"})
self.fields["end_date"].widget.attrs.update({"class": "oh-input w-100"})
class Meta:
"""
Meta class for additional options
"""
widgets = {
"start_date": forms.DateInput(attrs={"type": "date"}),
"end_date": forms.DateInput(attrs={"type": "date"}),
}
class PayrollSettingsForm(ModelForm):
"""
Form for PayrollSettings model
"""
class Meta:
"""
Meta class for additional options
"""
model = models.PayrollSettings
fields = "__all__"
excel_columns = [
("employee_id", _("Employee")),
("group_name", _("Batch")),
("start_date", _("Start Date")),
("end_date", _("End Date")),
("contract_wage", _("Contract Wage")),
("basic_pay", _("Basic Pay")),
("gross_pay", _("Gross Pay")),
("deduction", _("Deduction")),
("net_pay", _("Net Pay")),
("status", _("Status")),
("employee_id__employee_bank_details__bank_name", _("Bank Name")),
("employee_id__employee_bank_details__branch", _("Branch")),
("employee_id__employee_bank_details__account_number", _("Account Number")),
("employee_id__employee_bank_details__any_other_code1", _("Bank Code #1")),
("employee_id__employee_bank_details__any_other_code2", _("Bank Code #2")),
("employee_id__employee_bank_details__country", _("Country")),
("employee_id__employee_bank_details__state", _("State")),
("employee_id__employee_bank_details__city", _("City")),
]
class PayslipExportColumnForm(forms.Form):
selected_fields = forms.MultipleChoiceField(
choices=excel_columns,
widget=forms.CheckboxSelectMultiple,
initial=[
"employee_id",
"group_name",
"start_date",
"end_date",
"basic_pay",
"gross_pay",
"net_pay",
"status",
],
)
exclude_fields = ["id", "contract_document", "is_active", "note", "note"]
class ContractExportFieldForm(forms.Form):
model_fields = Contract._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 exclude_fields
]
selected_fields = forms.MultipleChoiceField(
choices=field_choices,
widget=forms.CheckboxSelectMultiple,
initial=[
"contract_name",
"employee_id",
"contract_start_date",
"contract_end_date",
"wage_type",
"wage",
"filing_status",
"contract_status",
],
)
class BonusForm(Form):
"""
Bonus Creating Form
"""
title = forms.CharField(max_length=100)
date = forms.DateField(widget=forms.DateInput())
employee_id = forms.IntegerField(label="Employee", widget=forms.HiddenInput())
amount = forms.DecimalField(label="Amount")
def save(self, commit=True):
title = self.cleaned_data["title"]
date = self.cleaned_data["date"]
employee_id = self.cleaned_data["employee_id"]
amount = self.cleaned_data["amount"]
bonus = Allowance()
bonus.title = title
bonus.one_time_date = date
bonus.only_show_under_employee = True
bonus.amount = amount
bonus.is_fixed = True
bonus.save()
bonus.include_active_employees = False
bonus.specific_employees.set([employee_id])
bonus.save()
return bonus
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for field_name, field in self.fields.items():
field.widget.attrs.update({"class": "oh-input w-100"})
self.fields["date"].widget = forms.DateInput(
attrs={"type": "date", "class": "oh-input w-100"}
)
class LoanAccountForm(ModelForm):
"""
LoanAccountForm
"""
verbose_name = "Loan / Advanced Sarlary"
class Meta:
model = LoanAccount
fields = "__all__"
widgets = {
"provided_date": forms.DateTimeInput(attrs={"type": "date"}),
"installment_start_date": forms.DateTimeInput(attrs={"type": "date"}),
}
def as_p(self):
"""
Render the form fields as HTML table rows with Bootstrap styling.
"""
context = {"form": self}
table_html = render_to_string("common_form.html", context)
return table_html
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if self.instance.pk:
self.verbose_name = self.instance.title
fields_to_exclude = ["employee_id", "installment_start_date"]
if Payslip.objects.filter(installment_ids__in=list(self.instance.deduction_ids.values_list("id",flat=True))).exists():
fields_to_exclude = fields_to_exclude + ["loan_amount", "installments"]
self.initial["provided_date"] = str(self.instance.provided_date)
for field in fields_to_exclude:
if field in self.fields:
del self.fields[field]