fix: exclude non-stock item's tax value from stock valuation

(cherry picked from commit 13e1f84eb1)
This commit is contained in:
Rohit Waghchaure
2026-06-16 15:03:14 +05:30
committed by Mergify
parent 3243e4237a
commit 3d6c7f4512
2 changed files with 72 additions and 3 deletions

View File

@@ -417,20 +417,21 @@ class BuyingController(SubcontractingController):
stock_and_asset_items_qty, stock_and_asset_items_amount = 0, 0
last_item_idx = 1
for d in self.get("items"):
if d.item_code and d.item_code in stock_and_asset_items:
if d.item_code:
stock_and_asset_items_qty += flt(d.qty)
stock_and_asset_items_amount += flt(d.base_net_amount)
last_item_idx = d.idx
tax_accounts, total_valuation_amount, total_actual_tax_amount = self.get_tax_details()
remaining_amount = total_actual_tax_amount
for i, item in enumerate(self.get("items")):
if item.item_code and (item.qty or item.get("rejected_qty")):
item_tax_amount, actual_tax_amount = 0.0, 0.0
if i == (last_item_idx - 1):
item_tax_amount = total_valuation_amount
actual_tax_amount = total_actual_tax_amount
actual_tax_amount = remaining_amount
else:
# calculate item tax amount
item_tax_amount = self.get_item_tax_amount(item, tax_accounts)
@@ -443,7 +444,8 @@ class BuyingController(SubcontractingController):
stock_and_asset_items_amount,
stock_and_asset_items_qty,
)
total_actual_tax_amount -= actual_tax_amount
remaining_amount -= actual_tax_amount
# This code is required here to calculate the correct valuation for stock items
if item.item_code not in stock_and_asset_items:

View File

@@ -1333,6 +1333,73 @@ class TestPurchaseReceipt(ERPNextTestSuite):
pr.delete()
def test_valuation_tax_distribution_with_non_stock_item(self):
"""A "Valuation and Total" tax is distributed across all items by net amount, but only
stock/asset items can carry valuation. For a document with 2 stock items + 1 service
item (each net 100) and a 30 valuation tax, each item's share is 10; only the two stock
items capitalize their share (20 total), so the non-stock item's 10 share must not be
capitalized onto the stock items."""
company = "_Test Company with perpetual inventory"
warehouse = "Stores - TCP1"
stock_item1 = make_item(properties={"is_stock_item": 1}).name
stock_item2 = make_item(properties={"is_stock_item": 1}).name
service_item = make_item(properties={"is_stock_item": 0}).name
pr = frappe.new_doc("Purchase Receipt")
pr.company = company
pr.supplier = "_Test Supplier"
pr.currency = "INR"
# Order matters: stock, service, stock (service item in the middle)
for code in (stock_item1, service_item, stock_item2):
pr.append(
"items",
{
"item_code": code,
"qty": 1,
"rate": 100,
"warehouse": warehouse,
"cost_center": "Main - TCP1",
"expense_account": "Cost of Goods Sold - TCP1",
},
)
pr.append(
"taxes",
{
"charge_type": "Actual",
"account_head": "_Test Account Shipping Charges - TCP1",
"category": "Valuation and Total",
"cost_center": "Main - TCP1",
"description": "Valuation Tax",
"tax_amount": 30,
},
)
pr.insert()
# 30 tax / 300 net = 10 per item. The two stock items capitalize 10 each; the service
# item's 10 share is excluded from valuation (not dumped onto the stock items).
self.assertAlmostEqual(pr.items[0].item_tax_amount, 10.0, places=2)
self.assertAlmostEqual(pr.items[1].item_tax_amount, 0.0, places=2)
self.assertAlmostEqual(pr.items[2].item_tax_amount, 10.0, places=2)
self.assertAlmostEqual(pr.items[0].valuation_rate, 110.0, places=2)
self.assertAlmostEqual(pr.items[2].valuation_rate, 110.0, places=2)
pr.submit()
gl_entries = get_gl_entries("Purchase Receipt", pr.name, skip_cancelled=True, as_dict=True)
gl_map = {row.account: row for row in gl_entries}
warehouse_account = get_warehouse_account_map(company)
stock_account = warehouse_account[warehouse]["account"]
# Stock asset = 200 (goods) + 20 (stock items' share of the valuation tax)
self.assertAlmostEqual(gl_map[stock_account].debit, 220.0, places=2)
self.assertAlmostEqual(gl_map["Stock Received But Not Billed - TCP1"].credit, 200.0, places=2)
# Only the stock items' share (20) is capitalized; the service item's 10 is excluded
self.assertAlmostEqual(gl_map["_Test Account Shipping Charges - TCP1"].credit, 20.0, places=2)
def test_po_to_pi_and_po_to_pr_worflow_full(self):
"""Test following behaviour:
- Create PO