Merge pull request #31104 from ruchamahabal/emp-adv-status-v13
feat(Employee Advance): add 'Returned' and 'Partly Claimed and Returned'
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
"actions": [],
|
||||
"allow_import": 1,
|
||||
"autoname": "naming_series:",
|
||||
"creation": "2017-10-09 14:26:29.612365",
|
||||
"creation": "2022-01-17 18:36:51.450395",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
@@ -121,7 +121,7 @@
|
||||
"fieldtype": "Select",
|
||||
"label": "Status",
|
||||
"no_copy": 1,
|
||||
"options": "Draft\nPaid\nUnpaid\nClaimed\nCancelled",
|
||||
"options": "Draft\nPaid\nUnpaid\nClaimed\nReturned\nPartly Claimed and Returned\nCancelled",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
@@ -200,7 +200,7 @@
|
||||
],
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2021-09-11 18:38:38.617478",
|
||||
"modified": "2022-05-23 19:33:52.345823",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Employee Advance",
|
||||
@@ -236,5 +236,6 @@
|
||||
"search_fields": "employee,employee_name",
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"title_field": "employee_name",
|
||||
"track_changes": 1
|
||||
}
|
||||
@@ -29,19 +29,43 @@ class EmployeeAdvance(Document):
|
||||
|
||||
def on_cancel(self):
|
||||
self.ignore_linked_doctypes = "GL Entry"
|
||||
self.set_status(update=True)
|
||||
|
||||
def set_status(self, update=False):
|
||||
precision = self.precision("paid_amount")
|
||||
total_amount = flt(flt(self.claimed_amount) + flt(self.return_amount), precision)
|
||||
status = None
|
||||
|
||||
def set_status(self):
|
||||
if self.docstatus == 0:
|
||||
self.status = "Draft"
|
||||
if self.docstatus == 1:
|
||||
if self.claimed_amount and flt(self.claimed_amount) == flt(self.paid_amount):
|
||||
self.status = "Claimed"
|
||||
elif self.paid_amount and self.advance_amount == flt(self.paid_amount):
|
||||
self.status = "Paid"
|
||||
status = "Draft"
|
||||
elif self.docstatus == 1:
|
||||
if flt(self.claimed_amount) > 0 and flt(self.claimed_amount, precision) == flt(
|
||||
self.paid_amount, precision
|
||||
):
|
||||
status = "Claimed"
|
||||
elif flt(self.return_amount) > 0 and flt(self.return_amount, precision) == flt(
|
||||
self.paid_amount, precision
|
||||
):
|
||||
status = "Returned"
|
||||
elif (
|
||||
flt(self.claimed_amount) > 0
|
||||
and (flt(self.return_amount) > 0)
|
||||
and total_amount == flt(self.paid_amount, precision)
|
||||
):
|
||||
status = "Partly Claimed and Returned"
|
||||
elif flt(self.paid_amount) > 0 and flt(self.advance_amount, precision) == flt(
|
||||
self.paid_amount, precision
|
||||
):
|
||||
status = "Paid"
|
||||
else:
|
||||
self.status = "Unpaid"
|
||||
status = "Unpaid"
|
||||
elif self.docstatus == 2:
|
||||
self.status = "Cancelled"
|
||||
status = "Cancelled"
|
||||
|
||||
if update:
|
||||
self.db_set("status", status)
|
||||
else:
|
||||
self.status = status
|
||||
|
||||
def set_total_advance_paid(self):
|
||||
gle = frappe.qb.DocType("GL Entry")
|
||||
@@ -89,8 +113,7 @@ class EmployeeAdvance(Document):
|
||||
|
||||
self.db_set("paid_amount", paid_amount)
|
||||
self.db_set("return_amount", return_amount)
|
||||
self.set_status()
|
||||
frappe.db.set_value("Employee Advance", self.name, "status", self.status)
|
||||
self.set_status(update=True)
|
||||
|
||||
def update_claimed_amount(self):
|
||||
claimed_amount = (
|
||||
@@ -112,8 +135,7 @@ class EmployeeAdvance(Document):
|
||||
|
||||
frappe.db.set_value("Employee Advance", self.name, "claimed_amount", flt(claimed_amount))
|
||||
self.reload()
|
||||
self.set_status()
|
||||
frappe.db.set_value("Employee Advance", self.name, "status", self.status)
|
||||
self.set_status(update=True)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
@@ -265,6 +287,7 @@ def make_return_entry(
|
||||
"party_type": "Employee",
|
||||
"party": employee,
|
||||
"is_advance": "Yes",
|
||||
"cost_center": erpnext.get_default_cost_center(company),
|
||||
},
|
||||
)
|
||||
|
||||
@@ -282,6 +305,7 @@ def make_return_entry(
|
||||
"account_currency": bank_cash_account.account_currency,
|
||||
"account_type": bank_cash_account.account_type,
|
||||
"exchange_rate": flt(exchange_rate) if bank_cash_account.account_currency == currency else 1,
|
||||
"cost_center": erpnext.get_default_cost_center(company),
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
15
erpnext/hr/doctype/employee_advance/employee_advance_list.js
Normal file
15
erpnext/hr/doctype/employee_advance/employee_advance_list.js
Normal file
@@ -0,0 +1,15 @@
|
||||
frappe.listview_settings["Employee Advance"] = {
|
||||
get_indicator: function(doc) {
|
||||
let status_color = {
|
||||
"Draft": "red",
|
||||
"Submitted": "blue",
|
||||
"Cancelled": "red",
|
||||
"Paid": "green",
|
||||
"Unpaid": "orange",
|
||||
"Claimed": "blue",
|
||||
"Returned": "gray",
|
||||
"Partly Claimed and Returned": "yellow"
|
||||
};
|
||||
return [__(doc.status), status_color[doc.status], "status,=,"+doc.status];
|
||||
}
|
||||
};
|
||||
@@ -12,13 +12,21 @@ from erpnext.hr.doctype.employee_advance.employee_advance import (
|
||||
EmployeeAdvanceOverPayment,
|
||||
create_return_through_additional_salary,
|
||||
make_bank_entry,
|
||||
make_return_entry,
|
||||
)
|
||||
from erpnext.hr.doctype.expense_claim.expense_claim import get_advances
|
||||
from erpnext.hr.doctype.expense_claim.test_expense_claim import (
|
||||
get_payable_account,
|
||||
make_expense_claim,
|
||||
)
|
||||
from erpnext.payroll.doctype.salary_component.test_salary_component import create_salary_component
|
||||
from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure
|
||||
|
||||
|
||||
class TestEmployeeAdvance(unittest.TestCase):
|
||||
def setUp(self):
|
||||
frappe.db.delete("Employee Advance")
|
||||
|
||||
def test_paid_amount_and_status(self):
|
||||
employee_name = make_employee("_T@employe.advance")
|
||||
advance = make_employee_advance(employee_name)
|
||||
@@ -53,9 +61,108 @@ class TestEmployeeAdvance(unittest.TestCase):
|
||||
self.assertEqual(advance.paid_amount, 0)
|
||||
self.assertEqual(advance.status, "Unpaid")
|
||||
|
||||
advance.cancel()
|
||||
advance.reload()
|
||||
self.assertEqual(advance.status, "Cancelled")
|
||||
|
||||
def test_claimed_status(self):
|
||||
# CLAIMED Status check, full amount claimed
|
||||
payable_account = get_payable_account("_Test Company")
|
||||
claim = make_expense_claim(
|
||||
payable_account, 1000, 1000, "_Test Company", "Travel Expenses - _TC", do_not_submit=True
|
||||
)
|
||||
|
||||
advance = make_employee_advance(claim.employee)
|
||||
pe = make_payment_entry(advance)
|
||||
pe.submit()
|
||||
|
||||
claim = get_advances_for_claim(claim, advance.name)
|
||||
claim.save()
|
||||
claim.submit()
|
||||
|
||||
advance.reload()
|
||||
self.assertEqual(advance.claimed_amount, 1000)
|
||||
self.assertEqual(advance.status, "Claimed")
|
||||
|
||||
# advance should not be shown in claims
|
||||
advances = get_advances(claim.employee)
|
||||
advances = [entry.name for entry in advances]
|
||||
self.assertTrue(advance.name not in advances)
|
||||
|
||||
# cancel claim; status should be Paid
|
||||
claim.cancel()
|
||||
advance.reload()
|
||||
self.assertEqual(advance.claimed_amount, 0)
|
||||
self.assertEqual(advance.status, "Paid")
|
||||
|
||||
def test_partly_claimed_and_returned_status(self):
|
||||
payable_account = get_payable_account("_Test Company")
|
||||
claim = make_expense_claim(
|
||||
payable_account, 1000, 1000, "_Test Company", "Travel Expenses - _TC", do_not_submit=True
|
||||
)
|
||||
|
||||
advance = make_employee_advance(claim.employee)
|
||||
pe = make_payment_entry(advance)
|
||||
pe.submit()
|
||||
|
||||
# PARTLY CLAIMED AND RETURNED status check
|
||||
# 500 Claimed, 500 Returned
|
||||
claim = make_expense_claim(
|
||||
payable_account, 500, 500, "_Test Company", "Travel Expenses - _TC", do_not_submit=True
|
||||
)
|
||||
|
||||
advance = make_employee_advance(claim.employee)
|
||||
pe = make_payment_entry(advance)
|
||||
pe.submit()
|
||||
|
||||
claim = get_advances_for_claim(claim, advance.name, amount=500)
|
||||
claim.save()
|
||||
claim.submit()
|
||||
|
||||
advance.reload()
|
||||
self.assertEqual(advance.claimed_amount, 500)
|
||||
self.assertEqual(advance.status, "Paid")
|
||||
|
||||
entry = make_return_entry(
|
||||
employee=advance.employee,
|
||||
company=advance.company,
|
||||
employee_advance_name=advance.name,
|
||||
return_amount=flt(advance.paid_amount - advance.claimed_amount),
|
||||
advance_account=advance.advance_account,
|
||||
mode_of_payment=advance.mode_of_payment,
|
||||
currency=advance.currency,
|
||||
exchange_rate=advance.exchange_rate,
|
||||
)
|
||||
|
||||
entry = frappe.get_doc(entry)
|
||||
entry.insert()
|
||||
entry.submit()
|
||||
|
||||
advance.reload()
|
||||
self.assertEqual(advance.return_amount, 500)
|
||||
self.assertEqual(advance.status, "Partly Claimed and Returned")
|
||||
|
||||
# advance should not be shown in claims
|
||||
advances = get_advances(claim.employee)
|
||||
advances = [entry.name for entry in advances]
|
||||
self.assertTrue(advance.name not in advances)
|
||||
|
||||
# Cancel return entry; status should change to PAID
|
||||
entry.cancel()
|
||||
advance.reload()
|
||||
self.assertEqual(advance.return_amount, 0)
|
||||
self.assertEqual(advance.status, "Paid")
|
||||
|
||||
# advance should be shown in claims
|
||||
advances = get_advances(claim.employee)
|
||||
advances = [entry.name for entry in advances]
|
||||
self.assertTrue(advance.name in advances)
|
||||
|
||||
def test_repay_unclaimed_amount_from_salary(self):
|
||||
employee_name = make_employee("_T@employe.advance")
|
||||
advance = make_employee_advance(employee_name, {"repay_unclaimed_amount_from_salary": 1})
|
||||
pe = make_payment_entry(advance)
|
||||
pe.submit()
|
||||
|
||||
args = {"type": "Deduction"}
|
||||
create_salary_component("Advance Salary - Deduction", **args)
|
||||
@@ -85,11 +192,13 @@ class TestEmployeeAdvance(unittest.TestCase):
|
||||
|
||||
advance.reload()
|
||||
self.assertEqual(advance.return_amount, 1000)
|
||||
self.assertEqual(advance.status, "Returned")
|
||||
|
||||
# update advance return amount on additional salary cancellation
|
||||
additional_salary.cancel()
|
||||
advance.reload()
|
||||
self.assertEqual(advance.return_amount, 700)
|
||||
self.assertEqual(advance.status, "Paid")
|
||||
|
||||
def tearDown(self):
|
||||
frappe.db.rollback()
|
||||
|
||||
@@ -171,7 +171,7 @@ frappe.ui.form.on("Expense Claim", {
|
||||
['docstatus', '=', 1],
|
||||
['employee', '=', frm.doc.employee],
|
||||
['paid_amount', '>', 0],
|
||||
['status', '!=', 'Claimed']
|
||||
['status', 'not in', ['Claimed', 'Returned', 'Partly Claimed and Returned']]
|
||||
]
|
||||
};
|
||||
});
|
||||
|
||||
@@ -414,25 +414,27 @@ def get_expense_claim_account(expense_claim_type, company):
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_advances(employee, advance_id=None):
|
||||
advance = frappe.qb.DocType("Employee Advance")
|
||||
|
||||
query = frappe.qb.from_(advance).select(
|
||||
advance.name,
|
||||
advance.posting_date,
|
||||
advance.paid_amount,
|
||||
advance.claimed_amount,
|
||||
advance.advance_account,
|
||||
)
|
||||
|
||||
if not advance_id:
|
||||
condition = "docstatus=1 and employee={0} and paid_amount > 0 and paid_amount > claimed_amount + return_amount".format(
|
||||
frappe.db.escape(employee)
|
||||
query = query.where(
|
||||
(advance.docstatus == 1)
|
||||
& (advance.employee == employee)
|
||||
& (advance.paid_amount > 0)
|
||||
& (advance.status.notin(["Claimed", "Returned", "Partly Claimed and Returned"]))
|
||||
)
|
||||
else:
|
||||
condition = "name={0}".format(frappe.db.escape(advance_id))
|
||||
query = query.where(advance.name == advance_id)
|
||||
|
||||
return frappe.db.sql(
|
||||
"""
|
||||
select
|
||||
name, posting_date, paid_amount, claimed_amount, advance_account
|
||||
from
|
||||
`tabEmployee Advance`
|
||||
where {0}
|
||||
""".format(
|
||||
condition
|
||||
),
|
||||
as_dict=1,
|
||||
)
|
||||
return query.run(as_dict=True)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
|
||||
@@ -366,3 +366,5 @@ erpnext.patches.v13_0.education_deprecation_warning
|
||||
erpnext.patches.v13_0.requeue_recoverable_reposts
|
||||
erpnext.patches.v13_0.create_accounting_dimensions_in_orders
|
||||
erpnext.patches.v13_0.set_per_billed_in_return_delivery_note
|
||||
erpnext.patches.v13_0.update_employee_advance_status
|
||||
|
||||
|
||||
29
erpnext/patches/v13_0/update_employee_advance_status.py
Normal file
29
erpnext/patches/v13_0/update_employee_advance_status.py
Normal file
@@ -0,0 +1,29 @@
|
||||
import frappe
|
||||
|
||||
|
||||
def execute():
|
||||
frappe.reload_doc("hr", "doctype", "employee_advance")
|
||||
|
||||
advance = frappe.qb.DocType("Employee Advance")
|
||||
(
|
||||
frappe.qb.update(advance)
|
||||
.set(advance.status, "Returned")
|
||||
.where(
|
||||
(advance.docstatus == 1)
|
||||
& ((advance.return_amount) & (advance.paid_amount == advance.return_amount))
|
||||
& (advance.status == "Paid")
|
||||
)
|
||||
).run()
|
||||
|
||||
(
|
||||
frappe.qb.update(advance)
|
||||
.set(advance.status, "Partly Claimed and Returned")
|
||||
.where(
|
||||
(advance.docstatus == 1)
|
||||
& (
|
||||
(advance.claimed_amount & advance.return_amount)
|
||||
& (advance.paid_amount == (advance.return_amount + advance.claimed_amount))
|
||||
)
|
||||
& (advance.status == "Paid")
|
||||
)
|
||||
).run()
|
||||
@@ -124,6 +124,8 @@ class AdditionalSalary(Document):
|
||||
return_amount += self.amount
|
||||
|
||||
frappe.db.set_value("Employee Advance", self.ref_docname, "return_amount", return_amount)
|
||||
advance = frappe.get_doc("Employee Advance", self.ref_docname)
|
||||
advance.set_status(update=True)
|
||||
|
||||
def update_employee_referral(self, cancel=False):
|
||||
if self.ref_doctype == "Employee Referral":
|
||||
|
||||
Reference in New Issue
Block a user