""" 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) self.initial["installment_start_date"] = str(datetime.date.today()) 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] class AssetFineForm(LoanAccountForm): verbose_name = "Asset Fine" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.fields['loan_amount'].label = 'Fine Amount' fields_to_exclude = ["employee_id", "provided_date","type"] for field in fields_to_exclude: if field in self.fields: del self.fields[field] pass