diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js index c98e77fdd24..fc261a5c9da 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.js +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js @@ -86,9 +86,9 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({ }; }); - me.frm.set_query("party_type", "accounts", function(doc, cdt, cdn) { - return { - filters: {"name": ["in", ["Customer", "Supplier"]]} + me.frm.set_query("party_type", "accounts", function() { + return{ + query: "erpnext.setup.doctype.party_type.party_type.get_party_type" } }); diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index a17668033e6..a13cc7dde20 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -9,6 +9,7 @@ from erpnext.controllers.accounts_controller import AccountsController from erpnext.accounts.utils import get_balance_on, get_account_currency from erpnext.setup.utils import get_company_currency from erpnext.accounts.party import get_party_account +from erpnext.hr.doctype.expense_claim.expense_claim import update_reimbursed_amount class JournalEntry(AccountsController): def __init__(self, arg1, arg2=None): @@ -375,7 +376,7 @@ class JournalEntry(AccountsController): bank_amount = party_amount = total_amount = 0.0 currency = bank_account_currency = party_account_currency = pay_to_recd_from= None for d in self.get('accounts'): - if d.party_type and d.party: + if d.party_type in ['Customer', 'Supplier'] and d.party: if not pay_to_recd_from: pay_to_recd_from = frappe.db.get_value(d.party_type, d.party, "customer_name" if d.party_type=="Customer" else "supplier_name") @@ -503,11 +504,9 @@ class JournalEntry(AccountsController): def update_expense_claim(self): for d in self.accounts: - if d.reference_type=="Expense Claim": - amt = frappe.db.sql("""select sum(debit) as amt from `tabJournal Entry Account` - where reference_type = "Expense Claim" and - reference_name = %s and docstatus = 1""", d.reference_name ,as_dict=1)[0].amt - frappe.db.set_value("Expense Claim", d.reference_name , "total_amount_reimbursed", amt) + if d.reference_type=="Expense Claim" and d.party: + doc = frappe.get_doc("Expense Claim", d.reference_name) + update_reimbursed_amount(doc) def validate_expense_claim(self): for d in self.accounts: diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index d3dbd314cc7..6528c8fd518 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -26,6 +26,12 @@ frappe.ui.form.on('Payment Entry', { } }); + frm.set_query("party_type", function() { + return{ + query: "erpnext.setup.doctype.party_type.party_type.get_party_type" + } + }); + frm.set_query("paid_to", function() { var account_types = in_list(["Receive", "Internal Transfer"], frm.doc.payment_type) ? ["Bank", "Cash"] : party_account_type; diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.json b/erpnext/accounts/doctype/payment_entry/payment_entry.json index c56a83dcaf9..98fa445c9b1 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.json +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.json @@ -97,95 +97,6 @@ "set_only_once": 0, "unique": 0 }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:in_list([\"Receive\", \"Pay\"], doc.payment_type)", - "fieldname": "party_type", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "in_standard_filter": 1, - "label": "Party Type", - "length": 0, - "no_copy": 0, - "options": "Customer\nSupplier", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "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_on_submit": 0, - "bold": 1, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:in_list([\"Receive\", \"Pay\"], doc.payment_type) && doc.party_type", - "fieldname": "party", - "fieldtype": "Dynamic Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "in_standard_filter": 1, - "label": "Party", - "length": 0, - "no_copy": 0, - "options": "party_type", - "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_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "", - "fieldname": "party_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Party Name", - "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_on_submit": 0, "bold": 0, @@ -212,7 +123,7 @@ "search_index": 0, "set_only_once": 0, "unique": 0 - }, + }, { "allow_on_submit": 0, "bold": 1, @@ -300,6 +211,152 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "eval:in_list([\"Receive\", \"Pay\"], doc.payment_type) && doc.party_type", + "fieldname": "party_section", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Payment From / To", + "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_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "eval:in_list([\"Receive\", \"Pay\"], doc.payment_type)", + "fieldname": "party_type", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "in_standard_filter": 1, + "label": "Party Type", + "length": 0, + "no_copy": 0, + "options": "DocType", + "permlevel": 0, + "precision": "", + "print_hide": 1, + "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_on_submit": 0, + "bold": 1, + "collapsible": 0, + "columns": 0, + "depends_on": "eval:in_list([\"Receive\", \"Pay\"], doc.payment_type) && doc.party_type", + "fieldname": "party", + "fieldtype": "Dynamic Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "in_standard_filter": 1, + "label": "Party", + "length": 0, + "no_copy": 0, + "options": "party_type", + "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_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_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_on_submit": 1, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "eval:in_list([\"Receive\", \"Pay\"], doc.payment_type) && doc.party_type", + "description": "", + "fieldname": "party_name", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Party Name", + "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_on_submit": 0, "bold": 0, @@ -1561,7 +1618,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2016-11-07 05:33:40.371480", + "modified": "2017-01-30 00:41:51.348616", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Entry", @@ -1578,7 +1635,6 @@ "export": 1, "if_owner": 0, "import": 1, - "is_custom": 0, "permlevel": 0, "print": 1, "read": 1, @@ -1599,7 +1655,6 @@ "export": 1, "if_owner": 0, "import": 1, - "is_custom": 0, "permlevel": 0, "print": 1, "read": 1, @@ -1617,5 +1672,6 @@ "sort_field": "modified", "sort_order": "DESC", "title_field": "title", + "track_changes": 0, "track_seen": 0 } \ No newline at end of file diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js index 6a951a0aff6..272e47448ef 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js @@ -6,12 +6,10 @@ frappe.provide("erpnext.accounts"); erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.extend({ onload: function() { var me = this - this.frm.set_query('party_type', function() { - return { - filters: { - "name": ["in", ["Customer", "Supplier"]] - } - }; + this.frm.set_query("party_type", function() { + return{ + query: "erpnext.setup.doctype.party_type.party_type.get_party_type" + } }); this.frm.set_query('receivable_payable_account', function() { diff --git a/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json b/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json index f77560c1396..57fce656437 100644 --- a/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json +++ b/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json @@ -22,6 +22,7 @@ "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, + "in_standard_filter": 0, "label": "Reference Type", "length": 0, "no_copy": 0, @@ -30,6 +31,7 @@ "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, @@ -48,7 +50,8 @@ "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 1, - "label": "Reference_name", + "in_standard_filter": 0, + "label": "Reference Name", "length": 0, "no_copy": 0, "options": "reference_type", @@ -57,6 +60,7 @@ "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, @@ -75,6 +79,7 @@ "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, + "in_standard_filter": 0, "label": "Posting Date", "length": 0, "no_copy": 0, @@ -82,6 +87,7 @@ "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, @@ -100,6 +106,7 @@ "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, + "in_standard_filter": 0, "label": "Is Advance", "length": 0, "no_copy": 0, @@ -107,6 +114,7 @@ "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, @@ -125,6 +133,7 @@ "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, + "in_standard_filter": 0, "label": "Reference Row", "length": 0, "no_copy": 0, @@ -132,6 +141,7 @@ "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, @@ -150,6 +160,7 @@ "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, + "in_standard_filter": 0, "label": "", "length": 0, "no_copy": 0, @@ -157,6 +168,7 @@ "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, @@ -175,6 +187,7 @@ "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 1, + "in_standard_filter": 0, "label": "Invoice Number", "length": 0, "no_copy": 0, @@ -183,6 +196,7 @@ "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, @@ -201,6 +215,7 @@ "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 1, + "in_standard_filter": 0, "label": "Amount", "length": 0, "no_copy": 0, @@ -208,6 +223,7 @@ "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, @@ -226,6 +242,7 @@ "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 1, + "in_standard_filter": 0, "label": "Allocated amount", "length": 0, "no_copy": 0, @@ -234,6 +251,7 @@ "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, @@ -252,6 +270,7 @@ "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, + "in_standard_filter": 0, "label": "", "length": 0, "no_copy": 0, @@ -259,6 +278,7 @@ "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, @@ -277,6 +297,7 @@ "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 1, + "in_standard_filter": 0, "label": "Remark", "length": 0, "no_copy": 0, @@ -284,6 +305,7 @@ "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, @@ -302,7 +324,7 @@ "istable": 1, "max_attachments": 0, "menu_index": 0, - "modified": "2016-08-26 02:08:35.879133", + "modified": "2017-01-30 01:04:22.557237", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Reconciliation Payment", @@ -314,5 +336,6 @@ "read_only_onload": 0, "sort_field": "modified", "sort_order": "DESC", + "track_changes": 0, "track_seen": 0 } \ No newline at end of file diff --git a/erpnext/accounts/doctype/sales_invoice/pos.py b/erpnext/accounts/doctype/sales_invoice/pos.py index bd547761770..070dbeb2b8f 100644 --- a/erpnext/accounts/doctype/sales_invoice/pos.py +++ b/erpnext/accounts/doctype/sales_invoice/pos.py @@ -30,6 +30,7 @@ def get_pos_data(): 'doc': doc, 'default_customer': pos_profile.get('customer'), 'items': get_items_list(pos_profile), + 'item_groups': get_item_group(pos_profile), 'customers': get_customers_list(pos_profile), 'serial_no_data': get_serial_no_data(pos_profile, doc.company), 'batch_no_data': get_batch_no_data(), @@ -140,6 +141,15 @@ def get_items_list(pos_profile): disabled = 0 and has_variants = 0 and is_sales_item = 1 and {cond} """.format(cond=cond), tuple(item_groups), as_dict=1) +def get_item_group(pos_profile): + if pos_profile.get('item_groups'): + item_groups = [] + for d in pos_profile.get('item_groups'): + item_groups.extend(get_child_nodes('Item Group', d.item_group)) + return item_groups + else: + return frappe.db.sql_list("""Select name from `tabItem Group` order by name""") + def get_customers_list(pos_profile): cond = "1=1" customer_groups = [] @@ -276,11 +286,33 @@ def validate_customer(doc): customer_doc = frappe.new_doc('Customer') customer_doc.customer_name = doc.get('customer') customer_doc.customer_type = 'Company' - customer_doc.customer_group = doc.get('customer_group') - customer_doc.territory = doc.get('territory') + customer_doc.customer_group = frappe.db.get_single_value('Selling Settings', 'customer_group') + customer_doc.territory = frappe.db.get_single_value('Selling Settings', 'territory') + customer_doc.flags.ignore_mandatory = True customer_doc.save(ignore_permissions = True) frappe.db.commit() doc['customer'] = customer_doc.name + if doc.get('contact_details'): + args = json.loads(doc.get("contact_details")) + make_address(doc, args, customer_doc.name) + +def make_address(doc, args, customer): + if args.get("address_line1"): + address = frappe.new_doc('Address') + address.address_line1 = args.get('address_line1') + address.address_line2 = args.get('address_line2') + address.city = args.get('city') + address.state = args.get('state') + address.zip_code = args.get('zip_code') + address.email_id = args.get('email_id') + address.flags.ignore_mandatory = True + address.country = frappe.db.get_value('Company', doc.get('company'), 'country') + address.append('links',{ + 'link_doctype': 'Customer', + 'link_name': customer + }) + address.save(ignore_permissions = True) + frappe.db.commit() def validate_item(doc): for item in doc.get('items'): diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json index 7ade3187e25..f026cb06fa2 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json @@ -28,7 +28,7 @@ "label": "", "length": 0, "no_copy": 0, - "options": "fa fa-user", + "options": "icon-user", "permlevel": 0, "print_hide": 0, "print_hide_if_no_value": 0, @@ -232,7 +232,7 @@ "in_standard_filter": 0, "label": "Offline POS Name", "length": 0, - "no_copy": 0, + "no_copy": 1, "permlevel": 0, "precision": "", "print_hide": 0, @@ -1035,7 +1035,7 @@ "length": 0, "no_copy": 0, "oldfieldtype": "Section Break", - "options": "fa fa-shopping-cart", + "options": "icon-shopping-cart", "permlevel": 0, "print_hide": 0, "print_hide_if_no_value": 0, @@ -1122,7 +1122,7 @@ "label": "Packing List", "length": 0, "no_copy": 0, - "options": "fa fa-suitcase", + "options": "icon-suitcase", "permlevel": 0, "print_hide": 1, "print_hide_if_no_value": 0, @@ -1462,7 +1462,7 @@ "length": 0, "no_copy": 0, "oldfieldtype": "Section Break", - "options": "fa fa-money", + "options": "icon-money", "permlevel": 0, "print_hide": 0, "print_hide_if_no_value": 0, @@ -1941,7 +1941,7 @@ "length": 0, "no_copy": 0, "oldfieldtype": "Section Break", - "options": "fa fa-money", + "options": "icon-money", "permlevel": 0, "print_hide": 1, "print_hide_if_no_value": 0, @@ -2238,7 +2238,7 @@ "length": 0, "no_copy": 0, "oldfieldtype": "Section Break", - "options": "fa fa-money", + "options": "icon-money", "permlevel": 0, "print_hide": 1, "print_hide_if_no_value": 0, @@ -2327,7 +2327,7 @@ "label": "Payments", "length": 0, "no_copy": 0, - "options": "fa fa-money", + "options": "icon-money", "permlevel": 0, "print_hide": 0, "print_hide_if_no_value": 0, @@ -3252,7 +3252,7 @@ "length": 0, "no_copy": 0, "oldfieldtype": "Section Break", - "options": "fa fa-file-text", + "options": "icon-file-text", "permlevel": 0, "print_hide": 1, "print_hide_if_no_value": 0, @@ -3515,7 +3515,7 @@ "length": 0, "no_copy": 0, "oldfieldtype": "Section Break", - "options": "fa fa-group", + "options": "icon-group", "permlevel": 0, "print_hide": 1, "print_hide_if_no_value": 0, @@ -3720,7 +3720,7 @@ "label": "Recurring", "length": 0, "no_copy": 0, - "options": "fa fa-time", + "options": "icon-time", "permlevel": 0, "print_hide": 1, "print_hide_if_no_value": 0, @@ -4173,7 +4173,7 @@ ], "hide_heading": 0, "hide_toolbar": 0, - "icon": "fa fa-file-text", + "icon": "icon-file-text", "idx": 181, "image_view": 0, "in_create": 0, @@ -4282,5 +4282,6 @@ "sort_order": "DESC", "timeline_field": "customer", "title_field": "title", + "track_changes": 1, "track_seen": 0 } \ No newline at end of file diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 3eb2f7d7d18..3dfe6808830 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -963,40 +963,6 @@ class TestSalesInvoice(unittest.TestCase): si.insert() self.assertEqual(si.get("items")[0].rate, flt((price_list_rate*25)/100 + price_list_rate)) - def test_party_status(self): - from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry - from frappe.utils import random_string - - customer_name = 'test customer for status' - - if frappe.db.exists('Customer', customer_name): - customer = frappe.get_doc('Customer', customer_name) - customer.db_set('status', 'Active') - else: - customer = frappe.get_doc({ - 'doctype': 'Customer', - 'customer_name': customer_name, - 'customer_group': 'Commercial', - 'customer_type': 'Individual', - 'territory': 'Rest of the World' - }).insert() - - self.assertEquals(frappe.db.get_value('Customer', customer.name, 'status'), 'Active') - - invoice = create_sales_invoice(customer="test customer for status", - debit_to="_Test Receivable - _TC", - currency="USD", conversion_rate=50) - - self.assertEquals(frappe.db.get_value('Customer', customer.name, 'status'), 'Open') - - pe = get_payment_entry(invoice.doctype, invoice.name) - pe.reference_no = random_string(10) - pe.reference_date = invoice.posting_date - pe.insert() - pe.submit() - - self.assertEquals(frappe.db.get_value('Customer', customer.name, 'status'), 'Active') - def test_outstanding_amount_after_advance_jv_cancelation(self): from erpnext.accounts.doctype.journal_entry.test_journal_entry \ import test_records as jv_test_records diff --git a/erpnext/accounts/page/pos/pos.js b/erpnext/accounts/page/pos/pos.js index 13abe145495..ed54ab400ec 100644 --- a/erpnext/accounts/page/pos/pos.js +++ b/erpnext/accounts/page/pos/pos.js @@ -1,7 +1,7 @@ frappe.provide("erpnext.pos"); {% include "erpnext/public/js/controllers/taxes_and_totals.js" %} -frappe.pages['pos'].on_page_load = function(wrapper) { +frappe.pages['pos'].on_page_load = function (wrapper) { var page = frappe.ui.make_app_page({ parent: wrapper, title: __('Point of Sale'), @@ -11,15 +11,15 @@ frappe.pages['pos'].on_page_load = function(wrapper) { wrapper.pos = new erpnext.pos.PointOfSale(wrapper) } -frappe.pages['pos'].refresh = function(wrapper) { +frappe.pages['pos'].refresh = function (wrapper) { window.onbeforeunload = function () { return wrapper.pos.beforeunload() } } - erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ - init: function(wrapper){ + init: function (wrapper) { + this.page_len = 20; this.page = wrapper.page; this.wrapper = $(wrapper).find('.page-content'); this.set_indicator(); @@ -28,13 +28,13 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ this.si_docs = this.get_doc_from_localstorage(); }, - beforeunload: function(e){ - if(this.connection_status == false && frappe.get_route()[0] == "pos"){ + beforeunload: function (e) { + if (this.connection_status == false && frappe.get_route()[0] == "pos") { e = e || window.event; // For IE and Firefox prior to version 4 if (e) { - e.returnValue = __("You are in offline mode. You will not be able to reload until you have network."); + e.returnValue = __("You are in offline mode. You will not be able to reload until you have network."); return } @@ -43,23 +43,23 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ } }, - check_internet_connection: function(){ + check_internet_connection: function () { var me = this; //Check Internet connection after every 30 seconds - setInterval(function(){ + setInterval(function () { me.set_indicator(); }, 5000) }, - set_indicator: function(){ + set_indicator: function () { var me = this; // navigator.onLine this.connection_status = false; this.page.set_indicator(__("Offline"), "grey") frappe.call({ - method:"frappe.handler.ping", - callback: function(r){ - if(r.message){ + method: "frappe.handler.ping", + callback: function (r) { + if (r.message) { me.connection_status = true; me.page.set_indicator(__("Online"), "green") } @@ -67,27 +67,23 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ }) }, - onload: function(){ + onload: function () { var me = this; - this.get_data_from_server(function(){ + this.get_data_from_server(function () { me.create_new(); }); }, - make_menu_list: function(){ + make_menu_list: function () { var me = this; - this.page.add_menu_item(__("New Sales Invoice"), function() { + this.page.add_menu_item(__("New Sales Invoice"), function () { me.save_previous_entry(); me.create_new(); }) - this.page.add_menu_item(__("View Offline Records"), function(){ - me.show_unsync_invoice_list(); - }); - - this.page.add_menu_item(__("Sync Master Data"), function(){ - me.get_data_from_server(function(){ + this.page.add_menu_item(__("Sync Master Data"), function () { + me.get_data_from_server(function () { me.load_data(false); me.make_customer(); me.make_item_list(); @@ -95,83 +91,29 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ }) }); - this.page.add_menu_item(__("Sync Offline Invoices"), function(){ + this.page.add_menu_item(__("Sync Offline Invoices"), function () { me.sync_sales_invoice() }); - this.page.add_menu_item(__("POS Profile"), function() { + this.page.add_menu_item(__("POS Profile"), function () { frappe.set_route('List', 'POS Profile'); }); }, - show_unsync_invoice_list: function(){ - var me = this; - this.si_docs = this.get_doc_from_localstorage(); - this.list_dialog = new frappe.ui.Dialog({ - title: 'Invoice List' - }); - - this.list_dialog.show(); - this.list_body = this.list_dialog.body; - if(me.pos_profile_data["allow_delete"]) { - this.list_dialog.set_primary_action(__("Delete"), function() { - frappe.confirm(__("Delete permanently?"), function () { - me.delete_records(); - }) - }).addClass("btn-danger"); - this.toggle_primary_action(); - } - - if(this.si_docs.length > 0){ - me.render_offline_data(); - me.dialog_actions() - }else{ - $(this.list_body).append(repl('
" + +__("Not items found")+"
").appendTo($wrap) } - if(this.items.length == 1 - && this.search.$input.val()) { - this.search.$input.val(""); + if (this.items.length == 1 + && this.serach_item.$input.val()) { + this.serach_item.$input.val(""); this.add_to_cart(); } // if form is local then allow this function - $(me.wrapper).find("div.pos-item").on("click", function() { + $(me.wrapper).find("div.pos-item").on("click", function () { + if(me.list_customers_btn.hasClass("view_customer")) return; + me.customer_validate(); - if(me.frm.doc.docstatus==0) { + if (me.frm.doc.docstatus == 0) { me.items = me.get_items($(this).attr("data-item-code")) me.add_to_cart(); } }); }, - get_items: function(item_code){ + get_items: function (item_code) { // To search item as per the key enter var me = this; this.item_serial_no = {}; this.item_batch_no = {}; - if(item_code){ - return $.grep(this.item_data, function(item){ - if(item.item_code == item_code ){ + if (item_code) { + return $.grep(this.item_data, function (item) { + if (item.item_code == item_code) { return true } }) } - key = this.search.$input.val().toLowerCase().replace(/[&\/\\#,+()\[\]$~.'":*?<>{}]/g,'\\$&'); + this.items_list = this.apply_category(); + + key = this.serach_item.$input.val().toLowerCase().replace(/[&\/\\#,+()\[\]$~.'":*?<>{}]/g, '\\$&'); var re = new RegExp('%', 'g'); var reg = new RegExp(key.replace(re, '[\\w*\\s*[a-zA-Z0-9]*]*')) search_status = true - if(key){ - return $.grep(this.item_data, function(item){ - if(search_status){ - if(in_list(me.batch_no_data[item.item_code], me.search.$input.val())){ + if (key) { + return $.grep(this.items_list, function (item) { + if (search_status) { + if (in_list(me.batch_no_data[item.item_code], me.serach_item.$input.val())) { search_status = false; - return me.item_batch_no[item.item_code] = me.search.$input.val() - } else if( me.serial_no_data[item.item_code] - && in_list(Object.keys(me.serial_no_data[item.item_code]), me.search.$input.val())) { + return me.item_batch_no[item.item_code] = me.serach_item.$input.val() + } else if (me.serial_no_data[item.item_code] + && in_list(Object.keys(me.serial_no_data[item.item_code]), me.serach_item.$input.val())) { search_status = false; - me.item_serial_no[item.item_code] = [me.search.$input.val(), me.serial_no_data[item.item_code][me.search.$input.val()]] + me.item_serial_no[item.item_code] = [me.serach_item.$input.val(), me.serial_no_data[item.item_code][me.serach_item.$input.val()]] return true - } else if(item.barcode == me.search.$input.val()) { + } else if (item.barcode == me.serach_item.$input.val()) { search_status = false; - return item.barcode == me.search.$input.val(); - } else if(reg.test(item.item_code.toLowerCase()) || reg.test(item.description.toLowerCase()) || - reg.test(item.item_name.toLowerCase()) || reg.test(item.item_group.toLowerCase()) ){ + return item.barcode == me.serach_item.$input.val(); + } else if (reg.test(item.item_code.toLowerCase()) || reg.test(item.description.toLowerCase()) || + reg.test(item.item_name.toLowerCase()) || reg.test(item.item_group.toLowerCase())) { return true } } }) - }else{ - return this.item_data; + } else { + return this.items_list; } }, - bind_qty_event: function() { + apply_category: function() { + var me = this; + category = this.search_item_group.$input.val(); + + if(category == 'All Item Groups') { + return this.item_data + } else { + return this.item_data.filter(function(element, index, array){ + return element.item_group == category; + }); + } + }, + + bind_qty_event: function () { var me = this; - $(this.wrapper).find(".pos-item-qty").on("change", function(){ + $(this.wrapper).find(".pos-item-qty").on("change", function () { var item_code = $(this).parents(".pos-bill-item").attr("data-item-code"); var qty = $(this).val(); me.update_qty(item_code, qty) }) - $(this.wrapper).find("[data-action='increase-qty']").on("click", function(){ + $(this.wrapper).find("[data-action='increase-qty']").on("click", function () { var item_code = $(this).parents(".pos-bill-item").attr("data-item-code"); var qty = flt($(this).parents(".pos-bill-item").find('.pos-item-qty').val()) + 1; me.update_qty(item_code, qty) }) - $(this.wrapper).find("[data-action='decrease-qty']").on("click", function(){ + $(this.wrapper).find("[data-action='decrease-qty']").on("click", function () { var item_code = $(this).parents(".pos-bill-item").attr("data-item-code"); var qty = flt($(this).parents(".pos-bill-item").find('.pos-item-qty').val()) - 1; me.update_qty(item_code, qty) }) - }, - update_qty: function(item_code, qty) { - var me = this; - this.items = this.get_items(item_code); - this.validate_serial_no() - this.update_qty_rate_against_item_code(item_code, "qty", qty); - }, - - update_rate: function() { - var me = this; - - $(this.wrapper).find(".pos-item-rate").on("change", function(){ + $(this.wrapper).find(".pos-item-discount").on("change", function () { var item_code = $(this).parents(".pos-bill-item").attr("data-item-code"); - me.update_qty_rate_against_item_code(item_code, "rate", $(this).val()); + var discount = $(this).val(); + me.update_discount(item_code, discount) }) }, - update_qty_rate_against_item_code: function(item_code, field, value){ + update_qty: function (item_code, qty) { var me = this; - if(value < 0){ + this.items = this.get_items(item_code); + this.validate_serial_no() + this.set_item_details(item_code, "qty", qty); + }, + + update_discount: function(item_code, discount) { + var me = this; + this.items = this.get_items(item_code); + this.set_item_details(item_code, "discount_percentage", discount); + }, + + update_rate: function () { + var me = this; + + $(this.wrapper).find(".pos-item-rate").on("change", function () { + var item_code = $(this).parents(".pos-bill-item").attr("data-item-code"); + me.set_item_details(item_code, "rate", $(this).val()); + }) + }, + + set_item_details: function (item_code, field, value) { + var me = this; + if (value < 0) { frappe.throw(__("Enter value must be positive")); } this.remove_item = [] - $.each(this.frm.doc["items"] || [], function(i, d) { - if(d.serial_no && field == 'qty'){ - me.validate_serial_no_qty(d, item_code, field, value) - } - + $.each(this.frm.doc["items"] || [], function (i, d) { if (d.item_code == item_code) { + if (d.serial_no && field == 'qty') { + me.validate_serial_no_qty(d, item_code, field, value) + } + d[field] = flt(value); d.amount = flt(d.rate) * flt(d.qty); - if(d.qty==0){ + if (d.qty == 0) { me.remove_item.push(d.idx) } } }); - if(field == 'qty'){ + if (field == 'qty') { this.remove_zero_qty_item(); } this.update_paid_amount_status(false) }, - remove_zero_qty_item: function(){ + remove_zero_qty_item: function () { var me = this; idx = 0 this.items = [] idx = 0 - $.each(this.frm.doc["items"] || [], function(i, d) { - if(!in_list(me.remove_item, d.idx)){ + $.each(this.frm.doc["items"] || [], function (i, d) { + if (!in_list(me.remove_item, d.idx)) { d.idx = idx; me.items.push(d); idx++; @@ -694,23 +886,23 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ this.frm.doc["items"] = this.items; }, - make_discount_field: function(){ + make_discount_field: function () { var me = this; - this.wrapper.find('input.discount-percentage').on("change", function() { + this.wrapper.find('input.discount-percentage').on("change", function () { me.frm.doc.additional_discount_percentage = flt($(this).val(), precision("additional_discount_percentage")); total = me.frm.doc.grand_total - if(me.frm.doc.apply_discount_on == 'Net Total'){ + if (me.frm.doc.apply_discount_on == 'Net Total') { total = me.frm.doc.net_total } - me.frm.doc.discount_amount = flt(total*flt(me.frm.doc.additional_discount_percentage) / 100, precision("discount_amount")); + me.frm.doc.discount_amount = flt(total * flt(me.frm.doc.additional_discount_percentage) / 100, precision("discount_amount")); me.wrapper.find('input.discount-amount').val(me.frm.doc.discount_amount) me.refresh(); }); - this.wrapper.find('input.discount-amount').on("change", function() { + this.wrapper.find('input.discount-amount').on("change", function () { me.frm.doc.discount_amount = flt($(this).val(), precision("discount_amount")); me.frm.doc.additional_discount_percentage = 0.0; me.wrapper.find('input.discount-percentage').val(0); @@ -718,14 +910,14 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ }); }, - customer_validate: function(){ + customer_validate: function () { var me = this; - if(!this.frm.doc.customer){ + if (!this.frm.doc.customer) { frappe.throw(__("Please select customer")) } }, - add_to_cart: function() { + add_to_cart: function () { var me = this; var caught = false; var no_of_items = me.wrapper.find(".pos-bill-item").length; @@ -736,17 +928,17 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ this.validate_warehouse(); if (no_of_items != 0) { - $.each(this.frm.doc["items"] || [], function(i, d) { + $.each(this.frm.doc["items"] || [], function (i, d) { if (d.item_code == me.items[0].item_code) { caught = true; d.qty += 1; d.amount = flt(d.rate) * flt(d.qty); - if(me.item_serial_no[d.item_code]){ + if (me.item_serial_no[d.item_code]) { d.serial_no += '\n' + me.item_serial_no[d.item_code][0] d.warehouse = me.item_serial_no[d.item_code][1] } - if(me.item_batch_no.length){ + if (me.item_batch_no.length) { d.batch_no = me.item_batch_no[d.item_code] } } @@ -760,7 +952,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ this.update_paid_amount_status(false) }, - add_new_item_to_grid: function() { + add_new_item_to_grid: function () { var me = this; this.child = frappe.model.add_child(this.frm.doc, this.frm.doc.doctype + " Item", "items"); this.child.item_code = this.items[0].item_code; @@ -772,7 +964,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ this.child.cost_center = this.pos_profile_data['cost_center'] || this.items[0].cost_center; this.child.income_account = this.pos_profile_data['income_account'] || this.items[0].income_account; this.child.warehouse = (this.item_serial_no[this.child.item_code] - ? this.item_serial_no[this.child.item_code][1] : (this.pos_profile_data['warehouse'] || this.items[0].default_warehouse) ); + ? this.item_serial_no[this.child.item_code][1] : (this.pos_profile_data['warehouse'] || this.items[0].default_warehouse)); this.child.price_list_rate = flt(this.price_list_data[this.child.item_code], 9) / flt(this.frm.doc.conversion_rate, 9); this.child.rate = flt(this.price_list_data[this.child.item_code], 9) / flt(this.frm.doc.conversion_rate, 9); this.child.actual_qty = me.get_actual_qty(this.items[0]); @@ -783,15 +975,15 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ this.child.item_tax_rate = JSON.stringify(this.tax_data[this.child.item_code]); }, - update_paid_amount_status: function(update_paid_amount){ - if(this.name){ + update_paid_amount_status: function (update_paid_amount) { + if (this.name) { update_paid_amount = update_paid_amount ? false : true; } this.refresh(update_paid_amount); }, - refresh: function(update_paid_amount) { + refresh: function (update_paid_amount) { var me = this; this.refresh_fields(update_paid_amount); this.bind_qty_event(); @@ -799,7 +991,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ this.set_primary_action(); }, - refresh_fields: function(update_paid_amount) { + refresh_fields: function (update_paid_amount) { this.apply_pricing_rule(); this.discount_amount_applied = false; this._calculate_taxes_and_totals(); @@ -810,40 +1002,45 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ this.set_totals(); }, - get_company_currency: function() { + get_company_currency: function () { return erpnext.get_currency(this.frm.doc.company); }, - show_item_wise_taxes: function(){ + show_item_wise_taxes: function () { return null; }, - show_items_in_item_cart: function() { + show_items_in_item_cart: function () { var me = this; var $items = this.wrapper.find(".items").empty(); - $.each(this.frm.doc.items|| [], function(i, d) { + $.each(this.frm.doc.items || [], function (i, d) { $(frappe.render_template("pos_bill_item", { item_code: d.item_code, - item_name: (d.item_name===d.item_code || !d.item_name) ? "" : ("
+
### Approving Expenses
Approver for the Expense Claim is selected by an Employee himself. Users to whom `Expense Approver` role is assigned will shown in the Expense Claim Approver field.
@@ -18,11 +22,25 @@ After saving Expense Claim, Employee should [Assign document to Approver]({{docs
Expense Claim Approver can update the “Sanctioned Amounts” against Claimed Amount of an Employee. If submitting, Approval Status should be submitted to Approved or Rejected. If Approved, then Expense Claim gets submitted. If rejected, then Expen
Comments can be added in the Comments section explaining why the claim was approved or rejected.
-### Booking the Expense and Reimbursement
+### Booking the Expense
-The approved Expense Claim must then be converted into a Journal Entry and a
-payment must be made. Note: This amount should not be clubbed with Salary
-because the amount will then be taxable to the Employee.
+On submission of Expense Claim, system books an expense against the expense account and the employee account
+
+
+User can view unpaid expense claim using report "Unclaimed Expense Claims"
+
+
+### Payment for Expense Claim
+
+To make payment against the expense claim, user has to click on Make > Bank Entry
+#### Expense Claim
+
+
+#### Payment Entry
+
+
+
+Note: This amount should not be clubbed with Salary because the amount will then be taxable to the Employee.
### Linking with Task & Project
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index 66acdb1b8ba..14ea74b70b5 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -147,13 +147,6 @@ doc_events = {
"on_update": "erpnext.shopping_cart.doctype.shopping_cart_settings.shopping_cart_settings.validate_cart_settings"
},
- # bubble transaction notification on master
- ('Opportunity', 'Quotation', 'Sales Order', 'Delivery Note', 'Sales Invoice',
- 'Supplier Quotation', 'Purchase Order', 'Purchase Receipt',
- 'Purchase Invoice', 'Project', 'Issue'): {
- 'on_change': 'erpnext.accounts.party_status.notify_status'
- },
-
"Website Settings": {
"validate": "erpnext.portal.doctype.products_settings.products_settings.home_page_is_products"
},
diff --git a/erpnext/hr/doctype/employee_loan/employee_loan.py b/erpnext/hr/doctype/employee_loan/employee_loan.py
index 4d7a9f38dfb..568596ee255 100644
--- a/erpnext/hr/doctype/employee_loan/employee_loan.py
+++ b/erpnext/hr/doctype/employee_loan/employee_loan.py
@@ -111,12 +111,11 @@ def check_repayment_method(repayment_method, loan_amount, monthly_repayment_amou
frappe.throw(_("Monthly Repayment Amount cannot be greater than Loan Amount"))
def get_monthly_repayment_amount(repayment_method, loan_amount, rate_of_interest, repayment_periods):
- if repayment_method == "Repay Over Number of Periods":
- if rate_of_interest:
- monthly_interest_rate = flt(rate_of_interest) / (12 *100)
- monthly_repayment_amount = math.ceil((loan_amount * monthly_interest_rate *
- (1 + monthly_interest_rate)**repayment_periods) \
- / ((1 + monthly_interest_rate)**repayment_periods - 1))
+ if rate_of_interest:
+ monthly_interest_rate = flt(rate_of_interest) / (12 *100)
+ monthly_repayment_amount = math.ceil((loan_amount * monthly_interest_rate *
+ (1 + monthly_interest_rate)**repayment_periods) \
+ / ((1 + monthly_interest_rate)**repayment_periods - 1))
else:
monthly_repayment_amount = math.ceil(flt(loan_amount) / repayment_periods)
return monthly_repayment_amount
diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.js b/erpnext/hr/doctype/expense_claim/expense_claim.js
index 8b3c45140c6..7d19051b62a 100644
--- a/erpnext/hr/doctype/expense_claim/expense_claim.js
+++ b/erpnext/hr/doctype/expense_claim/expense_claim.js
@@ -158,6 +158,21 @@ erpnext.expense_claim = {
}
}
+frappe.ui.form.on("Expense Claim", {
+ refresh: function(frm) {
+ if(frm.doc.docstatus == 1) {
+ 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"));
+ }
+ }
+})
+
frappe.ui.form.on("Expense Claim Detail", {
claim_amount: function(frm, cdt, cdn) {
var child = locals[cdt][cdn];
@@ -176,6 +191,47 @@ frappe.ui.form.on("Expense Claim Detail", {
}
})
+frappe.ui.form.on("Expense Claim",{
+ setup: function(frm) {
+ frm.trigger("set_query_for_cost_center")
+ frm.trigger("set_query_for_payable_account")
+ frm.add_fetch("company", "cost_center", "cost_center");
+ frm.add_fetch("company", "default_payable_account", "payable_account");
+ },
+
+ refresh: function(frm) {
+ frm.trigger("toggle_fields")
+ },
+
+ set_query_for_cost_center: function(frm) {
+ frm.fields_dict["cost_center"].get_query = function() {
+ return {
+ filters: {
+ "company": frm.doc.company
+ }
+ }
+ }
+ },
+
+ set_query_for_payable_account: function(frm) {
+ frm.fields_dict["payable_account"].get_query = function() {
+ return {
+ filters: {
+ "root_type": "Liability",
+ "account_type": "Payable"
+ }
+ }
+ }
+ },
+
+ is_paid: function(frm) {
+ frm.trigger("toggle_fields")
+ },
+
+ toggle_fields: function(frm) {
+ frm.toggle_reqd("mode_of_payment", frm.doc.is_paid)
+ }
+});
frappe.ui.form.on("Expense Claim", "employee_name", function(frm) {
erpnext.expense_claim.set_title(frm);
diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.json b/erpnext/hr/doctype/expense_claim/expense_claim.json
index 67132bf6984..f517b91d78e 100644
--- a/erpnext/hr/doctype/expense_claim/expense_claim.json
+++ b/erpnext/hr/doctype/expense_claim/expense_claim.json
@@ -41,6 +41,34 @@
"set_only_once": 0,
"unique": 0
},
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "is_paid",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Is Paid",
+ "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_on_submit": 0,
"bold": 0,
@@ -55,7 +83,7 @@
"ignore_xss_filter": 0,
"in_filter": 1,
"in_list_view": 0,
- "in_standard_filter": 1,
+ "in_standard_filter": 0,
"label": "Approval Status",
"length": 0,
"no_copy": 1,
@@ -86,7 +114,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
- "in_standard_filter": 1,
+ "in_standard_filter": 0,
"label": "Approver",
"length": 0,
"no_copy": 0,
@@ -175,7 +203,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
- "in_list_view": 0,
+ "in_list_view": 1,
"in_standard_filter": 0,
"label": "Total Sanctioned Amount",
"length": 0,
@@ -322,7 +350,7 @@
"ignore_xss_filter": 0,
"in_filter": 1,
"in_list_view": 0,
- "in_standard_filter": 1,
+ "in_standard_filter": 0,
"label": "From Employee",
"length": 0,
"no_copy": 0,
@@ -370,36 +398,6 @@
"unique": 0,
"width": "150px"
},
- {
- "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": 1,
- "in_list_view": 0,
- "in_standard_filter": 1,
- "label": "Company",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "company",
- "oldfieldtype": "Link",
- "options": "Company",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 1,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
{
"allow_on_submit": 0,
"bold": 0,
@@ -429,90 +427,6 @@
"set_only_once": 0,
"unique": 0
},
- {
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "cb1",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "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_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "total_amount_reimbursed",
- "fieldtype": "Currency",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Total Amount Reimbursed",
- "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_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "remark",
- "fieldtype": "Small Text",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Remark",
- "length": 0,
- "no_copy": 1,
- "oldfieldname": "remark",
- "oldfieldtype": "Small Text",
- "permlevel": 0,
- "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_on_submit": 0,
"bold": 0,
@@ -571,6 +485,90 @@
"set_only_once": 0,
"unique": 0
},
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "cb1",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "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_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "total_amount_reimbursed",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Total Amount Reimbursed",
+ "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_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "remark",
+ "fieldtype": "Small Text",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Remark",
+ "length": 0,
+ "no_copy": 1,
+ "oldfieldname": "remark",
+ "oldfieldtype": "Small Text",
+ "permlevel": 0,
+ "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_on_submit": 1,
"bold": 0,
@@ -613,7 +611,7 @@
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
- "label": "Employees Email Address",
+ "label": "Employees Email Id",
"length": 0,
"no_copy": 0,
"oldfieldname": "email_id",
@@ -629,6 +627,238 @@
"set_only_once": 0,
"unique": 0
},
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "accounting_details",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Accounting Details",
+ "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_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": 1,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Company",
+ "length": 0,
+ "no_copy": 0,
+ "oldfieldname": "company",
+ "oldfieldtype": "Link",
+ "options": "Company",
+ "permlevel": 0,
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 1,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "depends_on": "is_paid",
+ "fieldname": "mode_of_payment",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 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
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "column_break_24",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 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_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "depends_on": "",
+ "fieldname": "payable_account",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Payable 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_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "cost_center",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Employees Email Address",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Cost Center",
+ "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_on_submit": 0,
+ "bold": 0,
+ "collapsible": 1,
+ "columns": 0,
+ "fieldname": "more_details",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "More Details",
+ "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_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "default": "Draft",
+ "fieldname": "status",
+ "fieldtype": "Select",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 1,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Status",
+ "length": 0,
+ "no_copy": 1,
+ "options": "Draft\nPaid\nUnpaid\nSubmitted\nCancelled",
+ "permlevel": 0,
+ "precision": "",
+ "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_on_submit": 0,
"bold": 0,
@@ -663,7 +893,7 @@
],
"hide_heading": 0,
"hide_toolbar": 0,
- "icon": "fa fa-money",
+ "icon": "icon-money",
"idx": 1,
"image_view": 0,
"in_create": 0,
@@ -673,10 +903,11 @@
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
- "modified": "2016-11-07 05:52:48.548201",
+ "modified": "2017-01-19 18:15:35.968292",
"modified_by": "Administrator",
"module": "HR",
"name": "Expense Claim",
+ "name_case": "Title Case",
"owner": "harshada@webnotestech.com",
"permissions": [
{
@@ -689,7 +920,6 @@
"export": 1,
"if_owner": 0,
"import": 0,
- "is_custom": 0,
"permlevel": 0,
"print": 1,
"read": 1,
@@ -710,7 +940,6 @@
"export": 0,
"if_owner": 0,
"import": 0,
- "is_custom": 0,
"permlevel": 0,
"print": 1,
"read": 1,
@@ -732,7 +961,6 @@
"export": 0,
"if_owner": 0,
"import": 0,
- "is_custom": 0,
"permlevel": 0,
"print": 1,
"read": 1,
@@ -754,7 +982,6 @@
"export": 0,
"if_owner": 0,
"import": 0,
- "is_custom": 0,
"permlevel": 0,
"print": 1,
"read": 1,
@@ -775,5 +1002,6 @@
"sort_order": "DESC",
"timeline_field": "employee",
"title_field": "title",
+ "track_changes": 0,
"track_seen": 0
}
\ No newline at end of file
diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.py b/erpnext/hr/doctype/expense_claim/expense_claim.py
index efdee97906b..957753a46d4 100644
--- a/erpnext/hr/doctype/expense_claim/expense_claim.py
+++ b/erpnext/hr/doctype/expense_claim/expense_claim.py
@@ -4,13 +4,17 @@
from __future__ import unicode_literals
import frappe
from frappe import _
-from frappe.utils import get_fullname, flt
+from frappe.utils import get_fullname, flt, cstr
from frappe.model.document import Document
from erpnext.hr.utils import set_employee_name
+from erpnext.accounts.party import get_party_account
+from erpnext.accounts.general_ledger import make_gl_entries
+from erpnext.accounts.doctype.sales_invoice.sales_invoice import get_bank_cash_account
+from erpnext.controllers.accounts_controller import AccountsController
class InvalidExpenseApproverError(frappe.ValidationError): pass
-class ExpenseClaim(Document):
+class ExpenseClaim(AccountsController):
def get_feed(self):
return _("{0}: From {0} for {1}").format(self.approval_status,
self.employee_name, self.total_claimed_amount)
@@ -21,16 +25,53 @@ class ExpenseClaim(Document):
self.calculate_total_amount()
set_employee_name(self)
self.set_expense_account()
+ self.set_payable_account()
+ self.set_cost_center()
+ self.set_status()
if self.task and not self.project:
self.project = frappe.db.get_value("Task", self.task, "project")
+ def set_status(self):
+ self.status = {
+ "0": "Draft",
+ "1": "Submitted",
+ "2": "Cancelled"
+ }[cstr(self.docstatus or 0)]
+
+ if self.total_sanctioned_amount == self.total_amount_reimbursed and self.docstatus == 1:
+ self.status = "Paid"
+ elif self.docstatus == 1:
+ self.status = "Unpaid"
+
+ def set_payable_account(self):
+ if not self.payable_account and not self.is_paid:
+ self.payable_account = frappe.db.get_value("Company", self.company, "default_payable_account")
+
+ def set_cost_center(self):
+ if not self.cost_center:
+ self.cost_center = frappe.db.get_value('Company', self.company, 'cost_center')
+
def on_submit(self):
if self.approval_status=="Draft":
frappe.throw(_("""Approval Status must be 'Approved' or 'Rejected'"""))
+
self.update_task_and_project()
+ self.make_gl_entries()
+
+ if self.is_paid:
+ update_reimbursed_amount(self)
+
+ self.set_status()
def on_cancel(self):
self.update_task_and_project()
+ if self.payable_account:
+ self.make_gl_entries(cancel=True)
+
+ if self.is_paid:
+ update_reimbursed_amount(self)
+
+ self.set_status()
def update_task_and_project(self):
if self.task:
@@ -38,6 +79,79 @@ class ExpenseClaim(Document):
elif self.project:
frappe.get_doc("Project", self.project).update_project()
+ def make_gl_entries(self, cancel = False):
+ if flt(self.total_sanctioned_amount) > 0:
+ gl_entries = self.get_gl_entries()
+ make_gl_entries(gl_entries, cancel)
+
+ def get_gl_entries(self):
+ gl_entry = []
+ self.validate_account_details()
+
+ # 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
+ })
+ )
+
+ # expense entries
+ for data in self.expenses:
+ gl_entry.append(
+ self.get_gl_dict({
+ "account": data.default_account,
+ "debit": data.sanctioned_amount,
+ "debit_in_account_currency": data.sanctioned_amount,
+ "against": self.employee,
+ "cost_center": self.cost_center
+ })
+ )
+
+ if self.is_paid:
+ # 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,
+ "against": self.employee
+ })
+ )
+
+ gl_entry.append(
+ self.get_gl_dict({
+ "account": self.payable_account,
+ "party_type": "Employee",
+ "party": self.employee,
+ "against": payment_account,
+ "debit": self.total_sanctioned_amount,
+ "debit_in_account_currency": self.total_sanctioned_amount,
+ "against_voucher": self.name,
+ "against_voucher_type": self.doctype,
+ })
+ )
+
+ return gl_entry
+
+ def validate_account_details(self):
+ if not self.cost_center:
+ frappe.throw(_("Cost center is required to book an expense claim"))
+
+ if not self.payable_account:
+ frappe.throw(_("Please set default payable account in the employee {0}").format(self.employee))
+
+ if self.is_paid:
+ if not self.mode_of_payment:
+ frappe.throw(_("Mode of payment is required to make a payment").format(self.employee))
+
def calculate_total_amount(self):
self.total_claimed_amount = 0
self.total_sanctioned_amount = 0
@@ -64,7 +178,18 @@ class ExpenseClaim(Document):
for expense in self.expenses:
if not expense.default_account:
expense.default_account = get_expense_claim_account(expense.expense_type, self.company)["account"]
-
+
+def update_reimbursed_amount(doc):
+ amt = frappe.db.sql("""select ifnull(sum(debit_in_account_currency), 0) as amt
+ from `tabGL Entry` where against_voucher_type = 'Expense Claim' and against_voucher = %s
+ and party = %s """, (doc.name, doc.employee) ,as_dict=1)[0].amt
+
+ doc.total_amount_reimbursed = amt
+ frappe.db.set_value("Expense Claim", doc.name , "total_amount_reimbursed", amt)
+
+ doc.set_status()
+ frappe.db.set_value("Expense Claim", doc.name , "status", doc.status)
+
@frappe.whitelist()
def get_expense_approver(doctype, txt, searchfield, start, page_len, filters):
return frappe.db.sql("""
@@ -80,23 +205,26 @@ def make_bank_entry(docname):
expense_claim = frappe.get_doc("Expense Claim", docname)
default_bank_cash_account = get_default_bank_cash_account(expense_claim.company, "Bank")
+ if not default_bank_cash_account:
+ default_bank_cash_account = get_default_bank_cash_account(expense_claim.company, "Cash")
je = frappe.new_doc("Journal Entry")
je.voucher_type = 'Bank Entry'
je.company = expense_claim.company
je.remark = 'Payment against Expense Claim: ' + docname;
- for expense in expense_claim.expenses:
- je.append("accounts", {
- "account": expense.default_account,
- "debit_in_account_currency": expense.sanctioned_amount,
- "reference_type": "Expense Claim",
- "reference_name": expense_claim.name
- })
+ je.append("accounts", {
+ "account": expense_claim.payable_account,
+ "debit_in_account_currency": flt(expense_claim.total_sanctioned_amount - expense_claim.total_amount_reimbursed),
+ "reference_type": "Expense Claim",
+ "party_type": "Employee",
+ "party": expense_claim.employee,
+ "reference_name": expense_claim.name
+ })
je.append("accounts", {
"account": default_bank_cash_account.account,
- "credit_in_account_currency": expense_claim.total_sanctioned_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,
diff --git a/erpnext/hr/doctype/expense_claim/expense_claim_list.js b/erpnext/hr/doctype/expense_claim/expense_claim_list.js
index 54073edef5e..7cff8e2f212 100644
--- a/erpnext/hr/doctype/expense_claim/expense_claim_list.js
+++ b/erpnext/hr/doctype/expense_claim/expense_claim_list.js
@@ -2,7 +2,10 @@ frappe.listview_settings['Expense Claim'] = {
add_fields: ["approval_status", "total_claimed_amount", "docstatus"],
filters:[["approval_status","!=", "Rejected"]],
get_indicator: function(doc) {
- return [__(doc.approval_status), frappe.utils.guess_colour(doc.approval_status),
- "approval_status,=," + doc.approval_status];
+ if(doc.status == "Paid") {
+ return [__("Paid"), "green", "status,=,'Paid'"];
+ } else {
+ return [__("Unpaid"), "orange"];
+ }
}
};
diff --git a/erpnext/hr/doctype/expense_claim/test_expense_claim.py b/erpnext/hr/doctype/expense_claim/test_expense_claim.py
index 59d686ef036..20df7be0c4a 100644
--- a/erpnext/hr/doctype/expense_claim/test_expense_claim.py
+++ b/erpnext/hr/doctype/expense_claim/test_expense_claim.py
@@ -4,6 +4,8 @@ from __future__ import unicode_literals
import frappe
import unittest
+from frappe.utils import random_string, nowdate
+from erpnext.hr.doctype.expense_claim.expense_claim import make_bank_entry
test_records = frappe.get_test_records('Expense Claim')
@@ -11,8 +13,6 @@ class TestExpenseClaim(unittest.TestCase):
def test_total_expense_claim_for_project(self):
frappe.db.sql("""delete from `tabTask` where project = "_Test Project 1" """)
frappe.db.sql("""delete from `tabProject` where name = "_Test Project 1" """)
- frappe.db.sql("""delete from `tabExpense Claim`""")
- frappe.db.sql("""delete from `tabExpense Claim Detail`""")
frappe.get_doc({
"project_name": "_Test Project 1",
@@ -22,15 +22,17 @@ class TestExpenseClaim(unittest.TestCase):
}).save()
task_name = frappe.db.get_value("Task", {"project": "_Test Project 1"})
+ payable_account = get_payable_account("Wind Power LLC")
expense_claim = frappe.get_doc({
"doctype": "Expense Claim",
"employee": "_T-Employee-0001",
+ "payable_account": payable_account,
"approval_status": "Approved",
"project": "_Test Project 1",
"task": task_name,
"expenses":
- [{ "expense_type": "Food", "default_account": "Entertainment Expenses - _TC", "claim_amount": 300, "sanctioned_amount": 200 }]
+ [{ "expense_type": "Travel", "default_account": "Travel Expenses - WP", "claim_amount": 300, "sanctioned_amount": 200 }]
})
expense_claim.submit()
@@ -44,7 +46,7 @@ class TestExpenseClaim(unittest.TestCase):
"project": "_Test Project 1",
"task": task_name,
"expenses":
- [{ "expense_type": "Food", "default_account": "Entertainment Expenses - _TC", "claim_amount": 600, "sanctioned_amount": 500 }]
+ [{ "expense_type": "Travel", "default_account": "Travel Expenses - WP", "claim_amount": 600, "sanctioned_amount": 500 }]
})
expense_claim2.submit()
@@ -52,6 +54,64 @@ class TestExpenseClaim(unittest.TestCase):
self.assertEqual(frappe.db.get_value("Project", "_Test Project 1", "total_expense_claim"), 700)
expense_claim2.cancel()
+ frappe.delete_doc("Expenses Claim", expense_claim2.name)
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"), 200)
+
+ def test_expense_claim_status(self):
+ payable_account = get_payable_account("Wind Power LLC")
+ expense_claim = frappe.get_doc({
+ "doctype": "Expense Claim",
+ "employee": "_T-Employee-0001",
+ "payable_account": payable_account,
+ "approval_status": "Approved",
+ "expenses":
+ [{ "expense_type": "Travel", "default_account": "Travel Expenses - WP", "claim_amount": 300, "sanctioned_amount": 200 }]
+ })
+ expense_claim.submit()
+
+ je_dict = make_bank_entry(expense_claim.name)
+ je = frappe.get_doc(je_dict)
+ je.posting_date = nowdate()
+ je.cheque_no = random_string(5)
+ je.cheque_date = nowdate()
+ je.submit()
+
+ expense_claim = frappe.get_doc("Expense Claim", expense_claim.name)
+ self.assertEqual(expense_claim.status, "Paid")
+
+ je.cancel()
+ expense_claim = frappe.get_doc("Expense Claim", expense_claim.name)
+ self.assertEqual(expense_claim.status, "Unpaid")
+
+ def test_expense_claim_gl_entry(self):
+ payable_account = get_payable_account("Wind Power LLC")
+ expense_claim = frappe.get_doc({
+ "doctype": "Expense Claim",
+ "employee": "_T-Employee-0001",
+ "payable_account": payable_account,
+ "approval_status": "Approved",
+ "expenses":
+ [{ "expense_type": "Travel", "default_account": "Travel Expenses - WP", "claim_amount": 300, "sanctioned_amount": 200 }]
+ })
+ expense_claim.submit()
+
+ 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 [
+ [payable_account, 0.0, 200.0],
+ ["Travel Expenses - WP", 200.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):
+ return frappe.db.get_value('Company', company, 'default_payable_account')
diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.py b/erpnext/hr/doctype/salary_slip/salary_slip.py
index de205e3bdd2..5ff1248717d 100644
--- a/erpnext/hr/doctype/salary_slip/salary_slip.py
+++ b/erpnext/hr/doctype/salary_slip/salary_slip.py
@@ -89,8 +89,8 @@ class SalarySlip(TransactionBase):
frappe.throw(_("Name error: {0}".format(err)))
except SyntaxError as err:
frappe.throw(_("Syntax error in formula or condition: {0}".format(err)))
- except:
- frappe.throw(_("Error in formula or condition"))
+ except Exception, e:
+ frappe.throw(_("Error in formula or condition: {0}".format(e)))
raise
def get_data_for_eval(self):
@@ -99,7 +99,7 @@ class SalarySlip(TransactionBase):
for d in self._salary_structure_doc.employees:
if d.employee == self.employee:
- data.base, data.variable = d.base, d.variable
+ data.update(frappe.get_doc("Salary Structure Employee", {"employee": self.employee}).as_dict())
data.update(frappe.get_doc("Employee", self.employee).as_dict())
data.update(self.as_dict())
@@ -108,7 +108,6 @@ class SalarySlip(TransactionBase):
salary_components = frappe.get_all("Salary Component", fields=["salary_component_abbr"])
for salary_component in salary_components:
data[salary_component.salary_component_abbr] = 0
-
return data
diff --git a/erpnext/hr/doctype/salary_structure/salary_structure.json b/erpnext/hr/doctype/salary_structure/salary_structure.json
index ca59ded9405..b34cff19a1d 100644
--- a/erpnext/hr/doctype/salary_structure/salary_structure.json
+++ b/erpnext/hr/doctype/salary_structure/salary_structure.json
@@ -21,7 +21,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
- "in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
@@ -48,7 +47,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
- "in_filter": 1,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Company",
@@ -76,7 +74,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
- "in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Letter Head",
@@ -107,7 +104,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
- "in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Payroll Frequency",
@@ -136,7 +132,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
- "in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
@@ -164,7 +159,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
- "in_filter": 1,
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Is Active",
@@ -195,7 +189,6 @@
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
- "in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Is Default",
@@ -224,7 +217,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
- "in_filter": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "From Date",
@@ -253,7 +245,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
- "in_filter": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "To Date",
@@ -282,7 +273,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
- "in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
@@ -310,7 +300,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
- "in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Employees",
@@ -339,7 +328,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
- "in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "",
@@ -368,7 +356,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
- "in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Salary Slip Based on Timesheet",
@@ -396,7 +383,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
- "in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
@@ -425,7 +411,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
- "in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Salary Component",
@@ -455,7 +440,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
- "in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Hour Rate",
@@ -485,7 +469,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
- "in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "",
@@ -515,7 +498,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
- "in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Earning",
@@ -546,7 +528,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
- "in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Earnings",
@@ -576,7 +557,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
- "in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Deduction",
@@ -606,7 +586,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
- "in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Deductions",
@@ -637,7 +616,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
- "in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
@@ -664,7 +642,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
- "in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
@@ -691,7 +668,6 @@
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
- "in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Total Earning",
@@ -721,7 +697,6 @@
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
- "in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Total Deduction",
@@ -751,7 +726,6 @@
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
- "in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Net Pay",
@@ -779,7 +753,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
- "in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Account",
@@ -807,7 +780,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
- "in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Mode of Payment",
@@ -836,7 +808,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
- "in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
@@ -863,7 +834,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
- "in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Payment Account",
@@ -894,7 +864,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2017-01-11 02:02:10.848614",
+ "modified": "2017-02-02 01:23:49.179497",
"modified_by": "Administrator",
"module": "HR",
"name": "Salary Structure",
@@ -910,7 +880,6 @@
"export": 0,
"if_owner": 0,
"import": 0,
- "is_custom": 0,
"permlevel": 0,
"print": 1,
"read": 1,
@@ -928,10 +897,9 @@
"create": 1,
"delete": 1,
"email": 1,
- "export": 0,
+ "export": 1,
"if_owner": 0,
- "import": 0,
- "is_custom": 0,
+ "import": 1,
"permlevel": 0,
"print": 1,
"read": 1,
@@ -950,5 +918,6 @@
"sort_order": "DESC",
"timeline_field": "",
"title_field": "",
+ "track_changes": 0,
"track_seen": 0
-}
+}
\ No newline at end of file
diff --git a/erpnext/hr/doctype/salary_structure/salary_structure.py b/erpnext/hr/doctype/salary_structure/salary_structure.py
index 13622c35b3e..0e2dd1bb4ad 100644
--- a/erpnext/hr/doctype/salary_structure/salary_structure.py
+++ b/erpnext/hr/doctype/salary_structure/salary_structure.py
@@ -4,8 +4,7 @@
from __future__ import unicode_literals
import frappe
-from frappe.utils import cstr, flt, getdate, cint
-from frappe.model.naming import make_autoname
+from frappe.utils import flt, cint
from frappe import _
from frappe.model.mapper import get_mapped_doc
from frappe.model.document import Document
@@ -15,7 +14,6 @@ class SalaryStructure(Document):
def validate(self):
self.validate_amount()
- self.validate_joining_date()
for e in self.get('employees'):
set_employee_name(e)
@@ -29,12 +27,6 @@ class SalaryStructure(Document):
def validate_amount(self):
if flt(self.net_pay) < 0 and self.salary_slip_based_on_timesheet:
frappe.throw(_("Net pay cannot be negative"))
-
- def validate_joining_date(self):
- for e in self.get('employees'):
- joining_date = getdate(frappe.db.get_value("Employee", e.employee, "date_of_joining"))
- if getdate(self.from_date) < joining_date:
- frappe.throw(_("From Date in Salary Structure cannot be lesser than Employee Joining Date."))
@frappe.whitelist()
@@ -42,6 +34,7 @@ def make_salary_slip(source_name, target_doc = None, employee = None, as_print =
def postprocess(source, target):
if employee:
target.employee = employee
+ target.employee_name = frappe.get_value("Employee",employee, "employee_name")
target.run_method('process_salary_structure')
doc = get_mapped_doc("Salary Structure", source_name, {
diff --git a/erpnext/hr/doctype/salary_structure_employee/salary_structure_employee.json b/erpnext/hr/doctype/salary_structure_employee/salary_structure_employee.json
index aae33e3cac5..f5ac764171c 100644
--- a/erpnext/hr/doctype/salary_structure_employee/salary_structure_employee.json
+++ b/erpnext/hr/doctype/salary_structure_employee/salary_structure_employee.json
@@ -15,13 +15,14 @@
"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_list_view": 1,
+ "in_standard_filter": 0,
"label": "Employee",
"length": 0,
"no_copy": 0,
@@ -31,6 +32,7 @@
"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,
@@ -41,21 +43,24 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "employee_name",
- "fieldtype": "Data",
+ "fieldtype": "Read Only",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
- "in_filter": 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": 1,
+ "remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
@@ -66,13 +71,14 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "base",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
- "in_filter": 0,
"in_list_view": 1,
+ "in_standard_filter": 0,
"label": "Base",
"length": 0,
"no_copy": 0,
@@ -81,6 +87,7 @@
"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,
@@ -91,13 +98,14 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "variable",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
- "in_filter": 0,
"in_list_view": 1,
+ "in_standard_filter": 0,
"label": "Variable",
"length": 0,
"no_copy": 0,
@@ -106,6 +114,7 @@
"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,
@@ -123,7 +132,7 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
- "modified": "2016-08-11 12:18:14.526977",
+ "modified": "2017-02-02 02:06:33.809285",
"modified_by": "Administrator",
"module": "HR",
"name": "Salary Structure Employee",
@@ -135,5 +144,6 @@
"read_only_onload": 0,
"sort_field": "modified",
"sort_order": "DESC",
+ "track_changes": 0,
"track_seen": 0
}
\ No newline at end of file
diff --git a/erpnext/hr/page/team_updates/team_update_row.html b/erpnext/hr/page/team_updates/team_update_row.html
index c81a4cb95f2..e3adcb88887 100644
--- a/erpnext/hr/page/team_updates/team_update_row.html
+++ b/erpnext/hr/page/team_updates/team_update_row.html
@@ -7,7 +7,7 @@
+ {% if data.value %} + + {{ __("Open BOM {0}", [data.value.bold()]) }} + {% endif %} + + {{ __("Open Item {0}", [data.item_code.bold()]) }} +
+