From dc4f7011014b0e847cc57ed3f665a1cbbdb33c38 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Wed, 4 Jun 2025 10:37:54 +0530 Subject: [PATCH] refactor: item tax amount calculation for valuation rate (#47532) --- erpnext/controllers/buying_controller.py | 96 ++++++++++++++++++------ 1 file changed, 75 insertions(+), 21 deletions(-) diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index 34ed97f43fc..5a6e1f78178 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -2,6 +2,8 @@ # License: GNU General Public License v3. See license.txt +import json + import frappe from frappe import ValidationError, _, msgprint from frappe.contacts.doctype.address.address import render_address @@ -318,32 +320,40 @@ class BuyingController(SubcontractingController): if d.item_code and d.item_code in stock_and_asset_items: stock_and_asset_items_qty += flt(d.qty) stock_and_asset_items_amount += flt(d.base_net_amount) - last_item_idx = d.idx - total_valuation_amount = sum( - flt(d.base_tax_amount_after_discount_amount) - for d in self.get("taxes") - if d.category in ["Valuation", "Valuation and Total"] - ) + last_item_idx = d.idx + + tax_accounts, total_valuation_amount, total_actual_tax_amount = self.get_tax_details() - valuation_amount_adjustment = total_valuation_amount for i, item in enumerate(self.get("items")): - if item.item_code and item.qty and item.item_code in stock_and_asset_items: - item_proportion = ( - flt(item.base_net_amount) / stock_and_asset_items_amount - if stock_and_asset_items_amount - else flt(item.qty) / stock_and_asset_items_qty - ) - + if item.item_code and item.qty: + item_tax_amount, actual_tax_amount = 0.0, 0.0 if i == (last_item_idx - 1): - item.item_tax_amount = flt( - valuation_amount_adjustment, self.precision("item_tax_amount", item) - ) + item_tax_amount = total_valuation_amount + actual_tax_amount = total_actual_tax_amount else: - item.item_tax_amount = flt( - item_proportion * total_valuation_amount, self.precision("item_tax_amount", item) - ) - valuation_amount_adjustment -= item.item_tax_amount + # calculate item tax amount + item_tax_amount = self.get_item_tax_amount(item, tax_accounts) + total_valuation_amount -= item_tax_amount + + if total_actual_tax_amount: + actual_tax_amount = self.get_item_actual_tax_amount( + item, + total_actual_tax_amount, + stock_and_asset_items_amount, + stock_and_asset_items_qty, + ) + total_actual_tax_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: + item.valuation_rate = 0.0 + continue + + # Item tax amount is the total tax amount applied on that item and actual tax type amount + item.item_tax_amount = flt( + item_tax_amount + actual_tax_amount, self.precision("item_tax_amount", item) + ) self.round_floats_in(item) if flt(item.conversion_factor) == 0.0: @@ -376,6 +386,50 @@ class BuyingController(SubcontractingController): update_regional_item_valuation_rate(self) + def get_tax_details(self): + tax_accounts = [] + total_valuation_amount = 0.0 + total_actual_tax_amount = 0.0 + + for d in self.get("taxes"): + if d.category not in ["Valuation", "Valuation and Total"]: + continue + + if d.charge_type == "On Net Total": + total_valuation_amount += flt(d.base_tax_amount_after_discount_amount) + tax_accounts.append(d.account_head) + else: + total_actual_tax_amount += flt(d.base_tax_amount_after_discount_amount) + + return tax_accounts, total_valuation_amount, total_actual_tax_amount + + def get_item_tax_amount(self, item, tax_accounts): + item_tax_amount = 0.0 + if item.item_tax_rate: + tax_details = json.loads(item.item_tax_rate) + for account, rate in tax_details.items(): + if account not in tax_accounts: + continue + + net_rate = item.base_net_amount + if item.sales_incoming_rate: + net_rate = item.qty * item.sales_incoming_rate + + item_tax_amount += flt(net_rate) * flt(rate) / 100 + + return item_tax_amount + + def get_item_actual_tax_amount( + self, item, actual_tax_amount, stock_and_asset_items_amount, stock_and_asset_items_qty + ): + item_proportion = ( + flt(item.base_net_amount) / stock_and_asset_items_amount + if stock_and_asset_items_amount + else flt(item.qty) / stock_and_asset_items_qty + ) + + return flt(item_proportion * actual_tax_amount, self.precision("item_tax_amount", item)) + def set_incoming_rate(self): """ Override item rate with incoming rate for internal stock transfer