diff --git a/erpnext/accounts/doctype/account/test_account.py b/erpnext/accounts/doctype/account/test_account.py index 680f6e3ea97..a4253e2eace 100644 --- a/erpnext/accounts/doctype/account/test_account.py +++ b/erpnext/accounts/doctype/account/test_account.py @@ -52,6 +52,7 @@ def _make_test_records(verbose): ["_Test Account Customs Duty", "_Test Account Stock Expenses", 0, "Tax", None], ["_Test Account Insurance Charges", "_Test Account Stock Expenses", 0, "Chargeable", None], ["_Test Account Stock Adjustment", "_Test Account Stock Expenses", 0, "Stock Adjustment", None], + ["_Test Employee Advance", "Current Liabilities", 0, None, None], ["_Test Account Tax Assets", "Current Assets", 1, None, None], ["_Test Account VAT", "_Test Account Tax Assets", 0, "Tax", None], diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js index 4ce68865bea..41000f33b1c 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.js +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js @@ -124,6 +124,15 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({ }; } + if(jvd.reference_type==="Employee Advance") { + return { + filters: { + 'status': ['=', 'Unpaid'], + 'docstatus': 1 + } + }; + } + // journal entry if(jvd.reference_type==="Journal Entry") { frappe.model.validate_missing(jvd, "account"); diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index f010e67b998..0977037a313 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -54,8 +54,8 @@ class JournalEntry(AccountsController): def update_advance_paid(self): advance_paid = frappe._dict() for d in self.get("accounts"): - if d.is_advance == "Yes": - if d.reference_type in ("Sales Order", "Purchase Order"): + if d.is_advance: + if d.reference_type in ("Sales Order", "Purchase Order", "Employee Advance"): advance_paid.setdefault(d.reference_type, []).append(d.reference_name) for voucher_type, order_list in advance_paid.items(): @@ -101,8 +101,6 @@ class JournalEntry(AccountsController): if account_type in ["Receivable", "Payable"]: if not (d.party_type and d.party): frappe.throw(_("Row {0}: Party Type and Party is required for Receivable / Payable account {1}").format(d.idx, d.account)) - elif d.party_type and d.party: - frappe.throw(_("Row {0}: Party Type and Party is only applicable against Receivable / Payable account").format(d.idx)) def check_credit_limit(self): customers = list(set([d.party for d in self.get("accounts") diff --git a/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json index 2954f72fdce..23b6399c7bd 100644 --- a/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json +++ b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json @@ -618,7 +618,7 @@ "label": "Reference Type", "length": 0, "no_copy": 0, - "options": "\nSales Invoice\nPurchase Invoice\nJournal Entry\nSales Order\nPurchase Order\nExpense Claim\nAsset\nEmployee Loan\nPayroll Entry", + "options": "\nSales Invoice\nPurchase Invoice\nJournal Entry\nSales Order\nPurchase Order\nExpense Claim\nAsset\nEmployee Loan\nPayroll Entry\nEmployee Advance", "permlevel": 0, "precision": "", "print_hide": 0, @@ -668,7 +668,7 @@ "bold": 0, "collapsible": 0, "columns": 0, - "depends_on": "eval:doc.reference_type&&!in_list(doc.reference_type, ['Expense Claim', 'Asset', 'Employee Loan'])", + "depends_on": "eval:doc.reference_type&&!in_list(doc.reference_type, ['Expense Claim', 'Asset', 'Employee Loan', 'Employee Advance'])", "fieldname": "reference_due_date", "fieldtype": "Select", "hidden": 0, @@ -827,7 +827,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2017-12-06 19:54:19.851534", + "modified": "2017-12-07 19:54:19.851534", "modified_by": "Administrator", "module": "Accounts", "name": "Journal Entry Account", diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index ed9c74abbdb..197f1af52c8 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -3,7 +3,7 @@ # For license information, please see license.txt from __future__ import unicode_literals -import frappe, json +import frappe, erpnext, json from frappe import _, scrub, ValidationError from frappe.utils import flt, comma_or, nowdate from erpnext.accounts.utils import get_outstanding_invoices, get_account_currency, get_balance_on @@ -147,7 +147,7 @@ class PaymentEntry(AccountsController): if not frappe.db.exists(self.party_type, self.party): frappe.throw(_("Invalid {0}: {1}").format(self.party_type, self.party)) - if self.party_account: + if self.party_account and self.party_type != "Employee": party_account_type = "Receivable" if self.party_type in ("Customer", "Student") else "Payable" self.validate_account_type(self.party_account, [party_account_type]) @@ -188,7 +188,7 @@ class PaymentEntry(AccountsController): elif self.party_type == "Supplier": valid_reference_doctypes = ("Purchase Order", "Purchase Invoice", "Journal Entry") elif self.party_type == "Employee": - valid_reference_doctypes = ("Expense Claim", "Journal Entry") + valid_reference_doctypes = ("Expense Claim", "Journal Entry", "Employee Advance") for d in self.get("references"): if not d.allocated_amount: @@ -483,8 +483,9 @@ class PaymentEntry(AccountsController): def update_advance_paid(self): if self.payment_type in ("Receive", "Pay") and self.party: for d in self.get("references"): - if d.allocated_amount and d.reference_doctype in ("Sales Order", "Purchase Order"): - frappe.get_doc(d.reference_doctype, d.reference_name).set_total_advance_paid() + if d.allocated_amount \ + and d.reference_doctype in ("Sales Order", "Purchase Order", "Employee Advance"): + frappe.get_doc(d.reference_doctype, d.reference_name).set_total_advance_paid() def update_expense_claim(self): if self.payment_type in ("Pay") and self.party: @@ -654,15 +655,18 @@ def get_company_defaults(company): def get_reference_details(reference_doctype, reference_name, party_account_currency): total_amount = outstanding_amount = exchange_rate = None ref_doc = frappe.get_doc(reference_doctype, reference_name) + company_currency = ref_doc.get("company_currency") or erpnext.get_company_currency(ref_doc.company) if reference_doctype == "Fees": total_amount = ref_doc.get("grand_total") exchange_rate = 1 outstanding_amount = ref_doc.get("outstanding_amount") elif reference_doctype != "Journal Entry": - if party_account_currency == ref_doc.company_currency: + if party_account_currency == company_currency: if ref_doc.doctype == "Expense Claim": total_amount = ref_doc.total_sanctioned_amount + elif ref_doc.doctype == "Employee Advance": + total_amount = ref_doc.advance_amount else: total_amount = ref_doc.base_grand_total exchange_rate = 1 @@ -672,15 +676,18 @@ def get_reference_details(reference_doctype, reference_name, party_account_curre # Get the exchange rate from the original ref doc # or get it based on the posting date of the ref doc exchange_rate = ref_doc.get("conversion_rate") or \ - get_exchange_rate(party_account_currency, ref_doc.company_currency, ref_doc.posting_date) + get_exchange_rate(party_account_currency, company_currency, ref_doc.posting_date) - outstanding_amount = ref_doc.get("outstanding_amount") \ - if reference_doctype in ("Sales Invoice", "Purchase Invoice", "Expense Claim") \ - else flt(total_amount) - flt(ref_doc.advance_paid) + if reference_doctype in ("Sales Invoice", "Purchase Invoice", "Expense Claim"): + outstanding_amount = ref_doc.get("outstanding_amount") + elif reference_doctype == "Employee Advance": + outstanding_amount = ref_doc.advance_amount - flt(ref_doc.paid_amount) + else: + outstanding_amount = flt(total_amount) - flt(ref_doc.advance_paid) else: # Get the exchange rate based on the posting date of the ref doc exchange_rate = get_exchange_rate(party_account_currency, - ref_doc.company_currency, ref_doc.posting_date) + company_currency, ref_doc.posting_date) return frappe._dict({ "due_date": ref_doc.get("due_date"), @@ -700,7 +707,7 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount= party_type = "Customer" elif dt in ("Purchase Invoice", "Purchase Order"): party_type = "Supplier" - elif dt in ("Expense Claim"): + elif dt in ("Expense Claim", "Employee Advance"): party_type = "Employee" elif dt in ("Fees"): party_type = "Student" @@ -712,6 +719,8 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount= party_account = doc.credit_to elif dt == "Fees": party_account = doc.receivable_account + elif dt == "Employee Advance": + party_account = doc.advance_account else: party_account = get_party_account(party_type, doc.get(party_type.lower()), doc.company) @@ -736,7 +745,11 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount= outstanding_amount = doc.outstanding_amount elif dt in ("Expense Claim"): grand_total = doc.total_sanctioned_amount - outstanding_amount = doc.total_sanctioned_amount - doc.total_amount_reimbursed + outstanding_amount = doc.total_sanctioned_amount \ + - doc.total_amount_reimbursed - flt(doc.total_advance_amount) + elif dt == "Employee Advance": + grand_total = doc.advance_amount + outstanding_amount = flt(doc.advance_amount) - flt(doc.paid_amount) elif dt == "Fees": grand_total = doc.grand_total outstanding_amount = doc.outstanding_amount @@ -779,26 +792,36 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount= pe.received_amount = received_amount pe.allocate_payment_amount = 1 pe.letter_head = doc.get("letter_head") - args = { - 'party_account': party_account, 'company': pe.company, 'party_type': pe.party_type, - 'party': pe.party, 'posting_date': pe.posting_date - } - references = get_outstanding_reference_documents(args=args) - for reference in references: - if reference.voucher_no == dn: - allocated_amount = min(paid_amount, reference.outstanding_amount) - pe.append("references", { - 'reference_doctype': reference.voucher_type, - 'reference_name': reference.voucher_no, - 'due_date': reference.due_date, - 'total_amount': reference.invoice_amount, - 'outstanding_amount': reference.outstanding_amount, - 'allocated_amount': allocated_amount, - "bill_no": reference.get("bill_no") - }) - if paid_amount: - paid_amount -= allocated_amount + if dt == "Employee Advance": + pe.append("references", { + 'reference_doctype': dt, + 'reference_name': dn, + 'total_amount': grand_total, + 'outstanding_amount': outstanding_amount, + 'allocated_amount': outstanding_amount + }) + else: + args = { + 'party_account': party_account, 'company': pe.company, 'party_type': pe.party_type, + 'party': pe.party, 'posting_date': pe.posting_date + } + references = get_outstanding_reference_documents(args=args) + + for reference in references: + if reference.voucher_no == dn: + allocated_amount = min(paid_amount, reference.outstanding_amount) + pe.append("references", { + 'reference_doctype': reference.voucher_type, + 'reference_name': reference.voucher_no, + 'due_date': reference.due_date, + 'total_amount': reference.invoice_amount, + 'outstanding_amount': reference.outstanding_amount, + 'allocated_amount': allocated_amount, + "bill_no": reference.get("bill_no") + }) + if paid_amount: + paid_amount -= allocated_amount pe.setup_party_account_field() pe.set_missing_values() diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index abdadb3599c..9c42f6435e0 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -576,7 +576,7 @@ def get_outstanding_invoices(party_type, party, account, condition=None): outstanding_invoices = [] precision = frappe.get_precision("Sales Invoice", "outstanding_amount") - if party_type == "Customer" or party_type == "Student": + if party_type in ("Customer", "Student"): dr_or_cr = "debit_in_account_currency - credit_in_account_currency" payment_dr_or_cr = "payment_gl_entry.credit_in_account_currency - payment_gl_entry.debit_in_account_currency" else: diff --git a/erpnext/config/hr.py b/erpnext/config/hr.py index b433433bc47..d9152ef7bdd 100644 --- a/erpnext/config/hr.py +++ b/erpnext/config/hr.py @@ -121,6 +121,11 @@ def get_data(): { "label": _("Expense Claims"), "items": [ + { + "type": "doctype", + "name": "Employee Advance", + "description": _("Manage advance amount given to the Employee"), + }, { "type": "doctype", "name": "Expense Claim", diff --git a/erpnext/docs/assets/img/human-resources/employee_advance.png b/erpnext/docs/assets/img/human-resources/employee_advance.png new file mode 100644 index 00000000000..671c215f857 Binary files /dev/null and b/erpnext/docs/assets/img/human-resources/employee_advance.png differ diff --git a/erpnext/docs/assets/img/human-resources/employee_advance_journal_entry.png b/erpnext/docs/assets/img/human-resources/employee_advance_journal_entry.png new file mode 100644 index 00000000000..f9c25662fa8 Binary files /dev/null and b/erpnext/docs/assets/img/human-resources/employee_advance_journal_entry.png differ diff --git a/erpnext/docs/assets/img/human-resources/employee_advance_payment_entry.png b/erpnext/docs/assets/img/human-resources/employee_advance_payment_entry.png new file mode 100644 index 00000000000..7cfc6ceb66d Binary files /dev/null and b/erpnext/docs/assets/img/human-resources/employee_advance_payment_entry.png differ diff --git a/erpnext/docs/assets/img/human-resources/expense_claim_advances.png b/erpnext/docs/assets/img/human-resources/expense_claim_advances.png new file mode 100644 index 00000000000..c6d7fe95f9e Binary files /dev/null and b/erpnext/docs/assets/img/human-resources/expense_claim_advances.png differ diff --git a/erpnext/docs/user/manual/en/human-resources/employee-advance.md b/erpnext/docs/user/manual/en/human-resources/employee-advance.md new file mode 100644 index 00000000000..4e076113eee --- /dev/null +++ b/erpnext/docs/user/manual/en/human-resources/employee-advance.md @@ -0,0 +1,32 @@ +# Employee Advance + +Sometimes employees go outside for company's work and company pays some amount for their expenses in advance. In that time, the employee can create Employee Advance form and the expense approver can submit the advance record after verification. After Employee Advance gets submitted, the accountant releases the payment and makes the payment entry. + +To make a new Employee Advance, go to: + +> HR > Employee Advance > New Employee Advance + +Expense Claim + +Set the Employee ID, date, purpose and requested amount and “Save” the record. + +### Employee Advance Submission + +Employee Advance record can be created by any employee but they cannot submit the record. + +After saving Employee Advance, Employee should [Assign document to Approver](/docs/user/manual/en/using-eprnext/assignment.html). On assignment, approving user will also receive email notification. To automate email notification, you can also setup [Email Alert](/docs/user/manual/en/setting-up/email/email-alerts.html). + +After verification, approver can submit the Employee Advance form or reject the request. + +### Make Payment Entry +After submission of Employee Advance record, accounts user will be able to create payment entry via Journal Entry or Payment Entry form. +The payment entry will look like following: +Employee Advance Payment via Journal Entry + +Employee Advance Payment via Payment Entry + +On submission of payment entry, the paid amount and status will be updated in Employee Advance record. + +### Adjust advances on Expense Claim +Later when employee claims the expense and advance record can be fetched in Expense Claim and linked to the claim record. +Employee Advance Payment via Payment Entry \ No newline at end of file diff --git a/erpnext/docs/user/manual/en/human-resources/index.txt b/erpnext/docs/user/manual/en/human-resources/index.txt index 673efdb8386..04abb85aaa6 100644 --- a/erpnext/docs/user/manual/en/human-resources/index.txt +++ b/erpnext/docs/user/manual/en/human-resources/index.txt @@ -1,5 +1,6 @@ employee leave +employee-advance expense-claim attendance salary-and-payroll diff --git a/erpnext/hr/doctype/employee_advance/__init__.py b/erpnext/hr/doctype/employee_advance/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/hr/doctype/employee_advance/employee_advance.js b/erpnext/hr/doctype/employee_advance/employee_advance.js new file mode 100644 index 00000000000..3f57939c24e --- /dev/null +++ b/erpnext/hr/doctype/employee_advance/employee_advance.js @@ -0,0 +1,54 @@ +// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Employee Advance', { + setup: function(frm) { + frm.add_fetch("employee", "company", "company"); + frm.add_fetch("company", "default_employee_advance_account", "advance_account"); + + frm.set_query("employee", function() { + return { + filters: { + "status": "Active" + } + }; + }); + + frm.set_query("advance_account", function() { + return { + filters: { + "root_type": "Asset", + "is_group": 0, + "company": frm.doc.company + } + }; + }); + }, + + refresh: function(frm) { + if (frm.doc.docstatus===1 + && (flt(frm.doc.paid_amount) < flt(frm.doc.advance_amount)) + && frappe.model.can_create("Payment Entry")) { + frm.add_custom_button(__('Payment'), + function() { frm.events.make_payment_entry(frm); }, __("Make")); + } + }, + + make_payment_entry: function(frm) { + var method = "erpnext.accounts.doctype.payment_entry.payment_entry.get_payment_entry"; + if(frm.doc.__onload && frm.doc.__onload.make_payment_via_journal_entry) { + method = "erpnext.hr.doctype.employee_advance.employee_advance.make_bank_entry" + } + return frappe.call({ + method: method, + args: { + "dt": frm.doc.doctype, + "dn": frm.doc.name + }, + callback: function(r) { + var doclist = frappe.model.sync(r.message); + frappe.set_route("Form", doclist[0].doctype, doclist[0].name); + } + }); + }, +}); diff --git a/erpnext/hr/doctype/employee_advance/employee_advance.json b/erpnext/hr/doctype/employee_advance/employee_advance.json new file mode 100644 index 00000000000..cd8637755cc --- /dev/null +++ b/erpnext/hr/doctype/employee_advance/employee_advance.json @@ -0,0 +1,631 @@ +{ + "allow_copy": 0, + "allow_guest_to_view": 0, + "allow_import": 1, + "allow_rename": 0, + "autoname": "naming_series:", + "beta": 0, + "creation": "2017-10-09 14:26:29.612365", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "naming_series", + "fieldtype": "Select", + "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": "Series", + "length": 0, + "no_copy": 0, + "options": "EA-", + "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_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "employee", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Employee", + "length": 0, + "no_copy": 0, + "options": "Employee", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "employee_name", + "fieldtype": "Read Only", + "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": "Employee Name", + "length": 0, + "no_copy": 0, + "options": "employee.employee_name", + "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_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_4", + "fieldtype": "Column Break", + "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, + "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_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "Today", + "fieldname": "posting_date", + "fieldtype": "Date", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Posting Date", + "length": 0, + "no_copy": 0, + "options": "", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "section_break_8", + "fieldtype": "Section Break", + "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, + "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_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "purpose", + "fieldtype": "Small Text", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Purpose", + "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": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_11", + "fieldtype": "Column Break", + "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, + "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_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "advance_amount", + "fieldtype": "Currency", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Advance Amount", + "length": 0, + "no_copy": 0, + "options": "Company:company:default_currency", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "paid_amount", + "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": "Paid Amount", + "length": 0, + "no_copy": 0, + "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_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "claimed_amount", + "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": "Claimed Amount", + "length": 0, + "no_copy": 0, + "options": "Company:company:default_currency", + "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_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "section_break_7", + "fieldtype": "Section Break", + "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, + "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_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "status", + "fieldtype": "Select", + "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": "Status", + "length": 0, + "no_copy": 1, + "options": "Draft\nPaid\nUnpaid\nRejected\nClaimed\nSubmitted\nCancelled", + "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_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "company", + "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": "Company", + "length": 0, + "no_copy": 0, + "options": "Company", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "amended_from", + "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": "Amended From", + "length": 0, + "no_copy": 1, + "options": "Employee Advance", + "permlevel": 0, + "print_hide": 1, + "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_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_18", + "fieldtype": "Column Break", + "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, + "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_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "advance_account", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 1, + "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": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "mode_of_payment", + "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": "Mode of Payment", + "length": 0, + "no_copy": 0, + "options": "Mode of Payment", + "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 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 1, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "modified": "2017-12-07 16:31:48.931260", + "modified_by": "Administrator", + "module": "HR", + "name": "Employee Advance", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "apply_user_permissions": 1, + "cancel": 0, + "create": 1, + "delete": 0, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Employee", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + }, + { + "amend": 1, + "apply_user_permissions": 0, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Expense Approver", + "set_user_permissions": 0, + "share": 1, + "submit": 1, + "write": 1 + } + ], + "quick_entry": 0, + "read_only": 0, + "read_only_onload": 0, + "search_fields": "employee,employee_name", + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 0 +} \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_advance/employee_advance.py b/erpnext/hr/doctype/employee_advance/employee_advance.py new file mode 100644 index 00000000000..8fe5759d040 --- /dev/null +++ b/erpnext/hr/doctype/employee_advance/employee_advance.py @@ -0,0 +1,96 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe import _ +from frappe.model.document import Document +from frappe.utils import flt, nowdate + +class EmployeeAdvanceOverPayment(frappe.ValidationError): + pass + +class EmployeeAdvance(Document): + def onload(self): + self.get("__onload").make_payment_via_journal_entry = frappe.db.get_single_value('Accounts Settings', + 'make_payment_via_journal_entry') + + def validate(self): + self.set_status() + + def on_cancel(self): + self.set_status() + + def set_status(self): + if not self.status: + self.status = "Draft" + if self.docstatus == 1: + if flt(self.claimed_amount) == flt(self.paid_amount): + self.status = "Claimed" + elif self.advance_amount == flt(self.paid_amount): + self.status = "Paid" + else: + self.status = "Unpaid" + elif self.docstatus == 2: + self.status = "Cancelled" + + def set_total_advance_paid(self): + paid_amount = frappe.db.sql(""" + select ifnull(sum(debit_in_account_currency), 0) as paid_amount + from `tabGL Entry` + where against_voucher_type = 'Employee Advance' + and against_voucher = %s + and party_type = 'Employee' + and party = %s + """, (self.name, self.employee), as_dict=1)[0].paid_amount + + if flt(paid_amount) > self.advance_amount: + frappe.throw(_("Row {0}# Paid Amount cannot be greater than requested advance amount"), + EmployeeAdvanceOverPayment) + + self.db_set("paid_amount", paid_amount) + self.set_status() + frappe.db.set_value("Employee Advance", self.name , "status", self.status) + + def update_claimed_amount(self): + claimed_amount = frappe.db.sql(""" + select sum(ifnull(allocated_amount, 0)) + from `tabExpense Claim Advance` + where employee_advance = %s and docstatus=1 and allocated_amount > 0 + """, self.name)[0][0] + + frappe.db.set_value("Employee Advance", self.name, "claimed_amount", claimed_amount) + +@frappe.whitelist() +def make_bank_entry(dt, dn): + from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_bank_cash_account + + doc = frappe.get_doc(dt, dn) + payment_account = get_default_bank_cash_account(doc.company, account_type="Cash", + mode_of_payment=doc.mode_of_payment) + + je = frappe.new_doc("Journal Entry") + je.posting_date = nowdate() + je.voucher_type = 'Bank Entry' + je.company = doc.company + je.remark = 'Payment against Employee Advance: ' + dn + '\n' + doc.purpose + + je.append("accounts", { + "account": doc.advance_account, + "debit_in_account_currency": flt(doc.advance_amount), + "reference_type": "Employee Advance", + "reference_name": doc.name, + "party_type": "Employee", + "party": doc.employee, + "is_advance": "Yes" + }) + + je.append("accounts", { + "account": payment_account.account, + "credit_in_account_currency": flt(doc.advance_amount), + "account_currency": payment_account.account_currency, + "account_type": payment_account.account_type + }) + + return je.as_dict() \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_advance/test_employee_advance.js b/erpnext/hr/doctype/employee_advance/test_employee_advance.js new file mode 100644 index 00000000000..1b9ec6f6d0c --- /dev/null +++ b/erpnext/hr/doctype/employee_advance/test_employee_advance.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Employee Advance", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Employee Advance + () => frappe.tests.make('Employee Advance', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/hr/doctype/employee_advance/test_employee_advance.py b/erpnext/hr/doctype/employee_advance/test_employee_advance.py new file mode 100644 index 00000000000..cacf90ef059 --- /dev/null +++ b/erpnext/hr/doctype/employee_advance/test_employee_advance.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +import frappe +import unittest +from frappe.utils import nowdate +from erpnext.hr.doctype.employee_advance.employee_advance import make_bank_entry +from erpnext.hr.doctype.employee_advance.employee_advance import EmployeeAdvanceOverPayment + +class TestEmployeeAdvance(unittest.TestCase): + def test_paid_amount_and_status(self): + advance = make_employee_advance() + + journal_entry = make_payment_entry(advance) + journal_entry.submit() + + advance.reload() + + self.assertEqual(advance.paid_amount, 1000) + self.assertEqual(advance.status, "Paid") + + # try making over payment + journal_entry1 = make_payment_entry(advance) + self.assertRaises(EmployeeAdvanceOverPayment, journal_entry1.submit) + +def make_payment_entry(advance): + journal_entry = frappe.get_doc(make_bank_entry("Employee Advance", advance.name)) + journal_entry.cheque_no = "123123" + journal_entry.cheque_date = nowdate() + journal_entry.save() + + return journal_entry + +def make_employee_advance(): + doc = frappe.new_doc("Employee Advance") + doc.employee = "_T-Employee-0001" + doc.company = "_Test company" + doc.purpose = "For site visit" + doc.advance_amount = 1000 + doc.posting_date = nowdate() + doc.advance_account = "_Test Employee Advance - _TC" + doc.insert() + doc.submit() + + return doc \ No newline at end of file diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.js b/erpnext/hr/doctype/expense_claim/expense_claim.js index 84391b64a68..c6333db7cf9 100644 --- a/erpnext/hr/doctype/expense_claim/expense_claim.js +++ b/erpnext/hr/doctype/expense_claim/expense_claim.js @@ -245,6 +245,34 @@ frappe.ui.form.on("Expense Claim", { task: function(frm) { erpnext.expense_claim.set_title(frm); + }, + + employee: function(frm) { + frm.events.get_advances(frm); + }, + + get_advances: function(frm) { + return frappe.call({ + method: "erpnext.hr.doctype.expense_claim.expense_claim.get_advances", + args: { + employee: frm.doc.employee + }, + callback: function(r, rt) { + frappe.model.clear_table(frm.doc, "advances"); + if(r.message) { + $.each(r.message, function(i, d) { + var row = frappe.model.add_child(frm.doc, "Expense Claim Advance", "advances"); + row.employee_advance = d.name; + row.posting_date = d.posting_date; + row.advance_account = d.advance_account; + row.advance_paid = d.paid_amount; + row.unclaimed_amount = flt(d.paid_amount) - flt(d.claimed_amount); + row.allocated_amount = flt(d.paid_amount) - flt(d.claimed_amount); + }); + refresh_field("advances"); + } + } + }); } }); diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.json b/erpnext/hr/doctype/expense_claim/expense_claim.json index ec52e7180a5..4ccadb6a999 100644 --- a/erpnext/hr/doctype/expense_claim/expense_claim.json +++ b/erpnext/hr/doctype/expense_claim/expense_claim.json @@ -951,6 +951,98 @@ "set_only_once": 0, "unique": 0, "width": "160px" + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "advance_payments", + "fieldtype": "Section Break", + "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 Payments", + "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_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "advances", + "fieldtype": "Table", + "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": "Advances", + "length": 0, + "no_copy": 0, + "options": "Expense Claim Advance", + "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_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "total_advance_amount", + "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 Amount", + "length": 0, + "no_copy": 0, + "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 } ], "has_web_view": 0, @@ -965,7 +1057,7 @@ "istable": 0, "max_attachments": 0, "menu_index": 0, - "modified": "2017-11-15 01:05:24.323540", + "modified": "2017-12-07 01:05:24.323540", "modified_by": "Administrator", "module": "HR", "name": "Expense Claim", diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.py b/erpnext/hr/doctype/expense_claim/expense_claim.py index 507d6a97525..945de9dab75 100644 --- a/erpnext/hr/doctype/expense_claim/expense_claim.py +++ b/erpnext/hr/doctype/expense_claim/expense_claim.py @@ -25,6 +25,7 @@ class ExpenseClaim(AccountsController): self.employee_name, self.total_claimed_amount) def validate(self): + self.validate_advances() self.validate_sanctioned_amount() self.validate_expense_approver() self.calculate_total_amount() @@ -70,6 +71,7 @@ class ExpenseClaim(AccountsController): update_reimbursed_amount(self) self.set_status() + self.update_claimed_amount_in_employee_advance() def on_cancel(self): self.update_task_and_project() @@ -80,6 +82,11 @@ class ExpenseClaim(AccountsController): update_reimbursed_amount(self) self.set_status() + self.update_claimed_amount_in_employee_advance() + + def update_claimed_amount_in_employee_advance(self): + for d in self.get("advances"): + frappe.get_doc("Employee Advance", d.employee_advance).update_claimed_amount() def update_task_and_project(self): if self.task: @@ -95,20 +102,23 @@ class ExpenseClaim(AccountsController): def get_gl_entries(self): gl_entry = [] self.validate_account_details() + + payable_amount = flt(self.total_sanctioned_amount) - flt(self.total_advance_amount) # payable entry - gl_entry.append( - self.get_gl_dict({ - "account": self.payable_account, - "credit": self.total_sanctioned_amount, - "credit_in_account_currency": self.total_sanctioned_amount, - "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 - }) - ) + if payable_amount: + gl_entry.append( + self.get_gl_dict({ + "account": self.payable_account, + "credit": payable_amount, + "credit_in_account_currency": payable_amount, + "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 for data in self.expenses: @@ -122,14 +132,28 @@ class ExpenseClaim(AccountsController): }) ) - if self.is_paid: + for data in self.advances: + gl_entry.append( + self.get_gl_dict({ + "account": data.advance_account, + "credit": data.allocated_amount, + "credit_in_account_currency": data.allocated_amount, + "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 + }) + ) + + if self.is_paid and payable_amount: # payment entry payment_account = get_bank_cash_account(self.mode_of_payment, self.company).get("account") gl_entry.append( self.get_gl_dict({ "account": payment_account, - "credit": self.total_sanctioned_amount, - "credit_in_account_currency": self.total_sanctioned_amount, + "credit": payable_amount, + "credit_in_account_currency": payable_amount, "against": self.employee }) ) @@ -140,8 +164,8 @@ class ExpenseClaim(AccountsController): "party_type": "Employee", "party": self.employee, "against": payment_account, - "debit": self.total_sanctioned_amount, - "debit_in_account_currency": self.total_sanctioned_amount, + "debit": payable_amount, + "debit_in_account_currency": payable_amount, "against_voucher": self.name, "against_voucher_type": self.doctype, }) @@ -180,6 +204,29 @@ class ExpenseClaim(AccountsController): task.update_total_expense_claim() task.save() + def validate_advances(self): + self.total_advance_amount = 0 + for d in self.get("advances"): + ref_doc = frappe.db.get_value("Employee Advance", d.employee_advance, + ["posting_date", "paid_amount", "claimed_amount", "advance_account"], as_dict=1) + d.posting_date = ref_doc.posting_date + d.advance_account = ref_doc.advance_account + d.advance_paid = ref_doc.paid_amount + d.unclaimed_amount = flt(ref_doc.paid_amount) - flt(ref_doc.claimed_amount) + + if d.allocated_amount and flt(d.allocated_amount) > flt(d.unclaimed_amount): + frappe.throw(_("Row {0}# Allocated amount {1} cannot be greater than unclaimed amount {2}") + .format(d.idx, d.allocated_amount, d.unclaimed_amount)) + + self.total_advance_amount += flt(d.allocated_amount) + + if self.total_advance_amount: + if flt(self.total_advance_amount) > flt(self.total_claimed_amount): + frappe.throw(_("Total advance amount cannot be greater than total claimed amount")) + if self.total_sanctioned_amount \ + and flt(self.total_advance_amount) > flt(self.total_sanctioned_amount): + frappe.throw(_("Total advance amount cannot be greater than total sanctioned amount")) + def validate_sanctioned_amount(self): for d in self.get('expenses'): if flt(d.sanctioned_amount) > flt(d.claim_amount): @@ -219,6 +266,9 @@ def make_bank_entry(dt, dn): if not default_bank_cash_account: default_bank_cash_account = get_default_bank_cash_account(expense_claim.company, "Cash") + payable_amount = flt(expense_claim.total_sanctioned_amount) \ + - flt(expense_claim.total_amount_reimbursed) - flt(expense_claim.total_advance_amount) + je = frappe.new_doc("Journal Entry") je.voucher_type = 'Bank Entry' je.company = expense_claim.company @@ -226,7 +276,7 @@ def make_bank_entry(dt, dn): je.append("accounts", { "account": expense_claim.payable_account, - "debit_in_account_currency": flt(expense_claim.total_sanctioned_amount - expense_claim.total_amount_reimbursed), + "debit_in_account_currency": payable_amount, "reference_type": "Expense Claim", "party_type": "Employee", "party": expense_claim.employee, @@ -235,7 +285,7 @@ def make_bank_entry(dt, dn): je.append("accounts", { "account": default_bank_cash_account.account, - "credit_in_account_currency": flt(expense_claim.total_sanctioned_amount - expense_claim.total_amount_reimbursed), + "credit_in_account_currency": payable_amount, "reference_type": "Expense Claim", "reference_name": expense_claim.name, "balance": default_bank_cash_account.balance, @@ -256,4 +306,12 @@ def get_expense_claim_account(expense_claim_type, company): return { "account": account - } \ No newline at end of file + } + +@frappe.whitelist() +def get_advances(employee): + return frappe.db.sql(""" + select name, posting_date, paid_amount, claimed_amount, advance_account + from `tabEmployee Advance` + where docstatus=1 and employee=%s and paid_amount > 0 and paid_amount > claimed_amount + """, employee, as_dict=1) \ No newline at end of file diff --git a/erpnext/hr/doctype/expense_claim_advance/__init__.py b/erpnext/hr/doctype/expense_claim_advance/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/hr/doctype/expense_claim_advance/expense_claim_advance.json b/erpnext/hr/doctype/expense_claim_advance/expense_claim_advance.json new file mode 100644 index 00000000000..b5e4fd16f8e --- /dev/null +++ b/erpnext/hr/doctype/expense_claim_advance/expense_claim_advance.json @@ -0,0 +1,238 @@ +{ + "allow_copy": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2017-10-09 16:53:26.410762", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "Document", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 2, + "fieldname": "employee_advance", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Employee Advance", + "length": 0, + "no_copy": 1, + "oldfieldname": "journal_voucher", + "oldfieldtype": "Link", + "options": "Employee Advance", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "print_width": "250px", + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0, + "width": "250px" + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 2, + "fieldname": "posting_date", + "fieldtype": "Date", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Posting Date", + "length": 0, + "no_copy": 0, + "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_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 2, + "fieldname": "advance_paid", + "fieldtype": "Currency", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Advance Paid", + "length": 0, + "no_copy": 0, + "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_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 2, + "fieldname": "unclaimed_amount", + "fieldtype": "Currency", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Unclaimed amount", + "length": 0, + "no_copy": 1, + "oldfieldname": "advance_amount", + "oldfieldtype": "Currency", + "options": "Company:company.default_currency", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "print_width": "120px", + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0, + "width": "120px" + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 2, + "fieldname": "allocated_amount", + "fieldtype": "Currency", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Allocated amount", + "length": 0, + "no_copy": 1, + "oldfieldname": "allocated_amount", + "oldfieldtype": "Currency", + "options": "Company:company.default_currency", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "print_width": "120px", + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0, + "width": "120px" + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "advance_account", + "fieldtype": "Link", + "hidden": 1, + "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 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 1, + "max_attachments": 0, + "modified": "2017-10-09 19:59:48.818139", + "modified_by": "Administrator", + "module": "HR", + "name": "Expense Claim Advance", + "name_case": "", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 0, + "track_seen": 0 +} \ No newline at end of file diff --git a/erpnext/hr/doctype/expense_claim_advance/expense_claim_advance.py b/erpnext/hr/doctype/expense_claim_advance/expense_claim_advance.py new file mode 100644 index 00000000000..c4e7b026e3b --- /dev/null +++ b/erpnext/hr/doctype/expense_claim_advance/expense_claim_advance.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe.model.document import Document + +class ExpenseClaimAdvance(Document): + pass diff --git a/erpnext/hub_node/api.py b/erpnext/hub_node/api.py deleted file mode 100644 index b32efd9799f..00000000000 --- a/erpnext/hub_node/api.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright (c) 2015, Web Notes Technologies Pvt. Ltd. and Contributors and contributors -# For license information, please see license.txt - - -import frappe, json -from frappe.utils import now, nowdate -from erpnext.hub_node.doctype.hub_settings.hub_settings import get_hub_settings - -# API wrapper -@frappe.whitelist(allow_guest=True) -def call_method(access_token, method, message): - try: - args = json.loads(message) - if args: - return globals()[method](access_token, args) - else: - return globals()[method](access_token) - except: - print("Client Exception") - print(frappe.get_traceback()) - -def disable_and_suspend_hub_user(access_token): - hub_settings = get_hub_settings() - hub_settings.publish = 0 - hub_settings.publish_pricing = 0 - hub_settings.publish_availability = 0 - hub_settings.suspended = 1 - hub_settings.enabled = 0 - hub_settings.save(ignore_permissions=True) diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js index a0e11d63483..8b7e641e3a5 100644 --- a/erpnext/setup/doctype/company/company.js +++ b/erpnext/setup/doctype/company/company.js @@ -165,7 +165,8 @@ erpnext.company.setup_queries = function(frm) { ["default_inventory_account", {"account_type": "Stock"}], ["cost_center", {}], ["round_off_cost_center", {}], - ["depreciation_cost_center", {}] + ["depreciation_cost_center", {}], + ["default_employee_advance_account", {"root_type": "Asset"}], ], function(i, v) { erpnext.company.set_custom_query(frm, v); }); @@ -189,12 +190,14 @@ erpnext.company.set_custom_query = function(frm, v) { "company": frm.doc.name, "is_group": 0 }; - for (var key in v[1]) + + for (var key in v[1]) { filters[key] = v[1][key]; + } frm.set_query(v[0], function() { return { filters: filters - }; + } }); } diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json index ce39ae1a002..9824c706cf2 100644 --- a/erpnext/setup/doctype/company/company.json +++ b/erpnext/setup/doctype/company/company.json @@ -964,6 +964,37 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "default_employee_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": "Default Employee Advance Account", + "length": 0, + "no_copy": 1, + "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_on_submit": 0, @@ -2021,7 +2052,7 @@ "istable": 0, "max_attachments": 0, "menu_index": 0, - "modified": "2017-12-07 17:40:24.646920", + "modified": "2017-12-07 18:40:24.646920", "modified_by": "Administrator", "module": "Setup", "name": "Company",