From aa2cba97808cdebb227c067504317e41922ded3a Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sun, 19 Apr 2026 07:57:13 +0000 Subject: [PATCH] fix: validate south africa company in vat audit report (backport #54030) (#54394) fix: validate south africa company in vat audit report (#54030) * fix: validate south africa company in vat audit report * fix: use qb to get invoice data * fix: validate company region in south africa vat settings (cherry picked from commit 1c65cc108828bfb166b64aa238135829ae366884) Co-authored-by: Ravibharathi <131471282+ravibharathi656@users.noreply.github.com> --- .../south_africa_vat_settings.py | 12 ++- .../vat_audit_report/vat_audit_report.js | 7 ++ .../vat_audit_report/vat_audit_report.py | 79 +++++++++---------- 3 files changed, 56 insertions(+), 42 deletions(-) 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 index aa3cd4b05af..bb1b67e6e07 100644 --- 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 @@ -1,9 +1,12 @@ # Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt -# import frappe +import frappe +from frappe import _ from frappe.model.document import Document +from erpnext import get_region + class SouthAfricaVATSettings(Document): # begin: auto-generated types @@ -22,4 +25,9 @@ class SouthAfricaVATSettings(Document): vat_accounts: DF.Table[SouthAfricaVATAccount] # end: auto-generated types - pass + def validate(self): + self.validate_company_region() + + def validate_company_region(self): + if self.company and get_region(self.company) != "South Africa": + frappe.throw(_("Company {0} is not in South Africa.").format(frappe.bold(self.company))) diff --git a/erpnext/regional/report/vat_audit_report/vat_audit_report.js b/erpnext/regional/report/vat_audit_report/vat_audit_report.js index de4fde596cd..b9aa11f8061 100644 --- a/erpnext/regional/report/vat_audit_report/vat_audit_report.js +++ b/erpnext/regional/report/vat_audit_report/vat_audit_report.js @@ -10,6 +10,13 @@ frappe.query_reports["VAT Audit Report"] = { options: "Company", reqd: 1, default: frappe.defaults.get_user_default("Company"), + get_query: function () { + return { + filters: { + country: "South Africa", + }, + }; + }, }, { fieldname: "from_date", 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 78fdc62e29c..fdabdb4e9e5 100644 --- a/erpnext/regional/report/vat_audit_report/vat_audit_report.py +++ b/erpnext/regional/report/vat_audit_report/vat_audit_report.py @@ -8,6 +8,7 @@ import frappe from frappe import _ from frappe.utils import formatdate, get_link_to_form +from erpnext import get_region from erpnext.accounts.report.item_wise_sales_register.item_wise_sales_register import get_tax_details_query @@ -23,19 +24,10 @@ class VATAuditReport: self.doctypes = ["Purchase Invoice", "Sales Invoice"] def run(self): + self.validate_company_region() self.get_sa_vat_accounts() self.get_columns() for doctype in self.doctypes: - self.select_columns = """ - 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" - ) - self.select_columns += columns - self.get_invoice_data(doctype) if self.invoices: @@ -45,6 +37,14 @@ class VATAuditReport: return self.columns, self.data + def validate_company_region(self): + if self.filters.company and get_region(self.filters.company) != "South Africa": + frappe.throw( + _( + "The company {0} is not in South Africa. VAT Audit Report is only available for companies in South Africa." + ).format(frappe.bold(self.filters.company)) + ) + def get_sa_vat_accounts(self): self.sa_vat_accounts = frappe.get_all( "South Africa VAT Account", filters={"parent": self.filters.company}, pluck="account" @@ -56,27 +56,38 @@ class VATAuditReport: frappe.throw(_("Please set VAT Accounts in {0}").format(link_to_settings)) def get_invoice_data(self, doctype): - conditions = self.get_conditions() self.invoices = frappe._dict() - - invoice_data = frappe.db.sql( - f""" - SELECT - {self.select_columns} - FROM - `tab{doctype}` - WHERE - docstatus = 1 {conditions} - and is_opening = 'No' - ORDER BY - posting_date DESC - """, - self.filters, - as_dict=1, + invoice_doctype = frappe.qb.DocType(doctype) + party_field = invoice_doctype.supplier if doctype == "Purchase Invoice" else invoice_doctype.customer + account_field = ( + invoice_doctype.credit_to if doctype == "Purchase Invoice" else invoice_doctype.debit_to ) - for d in invoice_data: - self.invoices.setdefault(d.voucher_no, d) + query = ( + frappe.qb.from_(invoice_doctype) + .select( + invoice_doctype.name.as_("voucher_no"), + invoice_doctype.posting_date, + invoice_doctype.remarks, + party_field.as_("party"), + account_field.as_("account"), + ) + .where(invoice_doctype.docstatus == 1) + .where(invoice_doctype.is_opening == "No") + .orderby(invoice_doctype.posting_date, order=frappe.qb.desc) + ) + + if self.filters.get("company"): + query = query.where(invoice_doctype.company == self.filters.company) + if self.filters.get("from_date"): + query = query.where(invoice_doctype.posting_date >= self.filters.from_date) + if self.filters.get("to_date"): + query = query.where(invoice_doctype.posting_date <= self.filters.to_date) + + invoice_data = query.run(as_dict=True) + + for row in invoice_data: + self.invoices.setdefault(row.voucher_no, row) def get_invoice_items(self, doctype): self.invoice_items = frappe._dict() @@ -129,18 +140,6 @@ class VATAuditReport: self.items_based_on_tax_rate[parent][row.rate]["net_amount"] += row.taxable_amount self.items_based_on_tax_rate[parent][row.rate]["gross_amount"] += row.amount + row.taxable_amount - 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(doctype) section_name = _("Purchases") if doctype == "Purchase Invoice" else _("Sales")