Skip to content

Commit

Permalink
fix: add support for GL Repost for Purchase Invoice ITC Reversal (#1756)
Browse files Browse the repository at this point in the history
* fix: add support for gl repost for Purchase Invoice ITC Reversal

* fix: process futher to update stock ledger only if required

* fix: revert simplification of ineligible itc to fix repost item stock

* test: repost accounting ledger correctly with itc ineligibility

* refactor: check if debit entry is required before getting expense account

* test: enable repost settings for purchase invoice

* fix: repost purchases item valuation and its test cases
  • Loading branch information
vorasmit authored Feb 28, 2024
1 parent cea0a67 commit ef8921c
Show file tree
Hide file tree
Showing 6 changed files with 178 additions and 50 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from india_compliance.gst_india.overrides.ineligible_itc import (
update_landed_cost_voucher_for_gst_expense,
update_regional_gl_entries,
update_valuation_rate,
)
from india_compliance.gst_india.utils import get_gst_accounts_by_type

Expand Down Expand Up @@ -50,6 +51,7 @@ def validate(self):
self.validate_purchase_invoice()
self.validate_taxes()
self.reconciliation_status = "Unreconciled"
update_valuation_rate(self)

def on_submit(self):
gl_entries = self.get_gl_entries()
Expand Down
96 changes: 56 additions & 40 deletions india_compliance/gst_india/overrides/ineligible_itc.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,25 +58,16 @@ def update_valuation_rate(self):
# TODO: handle rounding off of gst amount from gst settings
self.update_item_valuation_rate(item, ineligible_tax_amount)

def update_stock_ledger_entries(self):
"""
Cancels and re-creates Stock Ledger Entries
This is done with new valuation rate
Reference: erpnext.stock.doctype.landed_cost_voucher.landed_cost_voucher.LandedCostVoucher.update_landed_cost
"""
self.doc.docstatus = 2
self.doc.update_stock_ledger()

self.doc.docstatus = 1
self.doc.update_stock_ledger()

def update_gl_entries(self, gl_entries):
self.update_valuation_rate()
self.update_stock_ledger_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()

if not self.doc.get("_has_ineligible_itc_items"):
return gl_entries

Expand Down Expand Up @@ -151,7 +142,13 @@ def make_gst_expense_entry(self, item):
)
)

if not self.is_debit_entry_required(item):
return

expense_account = self.get_item_expense_account(item)
if not expense_account:
return

against_account = self.get_against_account(item)
remarks = item.get("_remarks")

Expand Down Expand Up @@ -184,24 +181,40 @@ def reverse_stock_adjustment_entry(self, item):
This method reverses the Stock Adjustment Entry
"""
stock_account = self.get_item_expense_account(item)
cogs_account = self.doc.get_company_default("default_expense_account")
cogs_account = self.company.default_expense_account

ineligible_item_tax_amount = item.get("_ineligible_tax_amount", 0)

self.gl_entries.append(
self.doc.get_gl_dict(
{
"account": stock_account,
self.cr_or_dr: ineligible_item_tax_amount,
f"{self.cr_or_dr}_in_account_currency": ineligible_item_tax_amount,
"cost_center": item.cost_center or self.cost_center,
"remarks": item.get("_remarks"),
}
for entry in self.gl_entries:
if (
entry.get("account") != stock_account
or entry.get("cost_center") != item.cost_center
):
continue

entry[self.dr_or_cr] -= ineligible_item_tax_amount
entry[f"{self.dr_or_cr}_in_account_currency"] -= ineligible_item_tax_amount
break

else:
# FALLBACK: If Stock Account not found in GL Entries
self.gl_entries.append(
self.doc.get_gl_dict(
{
"account": stock_account,
self.cr_or_dr: ineligible_item_tax_amount,
f"{self.cr_or_dr}_in_account_currency": ineligible_item_tax_amount,
"cost_center": item.cost_center or self.cost_center,
"remarks": item.get("_remarks"),
}
)
)
)

for entry in self.gl_entries:
if entry.get("account") != cogs_account:
if (
entry.get("account") != cogs_account
or entry.get("cost_center") != item.cost_center
):
continue

entry[self.cr_or_dr] -= ineligible_item_tax_amount
Expand Down Expand Up @@ -290,6 +303,9 @@ def get_item_tax_amount(self, item, tax):

return abs(tax_amount)

def is_debit_entry_required(self, item):
return True

def update_asset_valuation_rate(self, item):
frappe.db.set_value(
"Asset",
Expand Down Expand Up @@ -321,7 +337,7 @@ def update_item_gl_entries(self, item):
if (item.get("_is_stock_item")) or item.get("is_fixed_asset"):
self.make_gst_expense_entry(item)

if self.doc.is_return and item.get("_is_stock_item"):
if item.get("_is_stock_item"):
self.reverse_stock_adjustment_entry(item)

def is_eligibility_restricted_due_to_pos(self):
Expand All @@ -348,20 +364,15 @@ def update_valuation_rate(self):

super().update_valuation_rate()

def update_stock_ledger_entries(self):
if not self.doc.update_stock:
return

super().update_stock_ledger_entries()

def update_item_gl_entries(self, item):
if self.doc.update_stock or self.is_expense_item(item):
self.make_gst_expense_entry(item)

self.reverse_input_taxes_entry(item)

if self.doc.is_return and item.get("_is_stock_item"):
self.reverse_stock_adjustment_entry(item)
def is_debit_entry_required(self, item):
# For Stock Entry / Fixed Asset in PI, Additional Debit is accounted automatically from valuation rates
return self.is_expense_item(item)

def is_expense_item(self, item):
"""
Expand Down Expand Up @@ -408,9 +419,6 @@ def update_valuation_rate(self):

