Merge pull request #31019 from frappe/mergify/bp/develop/pr-30978
fix: Multiple fixes in GST reporting (backport #30978)
This commit is contained in:
commit
a412d2f7e8
@ -321,6 +321,7 @@ doc_events = {
|
|||||||
"validate": [
|
"validate": [
|
||||||
"erpnext.regional.india.utils.validate_document_name",
|
"erpnext.regional.india.utils.validate_document_name",
|
||||||
"erpnext.regional.india.utils.update_taxable_values",
|
"erpnext.regional.india.utils.update_taxable_values",
|
||||||
|
"erpnext.regional.india.utils.validate_sez_and_export_invoices",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
"POS Invoice": {"on_submit": ["erpnext.regional.saudi_arabia.utils.create_qr_code"]},
|
"POS Invoice": {"on_submit": ["erpnext.regional.saudi_arabia.utils.create_qr_code"]},
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"actions": [],
|
||||||
"autoname": "field:hsn_code",
|
"autoname": "field:hsn_code",
|
||||||
"creation": "2017-06-21 10:48:56.422086",
|
"creation": "2017-06-21 10:48:56.422086",
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
@ -7,6 +8,7 @@
|
|||||||
"field_order": [
|
"field_order": [
|
||||||
"hsn_code",
|
"hsn_code",
|
||||||
"description",
|
"description",
|
||||||
|
"gst_rates",
|
||||||
"taxes"
|
"taxes"
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
@ -16,22 +18,37 @@
|
|||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "HSN Code",
|
"label": "HSN Code",
|
||||||
"reqd": 1,
|
"reqd": 1,
|
||||||
|
"show_days": 1,
|
||||||
|
"show_seconds": 1,
|
||||||
"unique": 1
|
"unique": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "description",
|
"fieldname": "description",
|
||||||
"fieldtype": "Small Text",
|
"fieldtype": "Small Text",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Description"
|
"label": "Description",
|
||||||
|
"show_days": 1,
|
||||||
|
"show_seconds": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "taxes",
|
"fieldname": "taxes",
|
||||||
"fieldtype": "Table",
|
"fieldtype": "Table",
|
||||||
"label": "Taxes",
|
"label": "Taxes",
|
||||||
"options": "Item Tax"
|
"options": "Item Tax",
|
||||||
|
"show_days": 1,
|
||||||
|
"show_seconds": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "gst_rates",
|
||||||
|
"fieldtype": "Table",
|
||||||
|
"label": "GST Rates",
|
||||||
|
"options": "HSN Tax Rate",
|
||||||
|
"show_days": 1,
|
||||||
|
"show_seconds": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"modified": "2019-11-01 11:18:59.556931",
|
"links": [],
|
||||||
|
"modified": "2022-05-11 13:42:27.286643",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Regional",
|
"module": "Regional",
|
||||||
"name": "GST HSN Code",
|
"name": "GST HSN Code",
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
frappe.ui.form.on('GST Settings', {
|
frappe.ui.form.on('GST Settings', {
|
||||||
refresh: function(frm) {
|
refresh: function(frm) {
|
||||||
frm.add_custom_button('Send GST Update Reminder', () => {
|
frm.add_custom_button(__('Send GST Update Reminder'), () => {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
return frappe.call({
|
return frappe.call({
|
||||||
method: 'erpnext.regional.doctype.gst_settings.gst_settings.send_reminder'
|
method: 'erpnext.regional.doctype.gst_settings.gst_settings.send_reminder'
|
||||||
@ -11,6 +11,12 @@ frappe.ui.form.on('GST Settings', {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
frm.add_custom_button(__('Sync HSN Codes'), () => {
|
||||||
|
frappe.call({
|
||||||
|
"method": "erpnext.regional.doctype.gst_settings.gst_settings.update_hsn_codes"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
$(frm.fields_dict.gst_summary.wrapper).empty().html(
|
$(frm.fields_dict.gst_summary.wrapper).empty().html(
|
||||||
`<table class="table table-bordered">
|
`<table class="table table-bordered">
|
||||||
<tbody>
|
<tbody>
|
||||||
|
@ -2,13 +2,14 @@
|
|||||||
# For license information, please see license.txt
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
|
||||||
|
import json
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.contacts.doctype.contact.contact import get_default_contact
|
from frappe.contacts.doctype.contact.contact import get_default_contact
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe.utils import date_diff, get_url, nowdate
|
from frappe.utils import date_diff, flt, get_url, nowdate
|
||||||
|
|
||||||
|
|
||||||
class EmailMissing(frappe.ValidationError):
|
class EmailMissing(frappe.ValidationError):
|
||||||
@ -129,3 +130,31 @@ def _send_gstin_reminder(party_type, party, default_email_id=None, sent_to=None)
|
|||||||
)
|
)
|
||||||
|
|
||||||
return email_id
|
return email_id
|
||||||
|
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def update_hsn_codes():
|
||||||
|
frappe.enqueue(enqueue_update)
|
||||||
|
frappe.msgprint(_("HSN/SAC Code sync started, this may take a few minutes..."))
|
||||||
|
|
||||||
|
|
||||||
|
def enqueue_update():
|
||||||
|
with open(os.path.join(os.path.dirname(__file__), "hsn_code_data.json"), "r") as f:
|
||||||
|
hsn_codes = json.loads(f.read())
|
||||||
|
|
||||||
|
for hsn_code in hsn_codes:
|
||||||
|
try:
|
||||||
|
hsn_code_doc = frappe.get_doc("GST HSN Code", hsn_code.get("hsn_code"))
|
||||||
|
hsn_code_doc.set("gst_rates", [])
|
||||||
|
for rate in hsn_code.get("gst_rates"):
|
||||||
|
hsn_code_doc.append(
|
||||||
|
"gst_rates",
|
||||||
|
{
|
||||||
|
"minimum_taxable_value": flt(hsn_code.get("minimum_taxable_value")),
|
||||||
|
"tax_rate": flt(rate.get("tax_rate")),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
hsn_code_doc.save()
|
||||||
|
except Exception as e:
|
||||||
|
pass
|
||||||
|
144352
erpnext/regional/doctype/gst_settings/hsn_code_data.json
Normal file
144352
erpnext/regional/doctype/gst_settings/hsn_code_data.json
Normal file
File diff suppressed because it is too large
Load Diff
0
erpnext/regional/doctype/hsn_tax_rate/__init__.py
Normal file
0
erpnext/regional/doctype/hsn_tax_rate/__init__.py
Normal file
39
erpnext/regional/doctype/hsn_tax_rate/hsn_tax_rate.json
Normal file
39
erpnext/regional/doctype/hsn_tax_rate/hsn_tax_rate.json
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
{
|
||||||
|
"actions": [],
|
||||||
|
"allow_rename": 1,
|
||||||
|
"creation": "2022-05-11 13:32:42.534779",
|
||||||
|
"doctype": "DocType",
|
||||||
|
"editable_grid": 1,
|
||||||
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"minimum_taxable_value",
|
||||||
|
"tax_rate"
|
||||||
|
],
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"columns": 2,
|
||||||
|
"fieldname": "minimum_taxable_value",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Minimum Taxable Value"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"columns": 2,
|
||||||
|
"fieldname": "tax_rate",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Tax Rate"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"index_web_pages_for_search": 1,
|
||||||
|
"istable": 1,
|
||||||
|
"links": [],
|
||||||
|
"modified": "2022-05-15 15:37:56.152470",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "Regional",
|
||||||
|
"name": "HSN Tax Rate",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"permissions": [],
|
||||||
|
"sort_field": "modified",
|
||||||
|
"sort_order": "DESC"
|
||||||
|
}
|
9
erpnext/regional/doctype/hsn_tax_rate/hsn_tax_rate.py
Normal file
9
erpnext/regional/doctype/hsn_tax_rate/hsn_tax_rate.py
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
# import frappe
|
||||||
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
|
||||||
|
class HSNTaxRate(Document):
|
||||||
|
pass
|
@ -840,6 +840,30 @@ def get_gst_accounts(
|
|||||||
return gst_accounts
|
return gst_accounts
|
||||||
|
|
||||||
|
|
||||||
|
def validate_sez_and_export_invoices(doc, method):
|
||||||
|
country = frappe.get_cached_value("Company", doc.company, "country")
|
||||||
|
|
||||||
|
if country != "India":
|
||||||
|
return
|
||||||
|
|
||||||
|
if (
|
||||||
|
doc.get("gst_category") in ("SEZ", "Overseas")
|
||||||
|
and doc.get("export_type") == "Without Payment of Tax"
|
||||||
|
):
|
||||||
|
gst_accounts = get_gst_accounts(doc.company)
|
||||||
|
|
||||||
|
for tax in doc.get("taxes"):
|
||||||
|
for tax in doc.get("taxes"):
|
||||||
|
if (
|
||||||
|
tax.account_head
|
||||||
|
in gst_accounts.get("igst_account", [])
|
||||||
|
+ gst_accounts.get("sgst_account", [])
|
||||||
|
+ gst_accounts.get("cgst_account", [])
|
||||||
|
and tax.tax_amount_after_discount_amount
|
||||||
|
):
|
||||||
|
frappe.throw(_("GST cannot be applied on SEZ or Export invoices without payment of tax"))
|
||||||
|
|
||||||
|
|
||||||
def validate_reverse_charge_transaction(doc, method):
|
def validate_reverse_charge_transaction(doc, method):
|
||||||
country = frappe.get_cached_value("Company", doc.company, "country")
|
country = frappe.get_cached_value("Company", doc.company, "country")
|
||||||
|
|
||||||
@ -887,6 +911,8 @@ def validate_reverse_charge_transaction(doc, method):
|
|||||||
|
|
||||||
frappe.throw(msg)
|
frappe.throw(msg)
|
||||||
|
|
||||||
|
doc.eligibility_for_itc = "ITC on Reverse Charge"
|
||||||
|
|
||||||
|
|
||||||
def update_itc_availed_fields(doc, method):
|
def update_itc_availed_fields(doc, method):
|
||||||
country = frappe.get_cached_value("Company", doc.company, "country")
|
country = frappe.get_cached_value("Company", doc.company, "country")
|
||||||
|
@ -226,7 +226,10 @@ class Gstr1Report(object):
|
|||||||
taxable_value += abs(net_amount)
|
taxable_value += abs(net_amount)
|
||||||
elif (
|
elif (
|
||||||
not tax_rate
|
not tax_rate
|
||||||
and self.filters.get("type_of_business") == "EXPORT"
|
and (
|
||||||
|
self.filters.get("type_of_business") == "EXPORT"
|
||||||
|
or invoice_details.get("gst_category") == "SEZ"
|
||||||
|
)
|
||||||
and invoice_details.get("export_type") == "Without Payment of Tax"
|
and invoice_details.get("export_type") == "Without Payment of Tax"
|
||||||
):
|
):
|
||||||
taxable_value += abs(net_amount)
|
taxable_value += abs(net_amount)
|
||||||
@ -328,12 +331,14 @@ class Gstr1Report(object):
|
|||||||
def get_invoice_items(self):
|
def get_invoice_items(self):
|
||||||
self.invoice_items = frappe._dict()
|
self.invoice_items = frappe._dict()
|
||||||
self.item_tax_rate = frappe._dict()
|
self.item_tax_rate = frappe._dict()
|
||||||
|
self.item_hsn_map = frappe._dict()
|
||||||
self.nil_exempt_non_gst = {}
|
self.nil_exempt_non_gst = {}
|
||||||
|
|
||||||
|
# nosemgrep
|
||||||
items = frappe.db.sql(
|
items = frappe.db.sql(
|
||||||
"""
|
"""
|
||||||
select item_code, parent, taxable_value, base_net_amount, item_tax_rate, is_nil_exempt,
|
select item_code, parent, taxable_value, base_net_amount, item_tax_rate, is_nil_exempt,
|
||||||
is_non_gst from `tab%s Item`
|
gst_hsn_code, is_non_gst from `tab%s Item`
|
||||||
where parent in (%s)
|
where parent in (%s)
|
||||||
"""
|
"""
|
||||||
% (self.doctype, ", ".join(["%s"] * len(self.invoices))),
|
% (self.doctype, ", ".join(["%s"] * len(self.invoices))),
|
||||||
@ -343,6 +348,7 @@ class Gstr1Report(object):
|
|||||||
|
|
||||||
for d in items:
|
for d in items:
|
||||||
self.invoice_items.setdefault(d.parent, {}).setdefault(d.item_code, 0.0)
|
self.invoice_items.setdefault(d.parent, {}).setdefault(d.item_code, 0.0)
|
||||||
|
self.item_hsn_map.setdefault(d.item_code, d.gst_hsn_code)
|
||||||
self.invoice_items[d.parent][d.item_code] += d.get("taxable_value", 0) or d.get(
|
self.invoice_items[d.parent][d.item_code] += d.get("taxable_value", 0) or d.get(
|
||||||
"base_net_amount", 0
|
"base_net_amount", 0
|
||||||
)
|
)
|
||||||
@ -367,6 +373,8 @@ class Gstr1Report(object):
|
|||||||
self.nil_exempt_non_gst[d.parent][2] += d.get("taxable_value", 0)
|
self.nil_exempt_non_gst[d.parent][2] += d.get("taxable_value", 0)
|
||||||
|
|
||||||
def get_items_based_on_tax_rate(self):
|
def get_items_based_on_tax_rate(self):
|
||||||
|
hsn_wise_tax_rate = get_hsn_wise_tax_rates()
|
||||||
|
|
||||||
self.tax_details = frappe.db.sql(
|
self.tax_details = frappe.db.sql(
|
||||||
"""
|
"""
|
||||||
select
|
select
|
||||||
@ -427,7 +435,7 @@ class Gstr1Report(object):
|
|||||||
alert=True,
|
alert=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Build itemised tax for export invoices where tax table is blank
|
# Build itemised tax for export invoices where tax table is blank (Export and SEZ Invoices)
|
||||||
for invoice, items in self.invoice_items.items():
|
for invoice, items in self.invoice_items.items():
|
||||||
if (
|
if (
|
||||||
invoice not in self.items_based_on_tax_rate
|
invoice not in self.items_based_on_tax_rate
|
||||||
@ -435,7 +443,17 @@ class Gstr1Report(object):
|
|||||||
and self.invoices.get(invoice, {}).get("export_type") == "Without Payment of Tax"
|
and self.invoices.get(invoice, {}).get("export_type") == "Without Payment of Tax"
|
||||||
and self.invoices.get(invoice, {}).get("gst_category") in ("Overseas", "SEZ")
|
and self.invoices.get(invoice, {}).get("gst_category") in ("Overseas", "SEZ")
|
||||||
):
|
):
|
||||||
self.items_based_on_tax_rate.setdefault(invoice, {}).setdefault(0, items.keys())
|
self.items_based_on_tax_rate.setdefault(invoice, {})
|
||||||
|
for item_code in items.keys():
|
||||||
|
hsn_code = self.item_hsn_map.get(item_code)
|
||||||
|
tax_rate = 0
|
||||||
|
taxable_value = items.get(item_code)
|
||||||
|
for rates in hsn_wise_tax_rate.get(hsn_code):
|
||||||
|
if taxable_value > rates.get("minimum_taxable_value"):
|
||||||
|
tax_rate = rates.get("tax_rate")
|
||||||
|
|
||||||
|
self.items_based_on_tax_rate[invoice].setdefault(tax_rate, [])
|
||||||
|
self.items_based_on_tax_rate[invoice][tax_rate].append(item_code)
|
||||||
|
|
||||||
def get_columns(self):
|
def get_columns(self):
|
||||||
self.other_columns = []
|
self.other_columns = []
|
||||||
@ -728,7 +746,7 @@ def get_json(filters, report_name, data):
|
|||||||
|
|
||||||
elif filters["type_of_business"] == "EXPORT":
|
elif filters["type_of_business"] == "EXPORT":
|
||||||
for item in report_data[:-1]:
|
for item in report_data[:-1]:
|
||||||
res.setdefault(item["export_type"], []).append(item)
|
res.setdefault(item["export_type"], {}).setdefault(item["invoice_number"], []).append(item)
|
||||||
|
|
||||||
out = get_export_json(res)
|
out = get_export_json(res)
|
||||||
gst_json["exp"] = out
|
gst_json["exp"] = out
|
||||||
@ -918,11 +936,21 @@ def get_export_json(res):
|
|||||||
for exp_type in res:
|
for exp_type in res:
|
||||||
exp_item, inv = {"exp_typ": exp_type, "inv": []}, []
|
exp_item, inv = {"exp_typ": exp_type, "inv": []}, []
|
||||||
|
|
||||||
for row in res[exp_type]:
|
for number, invoice in res[exp_type].items():
|
||||||
inv_item = get_basic_invoice_detail(row)
|
inv_item = get_basic_invoice_detail(invoice[0])
|
||||||
inv_item["itms"] = [
|
inv_item["itms"] = []
|
||||||
{"txval": flt(row["taxable_value"], 2), "rt": row["rate"] or 0, "iamt": 0, "csamt": 0}
|
|
||||||
]
|
for item in invoice:
|
||||||
|
inv_item["itms"].append(
|
||||||
|
{
|
||||||
|
"txval": flt(item["taxable_value"], 2),
|
||||||
|
"rt": flt(item["rate"]),
|
||||||
|
"iamt": flt((item["taxable_value"] * flt(item["rate"])) / 100.0, 2)
|
||||||
|
if exp_type != "WOPAY"
|
||||||
|
else 0,
|
||||||
|
"csamt": (flt(item.get("cess_amount"), 2) or 0),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
inv.append(inv_item)
|
inv.append(inv_item)
|
||||||
|
|
||||||
@ -1060,7 +1088,6 @@ def get_rate_and_tax_details(row, gstin):
|
|||||||
|
|
||||||
# calculate tax amount added
|
# calculate tax amount added
|
||||||
tax = flt((row["taxable_value"] * rate) / 100.0, 2)
|
tax = flt((row["taxable_value"] * rate) / 100.0, 2)
|
||||||
frappe.errprint([tax, tax / 2])
|
|
||||||
if row.get("billing_address_gstin") and gstin[0:2] == row["billing_address_gstin"][0:2]:
|
if row.get("billing_address_gstin") and gstin[0:2] == row["billing_address_gstin"][0:2]:
|
||||||
itm_det.update({"camt": flt(tax / 2.0, 2), "samt": flt(tax / 2.0, 2)})
|
itm_det.update({"camt": flt(tax / 2.0, 2), "samt": flt(tax / 2.0, 2)})
|
||||||
else:
|
else:
|
||||||
@ -1136,3 +1163,26 @@ def get_company_gstins(company):
|
|||||||
address_list = [""] + [d.gstin for d in addresses]
|
address_list = [""] + [d.gstin for d in addresses]
|
||||||
|
|
||||||
return address_list
|
return address_list
|
||||||
|
|
||||||
|
|
||||||
|
def get_hsn_wise_tax_rates():
|
||||||
|
hsn_wise_tax_rate = {}
|
||||||
|
gst_hsn_code = frappe.qb.DocType("GST HSN Code")
|
||||||
|
hsn_tax_rates = frappe.qb.DocType("HSN Tax Rate")
|
||||||
|
|
||||||
|
hsn_code_data = (
|
||||||
|
frappe.qb.from_(gst_hsn_code)
|
||||||
|
.inner_join(hsn_tax_rates)
|
||||||
|
.on(gst_hsn_code.name == hsn_tax_rates.parent)
|
||||||
|
.select(gst_hsn_code.hsn_code, hsn_tax_rates.tax_rate, hsn_tax_rates.minimum_taxable_value)
|
||||||
|
.orderby(hsn_tax_rates.minimum_taxable_value)
|
||||||
|
.run(as_dict=1)
|
||||||
|
)
|
||||||
|
|
||||||
|
for d in hsn_code_data:
|
||||||
|
hsn_wise_tax_rate.setdefault(d.hsn_code, [])
|
||||||
|
hsn_wise_tax_rate[d.hsn_code].append(
|
||||||
|
{"minimum_taxable_value": d.minimum_taxable_value, "tax_rate": d.tax_rate}
|
||||||
|
)
|
||||||
|
|
||||||
|
return hsn_wise_tax_rate
|
||||||
|
@ -221,7 +221,7 @@ def get_merged_data(columns, data):
|
|||||||
result = []
|
result = []
|
||||||
|
|
||||||
for row in data:
|
for row in data:
|
||||||
key = row[0] + "-" + str(row[4])
|
key = row[0] + "-" + row[2] + "-" + str(row[4])
|
||||||
merged_hsn_dict.setdefault(key, {})
|
merged_hsn_dict.setdefault(key, {})
|
||||||
for i, d in enumerate(columns):
|
for i, d in enumerate(columns):
|
||||||
if d["fieldtype"] not in ("Int", "Float", "Currency"):
|
if d["fieldtype"] not in ("Int", "Float", "Currency"):
|
||||||
|
Loading…
Reference in New Issue
Block a user