Skip to content

Commit

Permalink
fix: add GST Details and allow cess non advol in Bill of Entry (#1866)
Browse files Browse the repository at this point in the history
* fix: hsn-summary-of-inward-supply

* fix: add gst details field in boe

* fix: add function in hooks

* fix: calcualte gst details in bill of entry

* fix: removed additional lone

* fix: add patches and tests

* fix: patches

* fix: remove patch

* fix: tested patches and test_cases

* fix: test_cases

* fix: linters

* fix: changes as per review

* fix: set gst_treatment

* fix: allow cess non advol in bill of entry

* fix: changes to patch as per new fields

* fix: changes as per review

* fix: changes as per merge

* chore: rename var, remove unavailable code

---------

Co-authored-by: ljain112 <ljain112@gmail.com>
Co-authored-by: Smit Vora <smitvora203@gmail.com>
(cherry picked from commit 4af06b2)

# Conflicts:
#	india_compliance/gst_india/overrides/transaction.py
  • Loading branch information
Sanket322 authored and mergify[bot] committed Mar 21, 2024
1 parent a0fcbe2 commit f31eed8
Show file tree
Hide file tree
Showing 10 changed files with 643 additions and 63 deletions.
42 changes: 33 additions & 9 deletions india_compliance/gst_india/doctype/bill_of_entry/bill_of_entry.js
Expand Up @@ -115,13 +115,13 @@ frappe.ui.form.on("Bill of Entry Taxes", {

async charge_type(frm, cdt, cdn) {
const row = locals[cdt][cdn];
if (row.charge_type === "On Net Total") {
await frm.taxes_controller.set_item_wise_tax_rates(null, cdn);
frm.taxes_controller.update_tax_amount(cdt, cdn);
} else {
if (!row.charge_type || row.charge_type === "Actual") {
row.rate = 0;
row.item_wise_tax_rates = "{}";
frm.refresh_field("taxes");
} else {
await frm.taxes_controller.set_item_wise_tax_rates(null, cdn);
frm.taxes_controller.update_tax_amount(cdt, cdn);
}
},

Expand Down Expand Up @@ -301,8 +301,8 @@ class TaxesController {

async update_tax_rate(cdt, cdn) {
const row = locals[cdt][cdn];
if (row.charge_type === "Actual") row.rate = 0;
else if (row.charge_type === "On Net Total") {
if (!row.charge_type || row.charge_type === "Actual") row.rate = 0;
else {
this.update_item_wise_tax_rates(row);
await this.update_tax_amount(cdt, cdn);
}
Expand All @@ -323,16 +323,26 @@ class TaxesController {
else taxes = this.frm.doc.taxes;

taxes.forEach(async row => {
if (row.charge_type !== "On Net Total") return;
if (!row.charge_type || row.charge_type === "Actual") return;

let tax_amount = 0;

const tax_amount = this.get_tax_on_net_total(row);
if (row.charge_type === "On Net Total") {
tax_amount = this.get_tax_on_net_total(row);
}

if (row.charge_type == "On Item Quantity") {
tax_amount = this.get_tax_on_item_quantity(row);
}

// update if tax amount is changed manually
if (tax_amount !== row.tax_amount) {
row.tax_amount = tax_amount;
}

if (frappe.flags.round_off_applicable_accounts?.includes(row.account_head)) {
if (
frappe.flags.round_off_applicable_accounts?.includes(row.account_head)
) {
row.tax_amount = Math.round(row.tax_amount);
}
});
Expand Down Expand Up @@ -365,4 +375,18 @@ class TaxesController {
return total + (item.taxable_value * item_wise_tax_rates[item.name]) / 100;
}, 0);
}

get_tax_on_item_quantity(tax_row) {
/**
* This method is used to calculate the tax amount on item quntity (cess non advol)
* based on the item wise tax rates and item quantity.
*
* @param {object} tax_row - Tax row object.
*/

const item_wise_tax_rates = JSON.parse(tax_row.item_wise_tax_rates || "{}");
return this.frm.doc.items.reduce((total, item) => {
return total + (item.qty * item_wise_tax_rates[item.name]);
}, 0);
}
}
160 changes: 140 additions & 20 deletions india_compliance/gst_india/doctype/bill_of_entry/bill_of_entry.py
Expand Up @@ -7,7 +7,7 @@
from frappe import _
from frappe.model.document import Document
from frappe.model.mapper import get_mapped_doc
from frappe.utils import today
from frappe.utils import flt, today
import erpnext
from erpnext.accounts.general_ledger import make_gl_entries, make_reverse_gl_entries
from erpnext.controllers.accounts_controller import AccountsController
Expand All @@ -18,9 +18,76 @@
update_regional_gl_entries,
update_valuation_rate,
)
from india_compliance.gst_india.overrides.transaction import (
ItemGSTDetails,
ItemGSTTreatment,
validate_charge_type_for_cess_non_advol_accounts,
)
from india_compliance.gst_india.utils import get_gst_accounts_by_type


class BOEGSTDetails(ItemGSTDetails):
def set_item_wise_tax_details(self):
tax_details = frappe._dict()
item_map = {}

for row in self.doc.get("items"):
key = row.name
item_map[key] = row
tax_details[key] = self.item_defaults.copy()
tax_details[key]["count"] += 1

for row in self.doc.taxes:
if (
not row.tax_amount
or not row.item_wise_tax_rates
or row.account_head not in self.gst_account_map
):
continue

account_type = self.gst_account_map[row.account_head]
tax = account_type[:-8]
tax_rate_field = f"{tax}_rate"
tax_amount_field = f"{tax}_amount"

item_wise_tax_rates = json.loads(row.item_wise_tax_rates)

# update item taxes
for row_name in item_wise_tax_rates:
if row_name not in tax_details:
# Do not compute if Item is not present in Item table
# There can be difference in Item Table and Item Wise Tax Details
continue

item_taxes = tax_details[row_name]
tax_rate = item_wise_tax_rates.get(row_name)
precision = self.precision.get(tax_amount_field)
item = item_map.get(row_name)

multiplier = (
item.qty if tax == "cess_non_advol" else item.taxable_value / 100
)

# cases when charge type == "Actual"
if not tax_rate:
continue

tax_amount = flt(tax_rate * multiplier, precision)
item_taxes[tax_rate_field] = tax_rate
item_taxes[tax_amount_field] += tax_amount

self.item_tax_details = tax_details

def get_item_key(self, item):
return item.name


def update_gst_details(doc, method=None):
# TODO: add item tax template validation post exclude from GST
ItemGSTTreatment().set(doc)
BOEGSTDetails().update(doc)


class BillofEntry(Document):
get_gl_dict = AccountsController.get_gl_dict

Expand All @@ -43,6 +110,12 @@ def onload(self):
def before_validate(self):
self.set_taxes_and_totals()

def before_save(self):
update_gst_details(self)

def before_submit(self):
update_gst_details(self)

def validate(self):
self.validate_purchase_invoice()
self.validate_taxes()
Expand Down Expand Up @@ -110,26 +183,33 @@ def set_total_taxes(self):

round_off_accounts = get_round_off_applicable_accounts(self.company, [])
for tax in self.taxes:
if tax.charge_type == "On Net Total":
tax.tax_amount = self.get_tax_amount(tax.item_wise_tax_rates)
if tax.charge_type == "Actual":
continue

tax.tax_amount = self.get_tax_amount(
tax.item_wise_tax_rates, tax.charge_type
)

if tax.account_head in round_off_accounts:
tax.tax_amount = round(tax.tax_amount, 0)
if tax.account_head in round_off_accounts:
tax.tax_amount = round(tax.tax_amount, 0)

total_taxes += tax.tax_amount
tax.total = self.total_taxable_value + total_taxes

self.total_taxes = total_taxes

def get_tax_amount(self, item_wise_tax_rates):
def get_tax_amount(self, item_wise_tax_rates, charge_type):
if isinstance(item_wise_tax_rates, str):
item_wise_tax_rates = json.loads(item_wise_tax_rates)

tax_amount = 0
for item in self.items:
tax_amount += (
item_wise_tax_rates.get(item.name, 0) * item.taxable_value / 100
multiplier = (
item.qty
if charge_type == "On Item Quantity"
else item.taxable_value / 100
)
tax_amount += flt(item_wise_tax_rates.get(item.name, 0)) * multiplier

return tax_amount

Expand Down Expand Up @@ -169,21 +249,59 @@ def validate_purchase_invoice(self):

def validate_taxes(self):
input_accounts = get_gst_accounts_by_type(self.company, "Input", throw=True)
taxable_value_map = {}

for row in self.get("items"):
taxable_value_map[row.name] = row.taxable_value

for tax in self.taxes:
if (
tax.account_head
in (input_accounts.igst_account, input_accounts.cess_account)
or not tax.tax_amount
):
if not tax.tax_amount:
continue

frappe.throw(
_(
"Row #{0}: Only Input IGST and CESS accounts are allowed in"
" Bill of Entry"
).format(tax.idx)
if tax.account_head not in (
input_accounts.igst_account,
input_accounts.cess_account,
input_accounts.cess_non_advol_account,
):
frappe.throw(
_(
"Row #{0}: Only Input IGST and CESS accounts are allowed in"
" Bill of Entry"
).format(tax.idx)
)

validate_charge_type_for_cess_non_advol_accounts(
[input_accounts.cess_non_advol_account], tax
)

if tax.charge_type == "Actual":

item_wise_tax_rates = json.loads(tax.item_wise_tax_rates)
if not item_wise_tax_rates:
frappe.throw(
_(
"Tax Row #{0}: Charge Type is set to Actual. However, this would"
" not compute item taxes, and your further reporting will be affected."
).format(tax.idx),
title=_("Invalid Charge Type"),
)

# validating total tax
total_tax = 0
for item, rate in item_wise_tax_rates.items():
item_taxable_value = taxable_value_map.get(item, 0)
total_tax += item_taxable_value * rate / 100

tax_difference = abs(total_tax - tax.tax_amount)

if tax_difference > 1:
frappe.throw(
_(
"Tax Row #{0}: Charge Type is set to Actual. However, Tax Amount {1}"
" is incorrect. Try setting the Charge Type to On Net Total."
).format(row.idx, tax.tax_amount)
)

def get_gl_entries(self):
# company_currency is required by get_gl_dict
# nosemgrep
Expand Down Expand Up @@ -255,8 +373,10 @@ def set_item_wise_tax_rates(self, item_name=None, tax_name=None):
item_tax_map = self.get_item_tax_map(tax_templates, tax_accounts)

for tax in taxes:
if tax.charge_type != "On Net Total":
tax.item_wise_tax_rates = "{}"
if tax.charge_type == "Actual":
if not tax.item_wise_tax_rates:
tax.item_wise_tax_rates = "{}"

continue

item_wise_tax_rates = (
Expand Down

0 comments on commit f31eed8

Please sign in to comment.