Revert "Advance against expense claim (#10632)" (#10877)

This reverts commit cdd6ded790.
This commit is contained in:
Nabin Hait
2017-09-25 11:27:39 +05:30
committed by GitHub
parent 4ebac3380d
commit 3b61552836
14 changed files with 1676 additions and 1986 deletions

View File

@@ -8,7 +8,7 @@ from frappe import msgprint, _, scrub
from erpnext.controllers.accounts_controller import AccountsController from erpnext.controllers.accounts_controller import AccountsController
from erpnext.accounts.utils import get_balance_on, get_account_currency from erpnext.accounts.utils import get_balance_on, get_account_currency
from erpnext.accounts.party import get_party_account from erpnext.accounts.party import get_party_account
from erpnext.hr.doctype.expense_claim.expense_claim import update_paid_amount from erpnext.hr.doctype.expense_claim.expense_claim import update_reimbursed_amount
from erpnext.hr.doctype.employee_loan.employee_loan import update_disbursement_status from erpnext.hr.doctype.employee_loan.employee_loan import update_disbursement_status
class JournalEntry(AccountsController): class JournalEntry(AccountsController):
@@ -215,8 +215,8 @@ class JournalEntry(AccountsController):
if d.reference_type in ("Sales Invoice", "Purchase Invoice"): if d.reference_type in ("Sales Invoice", "Purchase Invoice"):
if (against_voucher[0] != d.party or against_voucher[1] != d.account): if (against_voucher[0] != d.party or against_voucher[1] != d.account):
frappe.throw(_("Row {0}: Party / Account does not match with {1} / {2} in {3} {4}") frappe.throw(_("Row {0}: Party / Account does not match with {1} / {2} in {3} {4}")
.format(d.idx, field_dict.get(d.reference_type)[0], .format(d.idx, field_dict.get(d.reference_type)[0], field_dict.get(d.reference_type)[1],
field_dict.get(d.reference_type)[1], d.reference_type, d.reference_name)) d.reference_type, d.reference_name))
# check if party matches for Sales / Purchase Order # check if party matches for Sales / Purchase Order
if d.reference_type in ("Sales Order", "Purchase Order"): if d.reference_type in ("Sales Order", "Purchase Order"):
@@ -225,17 +225,6 @@ class JournalEntry(AccountsController):
frappe.throw(_("Row {0}: {1} {2} does not match with {3}") \ frappe.throw(_("Row {0}: {1} {2} does not match with {3}") \
.format(d.idx, d.party_type, d.party, d.reference_type)) .format(d.idx, d.party_type, d.party, d.reference_type))
if d.reference_type == "Expense Claim":
ref_doc = frappe.get_doc("Expense Claim", d.reference_name)
if ref_doc.employee != d.party:
frappe.throw(_("Row {0}# Party must be {1}, same as Expense Claim {2}")
.format(d.idx, ref_doc.employee, d.reference_name))
account_field = "advance_account" if ref_doc.docstatus==0 else "payable_account"
if ref_doc.get(account_field) != d.account:
frappe.throw(_("Row {0}# Account must be {1}, same as Expense Claim {2}")
.format(d.idx, ref_doc.get(account_field), d.reference_name))
self.validate_orders() self.validate_orders()
self.validate_invoices() self.validate_invoices()
@@ -527,7 +516,7 @@ class JournalEntry(AccountsController):
for d in self.accounts: for d in self.accounts:
if d.reference_type=="Expense Claim" and d.reference_name: if d.reference_type=="Expense Claim" and d.reference_name:
doc = frappe.get_doc("Expense Claim", d.reference_name) doc = frappe.get_doc("Expense Claim", d.reference_name)
update_paid_amount(doc, d.account) update_reimbursed_amount(doc)
def update_employee_loan(self): def update_employee_loan(self):
for d in self.accounts: for d in self.accounts:

View File

@@ -12,7 +12,7 @@ from erpnext.accounts.doctype.journal_entry.journal_entry \
import get_average_exchange_rate, get_default_bank_cash_account import get_average_exchange_rate, get_default_bank_cash_account
from erpnext.setup.utils import get_exchange_rate from erpnext.setup.utils import get_exchange_rate
from erpnext.accounts.general_ledger import make_gl_entries from erpnext.accounts.general_ledger import make_gl_entries
from erpnext.hr.doctype.expense_claim.expense_claim import update_paid_amount from erpnext.hr.doctype.expense_claim.expense_claim import update_reimbursed_amount
from erpnext.controllers.accounts_controller import AccountsController from erpnext.controllers.accounts_controller import AccountsController
class InvalidPaymentEntry(ValidationError): pass class InvalidPaymentEntry(ValidationError): pass
@@ -219,20 +219,15 @@ class PaymentEntry(AccountsController):
elif self.party_type=="Supplier": elif self.party_type=="Supplier":
ref_party_account = ref_doc.credit_to ref_party_account = ref_doc.credit_to
elif self.party_type=="Employee": elif self.party_type=="Employee":
ref_party_account = ref_doc.payable_account \ ref_party_account = ref_doc.payable_account
if ref_doc.docstatus==1 else ref_doc.advance_account
if ref_party_account != self.party_account: if ref_party_account != self.party_account:
frappe.throw(_("{0} {1} is associated with {2}, but Party Account is {3}") frappe.throw(_("{0} {1} is associated with {2}, but Party Account is {3}")
.format(d.reference_doctype, d.reference_name, ref_party_account, self.party_account)) .format(d.reference_doctype, d.reference_name, ref_party_account, self.party_account))
if ref_doc.docstatus != 1: if ref_doc.docstatus != 1:
if d.reference_doctype!="Expense Claim": frappe.throw(_("{0} {1} must be submitted")
frappe.throw(_("{0} {1} must be submitted") .format(d.reference_doctype, d.reference_name))
.format(d.reference_doctype, d.reference_name))
elif not ref_doc.advance_required:
frappe.throw(_("Advance Payment Required should be checked in Expense Claim {0}")
.format(d.reference_name))
def validate_journal_entry(self): def validate_journal_entry(self):
for d in self.get("references"): for d in self.get("references"):
@@ -486,11 +481,11 @@ class PaymentEntry(AccountsController):
frappe.get_doc(d.reference_doctype, d.reference_name).set_total_advance_paid() frappe.get_doc(d.reference_doctype, d.reference_name).set_total_advance_paid()
def update_expense_claim(self): def update_expense_claim(self):
if self.payment_type =="Pay" and self.party: if self.payment_type in ("Pay") and self.party:
for d in self.get("references"): for d in self.get("references"):
if d.reference_doctype=="Expense Claim" and d.reference_name: if d.reference_doctype=="Expense Claim" and d.reference_name:
doc = frappe.get_doc("Expense Claim", d.reference_name) doc = frappe.get_doc("Expense Claim", d.reference_name)
update_paid_amount(doc, self.paid_to) update_reimbursed_amount(doc)
def on_recurring(self, reference_doc, subscription_doc): def on_recurring(self, reference_doc, subscription_doc):
self.reference_no = reference_doc.name self.reference_no = reference_doc.name
@@ -702,8 +697,6 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=
party_account = doc.debit_to party_account = doc.debit_to
elif dt == "Purchase Invoice": elif dt == "Purchase Invoice":
party_account = doc.credit_to party_account = doc.credit_to
elif dt == "Expense Claim":
party_account = doc.payable_account if doc.docstatus==1 else doc.advance_account
elif dt == "Fees": elif dt == "Fees":
party_account = doc.receivable_account party_account = doc.receivable_account
else: else:
@@ -723,13 +716,11 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=
if party_amount: if party_amount:
grand_total = outstanding_amount = party_amount grand_total = outstanding_amount = party_amount
elif dt in ("Sales Invoice", "Purchase Invoice"): elif dt in ("Sales Invoice", "Purchase Invoice"):
grand_total = doc.base_grand_total \ grand_total = doc.base_grand_total if party_account_currency == doc.company_currency else doc.grand_total
if party_account_currency == doc.company_currency else doc.grand_total
outstanding_amount = doc.outstanding_amount outstanding_amount = doc.outstanding_amount
elif dt in ("Expense Claim"): elif dt in ("Expense Claim"):
grand_total = doc.total_sanctioned_amount grand_total = doc.total_sanctioned_amount
outstanding_amount = flt(doc.total_sanctioned_amount) - flt(doc.total_amount_reimbursed) \ outstanding_amount = doc.total_sanctioned_amount - doc.total_amount_reimbursed
- flt(doc.total_advance_paid)
elif dt == "Fees": elif dt == "Fees":
grand_total = doc.grand_total grand_total = doc.grand_total
outstanding_amount = doc.outstanding_amount outstanding_amount = doc.outstanding_amount

View File

@@ -86,8 +86,9 @@ class TestPaymentEntry(unittest.TestCase):
self.assertEqual(outstanding_amount, 0) self.assertEqual(outstanding_amount, 0)
def test_payment_entry_against_ec(self): def test_payment_entry_against_ec(self):
payable = frappe.db.get_value('Company', "_Test Company", 'default_payable_account') payable = frappe.db.get_value('Company', "_Test Company", 'default_payable_account')
ec = make_expense_claim(300, 300, "Travel Expenses - _TC", "_Test Company", payable) ec = make_expense_claim(payable, 300, 300, "_Test Company","Travel Expenses - _TC")
pe = get_payment_entry("Expense Claim", ec.name, bank_account="_Test Bank USD - _TC", bank_amount=300) pe = get_payment_entry("Expense Claim", ec.name, bank_account="_Test Bank USD - _TC", bank_amount=300)
pe.reference_no = "1" pe.reference_no = "1"
pe.reference_date = "2016-01-01" pe.reference_date = "2016-01-01"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

View File

@@ -51,30 +51,6 @@ Set the Payment Type to "Pay", the Party Type to Employee, the Party to the empl
from. All outstanding expense claims will be pulled in and payments amounts can be allocated to each expense. from. All outstanding expense claims will be pulled in and payments amounts can be allocated to each expense.
<img class="screenshot" alt="Expense Claim" src="{{docs_base_url}}/assets/img/human-resources/expense_claim_payment_entry.png"> <img class="screenshot" alt="Expense Claim" src="{{docs_base_url}}/assets/img/human-resources/expense_claim_payment_entry.png">
### Managing Advance Payments
Sometimes an employee requires some advance payment before making expenses on behalf of the organisation. This can be managed from the Expense Claim
First make sure that the Default Advance Account has been set in the Company Master:
> Erpnext > Setup > Company
<img class="screenshot" alt="Expense Claim" src="{{docs_base_url}}/assets/img/human-resources/company_advance_account.png">
When creating the Expense Claim, check the 'Advance Payment Required' option
<img class="screenshot" alt="Expense Claim" src="{{docs_base_url}}/assets/img/human-resources/advance_payment_required.png">
After the Expense Claim is Saved and Approved by the Expense Approver, Journal Entry for Advance Payment can be raised by the accountant or user with appropriate permissions. To do that, just click on:
> Make > Advance Payment
<img class="screenshot" alt="Expense Claim" src="{{docs_base_url}}/assets/img/human-resources/make_advance_payment.png">
Note: Once the Expense Claim is Submitted, the button for making Advance Payment is no longer available. This is because expenses get booked on Submission of the Expense Claim and as such, the next logical step is settlement/reimbursement
Advance Payments are expected to be made 'before' the actual expenditure gets booked and settlement/reimbursement should be done against the Employee's Advance Account after submission of the Expense Claim
### Linking with Task & Project ### Linking with Task & Project
* To Link Expense Claim with Task or Project specify the Task or the Project while making an Expense Claim * To Link Expense Claim with Task or Project specify the Task or the Project while making an Expense Claim

View File

@@ -442,7 +442,7 @@
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{ {
"allow_bulk_edit": 0, "allow_bulk_edit": 0,
"allow_on_submit": 0, "allow_on_submit": 0,
@@ -2432,7 +2432,7 @@
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"modified": "2017-07-11 14:30:13.694009", "modified": "2017-06-13 14:29:13.694009",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "HR", "module": "HR",
"name": "Employee", "name": "Employee",
@@ -2511,4 +2511,4 @@
"title_field": "employee_name", "title_field": "employee_name",
"track_changes": 1, "track_changes": 1,
"track_seen": 0 "track_seen": 0
} }

View File

@@ -34,6 +34,9 @@ erpnext.hr.ExpenseClaimController = frappe.ui.form.Controller.extend({
$.extend(cur_frm.cscript, new erpnext.hr.ExpenseClaimController({frm: cur_frm})); $.extend(cur_frm.cscript, new erpnext.hr.ExpenseClaimController({frm: cur_frm}));
cur_frm.add_fetch('employee', 'company', 'company');
cur_frm.add_fetch('employee','employee_name','employee_name');
cur_frm.cscript.onload = function(doc) { cur_frm.cscript.onload = function(doc) {
if(!doc.approval_status) if(!doc.approval_status)
cur_frm.set_value("approval_status", "Draft"); cur_frm.set_value("approval_status", "Draft");
@@ -68,6 +71,34 @@ cur_frm.cscript.clear_sanctioned = function(doc) {
refresh_many(['sanctioned_amount', 'total_sanctioned_amount']); refresh_many(['sanctioned_amount', 'total_sanctioned_amount']);
}; };
cur_frm.cscript.refresh = function(doc) {
cur_frm.cscript.set_help(doc);
if(!doc.__islocal) {
cur_frm.toggle_enable("exp_approver", doc.approval_status=="Draft");
cur_frm.toggle_enable("approval_status", (doc.exp_approver==frappe.session.user && doc.docstatus==0));
if (doc.docstatus==0 && doc.exp_approver==frappe.session.user && doc.approval_status=="Approved")
cur_frm.savesubmit();
if (doc.docstatus===1 && doc.approval_status=="Approved") {
/* eslint-disable */
// no idea how `me` works here
if (cint(doc.total_amount_reimbursed) > 0 && frappe.model.can_read("Journal Entry")) {
cur_frm.add_custom_button(__('Bank Entries'), function() {
frappe.route_options = {
"Journal Entry Account.reference_type": me.frm.doc.doctype,
"Journal Entry Account.reference_name": me.frm.doc.name,
company: me.frm.doc.company
};
frappe.set_route("List", "Journal Entry");
}, __("View"));
}
/* eslint-enable */
}
}
};
cur_frm.cscript.set_help = function(doc) { cur_frm.cscript.set_help = function(doc) {
cur_frm.set_intro(""); cur_frm.set_intro("");
if(doc.__islocal && !in_list(frappe.user_roles, "HR User")) { if(doc.__islocal && !in_list(frappe.user_roles, "HR User")) {
@@ -123,75 +154,30 @@ erpnext.expense_claim = {
frappe.ui.form.on("Expense Claim", { frappe.ui.form.on("Expense Claim", {
setup: function(frm) { setup: function(frm) {
frm.trigger("set_query_for_cost_center"); frm.trigger("set_query_for_cost_center");
frm.trigger("set_query_for_advance_account");
frm.trigger("set_query_for_payable_account"); frm.trigger("set_query_for_payable_account");
frm.add_fetch("company", "cost_center", "cost_center"); frm.add_fetch("company", "cost_center", "cost_center");
frm.add_fetch("company", "default_advance_account", "advance_account");
frm.add_fetch("company", "default_payable_account", "payable_account"); frm.add_fetch("company", "default_payable_account", "payable_account");
frm.add_fetch('employee', 'company', 'company');
frm.add_fetch('employee','employee_name','employee_name');
}, },
refresh: function(frm) { refresh: function(frm) {
cur_frm.cscript.set_help(frm.doc); frm.trigger("toggle_fields");
if(!frm.doc.__islocal) { if(frm.doc.docstatus == 1 && frm.doc.approval_status == 'Approved') {
frm.trigger("toggle_fields"); frm.add_custom_button(__('Accounting Ledger'), function() {
frm.toggle_enable("exp_approver", frm.doc.approval_status=="Draft"); frappe.route_options = {
frm.toggle_enable("approval_status", voucher_no: frm.doc.name,
(frm.doc.exp_approver==frappe.session.user && frm.doc.docstatus==0)); company: frm.doc.company,
frm.toggle_enable("employee", !(frm.doc.status=="Approved" || frm.doc.total_advance_paid)); group_by_voucher: false
frm.toggle_enable("advance_account", !frm.doc.total_advance_paid); };
frm.toggle_enable("company", !(frm.doc.status=="Approved" || frm.doc.total_advance_paid)); frappe.set_route("query-report", "General Ledger");
}, __("View"));
}
if (frm.doc.docstatus==0 && frm.doc.exp_approver==frappe.session.user if (frm.doc.docstatus===1 && frm.doc.approval_status=="Approved"
&& frm.doc.approval_status=="Approved" && frm.doc.advance_required==0) { && (cint(frm.doc.total_amount_reimbursed) < cint(frm.doc.total_sanctioned_amount))
frm.savesubmit(); && frappe.model.can_create("Payment Entry")) {
} frm.add_custom_button(__('Payment'),
function() { frm.events.make_payment_entry(frm); }, __("Make"));
if (frm.doc.docstatus==0 && frm.doc.approval_status=="Approved"
&& frm.doc.advance_required
&& cint(frm.doc.total_advance_paid) < cint(frm.doc.total_sanctioned_amount)
&& frappe.model.can_create("Payment Entry")) {
frm.add_custom_button(__('Advance Payment'),
function() { frm.events.make_payment_entry(frm); }, __("Make"));
frm.page.set_inner_btn_group_as_primary(__("Make"));
}
if (frm.doc.docstatus===1 && frm.doc.approval_status=="Approved"
&& (cint(frm.doc.total_amount_reimbursed) < cint(frm.doc.total_sanctioned_amount))
&& frappe.model.can_create("Payment Entry")) {
frm.add_custom_button(__('Payment'),
function() { frm.events.make_payment_entry(frm); }, __("Make"));
}
if(frm.doc.docstatus == 1 && frm.doc.approval_status == 'Approved') {
frm.add_custom_button(__('Accounting Ledger'), function() {
frappe.route_options = {
voucher_no: frm.doc.name,
company: frm.doc.company,
group_by_voucher: false
};
frappe.set_route("query-report", "General Ledger");
}, __("View"));
}
if (frm.doc.docstatus===1 && frm.doc.approval_status=="Approved") {
/* eslint-disable */
// no idea how `me` works here
if (cint(frm.doc.total_amount_reimbursed) > 0 && frappe.model.can_read("Journal Entry")) {
frm.add_custom_button(__('Bank Entries'), function() {
frappe.route_options = {
"Journal Entry Account.reference_type": frm.doc.doctype,
"Journal Entry Account.reference_name": frm.doc.name,
company: frm.doc.company
};
frappe.set_route("List", "Journal Entry");
}, __("View"));
}
/* eslint-enable */
}
} }
}, },
@@ -212,11 +198,7 @@ frappe.ui.form.on("Expense Claim", {
} }
}); });
}, },
advance_required: function(frm) {
frm.refresh();
},
set_query_for_cost_center: function(frm) { set_query_for_cost_center: function(frm) {
frm.fields_dict["cost_center"].get_query = function() { frm.fields_dict["cost_center"].get_query = function() {
return { return {
@@ -227,18 +209,6 @@ frappe.ui.form.on("Expense Claim", {
}; };
}, },
set_query_for_advance_account: function(frm) {
frm.fields_dict["advance_account"].get_query = function() {
return {
filters: {
"report_type": "Balance Sheet",
"account_type": "Receivable",
"company": frm.doc.company
}
};
};
},
set_query_for_payable_account: function(frm) { set_query_for_payable_account: function(frm) {
frm.fields_dict["payable_account"].get_query = function() { frm.fields_dict["payable_account"].get_query = function() {
return { return {

View File

@@ -171,37 +171,7 @@
"set_only_once": 0, "set_only_once": 0,
"unique": 0, "unique": 0,
"width": "50%" "width": "50%"
}, },
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "advance_required",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Advance Payment Required",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{ {
"allow_bulk_edit": 0, "allow_bulk_edit": 0,
"allow_on_submit": 0, "allow_on_submit": 0,
@@ -575,38 +545,7 @@
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "total_advance_paid",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Total Advance Paid",
"length": 0,
"no_copy": 1,
"options": "Company:company:default_currency",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{ {
"allow_bulk_edit": 0, "allow_bulk_edit": 0,
"allow_on_submit": 0, "allow_on_submit": 0,
@@ -854,38 +793,6 @@
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "",
"fieldname": "advance_account",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Advance Account",
"length": 0,
"no_copy": 0,
"options": "Account",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{ {
"allow_bulk_edit": 0, "allow_bulk_edit": 0,
"allow_on_submit": 0, "allow_on_submit": 0,
@@ -1057,7 +964,7 @@
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"menu_index": 0, "menu_index": 0,
"modified": "2017-07-17 15:48:23.255142", "modified": "2017-07-17 15:47:23.255142",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "HR", "module": "HR",
"name": "Expense Claim", "name": "Expense Claim",
@@ -1159,4 +1066,4 @@
"title_field": "title", "title_field": "title",
"track_changes": 0, "track_changes": 0,
"track_seen": 0 "track_seen": 0
} }

View File

@@ -33,7 +33,6 @@ class ExpenseClaim(AccountsController):
self.set_payable_account() self.set_payable_account()
self.set_cost_center() self.set_cost_center()
self.set_status() self.set_status()
self.validate_advance_payment()
if self.task and not self.project: if self.task and not self.project:
self.project = frappe.db.get_value("Task", self.task, "project") self.project = frappe.db.get_value("Task", self.task, "project")
@@ -44,8 +43,7 @@ class ExpenseClaim(AccountsController):
"2": "Cancelled" "2": "Cancelled"
}[cstr(self.docstatus or 0)] }[cstr(self.docstatus or 0)]
total_paid_amount = flt(self.total_amount_reimbursed) + flt(self.total_advance_paid) if self.total_sanctioned_amount > 0 and self.total_sanctioned_amount == self.total_amount_reimbursed \
if self.total_sanctioned_amount > 0 and self.total_sanctioned_amount == total_paid_amount \
and self.docstatus == 1 and self.approval_status == 'Approved': and self.docstatus == 1 and self.approval_status == 'Approved':
self.status = "Paid" self.status = "Paid"
elif self.total_sanctioned_amount > 0 and self.docstatus == 1 and self.approval_status == 'Approved': elif self.total_sanctioned_amount > 0 and self.docstatus == 1 and self.approval_status == 'Approved':
@@ -69,7 +67,7 @@ class ExpenseClaim(AccountsController):
self.make_gl_entries() self.make_gl_entries()
if self.is_paid: if self.is_paid:
update_paid_amount(self, self.payable_account) update_reimbursed_amount(self)
self.set_status() self.set_status()
@@ -79,7 +77,7 @@ class ExpenseClaim(AccountsController):
self.make_gl_entries(cancel=True) self.make_gl_entries(cancel=True)
if self.is_paid: if self.is_paid:
update_paid_amount(self, self.payable_account) update_reimbursed_amount(self)
self.set_status() self.set_status()
@@ -98,36 +96,19 @@ class ExpenseClaim(AccountsController):
gl_entry = [] gl_entry = []
self.validate_account_details() self.validate_account_details()
outstanding_amount = flt(self.total_sanctioned_amount) - flt(self.total_advance_paid)
# payable entry # payable entry
if outstanding_amount: gl_entry.append(
gl_entry.append( self.get_gl_dict({
self.get_gl_dict({ "account": self.payable_account,
"account": self.payable_account, "credit": self.total_sanctioned_amount,
"credit": outstanding_amount, "credit_in_account_currency": self.total_sanctioned_amount,
"credit_in_account_currency": outstanding_amount, "against": ",".join([d.default_account for d in self.expenses]),
"against": ",".join([d.default_account for d in self.expenses]), "party_type": "Employee",
"party_type": "Employee", "party": self.employee,
"party": self.employee, "against_voucher_type": self.doctype,
"against_voucher_type": self.doctype, "against_voucher": self.name
"against_voucher": self.name })
}) )
)
if self.total_advance_paid:
gl_entry.append(
self.get_gl_dict({
"account": self.advance_account,
"credit": self.total_advance_paid,
"credit_in_account_currency": self.total_advance_paid,
"against": ",".join([d.default_account for d in self.expenses]),
"party_type": "Employee",
"party": self.employee,
"against_voucher_type": self.doctype,
"against_voucher": self.name
})
)
# expense entries # expense entries
for data in self.expenses: for data in self.expenses:
@@ -141,15 +122,14 @@ class ExpenseClaim(AccountsController):
}) })
) )
if self.is_paid and outstanding_amount: if self.is_paid:
# payment entry # payment entry
payment_account = get_bank_cash_account(self.mode_of_payment, self.company).get("account") payment_account = get_bank_cash_account(self.mode_of_payment, self.company).get("account")
gl_entry.append( gl_entry.append(
self.get_gl_dict({ self.get_gl_dict({
"account": payment_account, "account": payment_account,
"credit": outstanding_amount, "credit": self.total_sanctioned_amount,
"credit_in_account_currency": outstanding_amount, "credit_in_account_currency": self.total_sanctioned_amount,
"against": self.employee "against": self.employee
}) })
) )
@@ -160,8 +140,8 @@ class ExpenseClaim(AccountsController):
"party_type": "Employee", "party_type": "Employee",
"party": self.employee, "party": self.employee,
"against": payment_account, "against": payment_account,
"debit": outstanding_amount, "debit": self.total_sanctioned_amount,
"debit_in_account_currency": outstanding_amount, "debit_in_account_currency": self.total_sanctioned_amount,
"against_voucher": self.name, "against_voucher": self.name,
"against_voucher_type": self.doctype, "against_voucher_type": self.doctype,
}) })
@@ -208,37 +188,18 @@ class ExpenseClaim(AccountsController):
def set_expense_account(self): def set_expense_account(self):
for expense in self.expenses: for expense in self.expenses:
if not expense.default_account: if not expense.default_account:
expense.default_account = get_expense_claim_account(expense.expense_type, expense.default_account = get_expense_claim_account(expense.expense_type, self.company)["account"]
self.company)["account"]
def validate_advance_payment(self): def update_reimbursed_amount(doc):
if self.advance_required: amt = frappe.db.sql("""select ifnull(sum(debit_in_account_currency), 0) as amt
if self.docstatus == 1 and not self.total_advance_paid: from `tabGL Entry` where against_voucher_type = 'Expense Claim' and against_voucher = %s
frappe.throw(_("Advance payment required before submission of the Expense Claim")) and party = %s """, (doc.name, doc.employee) ,as_dict=1)[0].amt
elif self.total_advance_paid:
frappe.throw(_("As advance payment already done, cannot unset 'Advance Payment Required'"))
def update_paid_amount(doc, party_account): doc.total_amount_reimbursed = amt
paid_amount = frappe.db.sql(""" frappe.db.set_value("Expense Claim", doc.name , "total_amount_reimbursed", amt)
select ifnull(sum(debit_in_account_currency), 0) as amount
from `tabGL Entry`
where
against_voucher_type = 'Expense Claim' and against_voucher = %s
and party = %s and account = %s
""", (doc.name, doc.employee, party_account) ,as_dict=1)[0].amount
paid_amount_field = None doc.set_status()
if party_account == doc.payable_account: frappe.db.set_value("Expense Claim", doc.name , "status", doc.status)
paid_amount_field = "total_amount_reimbursed"
elif party_account == doc.advance_account:
paid_amount_field = "total_advance_paid"
if paid_amount_field:
doc.set(paid_amount_field, paid_amount)
frappe.db.set_value("Expense Claim", doc.name , paid_amount_field, paid_amount)
doc.set_status()
frappe.db.set_value("Expense Claim", doc.name , "status", doc.status)
@frappe.whitelist() @frappe.whitelist()
def get_expense_approver(doctype, txt, searchfield, start, page_len, filters): def get_expense_approver(doctype, txt, searchfield, start, page_len, filters):
@@ -258,31 +219,25 @@ def make_bank_entry(dt, dn):
if not default_bank_cash_account: if not default_bank_cash_account:
default_bank_cash_account = get_default_bank_cash_account(expense_claim.company, "Cash") default_bank_cash_account = get_default_bank_cash_account(expense_claim.company, "Cash")
if expense_claim.docstatus == 0:
party_account = expense_claim.advance_account
else:
party_account = expense_claim.payable_account
payment_amount = flt(expense_claim.total_sanctioned_amount) \
- flt(expense_claim.total_amount_reimbursed) - flt(expense_claim.total_advance_paid)
je = frappe.new_doc("Journal Entry") je = frappe.new_doc("Journal Entry")
je.voucher_type = 'Bank Entry' je.voucher_type = 'Bank Entry'
je.company = expense_claim.company je.company = expense_claim.company
je.remark = 'Advance ' if expense_claim.docstatus==0 else '' + 'Payment against Expense Claim: ' + dn; je.remark = 'Payment against Expense Claim: ' + dn;
je.append("accounts", { je.append("accounts", {
"account": party_account, "account": expense_claim.payable_account,
"debit_in_account_currency": payment_amount, "debit_in_account_currency": flt(expense_claim.total_sanctioned_amount - expense_claim.total_amount_reimbursed),
"reference_type": "Expense Claim", "reference_type": "Expense Claim",
"reference_name": expense_claim.name,
"party_type": "Employee", "party_type": "Employee",
"party": expense_claim.employee "party": expense_claim.employee,
"reference_name": expense_claim.name
}) })
je.append("accounts", { je.append("accounts", {
"account": default_bank_cash_account.account, "account": default_bank_cash_account.account,
"credit_in_account_currency": payment_amount, "credit_in_account_currency": flt(expense_claim.total_sanctioned_amount - expense_claim.total_amount_reimbursed),
"reference_type": "Expense Claim",
"reference_name": expense_claim.name,
"balance": default_bank_cash_account.balance, "balance": default_bank_cash_account.balance,
"account_currency": default_bank_cash_account.account_currency, "account_currency": default_bank_cash_account.account_currency,
"account_type": default_bank_cash_account.account_type "account_type": default_bank_cash_account.account_type

View File

@@ -6,7 +6,6 @@ import frappe
import unittest import unittest
from frappe.utils import random_string, nowdate from frappe.utils import random_string, nowdate
from erpnext.hr.doctype.expense_claim.expense_claim import make_bank_entry from erpnext.hr.doctype.expense_claim.expense_claim import make_bank_entry
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
test_records = frappe.get_test_records('Expense Claim') test_records = frappe.get_test_records('Expense Claim')
@@ -23,35 +22,28 @@ class TestExpenseClaim(unittest.TestCase):
[{ "title": "_Test Project Task 1", "status": "Open" }] [{ "title": "_Test Project Task 1", "status": "Open" }]
}).save() }).save()
existing_claimed_amount = frappe.db.get_value("Project", "_Test Project 1", "total_expense_claim")
task_name = frappe.db.get_value("Task", {"project": "_Test Project 1"}) task_name = frappe.db.get_value("Task", {"project": "_Test Project 1"})
payable_account = get_payable_account("Wind Power LLC") payable_account = get_payable_account("Wind Power LLC")
make_expense_claim(300, 200,"Travel Expenses - WP", "Wind Power LLC", make_expense_claim(payable_account, 300, 200, "Wind Power LLC","Travel Expenses - WP", "_Test Project 1", task_name)
payable_account, "_Test Project 1", task_name)
self.assertEqual(frappe.db.get_value("Task", task_name, "total_expense_claim"), 200) self.assertEqual(frappe.db.get_value("Task", task_name, "total_expense_claim"), 200)
self.assertEqual(frappe.db.get_value("Project", "_Test Project 1", "total_expense_claim"), self.assertEqual(frappe.db.get_value("Project", "_Test Project 1", "total_expense_claim"), 200)
existing_claimed_amount + 200)
expense_claim2 = make_expense_claim(600, 500, "Travel Expenses - WP", "Wind Power LLC", expense_claim2 = make_expense_claim(payable_account, 600, 500, "Wind Power LLC", "Travel Expenses - WP","_Test Project 1", task_name)
payable_account, "_Test Project 1", task_name)
self.assertEqual(frappe.db.get_value("Task", task_name, "total_expense_claim"), 700) self.assertEqual(frappe.db.get_value("Task", task_name, "total_expense_claim"), 700)
self.assertEqual(frappe.db.get_value("Project", "_Test Project 1", "total_expense_claim"), self.assertEqual(frappe.db.get_value("Project", "_Test Project 1", "total_expense_claim"), 700)
existing_claimed_amount + 700)
expense_claim2.cancel() expense_claim2.cancel()
frappe.delete_doc("Expense Claim", expense_claim2.name) frappe.delete_doc("Expense Claim", expense_claim2.name)
self.assertEqual(frappe.db.get_value("Task", task_name, "total_expense_claim"), 200) self.assertEqual(frappe.db.get_value("Task", task_name, "total_expense_claim"), 200)
self.assertEqual(frappe.db.get_value("Project", "_Test Project 1", "total_expense_claim"), self.assertEqual(frappe.db.get_value("Project", "_Test Project 1", "total_expense_claim"), 200)
existing_claimed_amount+200)
def test_expense_claim_status(self): def test_expense_claim_status(self):
payable_account = get_payable_account("Wind Power LLC") payable_account = get_payable_account("Wind Power LLC")
expense_claim = make_expense_claim(300, 200, "Travel Expenses - WP", expense_claim = make_expense_claim(payable_account, 300, 200, "Wind Power LLC", "Travel Expenses - WP")
"Wind Power LLC", payable_account)
je_dict = make_bank_entry("Expense Claim", expense_claim.name) je_dict = make_bank_entry("Expense Claim", expense_claim.name)
je = frappe.get_doc(je_dict) je = frappe.get_doc(je_dict)
@@ -69,8 +61,7 @@ class TestExpenseClaim(unittest.TestCase):
def test_expense_claim_gl_entry(self): def test_expense_claim_gl_entry(self):
payable_account = get_payable_account("Wind Power LLC") payable_account = get_payable_account("Wind Power LLC")
expense_claim = make_expense_claim(300, 200, "Travel Expenses - WP", expense_claim = make_expense_claim(payable_account, 300, 200, "Wind Power LLC", "Travel Expenses - WP")
"Wind Power LLC", payable_account)
expense_claim.submit() expense_claim.submit()
gl_entries = frappe.db.sql("""select account, debit, credit gl_entries = frappe.db.sql("""select account, debit, credit
@@ -107,82 +98,25 @@ class TestExpenseClaim(unittest.TestCase):
gl_entry = frappe.get_all('GL Entry', {'voucher_type': 'Expense Claim', 'voucher_no': expense_claim.name}) gl_entry = frappe.get_all('GL Entry', {'voucher_type': 'Expense Claim', 'voucher_no': expense_claim.name})
self.assertEquals(len(gl_entry), 0) self.assertEquals(len(gl_entry), 0)
def test_advance_payment(self):
expense_claim = make_expense_claim(150, 150, "Travel Expenses - _TC",
advance_required=1, submit=False)
payment_entry = get_payment_entry("Expense Claim", expense_claim.name, bank_amount=50)
payment_entry.received_amount = payment_entry.paid_amount = 50
payment_entry.get("references")[0].allocated_amount = 50
payment_entry.reference_no = "1"
payment_entry.reference_date = "2016-01-01"
payment_entry.save()
payment_entry.submit()
expense_claim.load_from_db()
self.assertEqual(expense_claim.total_advance_paid, 50)
expense_claim.submit()
payment_entry = get_payment_entry("Expense Claim", expense_claim.name)
payment_entry.reference_no = "1"
payment_entry.reference_date = "2016-01-01"
payment_entry.save()
payment_entry.submit()
expense_claim.load_from_db()
self.assertEqual(expense_claim.total_advance_paid, 50)
self.assertEqual(expense_claim.total_amount_reimbursed, 100)
gl_entries = frappe.db.sql("""select account, debit, credit
from `tabGL Entry` where voucher_type='Expense Claim' and voucher_no=%s
order by account asc""", expense_claim.name, as_dict=1)
self.assertTrue(gl_entries)
expected_values = dict((d[0], d) for d in [
[get_advance_account("_Test Company"), 0.0, 50.0],
[get_payable_account("_Test Company"), 0.0, 100.0],
["Travel Expenses - _TC", 150.0, 0.0]
])
for gle in gl_entries:
self.assertEquals(expected_values[gle.account][0], gle.account)
self.assertEquals(expected_values[gle.account][1], gle.debit)
self.assertEquals(expected_values[gle.account][2], gle.credit)
def get_payable_account(company): def get_payable_account(company):
return frappe.db.get_value('Company', company, 'default_payable_account') return frappe.db.get_value('Company', company, 'default_payable_account')
def get_advance_account(company): def make_expense_claim(payable_account,claim_amount, sanctioned_amount, company, account, project=None, task_name=None):
return frappe.db.get_value('Company', company, 'default_advance_account') \
or frappe.db.get_value('Company', company, 'default_receivable_account')
def make_expense_claim(claim_amount, sanctioned_amount, expense_account, company="_Test Company",
payable_account=None, project=None, task_name=None,
advance_required=0, advance_account=None, submit=True):
expense_claim = frappe.get_doc({ expense_claim = frappe.get_doc({
"doctype": "Expense Claim", "doctype": "Expense Claim",
"employee": "_T-Employee-0001", "employee": "_T-Employee-0001",
"payable_account": payable_account or get_payable_account(company), "payable_account": payable_account,
"advance_account": advance_account or get_advance_account(company),
"advance_required": advance_required,
"approval_status": "Approved", "approval_status": "Approved",
"company": company, "company": company,
"expenses": [{ "expenses":
"expense_type": "Travel", [{ "expense_type": "Travel", "default_account": account, "claim_amount": claim_amount, "sanctioned_amount": sanctioned_amount }]
"default_account": expense_account, })
"claim_amount": claim_amount,
"sanctioned_amount": sanctioned_amount
}]
})
if project: if project:
expense_claim.project = project expense_claim.project = project
if task_name: if task_name:
expense_claim.task = task_name expense_claim.task = task_name
expense_claim.save() expense_claim.submit()
if submit:
expense_claim.submit()
return expense_claim return expense_claim

View File

@@ -149,7 +149,6 @@ erpnext.company.setup_queries = function(frm) {
["default_bank_account", {"account_type": "Bank"}], ["default_bank_account", {"account_type": "Bank"}],
["default_cash_account", {"account_type": "Cash"}], ["default_cash_account", {"account_type": "Cash"}],
["default_receivable_account", {"account_type": "Receivable"}], ["default_receivable_account", {"account_type": "Receivable"}],
["default_advance_account", {"account_type": "Receivable"}],
["default_payable_account", {"account_type": "Payable"}], ["default_payable_account", {"account_type": "Payable"}],
["default_expense_account", {"root_type": "Expense"}], ["default_expense_account", {"root_type": "Expense"}],
["default_income_account", {"root_type": "Income"}], ["default_income_account", {"root_type": "Income"}],

File diff suppressed because it is too large Load Diff