Skip to content

Commit

Permalink
Merge pull request #2299 from resilient-tech/mergify/bp/version-15-ho…
Browse files Browse the repository at this point in the history
…tfix/pr-2206

fix(ineligible itc): update GL's with valuation hook, refactor (backport #2206)
  • Loading branch information
mergify[bot] authored Jun 24, 2024
2 parents 2137e60 + 175b51a commit 22d5370
Show file tree
Hide file tree
Showing 8 changed files with 114 additions and 330 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -120,24 +120,33 @@ def get_itc_reversal_entries(self):
self.update_itc_reversal_from_bill_of_entry()

def update_itc_reversal_from_purchase_invoice(self):
self.update_itc_reversal_for_purchase_us_17_4()
self.update_itc_reversal_for_purchase_due_to_pos()

def update_itc_reversal_for_purchase_due_to_pos(self):
ineligible_credit = IneligibleITC(
self.company, self.gst_details.get("gstin"), self.month_no, self.year
).get_ineligible_itc_us_17_5_for_purchase(group_by="ineligibility_reason")
).get_for_purchase(
"ITC restricted due to PoS rules", group_by="ineligibility_reason"
)

ineligible_credit_due_to_pos = IneligibleITC(
self.company, self.gst_details.get("gstin"), self.month_no, self.year
).get_ineligible_itc_due_to_pos_for_purchase(group_by="ineligibility_reason")
self.process_ineligible_credit(ineligible_credit)

ineligible_credit.extend(ineligible_credit_due_to_pos)
def update_itc_reversal_for_purchase_us_17_4(self):
ineligible_credit = IneligibleITC(
self.company, self.gst_details.get("gstin"), self.month_no, self.year
).get_for_purchase(
"Ineligible As Per Section 17(5)", group_by="ineligibility_reason"
)

return self.process_ineligible_credit(ineligible_credit)
self.process_ineligible_credit(ineligible_credit)

def update_itc_reversal_from_bill_of_entry(self):
ineligible_credit = IneligibleITC(
self.company, self.gst_details.get("gstin"), self.month_no, self.year
).get_for_bill_of_entry()

return self.process_ineligible_credit(ineligible_credit)
self.process_ineligible_credit(ineligible_credit)

def process_ineligible_credit(self, ineligible_credit):
if not ineligible_credit:
Expand Down
104 changes: 48 additions & 56 deletions india_compliance/gst_india/overrides/ineligible_itc.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from collections import defaultdict

