From ac9a4fe03c40409af44f2f101d75968991a5f579 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Tue, 11 Jun 2019 14:25:44 +0530 Subject: [PATCH] feat: add tax and charges in expense claim --- .../hr/doctype/expense_claim/expense_claim.js | 62 +++++++++++ .../doctype/expense_claim/expense_claim.json | 22 +++- .../hr/doctype/expense_claim/expense_claim.py | 33 +++++- .../expense_taxes_and_charges/__init__.py | 0 .../expense_taxes_and_charges.json | 103 ++++++++++++++++++ .../expense_taxes_and_charges.py | 10 ++ 6 files changed, 227 insertions(+), 3 deletions(-) create mode 100644 erpnext/hr/doctype/expense_taxes_and_charges/__init__.py create mode 100644 erpnext/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.json create mode 100644 erpnext/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.py diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.js b/erpnext/hr/doctype/expense_claim/expense_claim.js index 7c6abc7f94..0ff70cb680 100644 --- a/erpnext/hr/doctype/expense_claim/expense_claim.js +++ b/erpnext/hr/doctype/expense_claim/expense_claim.js @@ -157,6 +157,14 @@ frappe.ui.form.on("Expense Claim", { } }; }); + // frm.set_query("taxes", "account_head", function(doc) { + // return { + // filters: [ + // ['docstatus', '=', 1], + // ['company', '=', doc.company] + // ] + // }; + // }); }, onload: function(frm) { @@ -259,6 +267,18 @@ frappe.ui.form.on("Expense Claim", { frm.events.get_advances(frm); }, + get_taxes: function(frm) { + if(frm.doc.taxes) { + frappe.call({ + method: "calculate_taxes", + doc: frm.doc, + callback: (r) => { + refresh_field("taxes"); + } + }); + } + }, + get_advances: function(frm) { frappe.model.clear_table(frm.doc, "advances"); if (frm.doc.employee) { @@ -298,6 +318,7 @@ frappe.ui.form.on("Expense Claim Detail", { sanctioned_amount: function(frm, cdt, cdn) { var doc = frm.doc; cur_frm.cscript.calculate_total(doc,cdt,cdn); + frm.trigger("get_taxes"); } }); @@ -332,6 +353,47 @@ frappe.ui.form.on("Expense Claim Advance", { } }); +frappe.ui.form.on("Expense Taxes and Charges", { + account_head: function(frm, cdt, cdn) { + var child = locals[cdt][cdn]; + if(child.account_head && !child.description && !child.rate) { + // set description from account head + child.description = child.account_head.split(' - ').slice(0, -1).join(' - '); + + // set the tax rate from account head + frappe.db.get_value("Account", child.account_head, "tax_rate").then((r) => { + if(r.message) { + frappe.model.set_value(cdt, cdn, 'rate', r.message.tax_rate); + } + }); + refresh_field("taxes"); + } + }, + + calculate_total: function(frm, cdt, cdn) { + var child = locals[cdt][cdn]; + child.total = flt(frm.doc.total_sanctioned_amount) + flt(child.tax_amount); + + refresh_field("taxes"); + }, + + rate: function(frm, cdt, cdn) { + var child = locals[cdt][cdn]; + if(!child.amount) { + child.tax_amount = flt(frm.doc.total_sanctioned_amount) * (flt(child.rate)/100); + refresh_field("taxes"); + } + frm.trigger("calculate_total", cdt, cdn) + }, + + tax_amount: function(frm, cdt, cdn) { + var child = locals[cdt][cdn]; + child.rate = flt(child.tax_amount/frm.doc.total_sanctioned_amount) * 100; + frm.trigger("calculate_total", cdt, cdn) + refresh_field("taxes"); + } +}); + cur_frm.fields_dict['task'].get_query = function(doc) { return { filters:{ diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.json b/erpnext/hr/doctype/expense_claim/expense_claim.json index 6e04644036..007e646815 100644 --- a/erpnext/hr/doctype/expense_claim/expense_claim.json +++ b/erpnext/hr/doctype/expense_claim/expense_claim.json @@ -4,6 +4,7 @@ "creation": "2013-01-10 16:34:14", "doctype": "DocType", "document_type": "Setup", + "engine": "InnoDB", "field_order": [ "naming_series", "employee", @@ -18,6 +19,9 @@ "expense_details", "expenses", "sb1", + "taxes", + "net_total", + "section_break_16", "posting_date", "vehicle_log", "task", @@ -315,12 +319,28 @@ { "fieldname": "dimension_col_break", "fieldtype": "Column Break" + }, + { + "fieldname": "taxes", + "fieldtype": "Table", + "label": "Expense Taxes and Charges", + "options": "Expense Taxes and Charges" + }, + { + "fieldname": "section_break_16", + "fieldtype": "Section Break" + }, + { + "fieldname": "net_total", + "fieldtype": "Currency", + "label": "Net Total", + "read_only": 1 } ], "icon": "fa fa-money", "idx": 1, "is_submittable": 1, - "modified": "2019-05-25 22:53:31.682151", + "modified": "2019-06-11 13:21:42.386420", "modified_by": "Administrator", "module": "HR", "name": "Expense Claim", diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.py b/erpnext/hr/doctype/expense_claim/expense_claim.py index d6b0eca70e..c5b6ebe56b 100644 --- a/erpnext/hr/doctype/expense_claim/expense_claim.py +++ b/erpnext/hr/doctype/expense_claim/expense_claim.py @@ -12,6 +12,7 @@ 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 from frappe.utils.csvutils import getlink +from erpnext.accounts.utils import get_account_currency class InvalidExpenseApproverError(frappe.ValidationError): pass class ExpenseApproverIdentityError(frappe.ValidationError): pass @@ -29,6 +30,7 @@ class ExpenseClaim(AccountsController): self.set_expense_account(validate=True) self.set_payable_account() self.set_cost_center() + self.calculate_taxes() self.set_status() if self.task and not self.project: self.project = frappe.db.get_value("Task", self.task, "project") @@ -93,7 +95,7 @@ class ExpenseClaim(AccountsController): elif self.project: frappe.get_doc("Project", self.project).update_project() - def make_gl_entries(self, cancel = False): + 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) @@ -102,7 +104,7 @@ class ExpenseClaim(AccountsController): gl_entry = [] self.validate_account_details() - payable_amount = flt(self.total_sanctioned_amount) - flt(self.total_advance_amount) + payable_amount = flt(self.net_total) - flt(self.total_advance_amount) # payable entry if payable_amount: @@ -170,8 +172,26 @@ class ExpenseClaim(AccountsController): }) ) + gl_entry = self.make_tax_gl_entries(gl_entry) + return gl_entry + def make_tax_gl_entries(self, gl_entries): + # tax table gl entries + for tax in self.get("taxes"): + account_currency = get_account_currency(tax.account_head) + gl_entries.append( + self.get_gl_dict({ + "account": tax.account_head, + "debit": tax.tax_amount, + "against": self.employee, + "cost_center": self.cost_center, + "against_voucher_type": self.doctype, + "against_voucher": self.name + }, account_currency) + ) + return gl_entries + def validate_account_details(self): if not self.cost_center: frappe.throw(_("Cost center is required to book an expense claim")) @@ -193,6 +213,15 @@ class ExpenseClaim(AccountsController): self.total_claimed_amount += flt(d.claim_amount) self.total_sanctioned_amount += flt(d.sanctioned_amount) + def calculate_taxes(self): + for tax in self.taxes: + if tax.rate: + tax.tax_amount = flt(self.total_sanctioned_amount) * flt(tax.rate/100) + if tax.tax_amount: + tax.rate = flt(tax.tax_amount)/flt(self.total_sanctioned_amount) * 100 + tax.total = flt(tax.tax_amount) + flt(self.total_sanctioned_amount) + self.net_total += tax.total + def update_task(self): task = frappe.get_doc("Task", self.task) task.update_total_expense_claim() diff --git a/erpnext/hr/doctype/expense_taxes_and_charges/__init__.py b/erpnext/hr/doctype/expense_taxes_and_charges/__init__.py new file mode 100644 index 0000000000..e69de29bb2 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 new file mode 100644 index 0000000000..8caf0a975a --- /dev/null +++ b/erpnext/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.json @@ -0,0 +1,103 @@ +{ + "autoname": "hash", + "creation": "2019-06-03 11:42:33.123976", + "doctype": "DocType", + "document_type": "Setup", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "account_head", + "cost_center", + "col_break1", + "rate", + "description", + "section_break_6", + "tax_amount", + "column_break_8", + "total" + ], + "fields": [ + { + "fieldname": "col_break1", + "fieldtype": "Column Break" + }, + { + "columns": 2, + "fieldname": "account_head", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Account Head", + "oldfieldname": "account_head", + "oldfieldtype": "Link", + "options": "Account", + "reqd": 1 + }, + { + "default": ":Company", + "fieldname": "cost_center", + "fieldtype": "Link", + "label": "Cost Center", + "oldfieldname": "cost_center", + "oldfieldtype": "Link", + "options": "Cost Center" + }, + { + "fieldname": "description", + "fieldtype": "Small Text", + "label": "Description", + "oldfieldname": "description", + "oldfieldtype": "Small Text", + "print_width": "300px", + "reqd": 1, + "width": "300px" + }, + { + "columns": 2, + "fieldname": "rate", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Rate", + "oldfieldname": "rate", + "oldfieldtype": "Currency" + }, + { + "columns": 2, + "fieldname": "tax_amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Amount", + "oldfieldname": "tax_amount", + "oldfieldtype": "Currency", + "options": "currency" + }, + { + "columns": 2, + "fieldname": "total", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Total", + "oldfieldname": "total", + "oldfieldtype": "Currency", + "options": "currency", + "read_only": 1 + }, + { + "fieldname": "section_break_6", + "fieldtype": "Section Break" + }, + { + "fieldname": "column_break_8", + "fieldtype": "Column Break" + } + ], + "istable": 1, + "modified": "2019-06-11 14:19:34.780611", + "modified_by": "Administrator", + "module": "HR", + "name": "Expense Taxes and Charges", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "ASC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.py b/erpnext/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.py new file mode 100644 index 0000000000..4103bef1ff --- /dev/null +++ b/erpnext/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2019, 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 ExpenseTaxesandCharges(Document): + pass