Files
ihrm/report/views/payroll_report.py

417 lines
18 KiB
Python

from django.apps import apps
from django.http import JsonResponse
from django.shortcuts import render
from django.utils.dateparse import parse_date
if apps.is_installed("payroll"):
from base.models import Company
from horilla.decorators import login_required, permission_required
from payroll.filters import PayslipFilter
from payroll.models.models import Payslip
@login_required
@permission_required(perm="payroll.view_payslip")
def payroll_report(request):
company = "all"
selected_company = request.session.get("selected_company")
if selected_company != "all":
company = Company.objects.filter(id=selected_company).first()
if request.user.has_perm("payroll.view_payslip"):
payslips = Payslip.objects.all()
else:
payslips = Payslip.objects.filter(
employee_id__employee_user_id=request.user
)
filter_form = PayslipFilter(request.GET, payslips)
return render(
request,
"report/payroll_report.html",
{"company": company, "f": filter_form},
)
@login_required
@permission_required(perm="payroll.view_payslip")
def payroll_pivot(request):
model_type = request.GET.get("model", "payslip")
if model_type == "payslip":
qs = Payslip.objects.all()
if employee_id := request.GET.getlist("employee_id"):
qs = qs.filter(employee_id__id__in=employee_id)
if status := request.GET.get("status"):
qs = qs.filter(status=status)
if group_name := request.GET.get("group_name"):
qs = qs.filter(group_name=group_name)
start_date_from = parse_date(request.GET.get("start_date_from", ""))
start_date_to = parse_date(request.GET.get("start_date_till", ""))
if start_date_from:
qs = qs.filter(start_date__gte=start_date_from)
if start_date_to:
qs = qs.filter(start_date__lte=start_date_to)
end_date_from = parse_date(request.GET.get("end_date_from", ""))
end_date_to = parse_date(request.GET.get("end_date_till", ""))
if end_date_from:
qs = qs.filter(end_date__gte=end_date_from)
if end_date_to:
qs = qs.filter(end_date__lte=end_date_to)
# Gross Pay Range
gross_pay_gte = request.GET.get("gross_pay__gte")
gross_pay_lte = request.GET.get("gross_pay__lte")
if gross_pay_gte:
qs = qs.filter(gross_pay__gte=gross_pay_gte)
if gross_pay_lte:
qs = qs.filter(gross_pay__lte=gross_pay_lte)
# Deduction Range
deduction_gte = request.GET.get("deduction__gte")
deduction_lte = request.GET.get("deduction__lte")
if deduction_gte:
qs = qs.filter(deduction__gte=deduction_gte)
if deduction_lte:
qs = qs.filter(deduction__lte=deduction_lte)
# Net Pay Range
net_pay_gte = request.GET.get("net_pay__gte")
net_pay_lte = request.GET.get("net_pay__lte")
if net_pay_gte:
qs = qs.filter(net_pay__gte=net_pay_gte)
if net_pay_lte:
qs = qs.filter(net_pay__lte=net_pay_lte)
data = list(
qs.values(
"id", # Include payslip ID to fetch pay_head_data later
"employee_id__employee_first_name",
"employee_id__employee_last_name",
"employee_id__gender",
"employee_id__email",
"employee_id__phone",
"start_date",
"end_date",
"contract_wage",
"basic_pay",
"gross_pay",
"deduction",
"net_pay",
"group_name",
"status",
"employee_id__employee_work_info__department_id__department",
"employee_id__employee_work_info__job_role_id__job_role",
"employee_id__employee_work_info__job_position_id__job_position",
"employee_id__employee_work_info__work_type_id__work_type",
"employee_id__employee_work_info__shift_id__employee_shift",
"employee_id__employee_work_info__employee_type_id__employee_type",
"employee_id__employee_work_info__experience",
)
)
choice_gender = {
"male": "Male",
"female": "Female",
"other": "Other",
}
STATUS = {
"draft": "Draft",
"review_ongoing": "Review Ongoing",
"confirmed": "Confirmed",
"paid": "Paid",
}
# Fetch pay_head_data separately and map by payslip ID
payslip_ids = [item["id"] for item in data]
pay_head_data_dict = dict(
Payslip.objects.filter(id__in=payslip_ids).values_list(
"id", "pay_head_data"
)
)
data_list = []
for item in data:
# Load pay_head_data for current payslip
pay_head_data = pay_head_data_dict.get(item["id"], {})
# Extract allowances and deductions
allowances = pay_head_data.get("allowances", [])
deductions = pay_head_data.get(
"pretax_deductions", []
) + pay_head_data.get("post_tax_deductions", [])
# Prepare allowance and deduction lists with properly rounded amounts
allowance_titles = (
", ".join([allowance["title"] for allowance in allowances]) or "-"
)
allowance_amounts = (
", ".join(
[
str(round(float(allowance["amount"] or 0), 2))
for allowance in allowances
]
)
or "-"
)
deduction_titles = (
", ".join([deduction["title"] for deduction in deductions]) or "-"
)
deduction_amounts = (
", ".join(
[
str(round(float(deduction["amount"] or 0), 2))
for deduction in deductions
]
)
or "-"
)
# Calculate total allowance amount
total_allowance_amount = sum(
[
round(float(allowance["amount"] or 0), 2)
for allowance in allowances
]
)
# Calculate total deduction amount
total_deduction_amount = sum(
[
round(float(deduction["amount"] or 0), 2)
for deduction in deductions
]
)
# Main data structure
data_list.append(
{
"Employee": f"{item['employee_id__employee_first_name']} {item['employee_id__employee_last_name']}",
"Gender": choice_gender.get(item["employee_id__gender"]),
"Email": item["employee_id__email"],
"Phone": item["employee_id__phone"],
"Department": (
item[
"employee_id__employee_work_info__department_id__department"
]
if item[
"employee_id__employee_work_info__department_id__department"
]
else "-"
),
"Job Position": (
item[
"employee_id__employee_work_info__job_position_id__job_position"
]
if item[
"employee_id__employee_work_info__job_position_id__job_position"
]
else "-"
),
"Job Role": (
item[
"employee_id__employee_work_info__job_role_id__job_role"
]
if item[
"employee_id__employee_work_info__job_role_id__job_role"
]
else "-"
),
"Work Type": (
item[
"employee_id__employee_work_info__work_type_id__work_type"
]
if item[
"employee_id__employee_work_info__work_type_id__work_type"
]
else "-"
),
"Shift": (
item[
"employee_id__employee_work_info__shift_id__employee_shift"
]
if item[
"employee_id__employee_work_info__shift_id__employee_shift"
]
else "-"
),
"Employee Type": (
item[
"employee_id__employee_work_info__employee_type_id__employee_type"
]
if item[
"employee_id__employee_work_info__employee_type_id__employee_type"
]
else "-"
),
"Payslip Start Date": item["start_date"],
"Payslip End Date": item["end_date"],
"Batch Name": item["group_name"] if item["group_name"] else "-",
"Contract Wage": round(float(item["contract_wage"] or 0), 2),
"Basic Salary": round(float(item["basic_pay"] or 0), 2),
"Gross Pay": round(float(item["gross_pay"] or 0), 2),
"Net Pay": round(float(item["net_pay"] or 0), 2),
"Allowance Title": allowance_titles,
"Allowance Amount": allowance_amounts,
"Total Allowance Amount": round(total_allowance_amount, 2),
"Deduction Title": deduction_titles,
"Deduction Amount": deduction_amounts,
"Total Deduction Amount": round(total_deduction_amount, 2),
"Status": STATUS.get(item["status"]),
"Experience": round(
float(
item["employee_id__employee_work_info__experience"] or 0
),
2,
),
}
)
elif model_type == "allowance":
payslips = Payslip.objects.all()
payslip_filter = PayslipFilter(request.GET, queryset=payslips)
filtered_qs = payslip_filter.qs # This uses all custom filters you defined
data = list(
filtered_qs.values(
"id", # Include payslip ID to fetch pay_head_data later
"employee_id__employee_first_name",
"employee_id__employee_last_name",
"employee_id__gender",
"employee_id__email",
"employee_id__phone",
"start_date",
"end_date",
"status",
"employee_id__employee_work_info__department_id__department",
"employee_id__employee_work_info__job_role_id__job_role",
"employee_id__employee_work_info__job_position_id__job_position",
"employee_id__employee_work_info__work_type_id__work_type",
"employee_id__employee_work_info__shift_id__employee_shift",
)
)
choice_gender = {
"male": "Male",
"female": "Female",
"other": "Other",
}
STATUS = {
"draft": "Draft",
"review_ongoing": "Review Ongoing",
"confirmed": "Confirmed",
"paid": "Paid",
}
# Fetch pay_head_data separately and map by payslip ID
payslip_ids = [item["id"] for item in data]
pay_head_data_dict = dict(
Payslip.objects.filter(id__in=payslip_ids).values_list(
"id", "pay_head_data"
)
)
data_list = []
for item in data:
# Load pay_head_data for current payslip
pay_head_data = pay_head_data_dict.get(item["id"], {})
# Combine Allowances and Deductions in a single section
all_pay_data = []
# Add Allowances to combined data
for allowance in pay_head_data.get("allowances", []):
all_pay_data.append(
{
"Pay Type": "Allowance",
"Title": allowance["title"],
"Amount": round(float(allowance["amount"] or 0), 2),
}
)
# Add Deductions to combined data
for deduction in pay_head_data.get(
"pretax_deductions", []
) + pay_head_data.get("post_tax_deductions", []):
all_pay_data.append(
{
"Pay Type": "Deduction",
"Title": deduction["title"],
"Amount": round(float(deduction["amount"] or 0), 2),
}
)
# Add combined data to main data list
for pay_item in all_pay_data:
data_list.append(
{
"Employee": f"{item['employee_id__employee_first_name']} {item['employee_id__employee_last_name']}",
"Gender": choice_gender.get(item["employee_id__gender"]),
"Email": item["employee_id__email"],
"Phone": item["employee_id__phone"],
"Department": (
item[
"employee_id__employee_work_info__department_id__department"
]
if item[
"employee_id__employee_work_info__department_id__department"
]
else "-"
),
"Job Position": (
item[
"employee_id__employee_work_info__job_position_id__job_position"
]
if item[
"employee_id__employee_work_info__job_position_id__job_position"
]
else "-"
),
"Job Role": (
item[
"employee_id__employee_work_info__job_role_id__job_role"
]
if item[
"employee_id__employee_work_info__job_role_id__job_role"
]
else "-"
),
"Work Type": (
item[
"employee_id__employee_work_info__work_type_id__work_type"
]
if item[
"employee_id__employee_work_info__work_type_id__work_type"
]
else "-"
),
"Shift": (
item[
"employee_id__employee_work_info__shift_id__employee_shift"
]
if item[
"employee_id__employee_work_info__shift_id__employee_shift"
]
else "-"
),
"Payslip Start Date": item["start_date"],
"Payslip End Date": item["end_date"],
"Allowance & Deduction": pay_item["Pay Type"],
"Allowance & Deduction Title": pay_item["Title"],
"Allowance & Deduction Amount": pay_item["Amount"],
"Status": STATUS.get(item["status"]),
}
)
else:
data_list = []
return JsonResponse(data_list, safe=False)