Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions frontend/src/components/EmployeeAdvanceItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ const props = defineProps({

const colorMap = {
Paid: "green",
"Partially Paid": "blue",
Unpaid: "orange",
Claimed: "blue",
Returned: "gray",
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/views/employee_advance/List.vue
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,13 @@ const EMPLOYEE_ADVANCE_FIELDS = [
const STATUS_FILTER_OPTIONS = [
"Draft",
"Paid",
"Partially Paid",
"Unpaid",
"Claimed",
"Returned",
"Partly Claimed and Returned",
"Cancelled",
] // __("Draft"), __("Paid"), __("Unpaid"), __("Claimed"), __("Returned"), __("Partly Claimed and Returned"), __("Cancelled")
] // __("Draft"), __("Paid"), __("Partially Paid"), __("Unpaid"), __("Claimed"), __("Returned"), __("Partly Claimed and Returned"), __("Cancelled")
const FILTER_CONFIG = [
{
fieldname: "status",
Expand Down
2 changes: 1 addition & 1 deletion hrms/api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -660,7 +660,7 @@ def get_employee_advance_balance() -> list[dict]:
& (Advance.paid_amount)
& (Advance.employee == employee)
# don't need claimed & returned advances, only partly or completely paid ones
& (Advance.status.isin(["Paid", "Unpaid"]))
& (Advance.status.isin(["Paid", "Partially Paid", "Unpaid"]))
)
.orderby(Advance.posting_date, order=Order.desc)
).run(as_dict=True)
Expand Down
4 changes: 3 additions & 1 deletion hrms/hr/doctype/employee_advance/employee_advance.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ frappe.ui.form.on("Employee Advance", {
},
__("Create"),
);
} else if (
}

if (
frm.doc.docstatus === 1 &&
flt(frm.doc.claimed_amount) < flt(frm.doc.paid_amount) - flt(frm.doc.return_amount) &&
frappe.model.can_create("Expense Claim")
Expand Down
6 changes: 5 additions & 1 deletion hrms/hr/doctype/employee_advance/employee_advance.json
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@
"fieldtype": "Select",
"label": "Status",
"no_copy": 1,
"options": "Draft\nPaid\nUnpaid\nClaimed\nReturned\nPartly Claimed and Returned\nCancelled",
"options": "Draft\nPaid\nPartially Paid\nUnpaid\nClaimed\nReturned\nPartly Claimed and Returned\nCancelled",
"read_only": 1
},
{
Expand Down Expand Up @@ -291,6 +291,10 @@
"color": "Green",
"title": "Paid"
},
{
"color": "Blue",
"title": "Partially Paid"
},
{
"color": "Orange",
"title": "Unpaid"
Expand Down
28 changes: 21 additions & 7 deletions hrms/hr/doctype/employee_advance/employee_advance.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,14 @@ class EmployeeAdvance(Document):
repay_unclaimed_amount_from_salary: DF.Check
return_amount: DF.Currency
status: DF.Literal[
"Draft", "Paid", "Unpaid", "Claimed", "Returned", "Partly Claimed and Returned", "Cancelled"
"Draft",
"Paid",
"Partially Paid",
"Unpaid",
"Claimed",
"Returned",
"Partly Claimed and Returned",
"Cancelled",
]
# end: auto-generated types

Expand Down Expand Up @@ -149,11 +156,12 @@ def set_status(self, update=False):
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)
or (self.paid_amount and self.repay_unclaimed_amount_from_salary)
elif flt(self.paid_amount) > 0 and flt(self.advance_amount, precision) == flt(
self.paid_amount, precision
):
status = "Paid"
elif flt(self.paid_amount) > 0:
status = "Partially Paid"
else:
status = "Unpaid"
elif self.docstatus == 2:
Expand Down Expand Up @@ -233,6 +241,7 @@ def set_total_advance_paid(self):
self.db_set("paid_amount", paid_amount)
self.db_set("return_amount", return_amount)
self.set_status(update=True)
self.set_pending_amount(update=True)

base_paid_amount = aple_paid_amount.get("base_paid_amount") or 0
self.db_set("base_paid_amount", base_paid_amount)
Expand All @@ -257,19 +266,24 @@ def update_claimed_amount(self):
self.reload()
self.set_status(update=True)

def set_pending_amount(self):
def set_pending_amount(self, update=False):
Advance = frappe.qb.DocType("Employee Advance")
self.pending_amount = (
pending_amount = (
frappe.qb.from_(Advance)
.select(Sum(Advance.advance_amount - Advance.paid_amount))
.where(
(Advance.employee == self.employee)
& (Advance.docstatus == 1)
& (Advance.posting_date <= self.posting_date)
& (Advance.status == "Unpaid")
& (Advance.status.isin(["Unpaid", "Partially Paid"]))
)
).run()[0][0] or 0.0

if update:
self.db_set("pending_amount", pending_amount)
else:
self.pending_amount = pending_amount

def check_linked_payment_entry(self):
from erpnext.accounts.utils import (
remove_ref_doc_link_from_pe,
Expand Down
56 changes: 50 additions & 6 deletions hrms/hr/doctype/employee_advance/test_employee_advance.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
get_same_currency_bank_cash_account,
make_return_entry,
)
from hrms.hr.doctype.expense_claim.expense_claim import get_advances
from hrms.hr.doctype.expense_claim.expense_claim import get_advances, get_expense_claim
from hrms.hr.doctype.expense_claim.test_expense_claim import (
get_payable_account,
make_expense_claim,
Expand Down Expand Up @@ -206,7 +206,7 @@ def test_payment_entry_against_advance(self):

pe = make_payment_entry(advance, 700)
advance.reload()
self.assertEqual(advance.status, "Unpaid")
self.assertEqual(advance.status, "Partially Paid")
self.assertEqual(advance.paid_amount, 700)

pe = make_payment_entry(advance, 300)
Expand All @@ -216,9 +216,53 @@ def test_payment_entry_against_advance(self):

pe.cancel()
advance.reload()
self.assertEqual(advance.status, "Unpaid")
self.assertEqual(advance.status, "Partially Paid")
self.assertEqual(advance.paid_amount, 700)

def test_expense_claim_against_partially_paid_advance(self):
employee_name = make_employee("_T@employee.advance", "_Test Company")
advance = make_employee_advance(employee_name)
make_payment_entry(advance, 700)
advance.reload()

self.assertEqual(advance.status, "Partially Paid")

currency, cost_center = frappe.db.get_value(
"Company", "_Test Company", ["default_currency", "cost_center"]
)
claim = get_expense_claim(advance.name) # create claim from employee advance form
claim.update(
{
"payable_account": get_payable_account("_Test Company"),
"currency": currency,
"exchange_rate": 1,
"approval_status": "Approved",
}
)
claim.append(
"expenses",
{
"expense_type": "Travel",
"default_account": "Travel Expenses - _TC",
"amount": 1000,
"sanctioned_amount": 1000,
"cost_center": cost_center,
},
)
claim.save()
claim.submit()

self.assertEqual(len(claim.advances), 1)
self.assertEqual(claim.advances[0].employee_advance, advance.name)
self.assertEqual(claim.advances[0].advance_paid, 700)
self.assertEqual(claim.advances[0].allocated_amount, 700)
self.assertEqual(claim.total_claimed_amount, 1000)
self.assertEqual(claim.grand_total, 300)

advance.reload()
self.assertEqual(advance.claimed_amount, 700)
self.assertEqual(advance.status, "Claimed")

def test_precision(self):
employee_name = make_employee("_T@employee.advance", "_Test Company")
advance = make_employee_advance(employee_name)
Expand Down Expand Up @@ -261,6 +305,8 @@ def test_pending_amount(self):

advance1 = make_employee_advance(employee_name)
make_payment_entry(advance1, 500)
advance1.reload()
self.assertEqual(advance1.status, "Partially Paid")

advance2 = make_employee_advance(employee_name)
# 1000 - 500
Expand Down Expand Up @@ -412,12 +458,10 @@ def manual_journal_entry_for_advance(advance) -> dict:
def make_payment_entry(advance, amount=None):
from hrms.overrides.employee_payment_entry import get_payment_entry_for_employee

payment_entry = get_payment_entry_for_employee(advance.doctype, advance.name)
payment_entry = get_payment_entry_for_employee(advance.doctype, advance.name, party_amount=amount)

payment_entry.reference_no = "1"
payment_entry.reference_date = nowdate()
if amount:
payment_entry.references[0].allocated_amount = amount
payment_entry.submit()
return payment_entry

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ frappe.query_reports["Employee Advance Summary"] = {
fieldname: "status",
label: __("Status"),
fieldtype: "Select",
options: "\nDraft\nPaid\nUnpaid\nClaimed\nCancelled",
options: "\nDraft\nPaid\nPartially Paid\nUnpaid\nClaimed\nCancelled",
},
],
};
1 change: 1 addition & 0 deletions hrms/patches.txt
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,4 @@ hrms.patches.v16_0.set_currency_and_base_fields_in_expense_claim
hrms.patches.v16_0.remove_ess_user_type_limit
hrms.patches.v16_0.add_default_hr_role_permissions #2026-05-28
hrms.patches.v16_0.set_reference_fields_in_expense_claim_advance
hrms.patches.v16_0.update_partially_paid_employee_advance_status
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import frappe


def execute():
frappe.reload_doc("hr", "doctype", "employee_advance")

advance = frappe.qb.DocType("Employee Advance")
(
frappe.qb.update(advance)
.set(advance.status, "Partially Paid")
.where(
(advance.docstatus == 1)
& (advance.paid_amount > 0)
& (advance.paid_amount < advance.advance_amount)
& (advance.status == "Unpaid")
)
).run()
9 changes: 5 additions & 4 deletions hrms/public/js/erpnext/payment_entry.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,11 @@ frappe.ui.form.on("Payment Entry", {
filters["is_paid"] = 0;
}

if (
child.reference_doctype == "Employee Advance" ||
child.reference_doctype == "Leave Encashment"
) {
if (child.reference_doctype == "Employee Advance") {
filters["status"] = ["in", ["Unpaid", "Partially Paid"]];
}

if (child.reference_doctype == "Leave Encashment") {
filters["status"] = "Unpaid";
}

Expand Down
Loading