super().update_valuation_rate()

def update_stock_ledger_entries(self):
return

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:
Expand Down Expand Up @@ -469,6 +477,14 @@ def update_landed_cost_voucher(self, landed_cost_voucher):
}


def update_valuation_rate(doc, method=None):
if doc.get("is_opening") == "Yes" or not is_indian_registered_company(doc):
return

if doc.doctype in DOCTYPE_MAPPING:
DOCTYPE_MAPPING[doc.doctype](doc).update_valuation_rate()


def update_regional_gl_entries(gl_entries, doc):
if doc.get("is_opening") == "Yes" or not is_indian_registered_company(doc):
return gl_entries
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import frappe


def before_submit(doc, method=None):
frappe.flags.through_repost_accounting_ledger = True
115 changes: 105 additions & 10 deletions india_compliance/gst_india/overrides/test_ineligible_itc.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import (
make_purchase_invoice,
)
from erpnext.stock.doctype.repost_item_valuation.repost_item_valuation import (
repost_entries,
)

from india_compliance.gst_india.doctype.bill_of_entry.bill_of_entry import (
make_bill_of_entry,
Expand Down Expand Up @@ -163,6 +166,81 @@ def test_purchase_invoice_with_update_stock(self):
{"Test Fixed Asset": 1000, "Test Ineligible Fixed Asset": 1178.82},
) # 999 + 179.82

# Repost Accounting Ledger
if not frappe.db.exists(
"Repost Allowed Types",
{
"document_type": "Purchase Invoice",
"parent": "Repost Accounting Ledger Settings",
},
):
settings = frappe.get_single("Repost Accounting Ledger Settings")
settings.append(
"allowed_types", {"document_type": "Purchase Invoice", "allowed": 1}
)
settings.save()

doc.items[4].expense_account = "Office Rent - _TIRC"
doc.items[5].expense_account = "Office Rent - _TIRC"

doc.save()
doc.repost_accounting_entries()

expected_entries = [
{"account": "Round Off - _TIRC", "debit": 0.28, "credit": 0.0},
{
"account": "GST Expense - _TIRC",
"debit": 369.72,
"credit": 369.72,
}, # 179.64 + 179.82 + 10.26
{
"account": "Input Tax SGST - _TIRC",
"debit": 427.86,
"credit": 184.86, # 369.72 / 2
},
{
"account": "Input Tax CGST - _TIRC",
"debit": 427.86,
"credit": 184.86,
},
{
"account": "Office Rent - _TIRC",
"debit": 2677.64, # 500 * 3 + 499 * 2 + 179.64
"credit": 0.0,
},
{
"account": "CWIP Account - _TIRC",
"debit": 2178.82,
"credit": 0.0,
}, # 1000 + 999 + 179.82
{
"account": "Stock In Hand - _TIRC",
"debit": 267.26,
"credit": 0.0,
}, # 20 * 5 + 19 * 3 + 100 * 1 + 10.26
{"account": "Creditors - _TIRC", "debit": 0.0, "credit": 5610.0},
]

self.assertGLEntry(doc.name, expected_entries)

# Repost Item Valuation
repost_doc = frappe.get_doc(
{
"doctype": "Repost Item Valuation",
"voucher_type": "Purchase Invoice",
"voucher_no": doc.name,
"status": "Queued",
}
)
repost_doc.save()
repost_doc.submit()

repost_entries()

