Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: gst breakup table independent of tax breakup from erpnext #1644

Merged
merged 5 commits into from Feb 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
34 changes: 34 additions & 0 deletions india_compliance/gst_india/constants/custom_fields.py
Expand Up @@ -143,6 +143,23 @@
"print_hide": 1,
"default": 0,
},
{
"fieldname": "custom_gst_breakup",
"label": "GST Breakup",
"fieldtype": "Section Break",
"insert_after": "other_charges_calculation",
"collapsible": 1,
},
{
"fieldname": "custom_gst_breakup_table",
"label": "GST Breakup Table",
"fieldtype": "Long Text",
"insert_after": "custom_gst_breakup",
"is_virtual": 1,
"read_only": 1,
"allow_on_submit": 1,
"translatable": 0,
},
],
# Sales - Export with GST Payment
# POS Invoice excluded, since it isn't designed for exports
Expand Down Expand Up @@ -231,6 +248,23 @@
"length": 15,
"translatable": 0,
},
{
"fieldname": "custom_gst_breakup",
"label": "GST Breakup",
"fieldtype": "Section Break",
"insert_after": "other_charges_calculation",
"collapsible": 1,
},
{
"fieldname": "custom_gst_breakup_table",
"label": "GST Breakup Table",
"fieldtype": "Long Text",
"insert_after": "custom_gst_breakup",
"is_virtual": 1,
"read_only": 1,
"allow_on_submit": 1,
"translatable": 0,
},
],
# Sales Shipping Fields
("Delivery Note", "Sales Invoice"): [
Expand Down
87 changes: 13 additions & 74 deletions india_compliance/gst_india/overrides/transaction.py
Expand Up @@ -4,10 +4,6 @@
from frappe import _, bold
from frappe.utils import cint, flt
from erpnext.controllers.accounts_controller import get_taxes_and_charges
from erpnext.controllers.taxes_and_totals import (
get_itemised_tax,
get_itemised_taxable_amount,
)

from india_compliance.gst_india.constants import (
GST_TAX_TYPES,
Expand Down Expand Up @@ -47,6 +43,19 @@
}


def set_gst_breakup(doc, method=None):
if doc.doctype not in DOCTYPES_WITH_GST_DETAIL:
return

doc_meta = frappe.get_meta(doc.doctype)
if not (doc_meta and doc_meta.get_field("custom_gst_breakup_table")):
return

doc.custom_gst_breakup_table = frappe.render_template(
"templates/gst_breakup.html", dict(doc=doc)
)


def update_taxable_values(doc, valid_accounts):
if doc.doctype not in DOCTYPES_WITH_GST_DETAIL:
return
Expand Down Expand Up @@ -590,76 +599,6 @@ def validate_overseas_gst_category(doc, method=None):
frappe.throw(_("Cannot set GST Category to SEZ / Overseas in POS Invoice"))


def get_itemised_tax_breakup_header(item_doctype, tax_accounts):
if is_hsn_wise_breakup_needed(item_doctype):
return [_("HSN/SAC"), _("Taxable Amount")] + tax_accounts
else:
return [_("Item"), _("Taxable Amount")] + tax_accounts


def get_itemised_tax_breakup_data(doc):
itemised_tax = get_itemised_tax(doc.taxes)
taxable_amounts = get_itemised_taxable_amount(doc.items)

if is_hsn_wise_breakup_needed(doc.doctype + " Item"):
return get_hsn_wise_breakup(doc, itemised_tax, taxable_amounts)

return get_item_wise_breakup(itemised_tax, taxable_amounts)


def get_item_wise_breakup(itemised_tax, taxable_amounts):
itemised_tax_data = []
for item_code, taxes in itemised_tax.items():
itemised_tax_data.append(
frappe._dict(
{
"item": item_code,
"taxable_amount": taxable_amounts.get(item_code),
**taxes,
}
)
)

return itemised_tax_data


def get_hsn_wise_breakup(doc, itemised_tax, taxable_amounts):
hsn_tax_data = frappe._dict()
considered_items = set()
for item in doc.items:
item_code = item.item_code or item.item_name
if item_code in considered_items:
continue

hsn_code = item.gst_hsn_code
tax_row = itemised_tax.get(item_code, {})
tax_rate = next(iter(tax_row.values()), {}).get("tax_rate", 0)

hsn_tax = hsn_tax_data.setdefault(
(hsn_code, tax_rate),
frappe._dict({"item": hsn_code, "taxable_amount": 0}),
)

hsn_tax.taxable_amount += taxable_amounts.get(item_code, 0)
for tax_account, tax_details in tax_row.items():
hsn_tax.setdefault(
tax_account, frappe._dict({"tax_rate": 0, "tax_amount": 0})
)
hsn_tax[tax_account].tax_rate = tax_details.get("tax_rate")
hsn_tax[tax_account].tax_amount += tax_details.get("tax_amount")

considered_items.add(item_code)

return list(hsn_tax_data.values())


def is_hsn_wise_breakup_needed(doctype):
if frappe.get_meta(doctype).has_field("gst_hsn_code") and frappe.get_cached_value(
"GST Settings", None, "hsn_wise_tax_breakup"
):
return True


def get_regional_round_off_accounts(company, account_list):
country = frappe.get_cached_value("Company", company, "country")
if country != "India" or not frappe.get_cached_value(
Expand Down
105 changes: 105 additions & 0 deletions india_compliance/gst_india/utils/jinja.py
@@ -1,11 +1,16 @@
import base64
import json
from datetime import datetime
from io import BytesIO

import pyqrcode
from barcode import Code128
from barcode.writer import ImageWriter

import frappe
from frappe import scrub
from frappe.utils import flt

from india_compliance.gst_india.constants.e_waybill import (
SUB_SUPPLY_TYPES,
SUPPLY_TYPES,
Expand Down Expand Up @@ -147,3 +152,103 @@ def get_e_invoice_amount_fields(data, doc):
mandatory_fields.update(("CgstVal", "SgstVal"))

return get_fields_to_display(data, E_INVOICE_AMOUNT_FIELDS, mandatory_fields)


def get_gst_breakup(doc):
gst_breakup_data = GSTBreakup(doc).get()
return json.dumps(gst_breakup_data)


class GSTBreakup:
"""
Returns GST breakup data for the given document
Output could contain HSN/SAC wise breakup or Item wise breakup as per the GST Settings

example output:
[
{
"HSN/SAC": "1234",
"Taxable Amount": 1000,
"CGST": {
"tax_rate": 9,
"tax_amount": 90
},
"SGST": {
"tax_rate": 9,
"tax_amount": 90
}
}
]
"""

CESS_HEADERS = ["CESS", "CESS Non Advol"]

def __init__(self, doc):
self.doc = doc
self.tax_headers = ["IGST"] if is_inter_state_supply(doc) else ["CGST", "SGST"]
self.precision = doc.precision("tax_amount", "taxes")

if self.has_non_zero_cess():
self.tax_headers += self.CESS_HEADERS

self.needs_hsn_wise_breakup = self.is_hsn_wise_breakup_needed()

def get(self):
self.gst_breakup_data = {}

for item in self.doc.items:
gst_breakup_row = self.get_default_item_tax_row(item)
gst_breakup_row["Taxable Amount"] += flt(item.taxable_value, self.precision)

for tax_type in self.tax_headers:
_tax_type = scrub(tax_type)
tax_details = gst_breakup_row.setdefault(
tax_type,
{
"tax_rate": flt(item.get(f"{_tax_type}_rate", 0)),
"tax_amount": 0,
},
)

tax_details["tax_amount"] += flt(
item.get(f"{_tax_type}_amount", 0), self.precision
)

return list(self.gst_breakup_data.values())

def has_non_zero_cess(self):
if not self.doc.items:
return False

return any(
any(
getattr(item, f"{scrub(tax_type)}_amount", 0) != 0
for tax_type in self.CESS_HEADERS
)
for item in self.doc.items
)

def get_default_item_tax_row(self, item):
if self.needs_hsn_wise_breakup:
hsn_code = item.gst_hsn_code
tax_rates = [item.cgst_rate, item.sgst_rate, item.igst_rate]
tax_rate = next((rate for rate in tax_rates if rate != 0), 0)

return self.gst_breakup_data.setdefault(
(hsn_code, tax_rate), {"HSN/SAC": hsn_code, "Taxable Amount": 0}
)

else:
item_code = item.item_code or item.item_name
return self.gst_breakup_data.setdefault(
item_code, {"Item": item_code, "Taxable Amount": 0}
)

def is_hsn_wise_breakup_needed(self):
if not frappe.get_meta(self.doc.doctype + " Item").has_field("gst_hsn_code"):
return False

if not frappe.get_cached_value("GST Settings", None, "hsn_wise_tax_breakup"):
return False

return True
24 changes: 19 additions & 5 deletions india_compliance/hooks.py
Expand Up @@ -93,7 +93,10 @@
),
},
"Delivery Note": {
"onload": "india_compliance.gst_india.overrides.delivery_note.onload",
"onload": [
"india_compliance.gst_india.overrides.delivery_note.onload",
"india_compliance.gst_india.overrides.transaction.set_gst_breakup",
],
"before_save": "india_compliance.gst_india.overrides.transaction.update_gst_details",
"before_submit": "india_compliance.gst_india.overrides.transaction.update_gst_details",
"validate": (
Expand Down Expand Up @@ -121,7 +124,10 @@
"on_update_after_submit": "india_compliance.gst_india.overrides.payment_entry.on_update_after_submit",
},
"Purchase Invoice": {
"onload": "india_compliance.gst_india.overrides.purchase_invoice.onload",
"onload": [
"india_compliance.gst_india.overrides.purchase_invoice.onload",
"india_compliance.gst_india.overrides.transaction.set_gst_breakup",
],
"validate": "india_compliance.gst_india.overrides.purchase_invoice.validate",
"before_validate": (
"india_compliance.gst_india.overrides.transaction.before_validate"
Expand All @@ -136,6 +142,7 @@
"after_mapping": "india_compliance.gst_india.overrides.transaction.after_mapping",
},
"Purchase Order": {
"onload": "india_compliance.gst_india.overrides.transaction.set_gst_breakup",
"validate": (
"india_compliance.gst_india.overrides.transaction.validate_transaction"
),
Expand All @@ -146,6 +153,7 @@
"before_submit": "india_compliance.gst_india.overrides.transaction.update_gst_details",
},
"Purchase Receipt": {
"onload": "india_compliance.gst_india.overrides.transaction.set_gst_breakup",
"validate": (
"india_compliance.gst_india.overrides.transaction.validate_transaction"
),
Expand All @@ -161,7 +169,10 @@
"before_sl_preview": "india_compliance.gst_india.overrides.ineligible_itc.update_valuation_rate",
},
"Sales Invoice": {
"onload": "india_compliance.gst_india.overrides.sales_invoice.onload",
"onload": [
"india_compliance.gst_india.overrides.sales_invoice.onload",
"india_compliance.gst_india.overrides.transaction.set_gst_breakup",
],
"validate": "india_compliance.gst_india.overrides.sales_invoice.validate",
"before_save": "india_compliance.gst_india.overrides.transaction.update_gst_details",
"before_submit": "india_compliance.gst_india.overrides.transaction.update_gst_details",
Expand All @@ -173,6 +184,7 @@
"after_mapping": "india_compliance.gst_india.overrides.transaction.after_mapping",
},
"Sales Order": {
"onload": "india_compliance.gst_india.overrides.transaction.set_gst_breakup",
"validate": (
"india_compliance.gst_india.overrides.transaction.validate_transaction"
),
Expand All @@ -198,20 +210,23 @@
"before_submit": "india_compliance.gst_india.overrides.unreconcile_payment.before_submit",
},
"POS Invoice": {
"onload": "india_compliance.gst_india.overrides.transaction.set_gst_breakup",
"validate": (
"india_compliance.gst_india.overrides.transaction.validate_transaction"
),
"before_save": "india_compliance.gst_india.overrides.transaction.update_gst_details",
"before_submit": "india_compliance.gst_india.overrides.transaction.update_gst_details",
},
"Quotation": {
"onload": "india_compliance.gst_india.overrides.transaction.set_gst_breakup",
"validate": (
"india_compliance.gst_india.overrides.transaction.validate_transaction"
),
"before_save": "india_compliance.gst_india.overrides.transaction.update_gst_details",
"before_submit": "india_compliance.gst_india.overrides.transaction.update_gst_details",
},
"Supplier Quotation": {
"onload": "india_compliance.gst_india.overrides.transaction.set_gst_breakup",
"before_validate": (
"india_compliance.gst_india.overrides.transaction.before_validate"
),
Expand All @@ -237,8 +252,6 @@

regional_overrides = {
"India": {
"erpnext.controllers.taxes_and_totals.get_itemised_tax_breakup_header": "india_compliance.gst_india.overrides.transaction.get_itemised_tax_breakup_header",
"erpnext.controllers.taxes_and_totals.get_itemised_tax_breakup_data": "india_compliance.gst_india.overrides.transaction.get_itemised_tax_breakup_data",
"erpnext.controllers.taxes_and_totals.get_regional_round_off_accounts": (
"india_compliance.gst_india.overrides.transaction.get_regional_round_off_accounts"
),
Expand Down Expand Up @@ -285,6 +298,7 @@
"india_compliance.gst_india.utils.jinja.get_ewaybill_barcode",
"india_compliance.gst_india.utils.jinja.get_e_invoice_item_fields",
"india_compliance.gst_india.utils.jinja.get_e_invoice_amount_fields",
"india_compliance.gst_india.utils.jinja.get_gst_breakup",
],
}

Expand Down
2 changes: 1 addition & 1 deletion india_compliance/patches.txt
Expand Up @@ -3,7 +3,7 @@ execute:import frappe; frappe.delete_doc_if_exists("DocType", "GSTIN")

[post_model_sync]
india_compliance.patches.v14.set_default_for_overridden_accounts_setting
execute:from india_compliance.gst_india.setup import create_custom_fields; create_custom_fields() #41
execute:from india_compliance.gst_india.setup import create_custom_fields; create_custom_fields() #42
execute:from india_compliance.gst_india.setup import create_property_setters; create_property_setters() #7
india_compliance.patches.post_install.remove_old_fields
india_compliance.patches.post_install.update_company_gstin
Expand Down