[UPDT] PAYROLL: User level acccess for reimbursement and encashment feature
This commit is contained in:
@@ -18,6 +18,7 @@ from payroll.models.models import (
|
|||||||
Deduction,
|
Deduction,
|
||||||
FilingStatus,
|
FilingStatus,
|
||||||
LoanAccount,
|
LoanAccount,
|
||||||
|
Reimbursement,
|
||||||
)
|
)
|
||||||
from payroll.models.models import Payslip
|
from payroll.models.models import Payslip
|
||||||
|
|
||||||
@@ -319,6 +320,26 @@ class LoanAccountFilter(FilterSet):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class ReimbursementFilter(FilterSet):
|
||||||
|
"""
|
||||||
|
ReimbursementFilter
|
||||||
|
"""
|
||||||
|
|
||||||
|
search = django_filters.CharFilter(field_name="title", lookup_expr="icontains")
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Reimbursement
|
||||||
|
fields = [
|
||||||
|
"status",
|
||||||
|
"type",
|
||||||
|
"employee_id",
|
||||||
|
"approved_by",
|
||||||
|
"employee_id__employee_work_info__department_id",
|
||||||
|
"employee_id__employee_work_info__job_position_id",
|
||||||
|
"employee_id__employee_work_info__reporting_manager_id",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class ContractReGroup:
|
class ContractReGroup:
|
||||||
"""
|
"""
|
||||||
Class to keep the field name for group by option
|
Class to keep the field name for group by option
|
||||||
|
|||||||
@@ -2,15 +2,15 @@
|
|||||||
<div class="d-flex flex-row-reverse oh-wrapper">
|
<div class="d-flex flex-row-reverse oh-wrapper">
|
||||||
<span class="mb-3" onclick="$('[name=type]').val('fine').change(); $('.filterButton').click()" style="cursor: pointer; margin-right: 15px;">
|
<span class="mb-3" onclick="$('[name=type]').val('fine').change(); $('.filterButton').click()" style="cursor: pointer; margin-right: 15px;">
|
||||||
<span class="oh-dot oh-dot--small me-1" style="background-color:red"></span>
|
<span class="oh-dot oh-dot--small me-1" style="background-color:red"></span>
|
||||||
Fine
|
{% trans "Fine" %}
|
||||||
</span>
|
</span>
|
||||||
<span class="mb-3" onclick="$('[name=type]').val('loan').change(); $('.filterButton').click()" style="cursor: pointer; margin-right: 15px;">
|
<span class="mb-3" onclick="$('[name=type]').val('loan').change(); $('.filterButton').click()" style="cursor: pointer; margin-right: 15px;">
|
||||||
<span class="oh-dot oh-dot--small me-1" style="background-color:rgba(128, 128, 128, 0.482)"></span>
|
<span class="oh-dot oh-dot--small me-1" style="background-color:rgba(128, 128, 128, 0.482)"></span>
|
||||||
Loan
|
{% trans "Loan" %}
|
||||||
</span>
|
</span>
|
||||||
<span class="mb-3" onclick="$('[name=type]').val('advanced_salary').change();$('.filterButton').click()" style="cursor: pointer; margin-right: 15px;">
|
<span class="mb-3" onclick="$('[name=type]').val('advanced_salary').change();$('.filterButton').click()" style="cursor: pointer; margin-right: 15px;">
|
||||||
<span class="oh-dot oh-dot--small me-1" style="background-color:yellowgreen"></span>
|
<span class="oh-dot oh-dot--small me-1" style="background-color:yellowgreen"></span>
|
||||||
Advanced Salary
|
{% trans "Advanced Salary" %}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="oh-wrapper oh-faq-cards">
|
<div class="oh-wrapper oh-faq-cards">
|
||||||
|
|||||||
@@ -18,12 +18,20 @@ import pandas as pd
|
|||||||
from asset.models import Asset
|
from asset.models import Asset
|
||||||
from base.models import Company
|
from base.models import Company
|
||||||
from employee.models import Employee, EmployeeWorkInformation
|
from employee.models import Employee, EmployeeWorkInformation
|
||||||
from horilla.decorators import login_required, permission_required
|
from horilla.decorators import login_required, owner_can_enter, permission_required
|
||||||
from horilla.settings import EMAIL_HOST_USER
|
from horilla.settings import EMAIL_HOST_USER
|
||||||
from base.methods import get_key_instances
|
from base.methods import filter_own_recodes, get_key_instances
|
||||||
from base.methods import closest_numbers
|
from base.methods import closest_numbers
|
||||||
|
from leave.models import AvailableLeave
|
||||||
import payroll.models.models
|
import payroll.models.models
|
||||||
from payroll.models.models import Allowance, Deduction, LoanAccount, Payslip
|
from payroll.models.models import (
|
||||||
|
Allowance,
|
||||||
|
Deduction,
|
||||||
|
LoanAccount,
|
||||||
|
Payslip,
|
||||||
|
Reimbursement,
|
||||||
|
ReimbursementMultipleAttachment,
|
||||||
|
)
|
||||||
from payroll.methods.payslip_calc import (
|
from payroll.methods.payslip_calc import (
|
||||||
calculate_allowance,
|
calculate_allowance,
|
||||||
calculate_gross_pay,
|
calculate_gross_pay,
|
||||||
@@ -39,6 +47,7 @@ from payroll.filters import (
|
|||||||
DeductionFilter,
|
DeductionFilter,
|
||||||
LoanAccountFilter,
|
LoanAccountFilter,
|
||||||
PayslipFilter,
|
PayslipFilter,
|
||||||
|
ReimbursementFilter,
|
||||||
)
|
)
|
||||||
from payroll.forms import component_forms as forms
|
from payroll.forms import component_forms as forms
|
||||||
from payroll.methods.payslip_calc import (
|
from payroll.methods.payslip_calc import (
|
||||||
@@ -188,8 +197,8 @@ def payroll_calculation(employee, start_date, end_date):
|
|||||||
"gross_pay": gross_pay,
|
"gross_pay": gross_pay,
|
||||||
"contract_wage": contract_wage,
|
"contract_wage": contract_wage,
|
||||||
"basic_pay": basic_pay,
|
"basic_pay": basic_pay,
|
||||||
"paid_days":paid_days,
|
"paid_days": paid_days,
|
||||||
"unpaid_days":unpaid_days,
|
"unpaid_days": unpaid_days,
|
||||||
"taxable_gross_pay": taxable_gross_pay,
|
"taxable_gross_pay": taxable_gross_pay,
|
||||||
"basic_pay_deductions": basic_pay_deductions,
|
"basic_pay_deductions": basic_pay_deductions,
|
||||||
"gross_pay_deductions": gross_pay_deductions,
|
"gross_pay_deductions": gross_pay_deductions,
|
||||||
@@ -940,7 +949,7 @@ def delete_loan(request):
|
|||||||
Delete loan
|
Delete loan
|
||||||
"""
|
"""
|
||||||
ids = request.GET.getlist("ids")
|
ids = request.GET.getlist("ids")
|
||||||
loans = LoanAccount.objects.filter(id__in=ids,settled=False)
|
loans = LoanAccount.objects.filter(id__in=ids, settled=False)
|
||||||
# This 👇 would'nt trigger the delete method in the model
|
# This 👇 would'nt trigger the delete method in the model
|
||||||
# loans.delete()
|
# loans.delete()
|
||||||
for loan in loans:
|
for loan in loans:
|
||||||
@@ -988,9 +997,150 @@ def asset_fine(request):
|
|||||||
instance.provided_date = date.today()
|
instance.provided_date = date.today()
|
||||||
instance.asset_id = asset
|
instance.asset_id = asset
|
||||||
instance.save()
|
instance.save()
|
||||||
messages.success(request,"Asset fine added")
|
messages.success(request, "Asset fine added")
|
||||||
return render(
|
return render(
|
||||||
request,
|
request,
|
||||||
"payroll/asset_fine/form.html",
|
"payroll/asset_fine/form.html",
|
||||||
{"form": form, "asset_id": asset_id, "employee_id": employee_id},
|
{"form": form, "asset_id": asset_id, "employee_id": employee_id},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def view_reimbursement(request):
|
||||||
|
"""
|
||||||
|
This method is used to render template to view reimbursements
|
||||||
|
"""
|
||||||
|
filter_object = ReimbursementFilter({"status": "requested"})
|
||||||
|
requests = filter_own_recodes(
|
||||||
|
request, filter_object.qs, "payroll.view_reimbursement"
|
||||||
|
)
|
||||||
|
data_dict = {"status": ["requested"]}
|
||||||
|
|
||||||
|
return render(
|
||||||
|
request,
|
||||||
|
"payroll/reimbursement/view_reimbursement.html",
|
||||||
|
{
|
||||||
|
"requests": paginator_qry(requests, request.GET.get("page")),
|
||||||
|
"f": filter_object,
|
||||||
|
"pd":request.GET.urlencode(),
|
||||||
|
"filter_dict": data_dict,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def create_reimbursement(request):
|
||||||
|
"""
|
||||||
|
This method is used to create reimbursement
|
||||||
|
"""
|
||||||
|
instance_id = eval(str(request.GET.get("instance_id")))
|
||||||
|
instance = None
|
||||||
|
if instance_id:
|
||||||
|
instance = Reimbursement.objects.filter(id=instance_id).first()
|
||||||
|
form = forms.ReimbursementForm(instance=instance)
|
||||||
|
if request.method == "POST":
|
||||||
|
form = forms.ReimbursementForm(request.POST, request.FILES, instance=instance)
|
||||||
|
if form.is_valid():
|
||||||
|
form.save()
|
||||||
|
messages.success(request, "Reimbursent saved successfully")
|
||||||
|
return HttpResponse("<script>window.location.reload()</script>")
|
||||||
|
return render(request, "payroll/reimbursement/form.html", {"form": form})
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def search_reimbursement(request):
|
||||||
|
"""
|
||||||
|
This method is used to search/filter reimbursement
|
||||||
|
"""
|
||||||
|
requests = ReimbursementFilter(request.GET).qs
|
||||||
|
requests = filter_own_recodes(request, requests, "payroll.view_reimbursement")
|
||||||
|
data_dict = parse_qs(request.GET.urlencode())
|
||||||
|
return render(
|
||||||
|
request,
|
||||||
|
"payroll/reimbursement/request_cards.html",
|
||||||
|
{
|
||||||
|
"requests": paginator_qry(requests, request.GET.get("page")),
|
||||||
|
"filter_dict": data_dict,
|
||||||
|
"pd": request.GET.urlencode(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def get_assigned_leaves(request):
|
||||||
|
"""
|
||||||
|
This method is used to return assigned leaves of the employee
|
||||||
|
in Json
|
||||||
|
"""
|
||||||
|
assigned_leaves = (
|
||||||
|
AvailableLeave.objects.filter(
|
||||||
|
employee_id__id=request.GET["employeeId"], total_leave_days__gte=1
|
||||||
|
)
|
||||||
|
.values("leave_type_id__name", "available_days", "carryforward_days")
|
||||||
|
.distinct()
|
||||||
|
)
|
||||||
|
return JsonResponse(list(assigned_leaves), safe=False)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@permission_required("payroll.change_reimbursement")
|
||||||
|
def approve_reimbursements(request):
|
||||||
|
"""
|
||||||
|
This method is used to approve or reject the reimbursement request
|
||||||
|
"""
|
||||||
|
ids = request.GET.getlist("ids")
|
||||||
|
status = request.GET["status"]
|
||||||
|
amount = eval(request.GET.get("amount")) if request.GET.get("amount") else 0
|
||||||
|
amount = max(0, amount)
|
||||||
|
reimbursements = Reimbursement.objects.filter(id__in=ids)
|
||||||
|
if status and len(status):
|
||||||
|
for reimbursement in reimbursements:
|
||||||
|
if reimbursement.type == "leave_encashment":
|
||||||
|
reimbursement.amount = amount
|
||||||
|
reimbursement.status = status
|
||||||
|
reimbursement.save()
|
||||||
|
messages.success(
|
||||||
|
request, f"Request {reimbursement.get_status_display()} succesfully"
|
||||||
|
)
|
||||||
|
return redirect(view_reimbursement)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@permission_required("payroll.delete_reimbursement")
|
||||||
|
def delete_reimbursements(request):
|
||||||
|
"""
|
||||||
|
This method is used to delete the reimbursements
|
||||||
|
"""
|
||||||
|
ids = request.GET.getlist("ids")
|
||||||
|
reimbursements = Reimbursement.objects.filter(id__in=ids)
|
||||||
|
for reimbursement in reimbursements:
|
||||||
|
reimbursement.delete()
|
||||||
|
messages.success(request, "Reimbursements deleted")
|
||||||
|
|
||||||
|
return redirect(view_reimbursement)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@owner_can_enter("payroll.view_reimbursement", Reimbursement, True)
|
||||||
|
def reimbursement_attachments(request, instance_id):
|
||||||
|
"""
|
||||||
|
This method is used to render all the attachements under the reimbursement object
|
||||||
|
"""
|
||||||
|
reimbursement = Reimbursement.objects.get(id=instance_id)
|
||||||
|
return render(
|
||||||
|
request,
|
||||||
|
"payroll/reimbursement/attachments.html",
|
||||||
|
{"reimbursement": reimbursement},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@owner_can_enter("payroll.delete_reimbursement", Reimbursement, True)
|
||||||
|
def delete_attachments(request, _reimbursement_id):
|
||||||
|
"""
|
||||||
|
This mehtod is used to delete the attachements
|
||||||
|
"""
|
||||||
|
ids = request.GET.getlist("ids")
|
||||||
|
ReimbursementMultipleAttachment.objects.filter(id__in=ids).delete()
|
||||||
|
messages.success(request, "Attachment deleted")
|
||||||
|
return redirect(view_reimbursement)
|
||||||
|
|||||||
Reference in New Issue
Block a user