import frappe
from frappe import _
from frappe.utils import flt, get_link_to_form, rounded
from frappe.utils import flt, get_link_to_form
from erpnext.assets.doctype.asset.asset import (
get_asset_account,
is_cwip_accounting_enabled,
Expand Down Expand Up @@ -32,41 +34,27 @@ def update_valuation_rate(self):
- Only updates if its a stock item or fixed asset
- No updates for expense items
"""
self.doc._has_ineligible_itc_items = False
stock_items = self.doc.get_stock_items()

for item in self.doc.items:
if (
not self.is_eligibility_restricted_due_to_pos()
and not item.is_ineligible_for_itc
):
continue

self.update_ineligible_taxes(item)
self.update_item_ineligibility()

if item._ineligible_tax_amount:
self.doc._has_ineligible_itc_items = True
if not self.doc.get("_has_ineligible_itc_items"):
return

if item.item_code in stock_items and self.is_perpetual:
item._is_stock_item = True
for item in self.doc.items:
if not item.get("_ineligible_tax_amount"):
continue

if item.get("_is_stock_item") or item.get("is_fixed_asset"):
ineligible_tax_amount = item._ineligible_tax_amount
if self.doc.get("is_return"):
ineligible_tax_amount = -ineligible_tax_amount

# TODO: handle rounding off of gst amount from gst settings
self.update_item_valuation_rate(item, ineligible_tax_amount)

def update_gl_entries(self, gl_entries):
self.gl_entries = gl_entries

if (
frappe.flags.through_repost_accounting_ledger
or frappe.flags.through_repost_item_valuation
):
self.doc.update_valuation_rate()
self.update_valuation_rate()
self.update_item_ineligibility()

if not self.doc.get("_has_ineligible_itc_items"):
return gl_entries
Expand All @@ -84,6 +72,34 @@ def update_gl_entries(self, gl_entries):

self.update_item_gl_entries(item)

def update_item_ineligibility(self):
self.doc._has_ineligible_itc_items = False
stock_items = self.doc.get_stock_items()

self.tax_account_dict = {
row.gst_tax_type: row.account_head
for row in self.doc.taxes
if row.gst_tax_type
}

if not self.tax_account_dict:
return

for item in self.doc.items:
if (
not self.is_eligibility_restricted_due_to_pos()
and not item.is_ineligible_for_itc
):
continue

self.update_ineligible_taxes(item)

if item._ineligible_tax_amount:
self.doc._has_ineligible_itc_items = True

if item.item_code in stock_items and self.is_perpetual:
item._is_stock_item = True

def update_item_gl_entries(self, item):
return

Expand Down Expand Up @@ -272,39 +288,25 @@ def update_ineligible_taxes(self, item):
"Input SGST - FC": 50,
}
"""
ineligible_taxes = frappe._dict()
ineligible_taxes = defaultdict(float)
ineligible_tax_amount = 0

for tax in self.doc.taxes:
if tax.gst_tax_type not in GST_TAX_TYPES:
for tax_type in GST_TAX_TYPES:
tax_amount = abs(flt(item.get(f"{tax_type}_amount")))
tax_account = self.tax_account_dict.get(tax_type)

if not tax_amount:
continue

ineligible_taxes[tax.account_head] = self.get_item_tax_amount(item, tax)
ineligible_taxes[tax_account] += tax_amount
ineligible_tax_amount += tax_amount

item._ineligible_taxes = ineligible_taxes
item._ineligible_tax_amount = sum(ineligible_taxes.values())
item._ineligible_tax_amount = ineligible_tax_amount

def update_item_valuation_rate(self, item, ineligible_tax_amount):
item.valuation_rate += flt(ineligible_tax_amount / item.stock_qty, 2)

def get_item_tax_amount(self, item, tax):
"""
Returns proportionate item tax amount for each tax component
"""
tax_rate = rounded(
frappe.parse_json(tax.item_wise_tax_detail).get(
item.item_code or item.item_name
)[0],
3,
)

tax_amount = (
tax_rate * item.qty
if tax.charge_type == "On Item Quantity"
else tax_rate * item.taxable_value / 100
)

return abs(tax_amount)

def is_debit_entry_required(self, item):
return True

Expand Down Expand Up @@ -416,16 +418,6 @@ def update_valuation_rate(self):

super().update_valuation_rate()

def get_item_tax_amount(self, item, tax):
tax_rate = frappe.parse_json(tax.item_wise_tax_rates).get(item.name)
if tax_rate is None:
return 0

tax_rate = rounded(tax_rate, 3)
tax_amount = tax_rate * item.taxable_value / 100

return abs(tax_amount)

def update_item_valuation_rate(self, item, ineligible_tax_amount):
item.valuation_rate = ineligible_tax_amount

Expand Down
5 changes: 1 addition & 4 deletions india_compliance/gst_india/overrides/purchase_receipt.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,14 @@ def onload(doc, method=None):
if ignore_gst_validations(doc, throw=False):
return

doc.flags.ignore_mandatory = True
if (
validate_mandatory_fields(
doc, ("company_gstin", "place_of_supply", "gst_category")
doc, ("company_gstin", "place_of_supply", "gst_category"), throw=False
)
is False
):
return

doc.flags.ignore_mandatory = False

set_ineligibility_reason(doc, show_alert=False)


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,7 @@ def test_purchase_returns_with_update_stock(self):

doc = create_transaction(**transaction_details)
doc = make_return_doc("Purchase Invoice", doc.name)
doc.save()
doc.submit()

self.assertGLEntry(
Expand Down
5 changes: 4 additions & 1 deletion india_compliance/gst_india/overrides/transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ def is_indian_registered_company(doc):
return True


def validate_mandatory_fields(doc, fields, error_message=None):
def validate_mandatory_fields(doc, fields, error_message=None, throw=True):
if isinstance(fields, str):
fields = (fields,)

Expand All @@ -224,6 +224,9 @@ def validate_mandatory_fields(doc, fields, error_message=None):
if doc.flags.ignore_mandatory:
return False

if not throw:
return False

frappe.throw(
error_message.format(bold(_(doc.meta.get_label(field)))),
title=_("Missing Required Field"),
Expand Down
Loading

0 comments on commit 22d5370

Please sign in to comment.