Merge pull request #29335 from ruchamahabal/emp-adv-status
This commit is contained in:
commit
e874149b94
@ -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-01-17 19:33:52.345823",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Employee Advance",
|
||||
@ -237,5 +237,41 @@
|
||||
"search_fields": "employee,employee_name",
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"states": [
|
||||
{
|
||||
"color": "Red",
|
||||
"custom": 1,
|
||||
"title": "Draft"
|
||||
},
|
||||
{
|
||||
"color": "Green",
|
||||
"custom": 1,
|
||||
"title": "Paid"
|
||||
},
|
||||
{
|
||||
"color": "Orange",
|
||||
"custom": 1,
|
||||
"title": "Unpaid"
|
||||
},
|
||||
{
|
||||
"color": "Blue",
|
||||
"custom": 1,
|
||||
"title": "Claimed"
|
||||
},
|
||||
{
|
||||
"color": "Gray",
|
||||
"title": "Returned"
|
||||
},
|
||||
{
|
||||
"color": "Yellow",
|
||||
"title": "Partly Claimed and Returned"
|
||||
},
|
||||
{
|
||||
"color": "Red",
|
||||
"custom": 1,
|
||||
"title": "Cancelled"
|
||||
}
|
||||
],
|
||||
"title_field": "employee_name",
|
||||
"track_changes": 1
|
||||
}
|
@ -27,19 +27,33 @@ 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")
|
||||
@ -85,9 +99,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 = frappe.db.sql("""
|
||||
@ -103,8 +115,8 @@ 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()
|
||||
def get_pending_amount(employee, posting_date):
|
||||
@ -222,7 +234,8 @@ def make_return_entry(employee, company, employee_advance_name, return_amount,
|
||||
'reference_name': employee_advance_name,
|
||||
'party_type': 'Employee',
|
||||
'party': employee,
|
||||
'is_advance': 'Yes'
|
||||
'is_advance': 'Yes',
|
||||
'cost_center': erpnext.get_default_cost_center(company)
|
||||
})
|
||||
|
||||
bank_amount = flt(return_amount) if bank_cash_account.account_currency==currency \
|
||||
@ -233,7 +246,8 @@ def make_return_entry(employee, company, employee_advance_name, return_amount,
|
||||
"debit_in_account_currency": bank_amount,
|
||||
"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
|
||||
"exchange_rate": flt(exchange_rate) if bank_cash_account.account_currency == currency else 1,
|
||||
"cost_center": erpnext.get_default_cost_center(company)
|
||||
})
|
||||
|
||||
return je.as_dict()
|
||||
|
@ -4,7 +4,7 @@
|
||||
import unittest
|
||||
|
||||
import frappe
|
||||
from frappe.utils import nowdate
|
||||
from frappe.utils import flt, nowdate
|
||||
|
||||
import erpnext
|
||||
from erpnext.hr.doctype.employee.test_employee import make_employee
|
||||
@ -12,12 +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)
|
||||
@ -52,9 +61,102 @@ 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)
|
||||
@ -82,11 +184,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()
|
||||
@ -118,3 +222,24 @@ def make_employee_advance(employee_name, args=None):
|
||||
doc.submit()
|
||||
|
||||
return doc
|
||||
|
||||
|
||||
def get_advances_for_claim(claim, advance_name, amount=None):
|
||||
advances = get_advances(claim.employee, advance_name)
|
||||
|
||||
for entry in advances:
|
||||
if amount:
|
||||
allocated_amount = amount
|
||||
else:
|
||||
allocated_amount = flt(entry.paid_amount) - flt(entry.claimed_amount)
|
||||
|
||||
claim.append("advances", {
|
||||
"employee_advance": entry.name,
|
||||
"posting_date": entry.posting_date,
|
||||
"advance_account": entry.advance_account,
|
||||
"advance_paid": entry.paid_amount,
|
||||
"unclaimed_amount": allocated_amount,
|
||||
"allocated_amount": allocated_amount
|
||||
})
|
||||
|
||||
return claim
|
@ -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']]
|
||||
]
|
||||
};
|
||||
});
|
||||
|
@ -23,10 +23,10 @@ class ExpenseClaim(AccountsController):
|
||||
|
||||
def validate(self):
|
||||
validate_active_employee(self.employee)
|
||||
self.validate_advances()
|
||||
set_employee_name(self)
|
||||
self.validate_sanctioned_amount()
|
||||
self.calculate_total_amount()
|
||||
set_employee_name(self)
|
||||
self.validate_advances()
|
||||
self.set_expense_account(validate=True)
|
||||
self.set_payable_account()
|
||||
self.set_cost_center()
|
||||
@ -341,18 +341,27 @@ def get_expense_claim_account(expense_claim_type, company):
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_advances(employee, advance_id=None):
|
||||
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))
|
||||
else:
|
||||
condition = 'name={0}'.format(frappe.db.escape(advance_id))
|
||||
advance = frappe.qb.DocType("Employee Advance")
|
||||
|
||||
return frappe.db.sql("""
|
||||
select
|
||||
name, posting_date, paid_amount, claimed_amount, advance_account
|
||||
from
|
||||
`tabEmployee Advance`
|
||||
where {0}
|
||||
""".format(condition), as_dict=1)
|
||||
query = (
|
||||
frappe.qb.from_(advance)
|
||||
.select(
|
||||
advance.name, advance.posting_date, advance.paid_amount,
|
||||
advance.claimed_amount, advance.advance_account
|
||||
)
|
||||
)
|
||||
|
||||
if not advance_id:
|
||||
query = query.where(
|
||||
(advance.docstatus == 1)
|
||||
& (advance.employee == employee)
|
||||
& (advance.paid_amount > 0)
|
||||
& (advance.status.notin(["Claimed", "Returned", "Partly Claimed and Returned"]))
|
||||
)
|
||||
else:
|
||||
query = query.where(advance.name == advance_id)
|
||||
|
||||
return query.run(as_dict=True)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
|
@ -356,4 +356,5 @@ erpnext.patches.v14_0.delete_amazon_mws_doctype
|
||||
erpnext.patches.v13_0.set_work_order_qty_in_so_from_mr
|
||||
erpnext.patches.v13_0.update_accounts_in_loan_docs
|
||||
erpnext.patches.v14_0.update_batch_valuation_flag
|
||||
erpnext.patches.v14_0.delete_non_profit_doctypes
|
||||
erpnext.patches.v14_0.delete_non_profit_doctypes
|
||||
erpnext.patches.v14_0.update_employee_advance_status
|
26
erpnext/patches/v14_0/update_employee_advance_status.py
Normal file
26
erpnext/patches/v14_0/update_employee_advance_status.py
Normal file
@ -0,0 +1,26 @@
|
||||
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()
|
@ -105,6 +105,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":
|
||||
|
Loading…
x
Reference in New Issue
Block a user