diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 20f5e544e3e..d916789ae55 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -2,7 +2,7 @@ from __future__ import unicode_literals import frappe -__version__ = '7.2.17' +__version__ = '7.2.18' def get_default_company(user=None): '''Get default company for user''' 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 dafa67ab3c4..98fa445c9b1 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.json +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.json @@ -123,7 +123,7 @@ "search_index": 0, "set_only_once": 0, "unique": 0 - }, + }, { "allow_on_submit": 0, "bold": 1, @@ -247,7 +247,7 @@ "columns": 0, "depends_on": "eval:in_list([\"Receive\", \"Pay\"], doc.payment_type)", "fieldname": "party_type", - "fieldtype": "Select", + "fieldtype": "Link", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, @@ -257,7 +257,7 @@ "label": "Party Type", "length": 0, "no_copy": 0, - "options": "Customer\nSupplier", + "options": "DocType", "permlevel": 0, "precision": "", "print_hide": 1, 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/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/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) ? "" : ("
+
+### Employee Loan Application
+
+Employee can apply for loan by going to:
+
+> Human Resources > Employee Loan Management > Employee Loan Application > New Employee Loan Application
+
+
+
+#### In the Employee Loan Application,
+
+ * Enter Employee details and Loan details
+ * Select the repayment method, and based on your selection enter Repayment Period in Months or repayment Amount
+
+On save, Employee can see Repayment Information and make changes if required before submitting.
+
+
+
+### Employee Loan
+
+Once the Loan is approved, Manager can create Employee Loan record for the Employee.
+
+> Human Resources > Employee Loan Management > Employee Loan > New Employee Loan
+
+
+
+#### In the Employee Loan,
+
+ * Enter Employee and Loan Application
+ * Check "Repay from Salary" if the loan repayment will be deducted from the salary
+ * Enter Disbursement Date and Account Info
+ * As soon as you hit save, the repayment schedule is generated.
+
+
+
+#### Loan repayment deduction from Salary
+
+To auto deduct the Loan repayment from Salary, check "Repay from Salary" in Employee Loan. It will appear as Loan repayment in Salary Slip.
+
+
+
+
+
\ No newline at end of file
diff --git a/erpnext/docs/user/manual/en/human-resources/expense-claim.md b/erpnext/docs/user/manual/en/human-resources/expense-claim.md
index da3b2eedffd..1d4996dabcd 100644
--- a/erpnext/docs/user/manual/en/human-resources/expense-claim.md
+++ b/erpnext/docs/user/manual/en/human-resources/expense-claim.md
@@ -9,6 +9,10 @@ To make a new Expense Claim, go to:
Set the Employee ID, date and the list of expenses that are to be claimed and
“Submit” the record.
+### Set Account for Employee
+Set employee's expense account on the employee form, system books an expense amount of an employee under this account.
+
+
### 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/docs/user/manual/en/human-resources/index.txt b/erpnext/docs/user/manual/en/human-resources/index.txt
index f91edfaa3e9..673efdb8386 100644
--- a/erpnext/docs/user/manual/en/human-resources/index.txt
+++ b/erpnext/docs/user/manual/en/human-resources/index.txt
@@ -15,4 +15,5 @@ holiday-list
human-resource-setup
daily-work-summary
fleet-management
+employee-loan-management
articles
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index 8705e98f4d3..14ea74b70b5 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -107,7 +107,6 @@ portal_menu_items = [
{"title": _("Shipments"), "route": "/shipments", "reference_doctype": "Delivery Note", "role":"Customer"},
{"title": _("Issues"), "route": "/issues", "reference_doctype": "Issue", "role":"Customer"},
{"title": _("Addresses"), "route": "/addresses", "reference_doctype": "Address"},
- {"title": _("Announcements"), "route": "/announcement", "reference_doctype": "Announcement"},
{"title": _("Fees"), "route": "/fees", "reference_doctype": "Fees", "role":"Student"}
]
@@ -122,8 +121,7 @@ has_website_permission = {
"Sales Invoice": "erpnext.controllers.website_list_for_contact.has_website_permission",
"Supplier Quotation": "erpnext.controllers.website_list_for_contact.has_website_permission",
"Delivery Note": "erpnext.controllers.website_list_for_contact.has_website_permission",
- "Issue": "erpnext.support.doctype.issue.issue.has_website_permission",
- "Discussion": "erpnext.schools.web_form.discussion.discussion.has_website_permission"
+ "Issue": "erpnext.support.doctype.issue.issue.has_website_permission"
}
dump_report_map = "erpnext.startup.report_data_map.data_map"
diff --git a/erpnext/schools/doctype/discussion/__init__.py b/erpnext/hr/doctype/employee_loan/__init__.py
similarity index 100%
rename from erpnext/schools/doctype/discussion/__init__.py
rename to erpnext/hr/doctype/employee_loan/__init__.py
diff --git a/erpnext/hr/doctype/employee_loan/employee_loan.js b/erpnext/hr/doctype/employee_loan/employee_loan.js
new file mode 100644
index 00000000000..a03dbda4b15
--- /dev/null
+++ b/erpnext/hr/doctype/employee_loan/employee_loan.js
@@ -0,0 +1,88 @@
+// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Employee Loan', {
+ onload: function(frm) {
+ frm.set_query("employee_loan_application", function() {
+ return {
+ "filters": {
+ "employee": frm.doc.employee,
+ "docstatus": 1,
+ "status": "Approved"
+ }
+ };
+ });
+
+ $.each(["payment_account", "employee_loan_account"], function(i, field) {
+ frm.set_query(field, function() {
+ return {
+ "filters": {
+ "company": frm.doc.company,
+ "root_type": "Asset",
+ "is_group": 0
+ }
+ };
+ });
+ })
+ },
+
+ mode_of_payment: function(frm){
+ frappe.call({
+ method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.get_bank_cash_account",
+ args: {
+ "mode_of_payment": frm.doc.mode_of_payment,
+ "company": frm.doc.company
+ },
+ callback: function(r, rt) {
+ if(r.message) {
+ frm.set_value("payment_account", r.message.account);
+ }
+ }
+ });
+ },
+
+ refresh: function(frm) {
+ frm.trigger("toggle_fields");
+
+ if(frm.doc.docstatus==1) {
+ frm.add_custom_button(__('Ledger'), function() {
+ frappe.route_options = {
+ "voucher_no": frm.doc.name,
+ "from_date": frm.doc.posting_date,
+ "to_date": frm.doc.posting_date,
+ "company": frm.doc.company,
+ group_by_voucher: 0
+ };
+ frappe.set_route("query-report", "General Ledger");
+ }, "fa fa-table");
+ }
+ },
+
+ employee_loan_application: function(frm) {
+ return frm.call({
+ method: "erpnext.hr.doctype.employee_loan.employee_loan.get_employee_loan_application",
+ args: {
+ "employee_loan_application": frm.doc.employee_loan_application
+ },
+ callback: function(r){
+ if(!r.exc && r.message) {
+ frm.set_value("loan_type", r.message.loan_type);
+ frm.set_value("loan_amount", r.message.loan_amount);
+ frm.set_value("repayment_method", r.message.repayment_method);
+ frm.set_value("monthly_repayment_amount", r.message.repayment_amount);
+ frm.set_value("repayment_periods", r.message.repayment_periods);
+ frm.set_value("rate_of_interest", r.message.rate_of_interest);
+ }
+ }
+ })
+ },
+
+ repayment_method: function(frm) {
+ frm.trigger("toggle_fields")
+ },
+
+ toggle_fields: function(frm) {
+ frm.toggle_enable("monthly_repayment_amount", frm.doc.repayment_method=="Repay Fixed Amount per Period")
+ frm.toggle_enable("repayment_periods", frm.doc.repayment_method=="Repay Over Number of Periods")
+ }
+});
diff --git a/erpnext/hr/doctype/employee_loan/employee_loan.json b/erpnext/hr/doctype/employee_loan/employee_loan.json
new file mode 100644
index 00000000000..560515fde99
--- /dev/null
+++ b/erpnext/hr/doctype/employee_loan/employee_loan.json
@@ -0,0 +1,892 @@
+{
+ "allow_copy": 0,
+ "allow_import": 1,
+ "allow_rename": 0,
+ "autoname": "ELN.####",
+ "beta": 0,
+ "creation": "2016-12-02 10:11:49.673604",
+ "custom": 0,
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "Document",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "fields": [
+ {
+ "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": 0,
+ "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_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_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_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "employee_loan_application",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Employee Loan Application",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Employee Loan Application",
+ "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": "loan_type",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Loan Type",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Loan 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": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "column_break_3",
+ "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,
+ "default": "",
+ "fieldname": "posting_date",
+ "fieldtype": "Date",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Posting Date",
+ "length": 0,
+ "no_copy": 1,
+ "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_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,
+ "options": "Company",
+ "permlevel": 0,
+ "precision": "",
+ "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,
+ "fieldname": "status",
+ "fieldtype": "Select",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Status",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Loan Unpaid\nLoan Paid\nEMI in progress\nLoan Repaid",
+ "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": "repay_from_salary",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Repay from Salary",
+ "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": "section_break_8",
+ "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": "Loan 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": "loan_amount",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Loan Amount",
+ "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_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "default": "",
+ "fieldname": "rate_of_interest",
+ "fieldtype": "Percent",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Rate of Interest (%) / Year",
+ "length": 0,
+ "no_copy": 0,
+ "options": "loan_type.rate_of_interest",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "disbursement_date",
+ "fieldtype": "Date",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Disbursement Date",
+ "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_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": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "default": "Repay Over Number of Periods",
+ "fieldname": "repayment_method",
+ "fieldtype": "Select",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Repayment Method",
+ "length": 0,
+ "no_copy": 0,
+ "options": "\nRepay Fixed Amount per Period\nRepay Over Number of Periods",
+ "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_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "default": "",
+ "depends_on": "",
+ "fieldname": "repayment_periods",
+ "fieldtype": "Int",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Repayment Period in Months",
+ "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": "",
+ "depends_on": "",
+ "fieldname": "monthly_repayment_amount",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Repayment Amount",
+ "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": "account_info",
+ "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": "Account Info",
+ "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": "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": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "payment_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": "Payment 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_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "column_break_9",
+ "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,
+ "fieldname": "employee_loan_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": "Employee Loan 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_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "section_break_15",
+ "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": "Repayment Schedule",
+ "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": "repayment_schedule",
+ "fieldtype": "Table",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Repayment Schedule",
+ "length": 0,
+ "no_copy": 1,
+ "options": "Repayment Schedule",
+ "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": "section_break_17",
+ "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": "Totals",
+ "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": "0",
+ "fieldname": "total_payment",
+ "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 Payment",
+ "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_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "column_break_19",
+ "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,
+ "default": "0",
+ "fieldname": "total_interest_payable",
+ "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 Interest Payable",
+ "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_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_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Amended From",
+ "length": 0,
+ "no_copy": 1,
+ "options": "Employee Loan",
+ "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
+ }
+ ],
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "idx": 0,
+ "image_view": 0,
+ "in_create": 0,
+ "in_dialog": 0,
+ "is_submittable": 1,
+ "issingle": 0,
+ "istable": 0,
+ "max_attachments": 0,
+ "modified": "2017-01-17 07:13:10.704520",
+ "modified_by": "Administrator",
+ "module": "HR",
+ "name": "Employee Loan",
+ "name_case": "",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "amend": 0,
+ "apply_user_permissions": 0,
+ "cancel": 1,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "if_owner": 0,
+ "import": 0,
+ "is_custom": 0,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "HR Manager",
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 1,
+ "write": 1
+ }
+ ],
+ "quick_entry": 0,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "search_fields": "posting_date",
+ "sort_field": "creation",
+ "sort_order": "DESC",
+ "track_changes": 1,
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/erpnext/hr/doctype/employee_loan/employee_loan.py b/erpnext/hr/doctype/employee_loan/employee_loan.py
new file mode 100644
index 00000000000..4d7a9f38dfb
--- /dev/null
+++ b/erpnext/hr/doctype/employee_loan/employee_loan.py
@@ -0,0 +1,128 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe, math
+import erpnext
+from frappe import _
+from frappe.utils import flt, rounded, add_months, nowdate
+from erpnext.controllers.accounts_controller import AccountsController
+from erpnext.accounts.general_ledger import make_gl_entries
+
+class EmployeeLoan(AccountsController):
+ def validate(self):
+ check_repayment_method(self.repayment_method, self.loan_amount, self.monthly_repayment_amount, self.repayment_periods)
+ if not self.company:
+ self.company = erpnext.get_default_company()
+ if not self.posting_date:
+ self.posting_date = nowdate()
+ if self.loan_type and not self.rate_of_interest:
+ self.rate_of_interest = frappe.db.get_value("Loan Type", self.loan_type, "rate_of_interest")
+ if self.repayment_method == "Repay Over Number of Periods":
+ self.monthly_repayment_amount = get_monthly_repayment_amount(self.repayment_method, self.loan_amount, self.rate_of_interest, self.repayment_periods)
+
+ self.make_repayment_schedule()
+ self.set_repayment_period()
+ self.calculate_totals()
+
+ def on_submit(self):
+ self.make_gl_entries()
+
+ def on_cancel(self):
+ self.make_gl_entries()
+
+ def make_gl_entries(self):
+ gl_entries = []
+ # Gl entries for employee loan account
+ gl_entries.append(
+ self.get_gl_dict({
+ "account": self.employee_loan_account,
+ "party_type": "Employee",
+ "party": self.employee,
+ "debit": self.loan_amount,
+ "debit_in_account_currency": self.loan_amount
+ })
+ )
+ # Gl entries for payment account
+ gl_entries.append(
+ self.get_gl_dict({
+ "account": self.payment_account,
+ "credit": self.loan_amount,
+ "credit_in_account_currency": self.loan_amount
+ })
+ )
+ make_gl_entries(gl_entries, cancel=(self.docstatus == 2))
+
+ def make_repayment_schedule(self):
+ self.repayment_schedule = []
+ payment_date = self.disbursement_date
+ balance_amount = self.loan_amount
+
+ while(balance_amount > 0):
+ interest_amount = rounded(balance_amount * flt(self.rate_of_interest) / (12*100))
+ principal_amount = self.monthly_repayment_amount - interest_amount
+ balance_amount = rounded(balance_amount + interest_amount - self.monthly_repayment_amount)
+
+ if balance_amount < 0:
+ principal_amount += balance_amount
+ balance_amount = 0.0
+
+ total_payment = principal_amount + interest_amount
+
+ self.append("repayment_schedule", {
+ "payment_date": payment_date,
+ "principal_amount": principal_amount,
+ "interest_amount": interest_amount,
+ "total_payment": total_payment,
+ "balance_loan_amount": balance_amount
+ })
+
+ next_payment_date = add_months(payment_date, 1)
+ payment_date = next_payment_date
+
+ def set_repayment_period(self):
+ if self.repayment_method == "Repay Fixed Amount per Period":
+ repayment_periods = len(self.repayment_schedule)
+
+ self.repayment_periods = repayment_periods
+
+ def calculate_totals(self):
+ self.total_payment = 0
+ self.total_interest_payable = 0
+ for data in self.repayment_schedule:
+ self.total_payment += data.total_payment
+ self.total_interest_payable +=data.interest_amount
+
+ def update_status(self):
+ if self.disbursement_date:
+ self.status = "Loan Paid"
+ if len(self.repayment_schedule)>0:
+ self.status = "repayment in progress"
+
+def check_repayment_method(repayment_method, loan_amount, monthly_repayment_amount, repayment_periods):
+ if repayment_method == "Repay Over Number of Periods" and not repayment_periods:
+ frappe.throw(_("Please enter Repayment Periods"))
+
+ if repayment_method == "Repay Fixed Amount per Period":
+ if not monthly_repayment_amount:
+ frappe.throw(_("Please enter repayment Amount"))
+ if monthly_repayment_amount > loan_amount:
+ 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))
+ else:
+ monthly_repayment_amount = math.ceil(flt(loan_amount) / repayment_periods)
+ return monthly_repayment_amount
+
+@frappe.whitelist()
+def get_employee_loan_application(employee_loan_application):
+ employee_loan = frappe.get_doc("Employee Loan Application", employee_loan_application)
+ if employee_loan:
+ return employee_loan
\ No newline at end of file
diff --git a/erpnext/hr/doctype/employee_loan/test_employee_loan.py b/erpnext/hr/doctype/employee_loan/test_employee_loan.py
new file mode 100644
index 00000000000..c1c6ba24a99
--- /dev/null
+++ b/erpnext/hr/doctype/employee_loan/test_employee_loan.py
@@ -0,0 +1,68 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+import frappe
+import erpnext
+import unittest
+from frappe.utils import nowdate
+from erpnext.hr.doctype.salary_structure.test_salary_structure import make_employee
+
+class TestEmployeeLoan(unittest.TestCase):
+ def setUp(self):
+ create_loan_type("Personal Loan", 500000, 8.4)
+ self.employee = make_employee("robert_loan@loan.com")
+ create_employee_loan(self.employee, "Personal Loan", 280000, "Repay Over Number of Periods", 20)
+
+ def test_employee_loan(self):
+ employee_loan = frappe.get_doc("Employee Loan", {"employee":self.employee})
+ self.assertEquals(employee_loan.monthly_repayment_amount, 15052)
+ self.assertEquals(employee_loan.total_interest_payable, 21034)
+ self.assertEquals(employee_loan.total_payment, 301034)
+
+ schedule = employee_loan.repayment_schedule
+
+ self.assertEquals(len(schedule), 20)
+
+ for idx, principal_amount, interest_amount, balance_loan_amount in [[3, 13369, 1683, 227079], [19, 14941, 105, 0], [17, 14740, 312, 29785]]:
+ self.assertEquals(schedule[idx].principal_amount, principal_amount)
+ self.assertEquals(schedule[idx].interest_amount, interest_amount)
+ self.assertEquals(schedule[idx].balance_loan_amount, balance_loan_amount)
+
+ employee_loan.repayment_method = "Repay Fixed Amount per Period"
+ employee_loan.monthly_repayment_amount = 14000
+ employee_loan.save()
+
+ self.assertEquals(len(employee_loan.repayment_schedule), 22)
+ self.assertEquals(employee_loan.total_interest_payable, 22712)
+ self.assertEquals(employee_loan.total_payment, 302712)
+
+
+def create_loan_type(loan_name, maximum_loan_amount, rate_of_interest):
+ if not frappe.db.get_value("Loan Type", loan_name):
+ frappe.get_doc({
+ "doctype": "Loan Type",
+ "loan_name": loan_name,
+ "maximum_loan_amount": maximum_loan_amount,
+ "rate_of_interest": rate_of_interest
+ }).insert()
+
+def create_employee_loan(employee, loan_type, loan_amount, repayment_method, repayment_periods):
+ if not frappe.db.get_value("Employee Loan", {"employee":employee}):
+ employee_loan = frappe.new_doc("Employee Loan")
+ employee_loan.update({
+ "employee": employee,
+ "loan_type": loan_type,
+ "loan_amount": loan_amount,
+ "repayment_method": repayment_method,
+ "repayment_periods": repayment_periods,
+ "disbursement_date": nowdate(),
+ "mode_of_payment": frappe.db.get_value('Mode of Payment', {'type': 'Cash'}, 'name'),
+ "payment_account": frappe.db.get_value('Account', {'account_type': 'Cash', 'company': erpnext.get_default_company(),'is_group':0}, "name"),
+ "employee_loan_account": frappe.db.get_value('Account', {'account_type': 'Cash', 'company': erpnext.get_default_company(),'is_group':0}, "name")
+ })
+ employee_loan.insert()
+ return employee_loan
+ else:
+ return frappe.get_doc("Employee Loan", {"employee":employee})
\ No newline at end of file
diff --git a/erpnext/schools/doctype/topic/__init__.py b/erpnext/hr/doctype/employee_loan_application/__init__.py
similarity index 100%
rename from erpnext/schools/doctype/topic/__init__.py
rename to erpnext/hr/doctype/employee_loan_application/__init__.py
diff --git a/erpnext/hr/doctype/employee_loan_application/employee_loan_application.js b/erpnext/hr/doctype/employee_loan_application/employee_loan_application.js
new file mode 100644
index 00000000000..6958beabfa7
--- /dev/null
+++ b/erpnext/hr/doctype/employee_loan_application/employee_loan_application.js
@@ -0,0 +1,16 @@
+// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Employee Loan Application', {
+ refresh: function(frm) {
+ frm.trigger("toggle_fields")
+ },
+ repayment_method: function(frm) {
+ frm.trigger("toggle_fields")
+ },
+
+ toggle_fields: function(frm) {
+ frm.toggle_enable("repayment_amount", frm.doc.repayment_method=="Repay Fixed Amount per Period")
+ frm.toggle_enable("repayment_periods", frm.doc.repayment_method=="Repay Over Number of Periods")
+ }
+});
diff --git a/erpnext/hr/doctype/employee_loan_application/employee_loan_application.json b/erpnext/hr/doctype/employee_loan_application/employee_loan_application.json
new file mode 100644
index 00000000000..e30dc5a0012
--- /dev/null
+++ b/erpnext/hr/doctype/employee_loan_application/employee_loan_application.json
@@ -0,0 +1,681 @@
+{
+ "allow_copy": 0,
+ "allow_import": 0,
+ "allow_rename": 0,
+ "autoname": "ELA/.#####",
+ "beta": 0,
+ "creation": "2016-12-02 12:35:56.046811",
+ "custom": 0,
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "fields": [
+ {
+ "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_list_view": 0,
+ "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": 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": "employee",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "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_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": 1,
+ "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,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "column_break_2",
+ "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,
+ "fieldname": "status",
+ "fieldtype": "Select",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Status",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Open\nApproved\nRejected",
+ "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": 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_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "section_break_4",
+ "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": "Loan Info",
+ "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": "loan_type",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Loan Type",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Loan 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": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "loan_amount",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Loan Amount",
+ "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_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "required_by_date",
+ "fieldtype": "Date",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Required by Date",
+ "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": "column_break_7",
+ "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,
+ "fieldname": "description",
+ "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": "Reason",
+ "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": "repayment_info",
+ "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": "Repayment Info",
+ "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": "repayment_method",
+ "fieldtype": "Select",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Repayment Method",
+ "length": 0,
+ "no_copy": 0,
+ "options": "\nRepay Fixed Amount per Period\nRepay Over Number of Periods",
+ "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_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "rate_of_interest",
+ "fieldtype": "Percent",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Rate of Interest",
+ "length": 0,
+ "no_copy": 0,
+ "options": "loan_type.rate_of_interest",
+ "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": "total_payable_interest",
+ "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 Payable Interest",
+ "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_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": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "depends_on": "",
+ "fieldname": "repayment_amount",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Repayment Amount",
+ "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": "repayment_periods",
+ "fieldtype": "Int",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Repayment Period in Months",
+ "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": "total_payable_amount",
+ "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 Payable Amount",
+ "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_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_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Amended From",
+ "length": 0,
+ "no_copy": 1,
+ "options": "Employee Loan Application",
+ "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
+ }
+ ],
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "idx": 0,
+ "image_view": 0,
+ "in_create": 0,
+ "in_dialog": 0,
+ "is_submittable": 1,
+ "issingle": 0,
+ "istable": 0,
+ "max_attachments": 0,
+ "modified": "2017-01-09 12:02:36.562577",
+ "modified_by": "Administrator",
+ "module": "HR",
+ "name": "Employee Loan Application",
+ "name_case": "",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "amend": 0,
+ "apply_user_permissions": 0,
+ "cancel": 1,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "if_owner": 0,
+ "import": 0,
+ "is_custom": 0,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "HR Manager",
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 1,
+ "write": 1
+ },
+ {
+ "amend": 0,
+ "apply_user_permissions": 0,
+ "cancel": 0,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "if_owner": 0,
+ "import": 0,
+ "is_custom": 0,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Employee",
+ "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, loan_type, loan_amount",
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "timeline_field": "employee",
+ "title_field": "employee_name",
+ "track_changes": 1,
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/erpnext/hr/doctype/employee_loan_application/employee_loan_application.py b/erpnext/hr/doctype/employee_loan_application/employee_loan_application.py
new file mode 100644
index 00000000000..15dbd4e5e40
--- /dev/null
+++ b/erpnext/hr/doctype/employee_loan_application/employee_loan_application.py
@@ -0,0 +1,34 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe, math
+from frappe import _
+from frappe.utils import flt
+from frappe.model.document import Document
+
+from erpnext.hr.doctype.employee_loan.employee_loan import get_monthly_repayment_amount, check_repayment_method
+
+class EmployeeLoanApplication(Document):
+ def validate(self):
+ check_repayment_method(self.repayment_method, self.loan_amount, self.repayment_amount, self.repayment_periods)
+ self.validate_loan_amount()
+ self.get_repayment_details()
+
+ def validate_loan_amount(self):
+ maximum_loan_limit = frappe.db.get_value('Loan Type', self.loan_type, 'maximum_loan_amount')
+ if self.loan_amount > maximum_loan_limit:
+ frappe.throw(_("Loan Amount cannot exceed Maximum Loan Amount of {0}").format(maximum_loan_limit))
+
+ def get_repayment_details(self):
+ if self.repayment_method == "Repay Over Number of Periods":
+ self.repayment_amount = get_monthly_repayment_amount(self.repayment_method, self.loan_amount, self.rate_of_interest, self.repayment_periods)
+
+ if self.repayment_method == "Repay Fixed Amount per Period":
+ monthly_interest_rate = flt(self.rate_of_interest) / (12 *100)
+ self.repayment_periods = math.ceil((math.log(self.repayment_amount) - math.log(self.repayment_amount - \
+ (self.loan_amount*monthly_interest_rate)))/(math.log(1+monthly_interest_rate)))
+
+ self.total_payable_amount = self.repayment_amount * self.repayment_periods
+ self.total_payable_interest = self.total_payable_amount - self.loan_amount
\ No newline at end of file
diff --git a/erpnext/hr/doctype/employee_loan_application/test_employee_loan_application.py b/erpnext/hr/doctype/employee_loan_application/test_employee_loan_application.py
new file mode 100644
index 00000000000..1d157d6d810
--- /dev/null
+++ b/erpnext/hr/doctype/employee_loan_application/test_employee_loan_application.py
@@ -0,0 +1,51 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+import frappe
+import unittest
+from erpnext.hr.doctype.salary_structure.test_salary_structure import make_employee
+
+class TestEmployeeLoanApplication(unittest.TestCase):
+ def setUp(self):
+ self.create_loan_type()
+ self.employee = make_employee("kate_loan@loan.com")
+ self.create_loan_application()
+
+ def create_loan_type(self):
+ if not frappe.db.get_value("Loan Type", "Home Loan"):
+ frappe.get_doc({
+ "doctype": "Loan Type",
+ "loan_name": "Home Loan",
+ "maximum_loan_amount": 500000,
+ "rate_of_interest": 9.2
+ }).insert()
+
+ def create_loan_application(self):
+ if not frappe.db.get_value("Employee Loan Application", {"employee":self.employee}, "name"):
+ loan_application = frappe.new_doc("Employee Loan Application")
+ loan_application.update({
+ "employee": self.employee,
+ "loan_type": "Home Loan",
+ "rate_of_interest": 9.2,
+ "loan_amount": 250000,
+ "repayment_method": "Repay Over Number of Periods",
+ "repayment_periods": 24
+ })
+ loan_application.insert()
+
+
+ def test_loan_totals(self):
+ loan_application = frappe.get_doc("Employee Loan Application", {"employee":self.employee})
+ self.assertEquals(loan_application.repayment_amount, 11445)
+ self.assertEquals(loan_application.total_payable_interest, 24680)
+ self.assertEquals(loan_application.total_payable_amount, 274680)
+
+ loan_application.repayment_method = "Repay Fixed Amount per Period"
+ loan_application.repayment_amount = 15000
+ loan_application.save()
+
+ self.assertEquals(loan_application.repayment_periods, 18)
+ self.assertEquals(loan_application.total_payable_interest, 20000)
+ self.assertEquals(loan_application.total_payable_amount, 270000)
\ 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 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/loan_type/__init__.py b/erpnext/hr/doctype/loan_type/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/erpnext/hr/doctype/loan_type/loan_type.js b/erpnext/hr/doctype/loan_type/loan_type.js
new file mode 100644
index 00000000000..72f5775adde
--- /dev/null
+++ b/erpnext/hr/doctype/loan_type/loan_type.js
@@ -0,0 +1,7 @@
+// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Loan Type', {
+ refresh: function(frm) {
+ }
+});
diff --git a/erpnext/schools/doctype/discussion/discussion.json b/erpnext/hr/doctype/loan_type/loan_type.json
similarity index 58%
rename from erpnext/schools/doctype/discussion/discussion.json
rename to erpnext/hr/doctype/loan_type/loan_type.json
index 57ebfbaceb3..f9441ea1a28 100644
--- a/erpnext/schools/doctype/discussion/discussion.json
+++ b/erpnext/hr/doctype/loan_type/loan_type.json
@@ -1,15 +1,15 @@
{
"allow_copy": 0,
- "allow_import": 1,
+ "allow_import": 0,
"allow_rename": 0,
- "autoname": "Discussion.####",
+ "autoname": "field:loan_name",
"beta": 0,
- "creation": "2016-06-13 07:57:38.326925",
+ "creation": "2016-12-02 10:41:40.732843",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
- "document_type": "Document",
- "editable_grid": 0,
+ "document_type": "",
+ "editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
@@ -17,7 +17,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
- "fieldname": "subject",
+ "fieldname": "loan_name",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
@@ -25,7 +25,7 @@
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
- "label": "Subject",
+ "label": "Loan Name",
"length": 0,
"no_copy": 0,
"permlevel": 0,
@@ -36,7 +36,7 @@
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
- "search_index": 1,
+ "search_index": 0,
"set_only_once": 0,
"unique": 0
},
@@ -45,18 +45,17 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
- "fieldname": "course",
- "fieldtype": "Link",
+ "fieldname": "maximum_loan_amount",
+ "fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
- "in_list_view": 1,
- "in_standard_filter": 1,
- "label": "Course",
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Maximum Loan Amount",
"length": 0,
"no_copy": 0,
- "options": "Course",
"permlevel": 0,
"precision": "",
"print_hide": 0,
@@ -65,7 +64,92 @@
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
- "search_index": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "rate_of_interest",
+ "fieldtype": "Percent",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Rate of Interest (%) Yearly",
+ "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_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "column_break_2",
+ "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,
+ "default": "",
+ "fieldname": "disabled",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Disabled",
+ "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": 0,
+ "search_index": 0,
"set_only_once": 0,
"unique": 0
},
@@ -96,34 +180,6 @@
"search_index": 0,
"set_only_once": 0,
"unique": 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_list_view": 0,
- "in_standard_filter": 0,
- "label": "Amended From",
- "length": 0,
- "no_copy": 1,
- "options": "Discussion",
- "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
}
],
"hide_heading": 0,
@@ -132,14 +188,14 @@
"image_view": 0,
"in_create": 0,
"in_dialog": 0,
- "is_submittable": 1,
+ "is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2016-11-07 05:28:34.032169",
+ "modified": "2016-12-29 15:54:17.716285",
"modified_by": "Administrator",
- "module": "Schools",
- "name": "Discussion",
+ "module": "HR",
+ "name": "Loan Type",
"name_case": "",
"owner": "Administrator",
"permissions": [
@@ -158,18 +214,17 @@
"print": 1,
"read": 1,
"report": 1,
- "role": "Academics User",
+ "role": "HR Manager",
"set_user_permissions": 0,
"share": 1,
- "submit": 1,
+ "submit": 0,
"write": 1
}
],
- "quick_entry": 1,
+ "quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"sort_field": "modified",
"sort_order": "DESC",
- "title_field": "subject",
- "track_seen": 1
+ "track_seen": 0
}
\ No newline at end of file
diff --git a/erpnext/hr/doctype/loan_type/loan_type.py b/erpnext/hr/doctype/loan_type/loan_type.py
new file mode 100644
index 00000000000..2714e206d8d
--- /dev/null
+++ b/erpnext/hr/doctype/loan_type/loan_type.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2015, 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 LoanType(Document):
+ pass
diff --git a/erpnext/hr/doctype/loan_type/test_loan_type.py b/erpnext/hr/doctype/loan_type/test_loan_type.py
new file mode 100644
index 00000000000..078e11e2627
--- /dev/null
+++ b/erpnext/hr/doctype/loan_type/test_loan_type.py
@@ -0,0 +1,12 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+import frappe
+import unittest
+
+# test_records = frappe.get_test_records('Loan Type')
+
+class TestLoanType(unittest.TestCase):
+ pass
diff --git a/erpnext/hr/doctype/repayment_schedule/__init__.py b/erpnext/hr/doctype/repayment_schedule/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/erpnext/schools/doctype/topic/topic.json b/erpnext/hr/doctype/repayment_schedule/repayment_schedule.json
similarity index 59%
rename from erpnext/schools/doctype/topic/topic.json
rename to erpnext/hr/doctype/repayment_schedule/repayment_schedule.json
index 0a69f88222f..3a0dfd3d55a 100644
--- a/erpnext/schools/doctype/topic/topic.json
+++ b/erpnext/hr/doctype/repayment_schedule/repayment_schedule.json
@@ -1,60 +1,30 @@
{
"allow_copy": 0,
"allow_import": 0,
- "allow_rename": 1,
- "autoname": "Topic.####",
+ "allow_rename": 0,
"beta": 0,
- "creation": "2016-06-28 07:06:38.749398",
+ "creation": "2016-12-20 15:32:25.078334",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
- "document_type": "Document",
- "editable_grid": 0,
+ "document_type": "",
+ "editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
- "columns": 0,
- "fieldname": "course",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 1,
- "in_standard_filter": 1,
- "label": "Course",
- "length": 0,
- "no_copy": 0,
- "options": "Course",
- "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": 1,
- "set_only_once": 0,
- "unique": 0
- },
- {
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "topic_name",
- "fieldtype": "Data",
+ "columns": 2,
+ "fieldname": "payment_date",
+ "fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"in_standard_filter": 0,
- "label": "Topic Name",
+ "label": "Payment Date",
"length": 0,
"no_copy": 0,
"permlevel": 0,
@@ -64,7 +34,7 @@
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
- "reqd": 1,
+ "reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
@@ -73,26 +43,26 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
- "columns": 0,
- "fieldname": "introduction",
- "fieldtype": "Text",
+ "columns": 2,
+ "fieldname": "principal_amount",
+ "fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
- "in_list_view": 0,
+ "in_list_view": 1,
"in_standard_filter": 0,
- "label": "Introduction",
+ "label": "Principal Amount",
"length": 0,
- "no_copy": 0,
+ "no_copy": 1,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
- "read_only": 0,
+ "read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
- "reqd": 1,
+ "reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
@@ -101,26 +71,82 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
- "columns": 0,
- "fieldname": "content",
- "fieldtype": "Text Editor",
+ "columns": 2,
+ "fieldname": "interest_amount",
+ "fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
- "in_list_view": 0,
+ "in_list_view": 1,
"in_standard_filter": 0,
- "label": "Content",
+ "label": "Interest Amount",
+ "length": 0,
+ "no_copy": 1,
+ "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": 2,
+ "fieldname": "total_payment",
+ "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 Payment",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
- "read_only": 0,
+ "read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
- "reqd": 1,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 2,
+ "fieldname": "balance_loan_amount",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Balance Loan Amount",
+ "length": 0,
+ "no_copy": 1,
+ "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
@@ -134,42 +160,20 @@
"in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
- "istable": 0,
+ "istable": 1,
"max_attachments": 0,
- "modified": "2016-11-07 05:29:20.531725",
+ "modified": "2017-01-09 12:00:10.818772",
"modified_by": "Administrator",
- "module": "Schools",
- "name": "Topic",
+ "module": "HR",
+ "name": "Repayment Schedule",
"name_case": "",
"owner": "Administrator",
- "permissions": [
- {
- "amend": 0,
- "apply_user_permissions": 0,
- "cancel": 0,
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "if_owner": 0,
- "import": 0,
- "is_custom": 0,
- "permlevel": 0,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Academics User",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 0,
- "write": 1
- }
- ],
+ "permissions": [],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"sort_field": "modified",
"sort_order": "DESC",
- "title_field": "course",
+ "track_changes": 1,
"track_seen": 0
}
\ No newline at end of file
diff --git a/erpnext/hr/doctype/repayment_schedule/repayment_schedule.py b/erpnext/hr/doctype/repayment_schedule/repayment_schedule.py
new file mode 100644
index 00000000000..8abee5e0894
--- /dev/null
+++ b/erpnext/hr/doctype/repayment_schedule/repayment_schedule.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2015, 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 RepaymentSchedule(Document):
+ pass
diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.js b/erpnext/hr/doctype/salary_slip/salary_slip.js
index 8b0dd1655c0..02622598551 100644
--- a/erpnext/hr/doctype/salary_slip/salary_slip.js
+++ b/erpnext/hr/doctype/salary_slip/salary_slip.js
@@ -110,6 +110,7 @@ cur_frm.cscript.depends_on_lwp = function(doc,dt,dn){
calculate_earning_total(doc, dt, dn, true);
calculate_ded_total(doc, dt, dn, true);
calculate_net_pay(doc, dt, dn);
+ refresh_many(['amount','gross_pay', 'rounded_total', 'net_pay', 'loan_repayment']);
};
// Calculate earning total
diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.json b/erpnext/hr/doctype/salary_slip/salary_slip.json
index e558d73bcfb..7d5dbe9c9aa 100644
--- a/erpnext/hr/doctype/salary_slip/salary_slip.json
+++ b/erpnext/hr/doctype/salary_slip/salary_slip.json
@@ -1266,7 +1266,35 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
- "description": "Gross Pay + Arrear Amount +Encashment Amount - Total Deduction",
+ "fieldname": "loan_repayment",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Loan Repayment",
+ "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_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "description": "Gross Pay + Arrear Amount + Encashment Amount - Total Deduction - Loan Repayment",
"fieldname": "net_pay",
"fieldtype": "Currency",
"hidden": 0,
@@ -1362,7 +1390,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2016-12-14 08:26:31.400930",
+ "modified": "2017-01-09 12:37:03.802501",
"modified_by": "Administrator",
"module": "HR",
"name": "Salary Slip",
@@ -1413,7 +1441,7 @@
},
{
"amend": 0,
- "apply_user_permissions": 0,
+ "apply_user_permissions": 1,
"cancel": 0,
"create": 0,
"delete": 0,
@@ -1430,6 +1458,7 @@
"set_user_permissions": 0,
"share": 0,
"submit": 0,
+ "user_permission_doctypes": "[\"Employee\"]",
"write": 0
}
],
@@ -1440,5 +1469,6 @@
"sort_order": "DESC",
"timeline_field": "employee",
"title_field": "employee_name",
+ "track_changes": 0,
"track_seen": 0
}
\ No newline at end of file
diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.py b/erpnext/hr/doctype/salary_slip/salary_slip.py
index eeec6e8e769..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
@@ -329,11 +328,21 @@ class SalarySlip(TransactionBase):
self.sum_components('earnings', 'gross_pay')
self.sum_components('deductions', 'total_deduction')
+
+ self.set_loan_repayment()
- self.net_pay = flt(self.gross_pay) - flt(self.total_deduction)
+ self.net_pay = flt(self.gross_pay) - (flt(self.total_deduction) + flt(self.loan_repayment))
self.rounded_total = rounded(self.net_pay,
self.precision("net_pay") if disable_rounded_total else 0)
+ def set_loan_repayment(self):
+ employee_loan = frappe.db.sql("""select sum(total_payment) as loan_repayment from `tabRepayment Schedule`
+ where payment_date between %s and %s and parent in (select name from `tabEmployee Loan`
+ where employee = %s and repay_from_salary = 1 and docstatus = 1)""",
+ (self.start_date, self.end_date, self.employee), as_dict=True)
+ if employee_loan:
+ self.loan_repayment = employee_loan[0].loan_repayment
+
def on_submit(self):
if self.net_pay < 0:
frappe.throw(_("Net Pay cannot be less than 0"))
diff --git a/erpnext/hr/doctype/salary_slip/test_salary_slip.py b/erpnext/hr/doctype/salary_slip/test_salary_slip.py
index 3d119c3e383..9999f1ec088 100644
--- a/erpnext/hr/doctype/salary_slip/test_salary_slip.py
+++ b/erpnext/hr/doctype/salary_slip/test_salary_slip.py
@@ -7,7 +7,7 @@ import frappe
import erpnext
import calendar
from erpnext.accounts.utils import get_fiscal_year
-from frappe.utils import getdate, nowdate, add_days
+from frappe.utils import getdate, nowdate, add_days, flt
from erpnext.hr.doctype.salary_structure.salary_structure import make_salary_slip
from erpnext.hr.doctype.process_payroll.test_process_payroll import get_salary_component_account
from erpnext.hr.doctype.process_payroll.process_payroll import get_month_details
@@ -24,7 +24,6 @@ class TestSalarySlip(unittest.TestCase):
frappe.db.set_value("Company", erpnext.get_default_company(), "default_holiday_list", "Salary Slip Test Holiday List")
-
def tearDown(self):
frappe.db.set_value("HR Settings", None, "include_holidays_in_total_working_days", 0)
frappe.set_user("Administrator")
@@ -40,12 +39,12 @@ class TestSalarySlip(unittest.TestCase):
self.assertEquals(ss.total_working_days, no_of_days[0])
self.assertEquals(ss.payment_days, no_of_days[0])
- self.assertEquals(ss.earnings[0].amount, 5000)
+ self.assertEquals(ss.earnings[0].amount, 25000)
self.assertEquals(ss.earnings[1].amount, 3000)
self.assertEquals(ss.deductions[0].amount, 5000)
- self.assertEquals(ss.deductions[1].amount, 2500)
- self.assertEquals(ss.gross_pay, 10500)
- self.assertEquals(ss.net_pay, 3000)
+ self.assertEquals(ss.deductions[1].amount, 5000)
+ self.assertEquals(ss.gross_pay, 40500)
+ self.assertEquals(ss.net_pay, 29918)
def test_salary_slip_with_holidays_excluded(self):
no_of_days = self.get_no_of_days()
@@ -58,13 +57,13 @@ class TestSalarySlip(unittest.TestCase):
self.assertEquals(ss.total_working_days, no_of_days[0] - no_of_days[1])
self.assertEquals(ss.payment_days, no_of_days[0] - no_of_days[1])
- self.assertEquals(ss.earnings[0].amount, 5000)
- self.assertEquals(ss.earnings[0].default_amount, 5000)
+ self.assertEquals(ss.earnings[0].amount, 25000)
+ self.assertEquals(ss.earnings[0].default_amount, 25000)
self.assertEquals(ss.earnings[1].amount, 3000)
self.assertEquals(ss.deductions[0].amount, 5000)
- self.assertEquals(ss.deductions[1].amount, 2500)
- self.assertEquals(ss.gross_pay, 10500)
- self.assertEquals(ss.net_pay, 3000)
+ self.assertEquals(ss.deductions[1].amount, 5000)
+ self.assertEquals(ss.gross_pay, 40500)
+ self.assertEquals(ss.net_pay, 29918)
def test_payment_days(self):
no_of_days = self.get_no_of_days()
@@ -130,9 +129,23 @@ class TestSalarySlip(unittest.TestCase):
ss = frappe.get_doc("Salary Slip",
self.make_employee_salary_slip("test_employee@salary.com", "Monthly"))
ss.submit()
+
email_queue = frappe.db.sql("""select name from `tabEmail Queue`""")
self.assertTrue(email_queue)
+ def test_loan_repayment_salary_slip(self):
+ from erpnext.hr.doctype.employee_loan.test_employee_loan import create_loan_type, create_employee_loan
+ employee = self.make_employee("test_employee@salary.com")
+ create_loan_type("Car Loan", 500000, 6.4)
+ employee_loan = create_employee_loan(employee, "Car Loan", 11000, "Repay Over Number of Periods", 20)
+ employee_loan.repay_from_salary = 1
+ employee_loan.submit()
+ ss = frappe.get_doc("Salary Slip",
+ self.make_employee_salary_slip("test_employee@salary.com", "Monthly"))
+ ss.submit()
+ self.assertEquals(ss.loan_repayment, 582)
+ self.assertEquals(ss.net_pay, (flt(ss.gross_pay) - (flt(ss.total_deduction) + flt(ss.loan_repayment))))
+
def test_payroll_frequency(self):
fiscal_year = get_fiscal_year(nowdate(), company="_Test Company")[0]
month = "%02d" % getdate(nowdate()).month
@@ -167,7 +180,7 @@ class TestSalarySlip(unittest.TestCase):
}).insert()
if not frappe.db.get_value("Employee", {"user_id": user}):
- frappe.get_doc({
+ employee = frappe.get_doc({
"doctype": "Employee",
"naming_series": "EMP-",
"employee_name": user,
@@ -183,6 +196,9 @@ class TestSalarySlip(unittest.TestCase):
"status": "Active",
"employment_type": "Intern"
}).insert()
+ return employee.name
+ else:
+ return frappe.get_value("Employee", {"employee_name":user}, "name")
def make_holiday_list(self):
fiscal_year = get_fiscal_year(nowdate(), company="_Test Company")
@@ -278,7 +294,7 @@ def make_salary_structure(sal_struct, payroll_frequency, employee):
def get_employee_details(employee):
return [{"employee": employee,
- "base": 25000,
+ "base": 50000,
"variable": 5000
}
]
@@ -289,14 +305,14 @@ def get_earnings_component():
"salary_component": 'Basic Salary',
"abbr":'BS',
"condition": 'base > 10000',
- "formula": 'base*.2',
+ "formula": 'base*.5',
"idx": 1
},
{
"salary_component": 'Basic Salary',
"abbr":'BS',
"condition": 'base < 10000',
- "formula": 'base*.1',
+ "formula": 'base*.2',
"idx": 2
},
{
@@ -320,13 +336,13 @@ def get_deductions_component():
"salary_component": 'Professional Tax',
"abbr":'PT',
"condition": 'base > 10000',
- "formula": 'base*.2',
+ "formula": 'base*.1',
"idx": 1
},
{
"salary_component": 'TDS',
"abbr":'T',
- "formula": 'base*.5',
+ "formula": 'base*.1',
"idx": 2
},
{
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/test_salary_structure.py b/erpnext/hr/doctype/salary_structure/test_salary_structure.py
index fe88d9afcfc..36e50d319af 100644
--- a/erpnext/hr/doctype/salary_structure/test_salary_structure.py
+++ b/erpnext/hr/doctype/salary_structure/test_salary_structure.py
@@ -19,8 +19,8 @@ class TestSalaryStructure(unittest.TestCase):
frappe.db.set_value("Company", erpnext.get_default_company(), "default_holiday_list", "Salary Structure Test Holiday List")
make_earning_salary_component(["Basic Salary", "Allowance", "HRA"])
make_deduction_salary_component(["Professional Tax", "TDS"])
- self.make_employee("test_employee@salary.com")
- self.make_employee("test_employee_2@salary.com")
+ make_employee("test_employee@salary.com")
+ make_employee("test_employee_2@salary.com")
def make_holiday_list(self):
if not frappe.db.get_value("Holiday List", "Salary Structure Test Holiday List"):
@@ -33,37 +33,6 @@ class TestSalaryStructure(unittest.TestCase):
}).insert()
holiday_list.get_weekly_off_dates()
holiday_list.save()
-
- def make_employee(self, user):
- if not frappe.db.get_value("User", user):
- frappe.get_doc({
- "doctype": "User",
- "email": user,
- "first_name": user,
- "new_password": "password",
- "user_roles": [{"doctype": "UserRole", "role": "Employee"}]
- }).insert()
-
-
- if not frappe.db.get_value("Employee", {"user_id": user}):
- emp = frappe.get_doc({
- "doctype": "Employee",
- "naming_series": "EMP-",
- "employee_name": user,
- "company": erpnext.get_default_company(),
- "user_id": user,
- "date_of_birth": "1990-05-08",
- "date_of_joining": "2013-01-01",
- "relieving_date": "",
- "department": frappe.get_all("Department", fields="name")[0].name,
- "gender": "Female",
- "company_email": user,
- "status": "Active",
- "employment_type": "Intern"
- }).insert()
- return emp.name
- else:
- return frappe.get_value("Employee", {"employee_name":user}, "name")
def test_amount_totals(self):
sal_slip = frappe.get_value("Salary Slip", {"employee_name":"test_employee@salary.com"})
@@ -76,6 +45,36 @@ class TestSalaryStructure(unittest.TestCase):
self.assertEquals(sal_slip.get("total_deduction"), 7500)
self.assertEquals(sal_slip.get("net_pay"), 7500)
+def make_employee(user):
+ if not frappe.db.get_value("User", user):
+ frappe.get_doc({
+ "doctype": "User",
+ "email": user,
+ "first_name": user,
+ "new_password": "password",
+ "user_roles": [{"doctype": "UserRole", "role": "Employee"}]
+ }).insert()
+
+
+ if not frappe.db.get_value("Employee", {"user_id": user}):
+ emp = frappe.get_doc({
+ "doctype": "Employee",
+ "naming_series": "EMP-",
+ "employee_name": user,
+ "company": erpnext.get_default_company(),
+ "user_id": user,
+ "date_of_birth": "1990-05-08",
+ "date_of_joining": "2013-01-01",
+ "relieving_date": "",
+ "department": frappe.get_all("Department", fields="name")[0].name,
+ "gender": "Female",
+ "company_email": user,
+ "status": "Active",
+ "employment_type": "Intern"
+ }).insert()
+ return emp.name
+ else:
+ return frappe.get_value("Employee", {"employee_name":user}, "name")
def make_salary_slip_from_salary_structure(employee):
sal_struct = make_salary_structure('Salary Structure Sample')
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/__init__.py b/erpnext/hr/page/team_updates/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/erpnext/hr/page/team_updates/team_update_row.html b/erpnext/hr/page/team_updates/team_update_row.html
new file mode 100644
index 00000000000..e3adcb88887
--- /dev/null
+++ b/erpnext/hr/page/team_updates/team_update_row.html
@@ -0,0 +1,15 @@
++ {% if data.value %} + + {{ __("Open BOM {0}", [data.value.bold()]) }} + {% endif %} + + {{ __("Open Item {0}", [data.item_code.bold()]) }} +
+{{doc.description}}
-
- {% for file in attached_files%}
- {{file.file_name}}
-
- {% endfor %}
-
- {{ doc.posted_by }}
- {{ frappe.format_date(doc.modified) }}
-
{{doc.description}}
-Started by: {{doc.owner}}
-{% endblock %} - -{% block page_content %} - -{{ doc.content }}
- -{% endblock %} \ No newline at end of file diff --git a/erpnext/templates/pages/topics.py b/erpnext/templates/pages/topics.py deleted file mode 100644 index 8a55b640e77..00000000000 --- a/erpnext/templates/pages/topics.py +++ /dev/null @@ -1,15 +0,0 @@ -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - - -def get_context(context): - topic = frappe.get_doc('Topic', frappe.form_dict.topic) - context.no_cache = 1 - context.show_sidebar = True - context.doc = topic - attachments = frappe.db.sql("""select file_url, file_name from tabFile as file - where file.attached_to_name=%s """,(topic.name), as_dict = True) - - context.attached_files = attachments