status = frappe.db.get_value("Repost Item Valuation", repost_doc.name, "status")
self.assertEqual(status, "Completed")
self.assertGLEntry(doc.name, expected_entries)

def test_purchase_invoice_with_ineligible_pos(self):
transaction_details = {
"doctype": "Purchase Invoice",
Expand Down Expand Up @@ -272,6 +350,21 @@ def test_purchase_receipt_and_then_purchase_invoice(self):
{"Test Fixed Asset": 1000, "Test Ineligible Fixed Asset": 1178.82},
)

repost_doc = frappe.get_doc(
{
"doctype": "Repost Item Valuation",
"voucher_type": "Purchase Receipt",
"voucher_no": doc.name,
}
)
repost_doc.save()
repost_doc.submit()

repost_entries()

status = frappe.db.get_value("Repost Item Valuation", repost_doc.name, "status")
self.assertEqual(status, "Completed")

# Create Purchase Invoice
doc = make_purchase_invoice(doc.name)
doc.bill_no = "BILL-03"
Expand Down Expand Up @@ -768,7 +861,7 @@ def test_purchase_invoice_with_bill_of_entry(self):
def assertGLEntry(self, docname, expected_gl_entry):
gl_entries = frappe.get_all(
"GL Entry",
filters={"voucher_no": docname},
filters={"voucher_no": docname, "is_cancelled": 0},
fields=["account", "debit", "credit"],
)

Expand Down Expand Up @@ -833,7 +926,7 @@ def create_test_items():
"account_type": "Fixed Asset",
}
)
asset_account.insert()
asset_account.insert(ignore_if_duplicate=True)

asset_category = frappe.get_doc(
{
Expand All @@ -850,9 +943,11 @@ def create_test_items():
],
}
)
asset_category.insert()
asset_category.insert(ignore_if_duplicate=True)

frappe.get_doc({"doctype": "Location", "location_name": "Test Location"}).insert()
frappe.get_doc({"doctype": "Location", "location_name": "Test Location"}).insert(
ignore_if_duplicate=True
)

asset_item = {
"doctype": "Item",
Expand All @@ -879,31 +974,31 @@ def create_test_items():
}

# Stock Item
frappe.get_doc(stock_item).insert()
frappe.get_doc(stock_item).insert(ignore_if_duplicate=True)
frappe.get_doc(
{
**stock_item,
"item_code": "Test Ineligible Stock Item",
"is_ineligible_for_itc": 1,
}
).insert()
).insert(ignore_if_duplicate=True)

# Fixed Asset
frappe.get_doc(asset_item).insert()
frappe.get_doc(asset_item).insert(ignore_if_duplicate=True)
frappe.get_doc(
{
**asset_item,
"item_code": "Test Ineligible Fixed Asset",
"is_ineligible_for_itc": 1,
}
).insert()
).insert(ignore_if_duplicate=True)

# Service Item
frappe.get_doc(service_item).insert()
frappe.get_doc(service_item).insert(ignore_if_duplicate=True)
frappe.get_doc(
{
**service_item,
"item_code": "Test Ineligible Service Item",
"is_ineligible_for_itc": 1,
}
).insert()
).insert(ignore_if_duplicate=True)
9 changes: 9 additions & 0 deletions india_compliance/hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,10 @@
"before_save": "india_compliance.gst_india.overrides.transaction.update_gst_details",
"before_submit": [
"india_compliance.gst_india.overrides.transaction.update_gst_details",
"india_compliance.gst_india.overrides.ineligible_itc.update_valuation_rate",
],
"before_gl_preview": "india_compliance.gst_india.overrides.ineligible_itc.update_valuation_rate",
"before_sl_preview": "india_compliance.gst_india.overrides.ineligible_itc.update_valuation_rate",
"after_mapping": "india_compliance.gst_india.overrides.transaction.after_mapping",
},
"Purchase Order": {
Expand All @@ -164,7 +167,13 @@
"before_save": "india_compliance.gst_india.overrides.transaction.update_gst_details",
"before_submit": [
"india_compliance.gst_india.overrides.transaction.update_gst_details",
"india_compliance.gst_india.overrides.ineligible_itc.update_valuation_rate",
],
"before_gl_preview": "india_compliance.gst_india.overrides.ineligible_itc.update_valuation_rate",
"before_sl_preview": "india_compliance.gst_india.overrides.ineligible_itc.update_valuation_rate",
},
"Repost Accounting Ledger": {
"before_submit": "india_compliance.gst_india.overrides.repost_accounting_ledger.before_submit",
},
"Sales Invoice": {
"onload": [
Expand Down
Loading

0 comments on commit ef8921c

Please sign in to comment.