diff --git a/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.js b/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.js index d3040c8db87..1dea70ab3cb 100644 --- a/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.js +++ b/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.js @@ -3,11 +3,13 @@ cur_frm.set_query("default_account", "accounts", function(doc, cdt, cdn) { var d = locals[cdt][cdn]; + var company_currency = erpnext.get_currency(d.company); return{ filters: [ ['Account', 'account_type', 'in', 'Bank, Cash, Receivable'], ['Account', 'is_group', '=', 0], - ['Account', 'company', '=', d.company] + ['Account', 'company', '=', d.company], + ['Account', 'account_currency', '=', company_currency] ] } }); diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 11ab02021be..8284528a9ad 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -453,7 +453,7 @@ class PaymentEntry(AccountsController): frappe.throw(_("Reference No and Reference Date is mandatory for Bank transaction")) def set_remarks(self): - if self.custom_remarks: return + # if self.custom_remarks: return if self.payment_type=="Internal Transfer": remarks = [_("Amount {0} {1} transferred from {2} to {3}") @@ -909,17 +909,19 @@ def get_reference_details(reference_doctype, reference_name, party_account_curre exchange_rate = 1 outstanding_amount = get_outstanding_on_journal_entry(reference_name) elif reference_doctype != "Journal Entry": - if party_account_currency == company_currency: - if ref_doc.doctype == "Expense Claim": + if ref_doc.doctype == "Expense Claim": total_amount = flt(ref_doc.total_sanctioned_amount) + flt(ref_doc.total_taxes_and_charges) - elif ref_doc.doctype == "Employee Advance": + elif ref_doc.doctype == "Employee Advance": total_amount = ref_doc.advance_amount - else: + exchange_rate = ref_doc.get("exchange_rate") + if not total_amount: + if party_account_currency == company_currency: total_amount = ref_doc.base_grand_total - exchange_rate = 1 - else: - total_amount = ref_doc.grand_total + exchange_rate = 1 + else: + total_amount = ref_doc.grand_total + if not exchange_rate: # Get the exchange rate from the original ref doc # or get it based on the posting date of the ref doc exchange_rate = ref_doc.get("conversion_rate") or \ diff --git a/erpnext/accounts/doctype/salary_component_account/salary_component_account.json b/erpnext/accounts/doctype/salary_component_account/salary_component_account.json index 23dc6c47e8d..9808a9e85c2 100644 --- a/erpnext/accounts/doctype/salary_component_account/salary_component_account.json +++ b/erpnext/accounts/doctype/salary_component_account/salary_component_account.json @@ -1,92 +1,47 @@ { - "allow_copy": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2016-07-27 17:24:24.956896", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, + "actions": [], + "creation": "2016-07-27 17:24:24.956896", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "company", + "default_account", + "account_currency" + ], "fields": [ { - "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": 1, - "label": "Company", - "length": 0, - "no_copy": 0, - "options": "Company", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "company", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Company", + "options": "Company" + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "Default Bank / Cash account will be automatically updated in Salary Journal Entry when this mode is selected.", - "fieldname": "default_account", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "label": "Default Account", - "length": 0, - "no_copy": 0, - "options": "Account", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 + "description": "Default Bank / Cash account will be automatically updated in Salary Journal Entry when this mode is selected.", + "fieldname": "default_account", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Default Account", + "options": "Account" + }, + { + "fetch_from": "default_account.account_currency", + "fieldname": "account_currency", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Account Currency", + "read_only": 1 } - ], - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2016-09-02 07:49:06.567389", - "modified_by": "Administrator", - "module": "Accounts", - "name": "Salary Component Account", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_seen": 0 + ], + "istable": 1, + "links": [], + "modified": "2020-09-30 13:50:06.809353", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Salary Component Account", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC" } \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_advance/employee_advance.js b/erpnext/hr/doctype/employee_advance/employee_advance.js index cba8ee9a404..66988fb8e2d 100644 --- a/erpnext/hr/doctype/employee_advance/employee_advance.js +++ b/erpnext/hr/doctype/employee_advance/employee_advance.js @@ -15,11 +15,15 @@ frappe.ui.form.on('Employee Advance', { }); frm.set_query("advance_account", function() { + if (!frm.doc.employee) { + frappe.msgprint(__("Please select employee first")) + } return { filters: { "root_type": "Asset", "is_group": 0, - "company": frm.doc.company + "company": frm.doc.company, + "account_currency": frm.doc.currency, } }; }); @@ -48,7 +52,7 @@ frappe.ui.form.on('Employee Advance', { frm.add_custom_button( __("Expense Claim"), function() { - frm.events.make_expense_claim(frm); + frm.events.make_expense_claim(frm); //Change this }, __('Create') ); @@ -59,11 +63,11 @@ frappe.ui.form.on('Employee Advance', { if (frm.doc.repay_unclaimed_amount_from_salary == 0 && frappe.model.can_create("Journal Entry")){ frm.add_custom_button(__("Return"), function() { - frm.trigger('make_return_entry'); + frm.trigger('make_return_entry'); //Change this }, __('Create')); }else if (frm.doc.repay_unclaimed_amount_from_salary == 1 && frappe.model.can_create("Additional Salary")){ frm.add_custom_button(__("Deduction from salary"), function() { - frm.events.make_deduction_via_additional_salary(frm) + frm.events.make_deduction_via_additional_salary(frm) //Change this }, __('Create')); } } @@ -138,7 +142,7 @@ frappe.ui.form.on('Employee Advance', { employee: function (frm) { if (frm.doc.employee) { - return frappe.call({ + frappe.call({ method: "erpnext.hr.doctype.employee_advance.employee_advance.get_pending_amount", args: { "employee": frm.doc.employee, @@ -148,6 +152,51 @@ frappe.ui.form.on('Employee Advance', { frm.set_value("pending_amount",r.message); } }); + + frappe.call({ + method: "erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment.get_payroll_payable_account_currency", + args: { + employee: frm.doc.employee, + }, + callback: function(r) { + if(r.message) { + frm.set_value('currency', r.message); + frm.set_df_property('currency', 'hidden', 0); + frm.refresh_fields() + } + } + }); } - } + }, + + currency: function(frm) { + var from_currency = frm.doc.currency; + if (!frm.doc.company) { + var company_currency = erpnext.get_currency(frappe.defaults.get_default("Company")); + } + else { + var company_currency = erpnext.get_currency(frm.doc.company); + } + if(from_currency != company_currency) { + frappe.call({ + method: "erpnext.setup.utils.get_exchange_rate", + args: { + from_currency: from_currency, + to_currency: company_currency, + }, + callback: function(r) { + frm.set_value("exchange_rate", flt(r.message)); + frm.set_df_property('exchange_rate', 'hidden', 0); + cur_frm.set_df_property("exchange_rate", "description", "1 " + frm.doc.currency + + " = [?] " + company_currency); + frm.refresh_fields(); + } + }); + } else { + frm.set_value("exchange_rate", 1.0); + frm.set_df_property('exchange_rate', 'hidden', 1); + frm.set_df_property("exchange_rate", "description", "" ); + frm.refresh_fields(); + } + }, }); diff --git a/erpnext/hr/doctype/employee_advance/employee_advance.json b/erpnext/hr/doctype/employee_advance/employee_advance.json index 0d909138719..24739cbdbbf 100644 --- a/erpnext/hr/doctype/employee_advance/employee_advance.json +++ b/erpnext/hr/doctype/employee_advance/employee_advance.json @@ -13,6 +13,8 @@ "department", "column_break_4", "posting_date", + "currency", + "exchange_rate", "repay_unclaimed_amount_from_salary", "section_break_8", "purpose", @@ -91,7 +93,7 @@ "fieldtype": "Currency", "in_list_view": 1, "label": "Advance Amount", - "options": "Company:company:default_currency", + "options": "currency", "reqd": 1 }, { @@ -99,7 +101,7 @@ "fieldtype": "Currency", "label": "Paid Amount", "no_copy": 1, - "options": "Company:company:default_currency", + "options": "currency", "read_only": 1 }, { @@ -107,7 +109,7 @@ "fieldtype": "Currency", "label": "Claimed Amount", "no_copy": 1, - "options": "Company:company:default_currency", + "options": "currency", "read_only": 1 }, { @@ -161,7 +163,7 @@ "fieldname": "return_amount", "fieldtype": "Currency", "label": "Returned Amount", - "options": "Company:company:default_currency", + "options": "currency", "read_only": 1 }, { @@ -175,13 +177,33 @@ "fieldname": "pending_amount", "fieldtype": "Currency", "label": "Pending Amount", - "options": "Company:company:default_currency", + "options": "currency", "read_only": 1 + }, + { + "default": "Company:company:default_currency", + "depends_on": "eval:(doc.docstatus==1 || doc.employee)", + "fieldname": "currency", + "fieldtype": "Link", + "label": "Currency", + "options": "Currency", + "read_only": 1, + "reqd": 1 + }, + { + "depends_on": "currency", + "fieldname": "exchange_rate", + "fieldtype": "Float", + "label": "Exchange Rate", + "precision": "9", + "print_hide": 1, + "read_only": 1, + "reqd": 1 } ], "is_submittable": 1, "links": [], - "modified": "2020-06-12 12:42:39.833818", + "modified": "2020-09-30 19:05:42.364629", "modified_by": "Administrator", "module": "HR", "name": "Employee Advance", diff --git a/erpnext/hr/doctype/employee_advance/employee_advance.py b/erpnext/hr/doctype/employee_advance/employee_advance.py index 3c435b8cc3b..d53fa760c63 100644 --- a/erpnext/hr/doctype/employee_advance/employee_advance.py +++ b/erpnext/hr/doctype/employee_advance/employee_advance.py @@ -19,7 +19,7 @@ class EmployeeAdvance(Document): def validate(self): self.set_status() - self.validate_employee_advance_account() + # self.validate_employee_advance_account() def on_cancel(self): self.ignore_linked_doctypes = ('GL Entry') @@ -38,12 +38,12 @@ class EmployeeAdvance(Document): elif self.docstatus == 2: self.status = "Cancelled" - def validate_employee_advance_account(self): - company_currency = erpnext.get_company_currency(self.company) - if (self.advance_account and - company_currency != frappe.db.get_value('Account', self.advance_account, 'account_currency')): - frappe.throw(_("Advance account currency should be same as company currency {0}") - .format(company_currency)) + # def validate_employee_advance_account(self): + # company_currency = erpnext.get_company_currency(self.company) + # if (self.advance_account and + # company_currency != frappe.db.get_value('Account', self.advance_account, 'account_currency')): + # frappe.throw(_("Advance account currency should be same as company currency {0}") + # .format(company_currency)) def set_total_advance_paid(self): paid_amount = frappe.db.sql(""" @@ -107,15 +107,38 @@ def make_bank_entry(dt, dn): doc = frappe.get_doc(dt, dn) payment_account = get_default_bank_cash_account(doc.company, account_type="Cash", mode_of_payment=doc.mode_of_payment) + if not payment_account: + frappe.throw(_("Please set a Default Cash Account in Company defaults")) + multi_currency = 0 + credit_in_account_currency = flt(doc.advance_amount) + company_currency = erpnext.get_company_currency(doc.company) + + if payment_account.account_currency != doc.currency: + if return_account.account_currency != company_currency: + frappe.throw(_("""Account currency of default Cash or Bank account in Mode of Payment for + account type: {0} for company: {1} is different than company currency: {2} and currency: {3} specified + in employee advance {4}. Please set default Cash or Bank account in either currency: {5} or {6}""") + .format("Cash", doc.company, company_currency, doc.currency, doc.name, + company_currency, doc.currency), title=_("Currency Mismatch")) + else: + multi_currency = 1 + credit_in_account_currency = flt(doc.advance_amount) * flt(doc.exchange_rate) + + # if doc.currency != payment_account.account_currency: + # multi_currency = 1 + # credit_in_account_currency = flt(doc.advance_amount) * flt(doc.exchange_rate) je = frappe.new_doc("Journal Entry") je.posting_date = nowdate() je.voucher_type = 'Bank Entry' je.company = doc.company je.remark = 'Payment against Employee Advance: ' + dn + '\n' + doc.purpose + je.multi_currency = multi_currency je.append("accounts", { "account": doc.advance_account, + "account_currency": doc.currency, + "exchange_rate": doc.exchange_rate, "debit_in_account_currency": flt(doc.advance_amount), "reference_type": "Employee Advance", "reference_name": doc.name, @@ -128,7 +151,7 @@ def make_bank_entry(dt, dn): je.append("accounts", { "account": payment_account.account, "cost_center": erpnext.get_default_cost_center(doc.company), - "credit_in_account_currency": flt(doc.advance_amount), + "credit_in_account_currency": credit_in_account_currency, "account_currency": payment_account.account_currency, "account_type": payment_account.account_type }) @@ -141,6 +164,7 @@ def create_return_through_additional_salary(doc): doc = frappe._dict(json.loads(doc)) additional_salary = frappe.new_doc('Additional Salary') additional_salary.employee = doc.employee + additional_salary.currency = doc.currency additional_salary.amount = doc.paid_amount - doc.claimed_amount additional_salary.company = doc.company additional_salary.ref_doctype = doc.doctype @@ -151,6 +175,24 @@ def create_return_through_additional_salary(doc): @frappe.whitelist() def make_return_entry(employee, company, employee_advance_name, return_amount, advance_account, mode_of_payment=None): return_account = get_default_bank_cash_account(company, account_type='Cash', mode_of_payment = mode_of_payment) + if not return_account: + frappe.throw(_("Please set a Default Cash Account in Company defaults")) + company_currency = erpnext.get_company_currency(company) + + employee_advance_doc = frappe.get_doc("Employee Advance", employee_advance_name) + multi_currency = 0 + exchange_rate = 1 + + if return_account.account_currency != employee_advance_doc.currency: + if return_account.account_currency != company_currency: + frappe.throw(_("""Account currency of default Cash or Bank account in Mode of Payment for + account type: {0} for company: {1} is different than company currency: {2} and currency: {3} specified + in employee advance {4}. Please set default Cash or Bank account in either currency: {5} or {6}""") + .format("Cash", company, company_currency, employee_advance_doc.currency, employee_advance_name, + company_currency, employee_advance_doc.currency), title=_("Currency Mismatch")) + else: + exchange_rate = employee_advance_doc.exchange_rate + multi_currency = 1 mode_of_payment_type = '' if mode_of_payment: @@ -165,10 +207,13 @@ def make_return_entry(employee, company, employee_advance_name, return_amount, je.voucher_type = '{} Entry'.format(mode_of_payment_type) if mode_of_payment_type else 'Cash Entry' je.company = company je.remark = 'Return against Employee Advance: ' + employee_advance_name + je.multi_currency = multi_currency je.append('accounts', { 'account': advance_account, 'credit_in_account_currency': return_amount, + 'account_currency': employee_advance_doc.currency, + 'exchange_rate': exchange_rate, 'reference_type': 'Employee Advance', 'reference_name': employee_advance_name, 'party_type': 'Employee', @@ -178,7 +223,7 @@ def make_return_entry(employee, company, employee_advance_name, return_amount, je.append("accounts", { "account": return_account.account, - "debit_in_account_currency": return_amount, + "debit_in_account_currency": flt(return_amount) * exchange_rate, "account_currency": return_account.account_currency, "account_type": return_account.account_type }) diff --git a/erpnext/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.json b/erpnext/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.json index 885e3eed976..020457d4ec6 100644 --- a/erpnext/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.json +++ b/erpnext/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.json @@ -71,9 +71,7 @@ "fieldtype": "Currency", "in_list_view": 1, "label": "Amount", - "oldfieldname": "tax_amount", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency" + "options": "currency" }, { "columns": 2, @@ -81,9 +79,7 @@ "fieldtype": "Currency", "in_list_view": 1, "label": "Total", - "oldfieldname": "total", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", + "options": "currency", "read_only": 1 }, { @@ -106,7 +102,7 @@ ], "istable": 1, "links": [], - "modified": "2020-05-11 19:01:26.611758", + "modified": "2020-09-23 20:27:36.027728", "modified_by": "Administrator", "module": "HR", "name": "Expense Taxes and Charges", diff --git a/erpnext/hr/doctype/leave_encashment/leave_encashment.js b/erpnext/hr/doctype/leave_encashment/leave_encashment.js index 71a34226da4..93cf21943d4 100644 --- a/erpnext/hr/doctype/leave_encashment/leave_encashment.js +++ b/erpnext/hr/doctype/leave_encashment/leave_encashment.js @@ -37,6 +37,19 @@ frappe.ui.form.on('Leave Encashment', { doc: frm.doc, callback: function(r) { frm.refresh_fields(); + frappe.call({ + method: "erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment.get_payroll_payable_account_currency", + args: { + employee: frm.doc.employee, + }, + callback: function(r) { + if(r.message) { + frm.set_value('currency', r.message); + frm.set_df_property('currency', 'hidden', 0); + frm.refresh_fields(); + } + } + }); } }); } diff --git a/erpnext/hr/doctype/leave_encashment/leave_encashment.json b/erpnext/hr/doctype/leave_encashment/leave_encashment.json index 2cf6ccf5ca0..f04ab01ca58 100644 --- a/erpnext/hr/doctype/leave_encashment/leave_encashment.json +++ b/erpnext/hr/doctype/leave_encashment/leave_encashment.json @@ -19,9 +19,11 @@ "encashable_days", "amended_from", "payroll", - "encashment_amount", "encashment_date", - "additional_salary" + "additional_salary", + "column_break_14", + "currency", + "encashment_amount" ], "fields": [ { @@ -109,6 +111,7 @@ "in_list_view": 1, "label": "Encashment Amount", "no_copy": 1, + "options": "currency", "read_only": 1 }, { @@ -124,11 +127,26 @@ "no_copy": 1, "options": "Additional Salary", "read_only": 1 + }, + { + "default": "Company:company:default_currency", + "fieldname": "currency", + "fieldtype": "Link", + "hidden": 1, + "label": "Currency", + "options": "Currency", + "print_hide": 1, + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "column_break_14", + "fieldtype": "Column Break" } ], "is_submittable": 1, "links": [], - "modified": "2019-12-16 11:51:57.732223", + "modified": "2020-09-24 19:21:56.856605", "modified_by": "Administrator", "module": "HR", "name": "Leave Encashment", diff --git a/erpnext/hr/doctype/leave_encashment/leave_encashment.py b/erpnext/hr/doctype/leave_encashment/leave_encashment.py index 8913c648c52..7af313f9fa2 100644 --- a/erpnext/hr/doctype/leave_encashment/leave_encashment.py +++ b/erpnext/hr/doctype/leave_encashment/leave_encashment.py @@ -16,10 +16,16 @@ class LeaveEncashment(Document): def validate(self): set_employee_name(self) self.get_leave_details_for_encashment() + self.validate_salary_structure() if not self.encashment_date: self.encashment_date = getdate(nowdate()) + def validate_salary_structure(self): + if not frappe.db.exists('Salary Structure Assignment', {'employee': self.employee}): + frappe.throw(_("There is no Salary Structure assigned to {0}. First assign a Salary Stucture.").format(self.employee)) + + def before_submit(self): if self.encashment_amount <= 0: frappe.throw(_("You can only submit Leave Encashment for a valid encashment amount")) @@ -30,6 +36,7 @@ class LeaveEncashment(Document): additional_salary = frappe.new_doc("Additional Salary") additional_salary.company = frappe.get_value("Employee", self.employee, "company") additional_salary.employee = self.employee + additional_salary.currency = self.currency earning_component = frappe.get_value("Leave Type", self.leave_type, "earning_component") if not earning_component: frappe.throw(_("Please set Earning Component for Leave type: {0}.".format(self.leave_type))) diff --git a/erpnext/payroll/doctype/additional_salary/additional_salary.js b/erpnext/payroll/doctype/additional_salary/additional_salary.js index d56cd4e967d..36afd133242 100644 --- a/erpnext/payroll/doctype/additional_salary/additional_salary.js +++ b/erpnext/payroll/doctype/additional_salary/additional_salary.js @@ -12,5 +12,48 @@ frappe.ui.form.on('Additional Salary', { } }; }); + + if(!frm.doc.currency) return; + frm.set_query("salary_component", function() { + return { + query : "erpnext.payroll.doctype.salary_structure.salary_structure.get_earning_deduction_components", + filters: {type: "earning", currency: frm.doc.currency, company: frm.doc.company} + }; + }); + }, + + employee: function(frm) { + debugger; + if (frm.doc.employee) { + frappe.call({ + method: "erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment.get_payroll_payable_account_currency", + args: { + employee: frm.doc.employee, + }, + callback: function(r) { + if(r.message) { + frm.set_value('currency', r.message); + frm.set_df_property('currency', 'hidden', 0); + } + } + }); + frappe.call({ + method: "frappe.client.get_value", + args:{ + doctype: "Employee", + fieldname: "company", + filters:{ + name: frm.doc.employee + } + }, + callback: function(data) { + if(data.message){ + frm.set_value("company", data.message.company); + } + } + }); + } else { + frm.set_value("company", null); + } }, }); diff --git a/erpnext/payroll/doctype/additional_salary/additional_salary.json b/erpnext/payroll/doctype/additional_salary/additional_salary.json index 69cb5da893e..f3bbaffe505 100644 --- a/erpnext/payroll/doctype/additional_salary/additional_salary.json +++ b/erpnext/payroll/doctype/additional_salary/additional_salary.json @@ -11,20 +11,21 @@ "employee", "employee_name", "salary_component", - "overwrite_salary_structure_amount", - "deduct_full_tax_on_selected_payroll_date", + "type", + "amount", "ref_doctype", "ref_docname", + "amended_from", "column_break_5", "company", - "is_recurring", + "department", + "currency", "from_date", "to_date", "payroll_date", - "type", - "department", - "amount", - "amended_from" + "is_recurring", + "overwrite_salary_structure_amount", + "deduct_full_tax_on_selected_payroll_date" ], "fields": [ { @@ -59,6 +60,7 @@ "fieldtype": "Currency", "in_list_view": 1, "label": "Amount", + "options": "currency", "reqd": 1 }, { @@ -159,11 +161,22 @@ "label": "Reference Document", "options": "ref_doctype", "read_only": 1 + }, + { + "default": "Company:company:default_currency", + "fieldname": "currency", + "fieldtype": "Link", + "hidden": 1, + "label": "Currency", + "options": "Currency", + "print_hide": 1, + "read_only": 1, + "reqd": 1 } ], "is_submittable": 1, "links": [], - "modified": "2020-06-22 21:10:50.374063", + "modified": "2020-09-24 12:55:51.660506", "modified_by": "Administrator", "module": "Payroll", "name": "Additional Salary", diff --git a/erpnext/payroll/doctype/additional_salary/additional_salary.py b/erpnext/payroll/doctype/additional_salary/additional_salary.py index e3dc9070ec5..f5af677fce2 100644 --- a/erpnext/payroll/doctype/additional_salary/additional_salary.py +++ b/erpnext/payroll/doctype/additional_salary/additional_salary.py @@ -22,10 +22,15 @@ class AdditionalSalary(Document): def validate(self): self.validate_dates() + self.validate_salary_structure() self.validate_recurring_additional_salary_overlap() if self.amount < 0: frappe.throw(_("Amount should not be less than zero.")) + def validate_salary_structure(self): + if not frappe.db.exists('Salary Structure Assignment', {'employee': self.employee}): + frappe.throw(_("There is no Salary Structure assigned to {0}. First assign a Salary Stucture.").format(self.employee)) + def validate_recurring_additional_salary_overlap(self): if self.is_recurring: additional_salaries = frappe.db.sql(""" diff --git a/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.js b/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.js index f509df31e83..8f865e44d9b 100644 --- a/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.js +++ b/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.js @@ -36,6 +36,20 @@ frappe.ui.form.on('Employee Benefit Application', { filters: {date: frm.doc.date, employee: frm.doc.employee} }; }); + if (frm.doc.employee) { + frappe.call({ + method: "erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment.get_payroll_payable_account_currency", + args: { + employee: frm.doc.employee, + }, + callback: function(r) { + if(r.message) { + frm.set_value('currency', r.message); + frm.refresh_fields(); + } + } + }); + } }, payroll_period: function(frm) { @@ -56,6 +70,7 @@ frappe.ui.form.on('Employee Benefit Application', { }); var get_max_benefits=function(frm, method, args) { + debugger; frappe.call({ method: method, args: args, @@ -63,8 +78,11 @@ var get_max_benefits=function(frm, method, args) { if(!data.exc){ if(data.message){ frm.set_value("max_benefits", data.message); + } else { + frm.set_value("max_benefits", 0); } } + frm.refresh_fields(); } }); }; @@ -82,14 +100,19 @@ var calculate_all = function(doc) { var tbl = doc.employee_benefits || []; var pro_rata_dispensed_amount = 0; var total_amount = 0; - for(var i = 0; i < tbl.length; i++){ - if(cint(tbl[i].amount) > 0) { - total_amount += flt(tbl[i].amount); - } - if(tbl[i].pay_against_benefit_claim != 1){ - pro_rata_dispensed_amount += flt(tbl[i].amount); + if (doc.max_benefits == 0) { + doc.employee_benefits = [] + } else { + for(var i = 0; i < tbl.length; i++){ + if(cint(tbl[i].amount) > 0) { + total_amount += flt(tbl[i].amount); + } + if(tbl[i].pay_against_benefit_claim != 1){ + pro_rata_dispensed_amount += flt(tbl[i].amount); + } } } + doc.total_amount = total_amount; doc.remaining_benefit = doc.max_benefits - total_amount; doc.pro_rata_dispensed_amount = pro_rata_dispensed_amount; diff --git a/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.json b/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.json index b0c1bd6c3e5..3583d6da13c 100644 --- a/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.json +++ b/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.json @@ -10,6 +10,7 @@ "field_order": [ "employee", "employee_name", + "currency", "max_benefits", "remaining_benefit", "column_break_2", @@ -43,12 +44,14 @@ "fieldname": "max_benefits", "fieldtype": "Currency", "label": "Max Benefits (Yearly)", + "options": "currency", "read_only": 1 }, { "fieldname": "remaining_benefit", "fieldtype": "Currency", "label": "Remaining Benefits (Yearly)", + "options": "currency", "read_only": 1 }, { @@ -108,18 +111,30 @@ "fieldname": "total_amount", "fieldtype": "Currency", "label": "Total Amount", + "options": "currency", "read_only": 1 }, { "fieldname": "pro_rata_dispensed_amount", "fieldtype": "Currency", "label": "Dispensed Amount (Pro-rated)", + "options": "currency", "read_only": 1 + }, + { + "default": "Company:company:default_currency", + "depends_on": "eval:(doc.docstatus==1 || doc.employee)", + "fieldname": "currency", + "fieldtype": "Link", + "label": "Currency", + "options": "Currency", + "read_only": 1, + "reqd": 1 } ], "is_submittable": 1, "links": [], - "modified": "2020-06-22 22:58:31.271922", + "modified": "2020-09-24 19:35:37.250360", "modified_by": "Administrator", "module": "Payroll", "name": "Employee Benefit Application", diff --git a/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.py b/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.py index ef844fbd3b5..90f5a37d890 100644 --- a/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.py +++ b/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.py @@ -100,6 +100,8 @@ class EmployeeBenefitApplication(Document): if application: frappe.throw(_("Employee {0} already submited an apllication {1} for the payroll period {2}").format(self.employee, application, self.payroll_period)) + # def assign + @frappe.whitelist() def get_max_benefits(employee, on_date): sal_struct = get_assigned_salary_structure(employee, on_date) @@ -115,7 +117,7 @@ def get_max_benefits_remaining(employee, on_date, payroll_period): if max_benefits and max_benefits > 0: have_depends_on_payment_days = False per_day_amount_total = 0 - payroll_period_days = get_payroll_period_days(on_date, on_date, employee)[0] + payroll_period_days = get_payroll_period_days(on_date, on_date, employee)[1] payroll_period_obj = frappe.get_doc("Payroll Period", payroll_period) # Get all salary slip flexi amount in the payroll period @@ -239,4 +241,17 @@ def get_earning_components(doctype, txt, searchfield, start, page_len, filters): """, salary_structure) else: frappe.throw(_("Salary Structure not found for employee {0} and date {1}") - .format(filters['employee'], filters['date'])) \ No newline at end of file + .format(filters['employee'], filters['date'])) + +@frappe.whitelist() +def get_earning_components_max_benefits(employee, date, earning_component): + salary_structure = get_assigned_salary_structure(employee, date) + amount = frappe.db.sql(""" + select amount + from `tabSalary Detail` + where parent = %s and is_flexible_benefit = 1 + and salary_component = %s + order by name + """, salary_structure, earning_component) + + return amount if amount else 0 \ No newline at end of file diff --git a/erpnext/payroll/doctype/employee_benefit_application_detail/employee_benefit_application_detail.json b/erpnext/payroll/doctype/employee_benefit_application_detail/employee_benefit_application_detail.json index fa6b4da2af3..c93d356c209 100644 --- a/erpnext/payroll/doctype/employee_benefit_application_detail/employee_benefit_application_detail.json +++ b/erpnext/payroll/doctype/employee_benefit_application_detail/employee_benefit_application_detail.json @@ -33,6 +33,7 @@ "fieldtype": "Currency", "in_list_view": 1, "label": "Max Benefit Amount", + "options": "currency", "read_only": 1 }, { @@ -40,12 +41,13 @@ "fieldtype": "Currency", "in_list_view": 1, "label": "Amount", + "options": "currency", "reqd": 1 } ], "istable": 1, "links": [], - "modified": "2020-06-22 23:45:00.519134", + "modified": "2020-09-29 16:22:15.783854", "modified_by": "Administrator", "module": "Payroll", "name": "Employee Benefit Application Detail", diff --git a/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.js b/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.js index 6db6cb86b3d..a65843eaef4 100644 --- a/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.js +++ b/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.js @@ -12,5 +12,24 @@ frappe.ui.form.on('Employee Benefit Claim', { }, employee: function(frm) { frm.set_value("earning_component", null); + if (frm.doc.employee) { + frappe.call({ + method: "erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment.get_payroll_payable_account_currency", + args: { + employee: frm.doc.employee, + }, + callback: function(r) { + if(r.message) { + frm.set_value('currency', r.message); + frm.set_df_property('currency', 'hidden', 0); + } + } + }); + } + if (!frm.doc.earning_component){ + frm.doc.max_amount_eligible = null; + frm.doc.claimed_amount = null; + } + frm.refresh_fields(); } }); diff --git a/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.json b/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.json index ae4c218615a..d87ebe86043 100644 --- a/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.json +++ b/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.json @@ -12,6 +12,7 @@ "department", "column_break_3", "claim_date", + "currency", "benefit_type_and_amount", "earning_component", "max_amount_eligible", @@ -76,6 +77,7 @@ "fieldname": "max_amount_eligible", "fieldtype": "Currency", "label": "Max Amount Eligible", + "options": "currency", "read_only": 1 }, { @@ -92,6 +94,7 @@ "fieldtype": "Currency", "in_list_view": 1, "label": "Claimed Amount", + "options": "currency", "reqd": 1 }, { @@ -119,11 +122,21 @@ "fieldname": "attachments", "fieldtype": "Attach", "label": "Attachments" + }, + { + "default": "Company:company:default_currency", + "fieldname": "currency", + "fieldtype": "Link", + "hidden": 1, + "label": "Currency", + "options": "Currency", + "read_only": 1, + "reqd": 1 } ], "is_submittable": 1, "links": [], - "modified": "2020-06-22 23:01:50.791676", + "modified": "2020-09-23 11:58:09.049030", "modified_by": "Administrator", "module": "Payroll", "name": "Employee Benefit Claim", diff --git a/erpnext/payroll/doctype/employee_incentive/employee_incentive.js b/erpnext/payroll/doctype/employee_incentive/employee_incentive.js index db0f83aac9a..8326d5f7904 100644 --- a/erpnext/payroll/doctype/employee_incentive/employee_incentive.js +++ b/erpnext/payroll/doctype/employee_incentive/employee_incentive.js @@ -11,12 +11,48 @@ frappe.ui.form.on('Employee Incentive', { }; }); + if(!frm.doc.currency) return; frm.set_query("salary_component", function() { - return { - filters: { - "type": "Earning" - } - }; + return { + query : "erpnext.payroll.doctype.salary_structure.salary_structure.get_earning_deduction_components", + filters: {type: "earning", currency: frm.doc.currency, company: frm.doc.company} + }; }); - } + + }, + + employee: function(frm) { + frm.trigger("get_leave_details_for_encashment"); + if (frm.doc.employee) { + frappe.call({ + method: "erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment.get_payroll_payable_account_currency", + args: { + employee: frm.doc.employee, + }, + callback: function(r) { + if(r.message) { + frm.set_value('currency', r.message); + frm.set_df_property('currency', 'hidden', 0); + } + } + }); + frappe.call({ + method: "frappe.client.get_value", + args:{ + doctype: "Employee", + fieldname: "company", + filters:{ + name: frm.doc.employee + } + }, + callback: function(data) { + if(data.message){ + frm.set_value("company", data.message.company); + } + } + }); + } else { + frm.set_value("company", null); + } + }, }); diff --git a/erpnext/payroll/doctype/employee_incentive/employee_incentive.json b/erpnext/payroll/doctype/employee_incentive/employee_incentive.json index 204c9a40b1d..0bec4b1b664 100644 --- a/erpnext/payroll/doctype/employee_incentive/employee_incentive.json +++ b/erpnext/payroll/doctype/employee_incentive/employee_incentive.json @@ -7,10 +7,12 @@ "engine": "InnoDB", "field_order": [ "employee", - "incentive_amount", "employee_name", - "salary_component", + "company", + "currency", + "incentive_amount", "column_break_5", + "salary_component", "payroll_date", "department", "amended_from" @@ -28,6 +30,7 @@ "fieldname": "incentive_amount", "fieldtype": "Currency", "label": "Incentive Amount", + "options": "currency", "reqd": 1 }, { @@ -70,11 +73,30 @@ "label": "Salary Component", "options": "Salary Component", "reqd": 1 + }, + { + "default": "Company:company:default_currency", + "depends_on": "eval:(doc.docstatus==1 || doc.employee)", + "fieldname": "currency", + "fieldtype": "Link", + "hidden": 1, + "label": "Currency", + "options": "Currency", + "print_hide": 1, + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company", + "reqd": 1 } ], "is_submittable": 1, "links": [], - "modified": "2020-06-22 22:42:51.209630", + "modified": "2020-09-30 17:59:57.754477", "modified_by": "Administrator", "module": "Payroll", "name": "Employee Incentive", diff --git a/erpnext/payroll/doctype/employee_incentive/employee_incentive.py b/erpnext/payroll/doctype/employee_incentive/employee_incentive.py index 84a97f6bb2e..e607336a7e7 100644 --- a/erpnext/payroll/doctype/employee_incentive/employee_incentive.py +++ b/erpnext/payroll/doctype/employee_incentive/employee_incentive.py @@ -7,11 +7,19 @@ import frappe from frappe.model.document import Document class EmployeeIncentive(Document): + def validate(self): + self.validate_salary_structure() + + def validate_salary_structure(self): + if not frappe.db.exists('Salary Structure Assignment', {'employee': self.employee}): + frappe.throw(_("There is no Salary Structure assigned to {0}. First assign a Salary Stucture.").format(self.employee)) + def on_submit(self): company = frappe.db.get_value('Employee', self.employee, 'company') additional_salary = frappe.new_doc('Additional Salary') additional_salary.employee = self.employee + additional_salary.currency = self.currency additional_salary.salary_component = self.salary_component additional_salary.overwrite_salary_structure_amount = 0 additional_salary.amount = self.incentive_amount diff --git a/erpnext/payroll/doctype/payroll_employee_detail/payroll_employee_detail.json b/erpnext/payroll/doctype/payroll_employee_detail/payroll_employee_detail.json index bb68e1814a7..8a55224dca7 100644 --- a/erpnext/payroll/doctype/payroll_employee_detail/payroll_employee_detail.json +++ b/erpnext/payroll/doctype/payroll_employee_detail/payroll_employee_detail.json @@ -52,7 +52,7 @@ ], "istable": 1, "links": [], - "modified": "2020-06-22 23:25:13.779032", + "modified": "2020-09-30 12:40:07.999878", "modified_by": "Administrator", "module": "Payroll", "name": "Payroll Employee Detail", diff --git a/erpnext/payroll/doctype/payroll_entry/payroll_entry.js b/erpnext/payroll/doctype/payroll_entry/payroll_entry.js index 1abc869c539..58a4faf30c7 100644 --- a/erpnext/payroll/doctype/payroll_entry/payroll_entry.js +++ b/erpnext/payroll/doctype/payroll_entry/payroll_entry.js @@ -17,6 +17,17 @@ frappe.ui.form.on('Payroll Entry', { } }; }); + + frm.set_query("default_payroll_payable_account", function() { + return { + filters: { + "company": frm.doc.company, + "root_type": "Liability", + "is_group": 0, + "account_currency": frm.doc.currency + } + } + }); }, refresh: function(frm) { @@ -139,6 +150,30 @@ frappe.ui.form.on('Payroll Entry', { frm.events.clear_employee_table(frm); }, + currency: function (frm) { + debugger; + if (!frm.doc.company) { + var company_currency = erpnext.get_currency(frappe.defaults.get_default("Company")); + } + else { + var company_currency = erpnext.get_currency(frm.doc.company); + } + if (frm.doc.currency) { + if (company_currency != frm.doc.currency) { + cur_frm.set_df_property("exchange_rate", "description", "1 " + frm.doc.currency + + " = [?] " + company_currency); + cur_frm.set_df_property("exchange_rate", "hidden", 0); + frm.set_value('exchange_rate', null); + } + else { + cur_frm.set_df_property("exchange_rate", "description", ""); + cur_frm.set_df_property("exchange_rate", "hidden", 1); + frm.doc.exchange_rate = 1; + + } + } + }, + department: function (frm) { frm.events.clear_employee_table(frm); }, diff --git a/erpnext/payroll/doctype/payroll_entry/payroll_entry.json b/erpnext/payroll/doctype/payroll_entry/payroll_entry.json index 31a899699d7..bfaf6248d09 100644 --- a/erpnext/payroll/doctype/payroll_entry/payroll_entry.json +++ b/erpnext/payroll/doctype/payroll_entry/payroll_entry.json @@ -11,8 +11,11 @@ "column_break0", "posting_date", "payroll_frequency", - "column_break1", "company", + "column_break1", + "currency", + "exchange_rate", + "default_payroll_payable_account", "section_break_8", "branch", "department", @@ -257,12 +260,37 @@ { "fieldname": "column_break_33", "fieldtype": "Column Break" + }, + { + "depends_on": "company", + "fieldname": "currency", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Currency", + "options": "Currency", + "reqd": 1 + }, + { + "depends_on": "company", + "fieldname": "exchange_rate", + "fieldtype": "Float", + "label": "Exchange Rate", + "precision": "9", + "reqd": 1 + }, + { + "depends_on": "company", + "fieldname": "default_payroll_payable_account", + "fieldtype": "Link", + "label": "Default Payroll Payable Account", + "options": "Account", + "reqd": 1 } ], "icon": "fa fa-cog", "is_submittable": 1, "links": [], - "modified": "2020-06-22 20:06:06.953904", + "modified": "2020-09-30 12:47:45.729777", "modified_by": "Administrator", "module": "Payroll", "name": "Payroll Entry", diff --git a/erpnext/payroll/doctype/payroll_entry/payroll_entry.py b/erpnext/payroll/doctype/payroll_entry/payroll_entry.py index 30ea432678c..a97f8f88f85 100644 --- a/erpnext/payroll/doctype/payroll_entry/payroll_entry.py +++ b/erpnext/payroll/doctype/payroll_entry/payroll_entry.py @@ -3,7 +3,7 @@ # For license information, please see license.txt from __future__ import unicode_literals -import frappe +import frappe, erpnext from frappe.model.document import Document from dateutil.relativedelta import relativedelta from frappe.utils import cint, flt, nowdate, add_days, getdate, fmt_money, add_to_date, DATE_FORMAT, date_diff @@ -51,13 +51,15 @@ class PayrollEntry(Document): where docstatus = 1 and is_active = 'Yes' - and company = %(company)s and + and company = %(company)s + and currency = %(currency)s and ifnull(salary_slip_based_on_timesheet,0) = %(salary_slip_based_on_timesheet)s {condition}""".format(condition=condition), - {"company": self.company, "salary_slip_based_on_timesheet":self.salary_slip_based_on_timesheet}) + {"company": self.company, "currency": self.currency, "salary_slip_based_on_timesheet":self.salary_slip_based_on_timesheet}) if sal_struct: cond += "and t2.salary_structure IN %(sal_struct)s " + cond += "and t2.default_payroll_payable_account = %(default_payroll_payable_account)s " cond += "and %(from_date)s >= t2.from_date" emp_list = frappe.db.sql(""" select @@ -68,7 +70,7 @@ class PayrollEntry(Document): t1.name = t2.employee and t2.docstatus = 1 %s order by t2.from_date desc - """ % cond, {"sal_struct": tuple(sal_struct), "from_date": self.end_date}, as_dict=True) + """ % cond, {"sal_struct": tuple(sal_struct), "from_date": self.end_date, "default_payroll_payable_account": self.default_payroll_payable_account}, as_dict=True) return emp_list def fill_employee_details(self): @@ -217,7 +219,8 @@ class PayrollEntry(Document): self.check_permission('write') earnings = self.get_salary_component_total(component_type = "earnings") or {} deductions = self.get_salary_component_total(component_type = "deductions") or {} - default_payroll_payable_account = self.get_default_payroll_payable_account() + # default_payroll_payable_account = self.get_default_payroll_payable_account() + default_payroll_payable_account = self.default_payroll_payable_account jv_name = "" precision = frappe.get_precision("Journal Entry Account", "debit_in_account_currency") @@ -230,14 +233,19 @@ class PayrollEntry(Document): journal_entry.posting_date = self.posting_date accounts = [] + currencies = [] payable_amount = 0 + multi_currency = 0 + company_currency = erpnext.get_company_currency(self.company) # Earnings for acc_cc, amount in earnings.items(): + exchange_rate, conversion_rate = self.get_exchange_rate(acc_cc[0], company_currency, currencies) payable_amount += flt(amount, precision) accounts.append({ "account": acc_cc[0], - "debit_in_account_currency": flt(amount, precision), + "debit_in_account_currency": flt((amount * conversion_rate), precision), + "exchange_rate": flt(exchange_rate), "party_type": '', "cost_center": acc_cc[1] or self.cost_center, "project": self.project @@ -245,24 +253,31 @@ class PayrollEntry(Document): # Deductions for acc_cc, amount in deductions.items(): + exchange_rate, conversion_rate = self.get_exchange_rate(acc_cc[0], company_currency, currencies) payable_amount -= flt(amount, precision) accounts.append({ "account": acc_cc[0], - "credit_in_account_currency": flt(amount, precision), + "credit_in_account_currency": flt((amount * conversion_rate), precision), + "exchange_rate": flt(exchange_rate), "cost_center": acc_cc[1] or self.cost_center, "party_type": '', "project": self.project }) # Payable amount + exchange_rate, conversion_rate = self.get_exchange_rate(default_payroll_payable_account, company_currency, currencies) accounts.append({ "account": default_payroll_payable_account, - "credit_in_account_currency": flt(payable_amount, precision), + "credit_in_account_currency": flt((payable_amount * conversion_rate), precision), + "exchange_rate": flt(exchange_rate), "party_type": '', "cost_center": self.cost_center }) journal_entry.set("accounts", accounts) + if len(currencies) > 1: + multi_currency = 1 + journal_entry.multi_currency = multi_currency journal_entry.title = default_payroll_payable_account journal_entry.save() @@ -275,6 +290,17 @@ class PayrollEntry(Document): return jv_name + def get_exchange_rate(self, account, company_currency, currencies): + conversion_rate = 1 + exchange_rate = self.exchange_rate + account_currency = frappe.db.get_value('Account', account, 'account_currency') + if account_currency not in currencies: + currencies.append(account_currency) + if account_currency == company_currency: + conversion_rate = self.exchange_rate + exchange_rate = 1 + return exchange_rate, conversion_rate + def make_payment_entry(self): self.check_permission('write') @@ -303,31 +329,44 @@ class PayrollEntry(Document): self.create_journal_entry(salary_slip_total, "salary") def create_journal_entry(self, je_payment_amount, user_remark): - default_payroll_payable_account = self.get_default_payroll_payable_account() + # default_payroll_payable_account = self.get_default_payroll_payable_account() + default_payroll_payable_account = self.default_payroll_payable_account precision = frappe.get_precision("Journal Entry Account", "debit_in_account_currency") + accounts = [] + currencies = [] + multi_currency = 0 + company_currency = erpnext.get_company_currency(self.company) + + exchange_rate, conversion_rate = self.get_exchange_rate(self.payment_account, company_currency, currencies) + accounts.append({ + "account": self.payment_account, + "bank_account": self.bank_account, + "credit_in_account_currency": flt(je_payment_amount * conversion_rate, precision), + "exchange_rate": flt(exchange_rate), + }) + + exchange_rate, conversion_rate = self.get_exchange_rate(default_payroll_payable_account, company_currency, currencies) + accounts.append({ + "account": default_payroll_payable_account, + "debit_in_account_currency": flt(je_payment_amount * conversion_rate, precision), + "exchange_rate": flt(exchange_rate), + "reference_type": self.doctype, + "reference_name": self.name + }) + + if len(currencies) > 1: + multi_currency = 1 + journal_entry = frappe.new_doc('Journal Entry') journal_entry.voucher_type = 'Bank Entry' journal_entry.user_remark = _('Payment of {0} from {1} to {2}')\ .format(user_remark, self.start_date, self.end_date) journal_entry.company = self.company journal_entry.posting_date = self.posting_date + journal_entry.multi_currency = multi_currency - payment_amount = flt(je_payment_amount, precision) - - journal_entry.set("accounts", [ - { - "account": self.payment_account, - "bank_account": self.bank_account, - "credit_in_account_currency": payment_amount - }, - { - "account": default_payroll_payable_account, - "debit_in_account_currency": payment_amount, - "reference_type": self.doctype, - "reference_name": self.name - } - ]) + journal_entry.set("accounts", accounts) journal_entry.save(ignore_permissions = True) def update_salary_slip_status(self, jv_name = None): diff --git a/erpnext/payroll/doctype/salary_detail/salary_detail.json b/erpnext/payroll/doctype/salary_detail/salary_detail.json index cc87caeae1a..0137a0cabe2 100644 --- a/erpnext/payroll/doctype/salary_detail/salary_detail.json +++ b/erpnext/payroll/doctype/salary_detail/salary_detail.json @@ -147,7 +147,7 @@ "fieldtype": "Currency", "in_list_view": 1, "label": "Amount", - "options": "Company:company:default_currency" + "options": "currency" }, { "default": "0", @@ -160,7 +160,7 @@ "fieldname": "default_amount", "fieldtype": "Currency", "label": "Default Amount", - "options": "Company:company:default_currency", + "options": "currency", "print_hide": 1 }, { @@ -169,6 +169,7 @@ "hidden": 1, "label": "Additional Amount", "no_copy": 1, + "options": "currency", "print_hide": 1, "read_only": 1 }, @@ -177,6 +178,7 @@ "fieldname": "tax_on_flexible_benefit", "fieldtype": "Currency", "label": "Tax on flexible benefit", + "options": "currency", "read_only": 1 }, { @@ -184,6 +186,7 @@ "fieldname": "tax_on_additional_salary", "fieldtype": "Currency", "label": "Tax on additional salary", + "options": "currency", "read_only": 1 }, { @@ -206,38 +209,28 @@ "collapsible": 1, "fieldname": "section_break_5", "fieldtype": "Section Break", - "label": "Component properties and references ", - "show_days": 1, - "show_seconds": 1 + "label": "Component properties and references " }, { "fieldname": "column_break_11", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "fieldname": "section_break_19", - "fieldtype": "Section Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Section Break" }, { "fieldname": "column_break_18", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "fieldname": "column_break_24", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" } ], "istable": 1, "links": [], - "modified": "2020-07-01 12:13:41.956495", + "modified": "2020-09-30 11:29:36.926796", "modified_by": "Administrator", "module": "Payroll", "name": "Salary Detail", diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.js b/erpnext/payroll/doctype/salary_slip/salary_slip.js index 7b69dbe8d6d..0dee370d65f 100644 --- a/erpnext/payroll/doctype/salary_slip/salary_slip.js +++ b/erpnext/payroll/doctype/salary_slip/salary_slip.js @@ -76,12 +76,39 @@ frappe.ui.form.on("Salary Slip", { } }, + currency: function(frm) { + calculate_totals(frm.doc); + frm.trigger("set_dynamic_labels") + frm.refresh() + }, + + set_dynamic_labels: function(frm) { + debugger; + frm.set_currency_labels(["hour_rate", "gross_pay", "total_deduction", "net_pay", "rounded_total", "total_in_words"], + frm.doc.currency); + + // toggle fields + if(frappe.meta.get_docfield(cur_frm.doctype, "net_total")) + cur_frm.toggle_display("net_total", show); + + frm.set_currency_labels(["amount", "default_amount", "additional_amount", "tax_on_flexible_benefit", "tax_on_additional_salary"], + frm.doc.currency, "earnings"); + + frm.set_currency_labels(["amount", "default_amount", "additional_amount", "tax_on_flexible_benefit", "tax_on_additional_salary"], + frm.doc.currency, "deductions"); + + frm.refresh_fields(); + }, + refresh: function(frm) { + debugger; + frm.trigger("set_dynamic_labels") frm.trigger("toggle_fields") var salary_detail_fields = ["formula", "abbr", "statistical_component", "variable_based_on_taxable_salary"]; cur_frm.fields_dict['earnings'].grid.set_column_disp(salary_detail_fields,false); cur_frm.fields_dict['deductions'].grid.set_column_disp(salary_detail_fields,false); + calculate_totals(frm.doc); }, salary_slip_based_on_timesheet: function(frm) { @@ -118,6 +145,7 @@ frappe.ui.form.on("Salary Slip", { }, get_emp_and_working_day_details: function(frm) { + debugger; return frappe.call({ method: 'get_emp_and_working_day_details', doc: frm.doc, @@ -140,8 +168,14 @@ frappe.ui.form.on('Salary Slip Timesheet', { } }); + +cur_frm.cscript.amount = function(doc, cdt, cdn){ + calculate_totals(doc, cdt, cdn); +}; + // calculate total working hours, earnings based on hourly wages and totals var total_work_hours = function(frm, dt, dn) { + debugger; var total_working_hours = 0.0; $.each(frm.doc["timesheets"] || [], function(i, timesheet) { total_working_hours += timesheet.working_hours; @@ -166,3 +200,89 @@ var total_work_hours = function(frm, dt, dn) { refresh_many(['net_pay', 'rounded_total']); }); } + +var calculate_totals = function(doc) { + debugger; + var tbl1 = doc.earnings || []; + var tbl2 = doc.deductions || []; + + var total_earn = 0; var total_ded = 0; + for(var i = 0; i < tbl1.length; i++){ + total_earn += flt(tbl1[i].amount); + } + for(var j = 0; j < tbl2.length; j++){ + total_ded += flt(tbl2[j].amount); + } + doc.gross_pay = total_earn; + doc.total_deduction = total_ded; + doc.net_pay = 0.0 + if(doc.salary_slip_based_on_timesheet == 0){ + doc.net_pay = flt(total_earn) - flt(total_ded) - flt(doc.total_loan_repayment); + doc.rounded_total = Math.round(doc.net_pay) + } + refresh_many(['gross_pay', 'total_deduction', 'net_pay', 'rounded_total']); +} + +cur_frm.cscript.validate = function(doc, cdt, cdn) { + calculate_totals(doc); +} + + +frappe.ui.form.on('Salary Detail', { + amount: function(frm) { + calculate_totals(frm.doc); + }, + + earnings_remove: function(frm) { + calculate_totals(frm.doc); + }, + + deductions_remove: function(frm) { + calculate_totals(frm.doc); + }, + + salary_component: function(frm, cdt, cdn) { + debugger; + var child = locals[cdt][cdn]; + if(child.salary_component){ + frappe.call({ + method: "frappe.client.get", + args: { + doctype: "Salary Component", + name: child.salary_component + }, + callback: function(data) { + if(data.message){ + var result = data.message; + frappe.model.set_value(cdt, cdn, 'condition', result.condition); + frappe.model.set_value(cdt, cdn, 'amount_based_on_formula', result.amount_based_on_formula); + if(result.amount_based_on_formula == 1){ + frappe.model.set_value(cdt, cdn, 'formula', result.formula); + } + else{ + frappe.model.set_value(cdt, cdn, 'amount', result.amount); + } + frappe.model.set_value(cdt, cdn, 'statistical_component', result.statistical_component); + frappe.model.set_value(cdt, cdn, 'depends_on_payment_days', result.depends_on_payment_days); + frappe.model.set_value(cdt, cdn, 'do_not_include_in_total', result.do_not_include_in_total); + frappe.model.set_value(cdt, cdn, 'variable_based_on_taxable_salary', result.variable_based_on_taxable_salary); + frappe.model.set_value(cdt, cdn, 'is_tax_applicable', result.is_tax_applicable); + frappe.model.set_value(cdt, cdn, 'is_flexible_benefit', result.is_flexible_benefit); + refresh_field("earnings"); + refresh_field("deductions"); + } + } + }); + } + }, + + amount_based_on_formula: function(frm, cdt, cdn) { + var child = locals[cdt][cdn]; + if(child.amount_based_on_formula == 1){ + frappe.model.set_value(cdt, cdn, 'amount', null); + } + else{ + frappe.model.set_value(cdt, cdn, 'formula', null); + } + } +}) diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.json b/erpnext/payroll/doctype/salary_slip/salary_slip.json index 619c45fa4a1..16a9309c166 100644 --- a/erpnext/payroll/doctype/salary_slip/salary_slip.json +++ b/erpnext/payroll/doctype/salary_slip/salary_slip.json @@ -18,6 +18,7 @@ "journal_entry", "payroll_entry", "company", + "currency", "letter_head", "section_break_10", "start_date", @@ -67,6 +68,7 @@ "rounded_total", "section_break_55", "total_in_words", + "section_break_75", "amended_from" ], "fields": [ @@ -205,9 +207,13 @@ { "fieldname": "salary_structure", "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, "label": "Salary Structure", "options": "Salary Structure", - "read_only": 1 + "read_only": 1, + "reqd": 1, + "search_index": 1 }, { "depends_on": "eval:(!doc.salary_slip_based_on_timesheet)", @@ -265,7 +271,7 @@ "fieldname": "hour_rate", "fieldtype": "Currency", "label": "Hour Rate", - "options": "Company:company:default_currency", + "options": "currency", "print_hide_if_no_value": 1 }, { @@ -347,24 +353,13 @@ "fieldname": "gross_pay", "fieldtype": "Currency", "label": "Gross Pay", - "oldfieldname": "gross_pay", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", + "options": "currency", "read_only": 1 }, { "fieldname": "column_break_25", "fieldtype": "Column Break" }, - { - "fieldname": "total_deduction", - "fieldtype": "Currency", - "label": "Total Deduction", - "oldfieldname": "total_deduction", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "read_only": 1 - }, { "depends_on": "total_loan_repayment", "fieldname": "loan_repayment", @@ -420,9 +415,7 @@ "fieldname": "net_pay", "fieldtype": "Currency", "label": "Net Pay", - "oldfieldname": "net_pay", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", + "options": "currency", "read_only": 1 }, { @@ -434,22 +427,13 @@ "fieldname": "rounded_total", "fieldtype": "Currency", "label": "Rounded Total", - "options": "Company:company:default_currency", + "options": "currency", "read_only": 1 }, { "fieldname": "section_break_55", "fieldtype": "Section Break" }, - { - "description": "Net Pay (in words) will be visible once you save the Salary Slip.", - "fieldname": "total_in_words", - "fieldtype": "Data", - "label": "Total in words", - "oldfieldname": "net_pay_in_words", - "oldfieldtype": "Data", - "read_only": 1 - }, { "fieldname": "amended_from", "fieldtype": "Link", @@ -500,13 +484,44 @@ { "fieldname": "column_break_18", "fieldtype": "Column Break" + }, + { + "default": "Company:company:default_currency", + "depends_on": "eval:(doc.docstatus==1 || doc.salary_structure)", + "fetch_from": "salary_structure.currency", + "fieldname": "currency", + "fieldtype": "Link", + "label": "Currency", + "options": "Currency", + "print_hide": 1, + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "total_deduction", + "fieldtype": "Currency", + "label": "Total Deduction", + "options": "currency", + "read_only": 1 + }, + { + "description": "Net Pay (in words) will be visible once you save the Salary Slip.", + "fieldname": "total_in_words", + "fieldtype": "Data", + "label": "Total in words", + "length": 240, + "read_only": 1 + }, + { + "fieldname": "section_break_75", + "fieldtype": "Section Break" } ], "icon": "fa fa-file-text", "idx": 9, "is_submittable": 1, "links": [], - "modified": "2020-08-11 17:37:54.274384", + "modified": "2020-09-30 17:31:21.909671", "modified_by": "Administrator", "module": "Payroll", "name": "Salary Slip", diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.py b/erpnext/payroll/doctype/salary_slip/salary_slip.py index 4ccf56435dd..f597f909fda 100644 --- a/erpnext/payroll/doctype/salary_slip/salary_slip.py +++ b/erpnext/payroll/doctype/salary_slip/salary_slip.py @@ -50,9 +50,9 @@ class SalarySlip(TransactionBase): self.calculate_net_pay() - company_currency = erpnext.get_company_currency(self.company) + doc_currency = self.currency total = self.net_pay if self.is_rounding_total_disabled() else self.rounded_total - self.total_in_words = money_in_words(total, company_currency) + self.total_in_words = money_in_words(total, doc_currency) if frappe.db.get_single_value("Payroll Settings", "max_working_hours_against_timesheet"): max_working_hours = frappe.db.get_single_value("Payroll Settings", "max_working_hours_against_timesheet") diff --git a/erpnext/payroll/doctype/salary_structure/salary_structure.js b/erpnext/payroll/doctype/salary_structure/salary_structure.js index ad93a2fa4bf..c4e90028f4a 100755 --- a/erpnext/payroll/doctype/salary_structure/salary_structure.js +++ b/erpnext/payroll/doctype/salary_structure/salary_structure.js @@ -41,20 +41,6 @@ frappe.ui.form.on('Salary Structure', { frm.toggle_reqd(['payroll_frequency'], !frm.doc.salary_slip_based_on_timesheet) - frm.set_query("salary_component", "earnings", function() { - return { - filters: { - type: "earning" - } - } - }); - frm.set_query("salary_component", "deductions", function() { - return { - filters: { - type: "deduction" - } - } - }); frm.set_query("payment_account", function () { var account_types = ["Bank", "Cash"]; return { @@ -65,9 +51,55 @@ frappe.ui.form.on('Salary Structure', { } }; }); + frm.trigger('set_earning_deduction_component'); + }, + + set_earning_deduction_component: function(frm) { + if(!frm.doc.currency && !frm.doc.company) return; + frm.set_query("salary_component", "earnings", function() { + return { + query : "erpnext.payroll.doctype.salary_structure.salary_structure.get_earning_deduction_components", + filters: {type: "earning", currency: frm.doc.currency, company: frm.doc.company} + }; + }); + frm.set_query("salary_component", "deductions", function() { + return { + query : "erpnext.payroll.doctype.salary_structure.salary_structure.get_earning_deduction_components", + filters: {type: "deduction", currency: frm.doc.currency, company: frm.doc.company} + }; + }); + }, + + + currency: function(frm) { + calculate_totals(frm.doc); + frm.trigger("set_dynamic_labels") + frm.trigger('set_earning_deduction_component'); + frm.refresh() + }, + + set_dynamic_labels: function(frm) { + debugger; + frm.set_currency_labels(["net_pay","hour_rate", "leave_encashment_amount_per_day", "max_benefits", "total_earning", + "total_deduction"], frm.doc.currency); + + // toggle fields + frm.toggle_display(["total_earning", "total_deduction"], true); + + if(frappe.meta.get_docfield(cur_frm.doctype, "net_pay")) + cur_frm.toggle_display("net_pay", true); + + frm.set_currency_labels(["amount", "additional_amount", "tax_on_flexible_benefit", "tax_on_additional_salary"], + frm.doc.currency, "earnings"); + + frm.set_currency_labels(["amount", "additional_amount", "tax_on_flexible_benefit", "tax_on_additional_salary"], + frm.doc.currency, "deductions"); + + frm.refresh_fields(); }, refresh: function(frm) { + frm.trigger("set_dynamic_labels") frm.trigger("toggle_fields"); frm.fields_dict['earnings'].grid.set_column_disp("default_amount", false); frm.fields_dict['deductions'].grid.set_column_disp("default_amount", false); @@ -101,10 +133,12 @@ frappe.ui.form.on('Salary Structure', { fields: [ {fieldname: "sec_break", fieldtype: "Section Break", label: __("Filter Employees By (Optional)")}, {fieldname: "company", fieldtype: "Link", options: "Company", label: __("Company"), default: frm.doc.company, read_only:1}, + {fieldname: "currency", fieldtype: "Link", options: "Currency", label: __("Currency"), default: frm.doc.currency, read_only:1}, {fieldname: "grade", fieldtype: "Link", options: "Employee Grade", label: __("Employee Grade")}, {fieldname:'department', fieldtype:'Link', options: 'Department', label: __('Department')}, {fieldname:'designation', fieldtype:'Link', options: 'Designation', label: __('Designation')}, - {fieldname:"employee", fieldtype: "Link", options: "Employee", label: __("Employee")}, + {fieldname:"employee", fieldtype: "Link", options: "Employee", label: __("Employee")}, + {fieldname:"default_payroll_payable_account", fieldtype: "Link", options: "Account", filters: {"company": frm.doc.company, "root_type": "Liability", "is_group": 0, "account_currency": frm.doc.currency}, label: __("Default Payroll Payable Account")}, {fieldname:'base_variable', fieldtype:'Section Break'}, {fieldname:'from_date', fieldtype:'Date', label: __('From Date'), "reqd": 1}, {fieldname:'income_tax_slab', fieldtype:'Link', label: __('Income Tax Slab'), options: 'Income Tax Slab'}, @@ -221,6 +255,7 @@ cur_frm.cscript.amount = function(doc, cdt, cdn){ }; var calculate_totals = function(doc) { + debugger; var tbl1 = doc.earnings || []; var tbl2 = doc.deductions || []; diff --git a/erpnext/payroll/doctype/salary_structure/salary_structure.json b/erpnext/payroll/doctype/salary_structure/salary_structure.json index 5f94929f0b5..de56fc8457e 100644 --- a/erpnext/payroll/doctype/salary_structure/salary_structure.json +++ b/erpnext/payroll/doctype/salary_structure/salary_structure.json @@ -13,6 +13,7 @@ "column_break1", "is_active", "payroll_frequency", + "currency", "is_default", "time_sheet_earning_detail", "salary_slip_based_on_timesheet", @@ -26,9 +27,9 @@ "deductions", "conditions_and_formula_variable_and_example", "net_pay_detail", - "column_break2", "total_earning", "total_deduction", + "column_break2", "net_pay", "account", "mode_of_payment", @@ -43,23 +44,17 @@ "label": "Company", "options": "Company", "remember_last_selected_value": 1, - "reqd": 1, - "show_days": 1, - "show_seconds": 1 + "reqd": 1 }, { "fieldname": "letter_head", "fieldtype": "Link", "label": "Letter Head", - "options": "Letter Head", - "show_days": 1, - "show_seconds": 1 + "options": "Letter Head" }, { "fieldname": "column_break1", "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1, "width": "50%" }, { @@ -72,9 +67,7 @@ "oldfieldname": "is_active", "oldfieldtype": "Select", "options": "\nYes\nNo", - "reqd": 1, - "show_days": 1, - "show_seconds": 1 + "reqd": 1 }, { "default": "Monthly", @@ -82,9 +75,7 @@ "fieldname": "payroll_frequency", "fieldtype": "Select", "label": "Payroll Frequency", - "options": "\nMonthly\nFortnightly\nBimonthly\nWeekly\nDaily", - "show_days": 1, - "show_seconds": 1 + "options": "\nMonthly\nFortnightly\nBimonthly\nWeekly\nDaily" }, { "default": "No", @@ -95,62 +86,46 @@ "no_copy": 1, "options": "Yes\nNo", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "time_sheet_earning_detail", - "fieldtype": "Section Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Section Break" }, { "default": "0", "fieldname": "salary_slip_based_on_timesheet", "fieldtype": "Check", - "label": "Salary Slip Based on Timesheet", - "show_days": 1, - "show_seconds": 1 + "label": "Salary Slip Based on Timesheet" }, { "fieldname": "column_break_17", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "description": "Salary Component for timesheet based payroll.", "fieldname": "salary_component", "fieldtype": "Link", "label": "Salary Component", - "options": "Salary Component", - "show_days": 1, - "show_seconds": 1 + "options": "Salary Component" }, { "fieldname": "hour_rate", "fieldtype": "Currency", "label": "Hour Rate", - "options": "Company:company:default_currency", - "show_days": 1, - "show_seconds": 1 + "options": "currency" }, { "fieldname": "leave_encashment_amount_per_day", "fieldtype": "Currency", "label": "Leave Encashment Amount Per Day", - "options": "Company:company:default_currency", - "show_days": 1, - "show_seconds": 1 + "options": "currency" }, { "fieldname": "max_benefits", "fieldtype": "Currency", "label": "Max Benefits (Amount)", - "options": "Company:company:default_currency", - "show_days": 1, - "show_seconds": 1 + "options": "currency" }, { "description": "Salary breakup based on Earning and Deduction.", @@ -158,9 +133,7 @@ "fieldtype": "Section Break", "oldfieldname": "earning_deduction", "oldfieldtype": "Section Break", - "precision": "2", - "show_days": 1, - "show_seconds": 1 + "precision": "2" }, { "fieldname": "earnings", @@ -168,9 +141,7 @@ "label": "Earnings", "oldfieldname": "earning_details", "oldfieldtype": "Table", - "options": "Salary Detail", - "show_days": 1, - "show_seconds": 1 + "options": "Salary Detail" }, { "fieldname": "deductions", @@ -178,22 +149,16 @@ "label": "Deductions", "oldfieldname": "deduction_details", "oldfieldtype": "Table", - "options": "Salary Detail", - "show_days": 1, - "show_seconds": 1 + "options": "Salary Detail" }, { "fieldname": "net_pay_detail", "fieldtype": "Section Break", - "options": "Simple", - "show_days": 1, - "show_seconds": 1 + "options": "Simple" }, { "fieldname": "column_break2", "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1, "width": "50%" }, { @@ -201,63 +166,45 @@ "fieldtype": "Currency", "hidden": 1, "label": "Total Earning", - "oldfieldname": "total_earning", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "options": "currency", + "read_only": 1 }, { "fieldname": "total_deduction", "fieldtype": "Currency", "hidden": 1, "label": "Total Deduction", - "oldfieldname": "total_deduction", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "options": "currency", + "read_only": 1 }, { "fieldname": "net_pay", "fieldtype": "Currency", "hidden": 1, "label": "Net Pay", - "options": "Company:company:default_currency", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "options": "currency", + "read_only": 1 }, { "fieldname": "account", "fieldtype": "Section Break", - "label": "Account", - "show_days": 1, - "show_seconds": 1 + "label": "Account" }, { "fieldname": "mode_of_payment", "fieldtype": "Link", "label": "Mode of Payment", - "options": "Mode of Payment", - "show_days": 1, - "show_seconds": 1 + "options": "Mode of Payment" }, { "fieldname": "column_break_28", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "fieldname": "payment_account", "fieldtype": "Link", "label": "Payment Account", - "options": "Account", - "show_days": 1, - "show_seconds": 1 + "options": "Account" }, { "fieldname": "amended_from", @@ -266,23 +213,26 @@ "no_copy": 1, "options": "Salary Structure", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "conditions_and_formula_variable_and_example", "fieldtype": "HTML", - "label": "Conditions and Formula variable and example", - "show_days": 1, - "show_seconds": 1 + "label": "Conditions and Formula variable and example" + }, + { + "fieldname": "currency", + "fieldtype": "Link", + "label": "Currency", + "options": "Currency", + "reqd": 1 } ], "icon": "fa fa-file-text", "idx": 1, "is_submittable": 1, "links": [], - "modified": "2020-06-22 17:07:26.129355", + "modified": "2020-09-30 11:30:32.190798", "modified_by": "Administrator", "module": "Payroll", "name": "Salary Structure", diff --git a/erpnext/payroll/doctype/salary_structure/salary_structure.py b/erpnext/payroll/doctype/salary_structure/salary_structure.py index ffc16d73c25..530432c0200 100644 --- a/erpnext/payroll/doctype/salary_structure/salary_structure.py +++ b/erpnext/payroll/doctype/salary_structure/salary_structure.py @@ -2,7 +2,7 @@ # License: GNU General Public License v3. See license.txt from __future__ import unicode_literals -import frappe +import frappe, erpnext from frappe.utils import flt, cint, cstr from frappe import _ @@ -88,24 +88,26 @@ class SalaryStructure(Document): return employees @frappe.whitelist() - def assign_salary_structure(self, company=None, grade=None, department=None, designation=None,employee=None, - from_date=None, base=None, variable=None, income_tax_slab=None): - employees = self.get_employees(company= company, grade= grade,department= department,designation= designation,name=employee) + def assign_salary_structure(self, grade=None, department=None, designation=None,employee=None, + default_payroll_payable_account=None, from_date=None, base=None, variable=None, income_tax_slab=None): + employees = self.get_employees(company= self.company, grade= grade,department= department,designation= designation,name=employee) if employees: if len(employees) > 20: frappe.enqueue(assign_salary_structure_for_employees, timeout=600, - employees=employees, salary_structure=self,from_date=from_date, - base=base, variable=variable, income_tax_slab=income_tax_slab) + employees=employees, salary_structure=self, + default_payroll_payable_account=default_payroll_payable_account, + from_date=from_date, base=base, variable=variable, income_tax_slab=income_tax_slab) else: - assign_salary_structure_for_employees(employees, self, from_date=from_date, - base=base, variable=variable, income_tax_slab=income_tax_slab) + assign_salary_structure_for_employees(employees, self, + default_payroll_payable_account=default_payroll_payable_account, + from_date=from_date, base=base, variable=variable, income_tax_slab=income_tax_slab) else: frappe.msgprint(_("No Employee Found")) -def assign_salary_structure_for_employees(employees, salary_structure, from_date=None, base=None, variable=None, income_tax_slab=None): +def assign_salary_structure_for_employees(employees, salary_structure, default_payroll_payable_account=None, from_date=None, base=None, variable=None, income_tax_slab=None): salary_structures_assignments = [] existing_assignments_for = get_existing_assignments(employees, salary_structure, from_date) count=0 @@ -115,7 +117,7 @@ def assign_salary_structure_for_employees(employees, salary_structure, from_date count +=1 salary_structures_assignment = create_salary_structures_assignment(employee, - salary_structure, from_date, base, variable, income_tax_slab) + salary_structure, default_payroll_payable_account, from_date, base, variable, income_tax_slab) salary_structures_assignments.append(salary_structures_assignment) frappe.publish_progress(count*100/len(set(employees) - set(existing_assignments_for)), title = _("Assigning Structures...")) @@ -123,11 +125,21 @@ def assign_salary_structure_for_employees(employees, salary_structure, from_date frappe.msgprint(_("Structures have been assigned successfully")) -def create_salary_structures_assignment(employee, salary_structure, from_date, base, variable, income_tax_slab=None): +def create_salary_structures_assignment(employee, salary_structure, default_payroll_payable_account, from_date, base, variable, income_tax_slab=None): + if not default_payroll_payable_account: + default_payroll_payable_account = frappe.db.get_value('Company', salary_structure.company, 'default_payroll_payable_account') + if not default_payroll_payable_account: + frappe.throw(_('Please set "Default Payroll Payable Account" in Company Defaults')) + account_currency = frappe.db.get_value('Account', default_payroll_payable_account, 'account_currency') + if account_currency != salary_structure.currency: + frappe.throw(_("Account currency of Account: {0} is different than what is specified in salary structure: {1}").format(self.default_payroll_payable_account, self.salary_structure)) + assignment = frappe.new_doc("Salary Structure Assignment") assignment.employee = employee assignment.salary_structure = salary_structure.name assignment.company = salary_structure.company + assignment.currency = salary_structure.currency + assignment.default_payroll_payable_account = default_payroll_payable_account assignment.from_date = from_date assignment.base = base assignment.variable = variable @@ -170,7 +182,8 @@ def make_salary_slip(source_name, target_doc = None, employee = None, as_print = "doctype": "Salary Slip", "field_map": { "total_earning": "gross_pay", - "name": "salary_structure" + "name": "salary_structure", + "currency": "currency" } } }, target_doc, postprocess, ignore_child_tables=True, ignore_permissions=ignore_permissions) @@ -192,3 +205,24 @@ def get_employees(salary_structure): Assign {1} to an Employee to preview Salary Slip").format(salary_structure, salary_structure)) return list(set([d.employee for d in employees])) + +@frappe.whitelist() +@frappe.validate_and_sanitize_search_inputs +def get_earning_deduction_components(doctype, txt, searchfield, start, page_len, filters): + if len(filters) < 3: + return {} + + currency_list = [] + company_currency = erpnext.get_company_currency(filters['company']) + currency_list.append(company_currency) + if filters['currency'] != company_currency: + currency_list.append(filters['currency']) + return frappe.db.sql(""" + select t1.salary_component + from `tabSalary Component` t1, `tabSalary Component Account` t2 + where t1.salary_component = t2.parent + and t1.type = %s + and t2.company = %s + and t2.account_currency in %s + order by salary_component + """, (filters['type'], filters['company'], currency_list) ) diff --git a/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.js b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.js index 818e853154d..4033ce8951a 100644 --- a/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.js +++ b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.js @@ -6,9 +6,6 @@ frappe.ui.form.on('Salary Structure Assignment', { frm.set_query("employee", function() { return { query: "erpnext.controllers.queries.employee_query", - filters: { - company: frm.doc.company - } } }); frm.set_query("salary_structure", function() { @@ -30,8 +27,21 @@ frappe.ui.form.on('Salary Structure Assignment', { } } }); + + frm.set_query("default_payroll_payable_account", function() { + return { + filters: { + "company": frm.doc.company, + "root_type": "Liability", + "is_group": 0, + "account_currency": frm.doc.currency + } + } + }); }, + employee: function(frm) { + debugger; if(frm.doc.employee){ frappe.call({ method: "frappe.client.get_value", diff --git a/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.json b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.json index c84e034c727..6a22f33de5e 100644 --- a/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.json +++ b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.json @@ -11,11 +11,13 @@ "employee_name", "department", "company", + "default_payroll_payable_account", "column_break_6", "designation", "salary_structure", "from_date", "income_tax_slab", + "currency", "section_break_7", "base", "column_break_9", @@ -94,7 +96,7 @@ "fieldname": "base", "fieldtype": "Currency", "label": "Base", - "options": "Company:company:default_currency" + "options": "currency" }, { "fieldname": "column_break_9", @@ -104,7 +106,7 @@ "fieldname": "variable", "fieldtype": "Currency", "label": "Variable", - "options": "Company:company:default_currency" + "options": "currency" }, { "fieldname": "amended_from", @@ -120,11 +122,30 @@ "fieldtype": "Link", "label": "Income Tax Slab", "options": "Income Tax Slab" + }, + { + "depends_on": "salary_structure", + "fieldname": "default_payroll_payable_account", + "fieldtype": "Link", + "label": "Default Payroll Payable Account", + "options": "Account" + }, + { + "default": "Company:company:default_currency", + "depends_on": "eval:(doc.docstatus==1 || doc.salary_structure)", + "fetch_from": "salary_structure.currency", + "fieldname": "currency", + "fieldtype": "Link", + "label": "Currency", + "options": "Currency", + "print_hide": 1, + "read_only": 1, + "reqd": 1 } ], "is_submittable": 1, "links": [], - "modified": "2020-06-22 19:58:09.964692", + "modified": "2020-09-30 17:35:49.922227", "modified_by": "Administrator", "module": "Payroll", "name": "Salary Structure Assignment", diff --git a/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.py b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.py index 668e0ec4717..a277055a05a 100644 --- a/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.py +++ b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.py @@ -13,6 +13,7 @@ class DuplicateAssignment(frappe.ValidationError): pass class SalaryStructureAssignment(Document): def validate(self): self.validate_dates() + self.validate_default_payroll_payable_account() def validate_dates(self): joining_date, relieving_date = frappe.db.get_value("Employee", self.employee, @@ -31,6 +32,15 @@ class SalaryStructureAssignment(Document): frappe.throw(_("From Date {0} cannot be after employee's relieving Date {1}") .format(self.from_date, relieving_date)) + def validate_default_payroll_payable_account(self): + if not self.default_payroll_payable_account: + self.default_payroll_payable_account = frappe.db.get_value('Company', self.company, 'default_payroll_payable_account') + if not self.default_payroll_payable_account: + frappe.throw(_('Please set "Default Payroll Payable Account" in Company Defaults')) + account_currency = frappe.db.get_value('Account', self.default_payroll_payable_account, 'account_currency') + if account_currency != self.currency: + frappe.throw(_("Account currency of Account: {0} is different than what is specified in salary structure: {1}").format(self.default_payroll_payable_account, self.salary_structure)) + def get_assigned_salary_structure(employee, on_date): if not employee or not on_date: return None @@ -43,3 +53,11 @@ def get_assigned_salary_structure(employee, on_date): 'on_date': on_date, }) return salary_structure[0][0] if salary_structure else None + +@frappe.whitelist() +def get_payroll_payable_account_currency(employee): + default_payroll_payable_account = frappe.db.get_value('Salary Structure Assignment', {'employee': employee}, 'default_payroll_payable_account') + if not default_payroll_payable_account: + frappe.throw(_("There is no Salary Structure assigned to {0}. First assign a Salary Stucture.").format(employee)) + account_currency = frappe.db.get_value('Account', default_payroll_payable_account, 'account_currency') + return account_currency \ No newline at end of file diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js index f882db60c5d..9a5326e3fef 100644 --- a/erpnext/setup/doctype/company/company.js +++ b/erpnext/setup/doctype/company/company.js @@ -238,31 +238,31 @@ cur_frm.cscript.change_abbr = function() { erpnext.company.setup_queries = function(frm) { $.each([ - ["default_bank_account", {"account_type": "Bank"}], - ["default_cash_account", {"account_type": "Cash"}], - ["default_receivable_account", {"account_type": "Receivable"}], - ["default_payable_account", {"account_type": "Payable"}], - ["default_expense_account", {"root_type": "Expense"}], - ["default_income_account", {"root_type": "Income"}], - ["default_payroll_payable_account", {"root_type": "Liability"}], - ["round_off_account", {"root_type": "Expense"}], - ["write_off_account", {"root_type": "Expense"}], - ["discount_allowed_account", {"root_type": "Expense"}], - ["discount_received_account", {"root_type": "Income"}], - ["exchange_gain_loss_account", {"root_type": "Expense"}], - ["unrealized_exchange_gain_loss_account", {"root_type": "Expense"}], + ["default_bank_account", {"account_type": "Bank", "account_currency": frm.doc.default_currency}], + ["default_cash_account", {"account_type": "Cash", "account_currency": frm.doc.default_currency}], + ["default_receivable_account", {"account_type": "Receivable", "account_currency": frm.doc.default_currency}], + ["default_payable_account", {"account_type": "Payable", "account_currency": frm.doc.default_currency}], + ["default_expense_account", {"root_type": "Expense", "account_currency": frm.doc.default_currency}], + ["default_income_account", {"root_type": "Income", "account_currency": frm.doc.default_currency}], + ["default_payroll_payable_account", {"root_type": "Liability", "account_currency": frm.doc.default_currency}], + ["round_off_account", {"root_type": "Expense", "account_currency": frm.doc.default_currency}], + ["write_off_account", {"root_type": "Expense", "account_currency": frm.doc.default_currency}], + ["discount_allowed_account", {"root_type": "Expense", "account_currency": frm.doc.default_currency}], + ["discount_received_account", {"root_type": "Income", "account_currency": frm.doc.default_currency}], + ["exchange_gain_loss_account", {"root_type": "Expense", "account_currency": frm.doc.default_currency}], + ["unrealized_exchange_gain_loss_account", {"root_type": "Expense", "account_currency": frm.doc.default_currency}], ["accumulated_depreciation_account", - {"root_type": "Asset", "account_type": "Accumulated Depreciation"}], - ["depreciation_expense_account", {"root_type": "Expense", "account_type": "Depreciation"}], - ["disposal_account", {"report_type": "Profit and Loss"}], - ["default_inventory_account", {"account_type": "Stock"}], + {"root_type": "Asset", "account_type": "Accumulated Depreciation", "account_currency": frm.doc.default_currency}], + ["depreciation_expense_account", {"root_type": "Expense", "account_type": "Depreciation", "account_currency": frm.doc.default_currency}], + ["disposal_account", {"report_type": "Profit and Loss", "account_currency": frm.doc.default_currency}], + ["default_inventory_account", {"account_type": "Stock", "account_currency": frm.doc.default_currency}], ["cost_center", {}], ["round_off_cost_center", {}], ["depreciation_cost_center", {}], - ["default_employee_advance_account", {"root_type": "Asset"}], - ["expenses_included_in_asset_valuation", {"account_type": "Expenses Included In Asset Valuation"}], - ["capital_work_in_progress_account", {"account_type": "Capital Work in Progress"}], - ["asset_received_but_not_billed", {"account_type": "Asset Received But Not Billed"}] + ["default_employee_advance_account", {"root_type": "Asset", "account_currency": frm.doc.default_currency}], + ["expenses_included_in_asset_valuation", {"account_type": "Expenses Included In Asset Valuation", "account_currency": frm.doc.default_currency}], + ["capital_work_in_progress_account", {"account_type": "Capital Work in Progress", "account_currency": frm.doc.default_currency}], + ["asset_received_but_not_billed", {"account_type": "Asset Received But Not Billed", "account_currency": frm.doc.default_currency}] ], function(i, v) { erpnext.company.set_custom_query(frm, v); });