From 1dfb5eb5355eef7dbfbc511288be54c012765e12 Mon Sep 17 00:00:00 2001 From: Frappe Date: Wed, 14 Jul 2021 12:33:45 +0530 Subject: [PATCH 1/9] feat: South Africa VAT report --- .../south_africa_vat_account/__init__.py | 0 .../south_africa_vat_account.json | 34 +++ .../south_africa_vat_account.py | 8 + .../south_africa_vat_settings/__init__.py | 0 .../south_africa_vat_settings.js | 25 ++ .../south_africa_vat_settings.json | 76 ++++++ .../south_africa_vat_settings.py | 8 + .../test_south_africa_vat_settings.py | 8 + .../report/vat_audit_report/__init__.py | 0 .../vat_audit_report/vat_audit_report.js | 31 +++ .../vat_audit_report/vat_audit_report.json | 32 +++ .../vat_audit_report/vat_audit_report.py | 246 ++++++++++++++++++ erpnext/regional/south_africa/__init__.py | 0 erpnext/regional/south_africa/setup.py | 19 ++ 14 files changed, 487 insertions(+) create mode 100644 erpnext/accounts/doctype/south_africa_vat_account/__init__.py create mode 100644 erpnext/accounts/doctype/south_africa_vat_account/south_africa_vat_account.json create mode 100644 erpnext/accounts/doctype/south_africa_vat_account/south_africa_vat_account.py create mode 100644 erpnext/regional/doctype/south_africa_vat_settings/__init__.py create mode 100644 erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.js create mode 100644 erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.json create mode 100644 erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.py create mode 100644 erpnext/regional/doctype/south_africa_vat_settings/test_south_africa_vat_settings.py create mode 100644 erpnext/regional/report/vat_audit_report/__init__.py create mode 100644 erpnext/regional/report/vat_audit_report/vat_audit_report.js create mode 100644 erpnext/regional/report/vat_audit_report/vat_audit_report.json create mode 100644 erpnext/regional/report/vat_audit_report/vat_audit_report.py create mode 100644 erpnext/regional/south_africa/__init__.py create mode 100644 erpnext/regional/south_africa/setup.py diff --git a/erpnext/accounts/doctype/south_africa_vat_account/__init__.py b/erpnext/accounts/doctype/south_africa_vat_account/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/erpnext/accounts/doctype/south_africa_vat_account/south_africa_vat_account.json b/erpnext/accounts/doctype/south_africa_vat_account/south_africa_vat_account.json new file mode 100644 index 000000000000..fa1aa7da5948 --- /dev/null +++ b/erpnext/accounts/doctype/south_africa_vat_account/south_africa_vat_account.json @@ -0,0 +1,34 @@ +{ + "actions": [], + "autoname": "account", + "creation": "2021-07-08 22:04:24.634967", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "account" + ], + "fields": [ + { + "allow_in_quick_entry": 1, + "fieldname": "account", + "fieldtype": "Link", + "in_list_view": 1, + "in_preview": 1, + "label": "Account", + "options": "Account" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2021-07-08 22:35:33.202911", + "modified_by": "Administrator", + "module": "Accounts", + "name": "South Africa VAT Account", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/south_africa_vat_account/south_africa_vat_account.py b/erpnext/accounts/doctype/south_africa_vat_account/south_africa_vat_account.py new file mode 100644 index 000000000000..4bd8c65a046c --- /dev/null +++ b/erpnext/accounts/doctype/south_africa_vat_account/south_africa_vat_account.py @@ -0,0 +1,8 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + +class SouthAfricaVATAccount(Document): + pass diff --git a/erpnext/regional/doctype/south_africa_vat_settings/__init__.py b/erpnext/regional/doctype/south_africa_vat_settings/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.js b/erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.js new file mode 100644 index 000000000000..3f0b466d8258 --- /dev/null +++ b/erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.js @@ -0,0 +1,25 @@ +// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('South Africa VAT Settings', { + refresh: function(frm) { + frm.set_query("company", function() { + return { + filters: { + country: "South Africa", + } + } + }); + frm.set_query("account", "vat_accounts", function(doc, cdt, cdn) { + var row = locals[cdt][cdn]; + console.log(row); + return { + filters: { + company: frm.doc.company, + account_type: "Tax", + is_group: 0 + } + } + }) + } +}); diff --git a/erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.json b/erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.json new file mode 100644 index 000000000000..8a51829c4195 --- /dev/null +++ b/erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.json @@ -0,0 +1,76 @@ +{ + "actions": [], + "autoname": "field:company", + "creation": "2021-07-08 22:34:33.668015", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "company", + "vat_accounts" + ], + "fields": [ + { + "fieldname": "company", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Company", + "options": "Company", + "reqd": 1, + "unique": 1 + }, + { + "fieldname": "vat_accounts", + "fieldtype": "Table", + "label": "VAT Accounts", + "options": "South Africa VAT Account", + "reqd": 1 + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2021-07-14 02:17:52.476762", + "modified_by": "Administrator", + "module": "Regional", + "name": "South Africa VAT Settings", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts User", + "share": 1, + "write": 1 + }, + { + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Auditor", + "share": 1 + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.py b/erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.py new file mode 100644 index 000000000000..d74154bfe786 --- /dev/null +++ b/erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.py @@ -0,0 +1,8 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + +class SouthAfricaVATSettings(Document): + pass diff --git a/erpnext/regional/doctype/south_africa_vat_settings/test_south_africa_vat_settings.py b/erpnext/regional/doctype/south_africa_vat_settings/test_south_africa_vat_settings.py new file mode 100644 index 000000000000..1c36652ad6e6 --- /dev/null +++ b/erpnext/regional/doctype/south_africa_vat_settings/test_south_africa_vat_settings.py @@ -0,0 +1,8 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +# import frappe +import unittest + +class TestSouthAfricaVATSettings(unittest.TestCase): + pass diff --git a/erpnext/regional/report/vat_audit_report/__init__.py b/erpnext/regional/report/vat_audit_report/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/erpnext/regional/report/vat_audit_report/vat_audit_report.js b/erpnext/regional/report/vat_audit_report/vat_audit_report.js new file mode 100644 index 000000000000..39ef9b563ac7 --- /dev/null +++ b/erpnext/regional/report/vat_audit_report/vat_audit_report.js @@ -0,0 +1,31 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports["VAT Audit Report"] = { + "filters": [ + { + "fieldname": "company", + "label": __("Company"), + "fieldtype": "Link", + "options": "Company", + "reqd": 1, + "default": frappe.defaults.get_user_default("Company") + }, + { + "fieldname": "from_date", + "label": __("From Date"), + "fieldtype": "Date", + "reqd": 1, + "default": frappe.datetime.add_months(frappe.datetime.get_today(), -2), + "width": "80" + }, + { + "fieldname": "to_date", + "label": __("To Date"), + "fieldtype": "Date", + "reqd": 1, + "default": frappe.datetime.get_today() + } + ] +}; diff --git a/erpnext/regional/report/vat_audit_report/vat_audit_report.json b/erpnext/regional/report/vat_audit_report/vat_audit_report.json new file mode 100644 index 000000000000..8917e8f3c7e1 --- /dev/null +++ b/erpnext/regional/report/vat_audit_report/vat_audit_report.json @@ -0,0 +1,32 @@ +{ + "add_total_row": 0, + "columns": [], + "creation": "2021-07-09 11:07:43.473518", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [], + "idx": 0, + "is_standard": "Yes", + "modified": "2021-07-09 11:07:43.473518", + "modified_by": "Administrator", + "module": "Regional", + "name": "VAT Audit Report", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "GL Entry", + "report_name": "VAT Audit Report", + "report_type": "Script Report", + "roles": [ + { + "role": "Accounts User" + }, + { + "role": "Accounts Manager" + }, + { + "role": "Auditor" + } + ] +} \ No newline at end of file diff --git a/erpnext/regional/report/vat_audit_report/vat_audit_report.py b/erpnext/regional/report/vat_audit_report/vat_audit_report.py new file mode 100644 index 000000000000..e66212689ee7 --- /dev/null +++ b/erpnext/regional/report/vat_audit_report/vat_audit_report.py @@ -0,0 +1,246 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe, json +from frappe import _ +from frappe.utils import flt, formatdate, now_datetime, getdate +from datetime import date + +def execute(filters=None): + return VATAuditReport(filters).run() + +class VATAuditReport(object): + + def __init__(self, filters=None): + self.filters = frappe._dict(filters or {}) + self.columns = [] + self.data = [] + self.doctypes = ["Purchase Invoice", "Sales Invoice"] + + def run(self): + self.get_sa_vat_accounts() + for doctype in self.doctypes: + self.get_columns(doctype) + self.select_columns = """ + name as invoice_number, + posting_date, remarks""" + columns = ", supplier as party, credit_to as account" if doctype=="Purchase Invoice" \ + else ", customer as party, debit_to as account" + self.select_columns += columns + + self.get_invoice_data(doctype) + + if self.invoices: + self.get_invoice_items(doctype) + self.get_items_based_on_tax_rate(doctype) + self.get_data(doctype) + + return self.columns, self.data + + def get_sa_vat_accounts(self): + self.sa_vat_accounts = frappe.get_list("South Africa VAT Account", \ + filters = {"parent":self.filters.company}, pluck="account") + if not self.sa_vat_accounts and not frappe.flags.in_test and not frappe.flags.in_migrate: + frappe.throw(_("Please set VAT Accounts in South Africa VAT Settings")) + + def get_invoice_data(self,doctype): + conditions = self.get_conditions() + self.invoices = frappe._dict() + + invoice_data = frappe.db.sql(""" + SELECT + {select_columns} + FROM + `tab{doctype}` + WHERE + docstatus = 1 {where_conditions} + and is_opening = 'No' + ORDER BY + posting_date DESC + """.format(select_columns=self.select_columns, doctype=doctype, + where_conditions=conditions), self.filters, as_dict=1) + + for d in invoice_data: + self.invoices.setdefault(d.invoice_number, d) + + def get_invoice_items(self,doctype): + self.invoice_items = frappe._dict() + self.item_tax_rate = frappe._dict() + + items = frappe.db.sql(""" + SELECT + item_code, parent, taxable_value, base_net_amount, item_tax_rate + FROM + `tab%s Item` + WHERE + parent in (%s) + """ % (doctype, ', '.join(['%s']*len(self.invoices))), tuple(self.invoices), as_dict=1) + for d in items: + if d.item_code not in self.invoice_items.get(d.parent, {}): + self.invoice_items.setdefault(d.parent, {}).setdefault(d.item_code, + sum((i.get('taxable_value', 0) or i.get('base_net_amount', 0)) for i in items + if i.item_code == d.item_code and i.parent == d.parent)) + + def get_items_based_on_tax_rate(self,doctype): + self.items_based_on_tax_rate = frappe._dict() + self.tax_doctype = "Purchase Taxes and Charges" if doctype=="Purchase Invoice" \ + else "Sales Taxes and Charges" + self.tax_details = frappe.db.sql(""" + SELECT + parent, account_head, item_wise_tax_detail, base_tax_amount_after_discount_amount + FROM + `tab%s` + WHERE + parenttype = %s and docstatus = 1 + and parent in (%s) + ORDER BY + account_head + """ % (self.tax_doctype, '%s', ', '.join(['%s']*len(self.invoices.keys()))), + tuple([doctype] + list(self.invoices.keys()))) + + for parent, account, item_wise_tax_detail, tax_amount in self.tax_details: + if item_wise_tax_detail: + try: + if account in self.sa_vat_accounts: + item_wise_tax_detail = json.loads(item_wise_tax_detail) + else: + continue + for item_code, taxes in item_wise_tax_detail.items(): + tax_rate, item_amount_map = self.get_item_amount_map(parent, item_code, taxes) + + if tax_rate is not None: + rate_based_dict = self.items_based_on_tax_rate.setdefault(parent, {}) \ + .setdefault(tax_rate, []) + if item_code not in rate_based_dict: + rate_based_dict.append(item_code) + except ValueError: + continue + + def get_item_amount_map(self, parent, item_code, taxes): + net_amount = abs(self.invoice_items.get(parent).get(item_code)) + tax_rate = taxes[0] + tax_amount = taxes[1] + gross_amount = net_amount + tax_amount + item_amount_map = self.item_tax_rate.setdefault(parent, {}) \ + .setdefault(item_code, []) + amount_dict = { + "tax_rate": tax_rate, + "gross_amount": gross_amount, + "tax_amount": tax_amount, + "net_amount": net_amount + } + item_amount_map.append(amount_dict) + + return tax_rate, item_amount_map + + def get_conditions(self): + conditions = "" + for opts in (("company", " and company=%(company)s"), + ("from_date", " and posting_date>=%(from_date)s"), + ("to_date", " and posting_date<=%(to_date)s")): + if self.filters.get(opts[0]): + conditions += opts[1] + + return conditions + + def get_data(self,doctype): + consolidated_data = self.get_consolidated_data() + section_name = _("Purchases ") if doctype == "Purchase Invoice" else _("Sales ") + + for rate, section in consolidated_data.items(): + rate = int(rate) + label = frappe.bold(_("Standard Rate ") + section_name + str(rate) + "%") + section_head = {"posting_date": label } + total_gross = total_tax = total_net = 0 + self.data.append(section_head) + for row in section.get("data"): + self.data.append(row) + total_gross += row["gross_amount"] + total_tax += row["tax_amount"] + total_net += row["net_amount"] + + total = { + "posting_date": frappe.bold(_("Total")), + "gross_amount": total_gross, + "tax_amount": total_tax, + "net_amount": total_net, + "bold":1 + } + self.data.append(total) + self.data.append({}) + + def get_consolidated_data(self): + consolidated_data_map={} + for inv, inv_data in self.invoices.items(): + for rate, items in self.items_based_on_tax_rate.get(inv).items(): + consolidated_data_map.setdefault(rate, {"data": []}) + for item in items: + row = {} + item_details = self.item_tax_rate.get(inv).get(item) + row["account"] = inv_data.get("account") + row["posting_date"] = formatdate(inv_data.get("posting_date"), 'dd-mm-yyyy') + row["invoice_number"] = inv + row["party"] = inv_data.get("party") + row["remarks"] = inv_data.get("remarks") + row["gross_amount"]= item_details[0].get("gross_amount") + row["tax_amount"]= item_details[0].get("tax_amount") + row["net_amount"]= item_details[0].get("net_amount") + consolidated_data_map[rate]["data"].append(row) + + return consolidated_data_map + + def get_columns(self,doctype): + self.columns = [ + { + "fieldname": "posting_date", + "label": "Posting Date", + "fieldtype": "Data", + "width": 200 + }, + { + "fieldname": "account", + "label": "Account", + "fieldtype": "Link", + "options": "Account", + "width": 140 + }, + { + "fieldname": "invoice_number", + "label": "Reference", + "fieldtype": "Link", + "options": doctype, + "width": 140 + }, + { + "fieldname": "party", + "label": "Party", + "fieldtype": "Link", + "options": "Supplier" if doctype == "Purchase Invoice" else "Customer", + "width": 140 + }, + { + "fieldname": "remarks", + "label": "Details", + "fieldtype": "Data", + "width": 140 + }, + { + "fieldname": "net_amount", + "label": "Net Amount", + "fieldtype": "Currency", + "width": 140 + }, + { + "fieldname": "tax_amount", + "label": "Tax Amount", + "fieldtype": "Currency", + "width": 140 + }, + { + "fieldname": "gross_amount", + "label": "Gross Amount", + "fieldtype": "Currency", + "width": 140 + }, + ] diff --git a/erpnext/regional/south_africa/__init__.py b/erpnext/regional/south_africa/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/erpnext/regional/south_africa/setup.py b/erpnext/regional/south_africa/setup.py new file mode 100644 index 000000000000..65e9f5bc330f --- /dev/null +++ b/erpnext/regional/south_africa/setup.py @@ -0,0 +1,19 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals + +import frappe, os, json +from frappe.permissions import add_permission, update_permission_property + +def setup(): + add_permissions() + +def add_permissions(): + """Add Permissions for South Africa VAT Settings and South Africa VAT Account""" + for doctype in ('South Africa VAT Settings', 'South Africa VAT Account'): + add_permission(doctype, 'All', 0) + for role in ('Accounts Manager', 'Accounts User', 'System Manager'): + add_permission(doctype, role, 0) + update_permission_property(doctype, role, 0, 'write', 1) + update_permission_property(doctype, role, 0, 'create', 1) \ No newline at end of file From b37ff0d3cf3a05734e15a7fc31079b64237ac6e1 Mon Sep 17 00:00:00 2001 From: Anuja Pawar Date: Wed, 14 Jul 2021 15:06:56 +0530 Subject: [PATCH 2/9] fix: sider fixes --- .../south_africa_vat_settings.js | 7 ++-- .../vat_audit_report/vat_audit_report.py | 34 +++++++++---------- erpnext/regional/south_africa/setup.py | 2 +- 3 files changed, 21 insertions(+), 22 deletions(-) diff --git a/erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.js b/erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.js index 3f0b466d8258..7d4ef12a9a97 100644 --- a/erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.js +++ b/erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.js @@ -8,18 +8,17 @@ frappe.ui.form.on('South Africa VAT Settings', { filters: { country: "South Africa", } - } + }; }); frm.set_query("account", "vat_accounts", function(doc, cdt, cdn) { var row = locals[cdt][cdn]; - console.log(row); return { filters: { company: frm.doc.company, account_type: "Tax", is_group: 0 } - } - }) + }; + }); } }); diff --git a/erpnext/regional/report/vat_audit_report/vat_audit_report.py b/erpnext/regional/report/vat_audit_report/vat_audit_report.py index e66212689ee7..6918aa1e642c 100644 --- a/erpnext/regional/report/vat_audit_report/vat_audit_report.py +++ b/erpnext/regional/report/vat_audit_report/vat_audit_report.py @@ -2,10 +2,10 @@ # For license information, please see license.txt from __future__ import unicode_literals -import frappe, json +import frappe +import json from frappe import _ -from frappe.utils import flt, formatdate, now_datetime, getdate -from datetime import date +from frappe.utils import formatdate def execute(filters=None): return VATAuditReport(filters).run() @@ -39,7 +39,7 @@ def run(self): return self.columns, self.data def get_sa_vat_accounts(self): - self.sa_vat_accounts = frappe.get_list("South Africa VAT Account", \ + self.sa_vat_accounts = frappe.get_list("South Africa VAT Account", filters = {"parent":self.filters.company}, pluck="account") if not self.sa_vat_accounts and not frappe.flags.in_test and not frappe.flags.in_migrate: frappe.throw(_("Please set VAT Accounts in South Africa VAT Settings")) @@ -84,7 +84,7 @@ def get_invoice_items(self,doctype): def get_items_based_on_tax_rate(self,doctype): self.items_based_on_tax_rate = frappe._dict() - self.tax_doctype = "Purchase Taxes and Charges" if doctype=="Purchase Invoice" \ + self.tax_doctype = "Purchase Taxes and Charges" if doctype=="Purchase Invoice" \ else "Sales Taxes and Charges" self.tax_details = frappe.db.sql(""" SELECT @@ -125,10 +125,10 @@ def get_item_amount_map(self, parent, item_code, taxes): item_amount_map = self.item_tax_rate.setdefault(parent, {}) \ .setdefault(item_code, []) amount_dict = { - "tax_rate": tax_rate, - "gross_amount": gross_amount, - "tax_amount": tax_amount, - "net_amount": net_amount + "tax_rate": tax_rate, + "gross_amount": gross_amount, + "tax_amount": tax_amount, + "net_amount": net_amount } item_amount_map.append(amount_dict) @@ -139,19 +139,19 @@ def get_conditions(self): for opts in (("company", " and company=%(company)s"), ("from_date", " and posting_date>=%(from_date)s"), ("to_date", " and posting_date<=%(to_date)s")): - if self.filters.get(opts[0]): - conditions += opts[1] + if self.filters.get(opts[0]): + conditions += opts[1] return conditions def get_data(self,doctype): consolidated_data = self.get_consolidated_data() - section_name = _("Purchases ") if doctype == "Purchase Invoice" else _("Sales ") + section_name = _("Purchases") if doctype == "Purchase Invoice" else _("Sales") for rate, section in consolidated_data.items(): rate = int(rate) - label = frappe.bold(_("Standard Rate ") + section_name + str(rate) + "%") - section_head = {"posting_date": label } + label = frappe.bold(_("Standard Rate ") + section_name + " " + str(rate) + "%") + section_head = {"posting_date": label} total_gross = total_tax = total_net = 0 self.data.append(section_head) for row in section.get("data"): @@ -178,11 +178,11 @@ def get_consolidated_data(self): for item in items: row = {} item_details = self.item_tax_rate.get(inv).get(item) - row["account"] = inv_data.get("account") + row["account"] = inv_data.get("account") row["posting_date"] = formatdate(inv_data.get("posting_date"), 'dd-mm-yyyy') row["invoice_number"] = inv - row["party"] = inv_data.get("party") - row["remarks"] = inv_data.get("remarks") + row["party"] = inv_data.get("party") + row["remarks"] = inv_data.get("remarks") row["gross_amount"]= item_details[0].get("gross_amount") row["tax_amount"]= item_details[0].get("tax_amount") row["net_amount"]= item_details[0].get("net_amount") diff --git a/erpnext/regional/south_africa/setup.py b/erpnext/regional/south_africa/setup.py index 65e9f5bc330f..2c44c6e6b17d 100644 --- a/erpnext/regional/south_africa/setup.py +++ b/erpnext/regional/south_africa/setup.py @@ -3,7 +3,7 @@ from __future__ import unicode_literals -import frappe, os, json +# import frappe, os, json from frappe.permissions import add_permission, update_permission_property def setup(): From c5d7a13513ea46d5e72ad643cec3d06cb156d760 Mon Sep 17 00:00:00 2001 From: Anuja Pawar Date: Wed, 14 Jul 2021 15:49:22 +0530 Subject: [PATCH 3/9] fix: sider & translation fixes --- .../south_africa_vat_settings/south_africa_vat_settings.js | 1 - erpnext/regional/report/vat_audit_report/vat_audit_report.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.js b/erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.js index 7d4ef12a9a97..393f4ec10e32 100644 --- a/erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.js +++ b/erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.js @@ -11,7 +11,6 @@ frappe.ui.form.on('South Africa VAT Settings', { }; }); frm.set_query("account", "vat_accounts", function(doc, cdt, cdn) { - var row = locals[cdt][cdn]; return { filters: { company: frm.doc.company, diff --git a/erpnext/regional/report/vat_audit_report/vat_audit_report.py b/erpnext/regional/report/vat_audit_report/vat_audit_report.py index 6918aa1e642c..5633b64bfb8d 100644 --- a/erpnext/regional/report/vat_audit_report/vat_audit_report.py +++ b/erpnext/regional/report/vat_audit_report/vat_audit_report.py @@ -150,7 +150,7 @@ def get_data(self,doctype): for rate, section in consolidated_data.items(): rate = int(rate) - label = frappe.bold(_("Standard Rate ") + section_name + " " + str(rate) + "%") + label = frappe.bold(_("Standard Rate") + " " + section_name + " " + str(rate) + "%") section_head = {"posting_date": label} total_gross = total_tax = total_net = 0 self.data.append(section_head) From b43974a9a6d072b511d8f9f77e7c312e74fd9546 Mon Sep 17 00:00:00 2001 From: Anuja Pawar Date: Wed, 14 Jul 2021 20:37:48 +0530 Subject: [PATCH 4/9] fix: more sider fixes --- .../south_africa_vat_settings/south_africa_vat_settings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.js b/erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.js index 393f4ec10e32..e37a61ac853c 100644 --- a/erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.js +++ b/erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.js @@ -10,7 +10,7 @@ frappe.ui.form.on('South Africa VAT Settings', { } }; }); - frm.set_query("account", "vat_accounts", function(doc, cdt, cdn) { + frm.set_query("account", "vat_accounts", function() { return { filters: { company: frm.doc.company, From abc63cffa838fb83b565411bfa61cfdc8018ed79 Mon Sep 17 00:00:00 2001 From: Anuja Pawar Date: Mon, 26 Jul 2021 18:11:38 +0530 Subject: [PATCH 5/9] fix: suggested changes --- .../vat_audit_report/vat_audit_report.py | 33 ++++++++++--------- erpnext/regional/south_africa/setup.py | 2 +- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/erpnext/regional/report/vat_audit_report/vat_audit_report.py b/erpnext/regional/report/vat_audit_report/vat_audit_report.py index 5633b64bfb8d..feb2a1620d49 100644 --- a/erpnext/regional/report/vat_audit_report/vat_audit_report.py +++ b/erpnext/regional/report/vat_audit_report/vat_audit_report.py @@ -40,7 +40,7 @@ def run(self): def get_sa_vat_accounts(self): self.sa_vat_accounts = frappe.get_list("South Africa VAT Account", - filters = {"parent":self.filters.company}, pluck="account") + filters = {"parent": self.filters.company}, pluck="account") if not self.sa_vat_accounts and not frappe.flags.in_test and not frappe.flags.in_migrate: frappe.throw(_("Please set VAT Accounts in South Africa VAT Settings")) @@ -144,7 +144,7 @@ def get_conditions(self): return conditions - def get_data(self,doctype): + def get_data(self, doctype): consolidated_data = self.get_consolidated_data() section_name = _("Purchases") if doctype == "Purchase Invoice" else _("Sales") @@ -173,20 +173,21 @@ def get_data(self,doctype): def get_consolidated_data(self): consolidated_data_map={} for inv, inv_data in self.invoices.items(): - for rate, items in self.items_based_on_tax_rate.get(inv).items(): - consolidated_data_map.setdefault(rate, {"data": []}) - for item in items: - row = {} - item_details = self.item_tax_rate.get(inv).get(item) - row["account"] = inv_data.get("account") - row["posting_date"] = formatdate(inv_data.get("posting_date"), 'dd-mm-yyyy') - row["invoice_number"] = inv - row["party"] = inv_data.get("party") - row["remarks"] = inv_data.get("remarks") - row["gross_amount"]= item_details[0].get("gross_amount") - row["tax_amount"]= item_details[0].get("tax_amount") - row["net_amount"]= item_details[0].get("net_amount") - consolidated_data_map[rate]["data"].append(row) + if self.items_based_on_tax_rate.get(inv): + for rate, items in self.items_based_on_tax_rate.get(inv).items(): + consolidated_data_map.setdefault(rate, {"data": []}) + for item in items: + row = {} + item_details = self.item_tax_rate.get(inv).get(item) + row["account"] = inv_data.get("account") + row["posting_date"] = formatdate(inv_data.get("posting_date"), 'dd-mm-yyyy') + row["invoice_number"] = inv + row["party"] = inv_data.get("party") + row["remarks"] = inv_data.get("remarks") + row["gross_amount"]= item_details[0].get("gross_amount") + row["tax_amount"]= item_details[0].get("tax_amount") + row["net_amount"]= item_details[0].get("net_amount") + consolidated_data_map[rate]["data"].append(row) return consolidated_data_map diff --git a/erpnext/regional/south_africa/setup.py b/erpnext/regional/south_africa/setup.py index 2c44c6e6b17d..a4335bddef2e 100644 --- a/erpnext/regional/south_africa/setup.py +++ b/erpnext/regional/south_africa/setup.py @@ -6,7 +6,7 @@ # import frappe, os, json from frappe.permissions import add_permission, update_permission_property -def setup(): +def setup(company=None, patch=True): add_permissions() def add_permissions(): From 5dcd5e48e7f84ed9cdcfb2a01d57a7ad5b4b7e76 Mon Sep 17 00:00:00 2001 From: Anuja Date: Thu, 29 Jul 2021 00:54:48 +0530 Subject: [PATCH 6/9] fix: fixed zero tax rate issue by adding custom field --- .../vat_audit_report/vat_audit_report.py | 57 ++++++++++--------- erpnext/regional/south_africa/setup.py | 17 ++++++ 2 files changed, 47 insertions(+), 27 deletions(-) diff --git a/erpnext/regional/report/vat_audit_report/vat_audit_report.py b/erpnext/regional/report/vat_audit_report/vat_audit_report.py index feb2a1620d49..bce8586720d5 100644 --- a/erpnext/regional/report/vat_audit_report/vat_audit_report.py +++ b/erpnext/regional/report/vat_audit_report/vat_audit_report.py @@ -20,10 +20,10 @@ def __init__(self, filters=None): def run(self): self.get_sa_vat_accounts() + self.get_columns() for doctype in self.doctypes: - self.get_columns(doctype) self.select_columns = """ - name as invoice_number, + name as voucher_no, posting_date, remarks""" columns = ", supplier as party, credit_to as account" if doctype=="Purchase Invoice" \ else ", customer as party, debit_to as account" @@ -44,7 +44,7 @@ def get_sa_vat_accounts(self): if not self.sa_vat_accounts and not frappe.flags.in_test and not frappe.flags.in_migrate: frappe.throw(_("Please set VAT Accounts in South Africa VAT Settings")) - def get_invoice_data(self,doctype): + def get_invoice_data(self, doctype): conditions = self.get_conditions() self.invoices = frappe._dict() @@ -62,9 +62,9 @@ def get_invoice_data(self,doctype): where_conditions=conditions), self.filters, as_dict=1) for d in invoice_data: - self.invoices.setdefault(d.invoice_number, d) + self.invoices.setdefault(d.voucher_no, d) - def get_invoice_items(self,doctype): + def get_invoice_items(self, doctype): self.invoice_items = frappe._dict() self.item_tax_rate = frappe._dict() @@ -82,7 +82,7 @@ def get_invoice_items(self,doctype): sum((i.get('taxable_value', 0) or i.get('base_net_amount', 0)) for i in items if i.item_code == d.item_code and i.parent == d.parent)) - def get_items_based_on_tax_rate(self,doctype): + def get_items_based_on_tax_rate(self, doctype): self.items_based_on_tax_rate = frappe._dict() self.tax_doctype = "Purchase Taxes and Charges" if doctype=="Purchase Invoice" \ else "Sales Taxes and Charges" @@ -107,6 +107,9 @@ def get_items_based_on_tax_rate(self,doctype): else: continue for item_code, taxes in item_wise_tax_detail.items(): + is_zero_rated = frappe.get_value("Item", item_code, "is_zero_rated") + if taxes[0] == 0 and not is_zero_rated: + continue tax_rate, item_amount_map = self.get_item_amount_map(parent, item_code, taxes) if tax_rate is not None: @@ -145,12 +148,12 @@ def get_conditions(self): return conditions def get_data(self, doctype): - consolidated_data = self.get_consolidated_data() + consolidated_data = self.get_consolidated_data(doctype) section_name = _("Purchases") if doctype == "Purchase Invoice" else _("Sales") for rate, section in consolidated_data.items(): rate = int(rate) - label = frappe.bold(_("Standard Rate") + " " + section_name + " " + str(rate) + "%") + label = frappe.bold(section_name + "- " + "Rate" + " " + str(rate) + "%") section_head = {"posting_date": label} total_gross = total_tax = total_net = 0 self.data.append(section_head) @@ -170,7 +173,7 @@ def get_data(self, doctype): self.data.append(total) self.data.append({}) - def get_consolidated_data(self): + def get_consolidated_data(self, doctype): consolidated_data_map={} for inv, inv_data in self.invoices.items(): if self.items_based_on_tax_rate.get(inv): @@ -181,8 +184,8 @@ def get_consolidated_data(self): item_details = self.item_tax_rate.get(inv).get(item) row["account"] = inv_data.get("account") row["posting_date"] = formatdate(inv_data.get("posting_date"), 'dd-mm-yyyy') - row["invoice_number"] = inv - row["party"] = inv_data.get("party") + row["voucher_type"] = doctype + row["voucher_no"] = inv row["remarks"] = inv_data.get("remarks") row["gross_amount"]= item_details[0].get("gross_amount") row["tax_amount"]= item_details[0].get("tax_amount") @@ -191,7 +194,7 @@ def get_consolidated_data(self): return consolidated_data_map - def get_columns(self,doctype): + def get_columns(self): self.columns = [ { "fieldname": "posting_date", @@ -204,44 +207,44 @@ def get_columns(self,doctype): "label": "Account", "fieldtype": "Link", "options": "Account", - "width": 140 + "width": 150 }, { - "fieldname": "invoice_number", - "label": "Reference", - "fieldtype": "Link", - "options": doctype, - "width": 140 + "fieldname": "voucher_type", + "label": "Voucher Type", + "fieldtype": "Data", + "width": 140, + "hidden": 1 }, { - "fieldname": "party", - "label": "Party", - "fieldtype": "Link", - "options": "Supplier" if doctype == "Purchase Invoice" else "Customer", - "width": 140 + "fieldname": "voucher_no", + "label": "Reference", + "fieldtype": "Dynamic Link", + "options": "voucher_type", + "width": 150 }, { "fieldname": "remarks", "label": "Details", "fieldtype": "Data", - "width": 140 + "width": 150 }, { "fieldname": "net_amount", "label": "Net Amount", "fieldtype": "Currency", - "width": 140 + "width": 150 }, { "fieldname": "tax_amount", "label": "Tax Amount", "fieldtype": "Currency", - "width": 140 + "width": 150 }, { "fieldname": "gross_amount", "label": "Gross Amount", "fieldtype": "Currency", - "width": 140 + "width": 150 }, ] diff --git a/erpnext/regional/south_africa/setup.py b/erpnext/regional/south_africa/setup.py index a4335bddef2e..ac783b848848 100644 --- a/erpnext/regional/south_africa/setup.py +++ b/erpnext/regional/south_africa/setup.py @@ -4,11 +4,28 @@ from __future__ import unicode_literals # import frappe, os, json +from frappe.custom.doctype.custom_field.custom_field import create_custom_fields from frappe.permissions import add_permission, update_permission_property def setup(company=None, patch=True): add_permissions() +def make_custom_fields(update=True): + is_zero_rated = dict(fieldname='is_zero_rated', label='Is Zero Rated', + fieldtype='Check', fetch_from='item_code.is_zero_rated', + insert_after='description', print_hide=1) + custom_fields = { + 'Item': [ + dict(fieldname='is_zero_rated', label='Is Zero Rated', + fieldtype='Check', insert_after='item_group', + print_hide=1) + ], + 'Sales Invoice Item': is_zero_rated, + 'Purchase Invoice Item': is_zero_rated + } + + create_custom_fields(custom_fields, update=update) + def add_permissions(): """Add Permissions for South Africa VAT Settings and South Africa VAT Account""" for doctype in ('South Africa VAT Settings', 'South Africa VAT Account'): From f9fc3bbfa83ac3d90ce3636cc2fae4f319c4a340 Mon Sep 17 00:00:00 2001 From: Anuja Date: Mon, 2 Aug 2021 12:54:30 +0530 Subject: [PATCH 7/9] fix: added patch for custom field and minor fixes --- erpnext/patches.txt | 1 + .../add_custom_field_for_south_africa.py | 13 +++++++++ .../vat_audit_report/vat_audit_report.py | 28 +++++++++++-------- 3 files changed, 31 insertions(+), 11 deletions(-) create mode 100644 erpnext/patches/v13_0/add_custom_field_for_south_africa.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index a029627ab123..942acd59a53e 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -297,3 +297,4 @@ erpnext.patches.v13_0.update_level_in_bom #1234sswef erpnext.patches.v13_0.add_missing_fg_item_for_stock_entry erpnext.patches.v13_0.update_subscription_status_in_memberships erpnext.patches.v13_0.update_amt_in_work_order_required_items +erpnext.patches.v13_0.add_custom_field_for_south_africa diff --git a/erpnext/patches/v13_0/add_custom_field_for_south_africa.py b/erpnext/patches/v13_0/add_custom_field_for_south_africa.py new file mode 100644 index 000000000000..73e9af9e4b9b --- /dev/null +++ b/erpnext/patches/v13_0/add_custom_field_for_south_africa.py @@ -0,0 +1,13 @@ +# Copyright (c) 2020, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe +from erpnext.regional.south_africa.setup import make_custom_fields + +def execute(): + company = frappe.get_all('Company', filters = {'country': 'South Africa'}) + if not company: + return + + make_custom_fields() diff --git a/erpnext/regional/report/vat_audit_report/vat_audit_report.py b/erpnext/regional/report/vat_audit_report/vat_audit_report.py index bce8586720d5..0c716cdab241 100644 --- a/erpnext/regional/report/vat_audit_report/vat_audit_report.py +++ b/erpnext/regional/report/vat_audit_report/vat_audit_report.py @@ -55,7 +55,7 @@ def get_invoice_data(self, doctype): `tab{doctype}` WHERE docstatus = 1 {where_conditions} - and is_opening = 'No' + and is_opening = "No" ORDER BY posting_date DESC """.format(select_columns=self.select_columns, doctype=doctype, @@ -66,26 +66,31 @@ def get_invoice_data(self, doctype): def get_invoice_items(self, doctype): self.invoice_items = frappe._dict() - self.item_tax_rate = frappe._dict() items = frappe.db.sql(""" SELECT - item_code, parent, taxable_value, base_net_amount, item_tax_rate + item_code, parent, taxable_value, base_net_amount, is_zero_rated FROM `tab%s Item` WHERE parent in (%s) - """ % (doctype, ', '.join(['%s']*len(self.invoices))), tuple(self.invoices), as_dict=1) + """ % (doctype, ", ".join(["%s"]*len(self.invoices))), tuple(self.invoices), as_dict=1) for d in items: if d.item_code not in self.invoice_items.get(d.parent, {}): - self.invoice_items.setdefault(d.parent, {}).setdefault(d.item_code, - sum((i.get('taxable_value', 0) or i.get('base_net_amount', 0)) for i in items - if i.item_code == d.item_code and i.parent == d.parent)) + self.invoice_items.setdefault(d.parent, {}).setdefault(d.item_code, {}) \ + .setdefault("net_amount", sum((i.get("taxable_value", 0) \ + or i.get("base_net_amount", 0)) for i in items \ + if (i.item_code == d.item_code and i.parent == d.parent))) + + self.invoice_items.setdefault(d.parent, {}).setdefault(d.item_code, {}) \ + .setdefault("is_zero_rated", d.is_zero_rated) def get_items_based_on_tax_rate(self, doctype): self.items_based_on_tax_rate = frappe._dict() + self.item_tax_rate = frappe._dict() self.tax_doctype = "Purchase Taxes and Charges" if doctype=="Purchase Invoice" \ else "Sales Taxes and Charges" + self.tax_details = frappe.db.sql(""" SELECT parent, account_head, item_wise_tax_detail, base_tax_amount_after_discount_amount @@ -96,7 +101,7 @@ def get_items_based_on_tax_rate(self, doctype): and parent in (%s) ORDER BY account_head - """ % (self.tax_doctype, '%s', ', '.join(['%s']*len(self.invoices.keys()))), + """ % (self.tax_doctype, "%s", ", ".join(["%s"]*len(self.invoices.keys()))), tuple([doctype] + list(self.invoices.keys()))) for parent, account, item_wise_tax_detail, tax_amount in self.tax_details: @@ -107,7 +112,8 @@ def get_items_based_on_tax_rate(self, doctype): else: continue for item_code, taxes in item_wise_tax_detail.items(): - is_zero_rated = frappe.get_value("Item", item_code, "is_zero_rated") + is_zero_rated = self.invoice_items.get(parent).get(item_code).get("is_zero_rated") + #to skip items with non-zero tax rate in multiple rows if taxes[0] == 0 and not is_zero_rated: continue tax_rate, item_amount_map = self.get_item_amount_map(parent, item_code, taxes) @@ -121,7 +127,7 @@ def get_items_based_on_tax_rate(self, doctype): continue def get_item_amount_map(self, parent, item_code, taxes): - net_amount = abs(self.invoice_items.get(parent).get(item_code)) + net_amount = self.invoice_items.get(parent).get(item_code).get("net_amount") tax_rate = taxes[0] tax_amount = taxes[1] gross_amount = net_amount + tax_amount @@ -183,7 +189,7 @@ def get_consolidated_data(self, doctype): row = {} item_details = self.item_tax_rate.get(inv).get(item) row["account"] = inv_data.get("account") - row["posting_date"] = formatdate(inv_data.get("posting_date"), 'dd-mm-yyyy') + row["posting_date"] = formatdate(inv_data.get("posting_date"), "dd-mm-yyyy") row["voucher_type"] = doctype row["voucher_no"] = inv row["remarks"] = inv_data.get("remarks") From f2ee1155c0e398ec85094c1c7e27c047ba9e4a95 Mon Sep 17 00:00:00 2001 From: Anuja Date: Mon, 2 Aug 2021 23:13:21 +0530 Subject: [PATCH 8/9] fix: sider fixes --- erpnext/patches/v13_0/add_custom_field_for_south_africa.py | 2 +- .../regional/report/vat_audit_report/vat_audit_report.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/patches/v13_0/add_custom_field_for_south_africa.py b/erpnext/patches/v13_0/add_custom_field_for_south_africa.py index 73e9af9e4b9b..f882fdedf383 100644 --- a/erpnext/patches/v13_0/add_custom_field_for_south_africa.py +++ b/erpnext/patches/v13_0/add_custom_field_for_south_africa.py @@ -3,7 +3,7 @@ from __future__ import unicode_literals import frappe -from erpnext.regional.south_africa.setup import make_custom_fields +from erpnext.regional.south_africa.setup import make_custom_fields def execute(): company = frappe.get_all('Company', filters = {'country': 'South Africa'}) diff --git a/erpnext/regional/report/vat_audit_report/vat_audit_report.py b/erpnext/regional/report/vat_audit_report/vat_audit_report.py index 0c716cdab241..a9c3858bb13b 100644 --- a/erpnext/regional/report/vat_audit_report/vat_audit_report.py +++ b/erpnext/regional/report/vat_audit_report/vat_audit_report.py @@ -78,9 +78,9 @@ def get_invoice_items(self, doctype): for d in items: if d.item_code not in self.invoice_items.get(d.parent, {}): self.invoice_items.setdefault(d.parent, {}).setdefault(d.item_code, {}) \ - .setdefault("net_amount", sum((i.get("taxable_value", 0) \ - or i.get("base_net_amount", 0)) for i in items \ - if (i.item_code == d.item_code and i.parent == d.parent))) + .setdefault("net_amount", sum((i.get("taxable_value", 0) + or i.get("base_net_amount", 0)) for i in items + if(i.item_code == d.item_code and i.parent == d.parent))) self.invoice_items.setdefault(d.parent, {}).setdefault(d.item_code, {}) \ .setdefault("is_zero_rated", d.is_zero_rated) From 4e1a205c119012752e2eba60a7550b2ed36f4e95 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 3 Aug 2021 14:58:45 +0530 Subject: [PATCH 9/9] fix: Optimize item updation --- .../report/vat_audit_report/vat_audit_report.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/erpnext/regional/report/vat_audit_report/vat_audit_report.py b/erpnext/regional/report/vat_audit_report/vat_audit_report.py index a9c3858bb13b..f45ba01dea55 100644 --- a/erpnext/regional/report/vat_audit_report/vat_audit_report.py +++ b/erpnext/regional/report/vat_audit_report/vat_audit_report.py @@ -77,13 +77,10 @@ def get_invoice_items(self, doctype): """ % (doctype, ", ".join(["%s"]*len(self.invoices))), tuple(self.invoices), as_dict=1) for d in items: if d.item_code not in self.invoice_items.get(d.parent, {}): - self.invoice_items.setdefault(d.parent, {}).setdefault(d.item_code, {}) \ - .setdefault("net_amount", sum((i.get("taxable_value", 0) - or i.get("base_net_amount", 0)) for i in items - if(i.item_code == d.item_code and i.parent == d.parent))) - - self.invoice_items.setdefault(d.parent, {}).setdefault(d.item_code, {}) \ - .setdefault("is_zero_rated", d.is_zero_rated) + self.invoice_items.setdefault(d.parent, {}).setdefault(d.item_code, { + 'net_amount': 0.0}) + self.invoice_items[d.parent][d.item_code]['net_amount'] += d.get('taxable_value', 0) or d.get('base_net_amount', 0) + self.invoice_items[d.parent][d.item_code]['is_zero_rated'] = d.is_zero_rated def get_items_based_on_tax_rate(self, doctype): self.items_based_on_tax_rate = frappe._dict()