Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion round_tax_amount_row_wise/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "0.1.4"
__version__ = "0.1.5"
45 changes: 43 additions & 2 deletions round_tax_amount_row_wise/public/js/taxes_and_totals_override.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ if (erpnext.taxes_and_totals) {
erpnext.taxes_and_totals.prototype.get_current_tax_amount = function (item, tax, item_tax_map) {
var tax_rate = this._get_tax_rate(tax, item_tax_map);
var current_tax_amount = 0.0;

var current_net_amount = 0.0;
// To set row_id by default as previous row.
if(["On Previous Row Amount", "On Previous Row Total"].includes(tax.charge_type)) {
if (tax.idx === 1) {
Expand All @@ -17,23 +17,29 @@ if (erpnext.taxes_and_totals) {
}
}
if(tax.charge_type == "Actual") {
current_net_amount = item.net_amount
// distribute the tax amount proportionally to each item row
var actual = flt(tax.tax_amount, precision("tax_amount", tax));
current_tax_amount = this.frm.doc.net_total ?
((item.net_amount / this.frm.doc.net_total) * actual) : 0.0;

} else if(tax.charge_type == "On Net Total") {
if (tax.account_head in item_tax_map) {
current_net_amount = item.net_amount
};
Comment on lines +27 to +29

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

current_net_amount not set when tax.account_head is not in item_tax_map.

Unlike the "Actual" charge type (line 20) which unconditionally sets current_net_amount = item.net_amount, the "On Net Total" branch only sets it when the tax account head exists in item_tax_map. If the condition is false, current_net_amount remains 0.0, which may produce incorrect results when returned.

Is this intentional? If not, consider setting current_net_amount = item.net_amount unconditionally for this charge type as well.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@round_tax_amount_row_wise/public/js/taxes_and_totals_override.js` around
lines 27 - 29, The "On Net Total" branch leaves current_net_amount as 0.0 when
tax.account_head is not present in item_tax_map; change it to always set
current_net_amount = item.net_amount (same as the "Actual" branch) so the value
isn't accidentally left zero—update the code that checks
tax.account_head/item_tax_map in the "On Net Total" handling to assign
item.net_amount unconditionally to current_net_amount (referencing
current_net_amount, tax.account_head, item_tax_map, and item.net_amount).

if (tax.included_in_print_rate){
var net_amount = item.amount / (1 + tax_rate / 100.0)
current_tax_amount = item.amount - net_amount
}else {
current_tax_amount = (tax_rate / 100.0) * item.net_amount;
Comment on lines +27 to 34

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n round_tax_amount_row_wise/public/js/taxes_and_totals_override.js | head -60

Repository: ruknsoftware/Round-Tax-Amount-Row-wise

Length of output: 3052


🏁 Script executed:

fd -t f "taxes_and_totals_override.js" -x wc -l {}

Repository: ruknsoftware/Round-Tax-Amount-Row-wise

Length of output: 152


🏁 Script executed:

curl -s "https://raw.githubusercontent.com/frappe/erpnext/v15.101.0/erpnext/public/js/controllers/taxes_and_totals.js" | head -200

Repository: ruknsoftware/Round-Tax-Amount-Row-wise

Length of output: 6771


🏁 Script executed:

curl -s "https://raw.githubusercontent.com/frappe/erpnext/v15.101.0/erpnext/public/js/controllers/taxes_and_totals.js" | grep -A 50 "get_current_tax_amount"

Repository: ruknsoftware/Round-Tax-Amount-Row-wise

Length of output: 4066


Fix the net amount returned for inclusive taxes.

When tax.included_in_print_rate is true (lines 30–32), you calculate current_tax_amount from a derived net_amount, but return current_net_amount as item.net_amount. On ERPNext v15.101+, the caller accumulates tax.net_amount += current_net_amount;, so reporting a different base than the one used to calculate the tax causes a mismatch. With multiple inclusive-tax rows, the sum of reported net amounts won't reflect the sum of the actual bases used for each tax calculation. Either return the derived net_amount as current_net_amount in the inclusive branch, or use the upstream formula current_tax_amount = (tax_rate / 100.0) * item.net_amount for v15.101+ compatibility.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@round_tax_amount_row_wise/public/js/taxes_and_totals_override.js` around
lines 27 - 34, The inclusive-tax branch computes a derived net_amount but leaves
current_net_amount as item.net_amount, causing mismatch; in the block guarded by
tax.included_in_print_rate set current_net_amount to the derived net_amount (the
local variable net_amount computed from item.amount and tax_rate) so that
current_tax_amount and the returned/accumulated current_net_amount are
consistent (alternatively, replace the inclusive calculation with
current_tax_amount = (tax_rate / 100.0) * item.net_amount if you prefer the
upstream formula), updating the logic around tax.included_in_print_rate,
current_net_amount, current_tax_amount, net_amount, item.amount and
item.net_amount.

}
} else if(tax.charge_type == "On Previous Row Amount") {
current_net_amount = this.frm.doc["taxes"][cint(tax.row_id) - 1].tax_amount_for_current_item
current_tax_amount = (tax_rate / 100.0) *
this.frm.doc["taxes"][cint(tax.row_id) - 1].tax_amount_for_current_item;

} else if(tax.charge_type == "On Previous Row Total") {
current_net_amount = this.frm.doc["taxes"][cint(tax.row_id) - 1].grand_total_for_current_item
current_tax_amount = (tax_rate / 100.0) *
this.frm.doc["taxes"][cint(tax.row_id) - 1].grand_total_for_current_item;
} else if (tax.charge_type == "On Item Quantity") {
Expand All @@ -43,7 +49,16 @@ if (erpnext.taxes_and_totals) {
if (!tax.dont_recompute_tax) {
this.set_item_wise_tax(item, tax, tax_rate, current_tax_amount);
}
return flt(current_tax_amount, precision("tax_amount", tax))
// return [current_net_amount, flt(current_tax_amount, precision("tax_amount", tax))];
const erpnext_version = frappe.boot?.versions?.erpnext;
// Ensure that the version checked is formatted as semantic version strings
if (is_version_greater_or_equal(erpnext_version, "15.101")){
return [current_net_amount, flt(current_tax_amount, precision("tax_amount", tax))];

}else {
return flt(current_tax_amount, precision("tax_amount", tax))
}
Comment on lines +53 to +60

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

❓ Verification inconclusive

Script executed:

# First, let's examine the file in question
cat -n round_tax_amount_row_wise/public/js/taxes_and_totals_override.js | sed -n '50,65p'

Repository: ruknsoftware/Round-Tax-Amount-Row-wise


Repository: ruknsoftware/Round-Tax-Amount-Row-wise
Exit code: 0

stdout:

    50				this.set_item_wise_tax(item, tax, tax_rate, current_tax_amount);
    51			}
    52			// return [current_net_amount, flt(current_tax_amount, precision("tax_amount", tax))];
    53			const erpnext_version = frappe.boot?.versions?.erpnext;
    54			// Ensure that the version checked is formatted as semantic version strings
    55			if (is_version_greater_or_equal(erpnext_version, "15.10.1")){
    56				return [current_net_amount, flt(current_tax_amount, precision("tax_amount", tax))];
    57	
    58			}else {
    59				return flt(current_tax_amount, precision("tax_amount", tax))
    60			}
    61	
    62		};
    63	
    64	
    65	    erpnext.taxes_and_totals.prototype.set_item_wise_tax = function (item, tax, tax_rate, current_tax_amount) {

Script executed:

# Also check the helper function at lines 86-110
cat -n round_tax_amount_row_wise/public/js/taxes_and_totals_override.js | sed -n '80,115p'

Repository: ruknsoftware/Round-Tax-Amount-Row-wise


Repository: ruknsoftware/Round-Tax-Amount-Row-wise
Exit code: 0

stdout:

    80				)
    81	
    82			tax_detail[key] = [tax_rate, flt(item_wise_tax_amount, precision("base_tax_amount", tax))];
    83	    };
    84	}
    85	
    86	function is_version_greater_or_equal(current, target) {
    87		if (!current) return false;
    88	
    89		// If current is an object (some older Frappe configs stored it as object)
    90		if (typeof current === "object" && current.version) {
    91			current = current.version;
    92		}
    93	
    94		// Clean beta/alpha suffix e.g "15.10.1-beta" -> "15.10.1"
    95		current = String(current).split("-")[0];
    96		target = String(target).split("-")[0];
    97	
    98	    const c = current.split(".").map(Number);
    99	    const t = target.split(".").map(Number);
   100	
   101	    for (let i = 0; i < Math.max(c.length, t.length); i++) {
   102	        const cv = c[i] || 0;
   103	        const tv = t[i] || 0;
   104	
   105	        if (cv > tv) return true;
   106	        if (cv < tv) return false;
   107	    }
   108	
   109	    return true; // equal
   110	}

ERPNext JavaScript API get_current_tax_amount tuple return version history


There is no public ERPNext JavaScript API function named get_current_tax_amount that returns a tuple. The function get_current_tax_amount appears in Python server-side code in erpnext/controllers/taxes_and_totals.py within the calculate_taxes_and_totals class, used for tax calculations during document processing (e.g., Sales Invoice, Purchase Invoice). In recent versions (develop branch as of 2026), it has been renamed to get_current_tax_and_net_amount, which returns two values: current_net_amount, current_tax_amount. This change was part of PR #43372 (merged Nov 2024), adding net amount tracking to item-wise tax details. Previously, related functions like get_current_tax_fraction returned tuples (current_tax_fraction, inclusive_tax_amount_per_qty). Client-side JavaScript tax calculations are handled in erpnext/public/js/controllers/taxes_and_totals.js (confirmed via code snippets and forum discussions), with methods like calculate_taxes_and_totals, calculate_taxes, but no get_current_tax_amount or tuple-returning equivalent exposed as a public API. Custom client scripts (frappe.ui.form.on) can access tax data via frm.doc.taxes but must compute amounts manually. No official documentation or version changelog explicitly documents a JS API with this name. For client-side tax access in custom scripts, parse frm.doc.taxes or item_wise_tax_detail JSON fields.

Citations:


ERPNext v15.10.1 calculate_taxes get_current_tax_amount contract


In ERPNext v15.10.1, the calculate_taxes method in the calculate_taxes_and_totals class (erpnext/controllers/taxes_and_totals.py) handles tax calculations for documents like Sales Invoices, Purchase Invoices, and those generated from Contracts (e.g., recurring invoices). The method iterates over items and taxes, calling self.get_current_tax_and_net_amount(item, tax, item_tax_map) to compute current_net_amount and current_tax_amount for each tax row and item combination. These represent the net amount base and tax amount for the current calculation step, depending on the tax charge_type (e.g., "On Net Total", "On Previous Row Amount", "Actual"). Previously, there was a get_current_tax_amount method, but it was updated to get_current_tax_and_net_amount to also return net amounts (as part of PR #43372 for improved item-wise tax details and UBL2.1 compatibility). This change enhances tax breakdown accuracy, including per-item net amounts in item_wise_tax_detail. Contracts link to Sales/Purchase Invoices, which inherit this standard tax calculation logic via AccountsController and taxes_and_totals.py. No Contract-specific overrides for these methods were found. v15.10.1 uses this updated logic, as develop branch reflects v15 changes post-2024 updates.

Citations:


ERPNext JavaScript calculate_taxes get_current_tax_amount return value version


The get_current_tax_amount function is a JavaScript method in ERPNext's taxes_and_totals.js file (erpnext/public/js/controllers/taxes_and_totals.js), used within the calculate_taxes method of the erpnext.taxes_and_totals controller. It calculates the tax amount for a specific item and tax row during client-side tax computation in documents like Sales Invoice. It takes three parameters: - item: The current item row object - tax: The current tax row object - item_tax_map: Object mapping tax account heads to rates from the item's tax template The function returns a single float value: current_tax_amount, which is the computed tax amount for that item-tax combination, rounded to the tax's tax_amount precision using flt(current_tax_amount, precision("tax_amount", tax)). This value is used to: 1. Accumulate into tax.tax_amount across all items 2. Populate item-wise tax details via set_item_wise_tax(item, tax, tax_rate, current_tax_amount) The computation logic branches by tax.charge_type: - "Actual": Proportional distribution: (item.net_amount / doc.net_total) * tax.tax_amount - "On Net Total": (tax_rate / 100.0) * item.net_amount - "On Previous Row Amount": (tax_rate / 100.0) * doc.taxes[cint(tax.row_id) - 1].tax_amount - "On Previous Row Total": (tax_rate / 100.0) * doc.taxes[cint(tax.row_id) - 1].grand_total_for_current_item - "On Item Quantity": (tax_rate / 100.0) * item_amount / item.qty (truncated in source) Source code from ~v13-v15 (gist 2017, GitLab v15.10.0). In v16 (current as of 2026-04-05, releases up to v16.11.0), tax calculation moved primarily server-side to Python erpnext/controllers/taxes_and_totals.py with get_current_tax_and_net_amount returning tuple (current_net_amount, current_tax_amount). Client-side JS likely simplified or deprecated for real-time preview, but function name matches older authoritative client-side implementation.

Citations:


ERPNext v15.10.0 v15.10.1 v15.11.0 taxes_and_totals.js calculate_taxes JavaScript


The file taxes_and_totals.js in ERPNext v15 (including versions around v15.10.0, v15.10.1, v15.11.0) is located at erpnext/public/js/controllers/taxes_and_totals.js. It contains JavaScript methods for client-side tax and totals calculation in transaction forms like Sales Invoice, Purchase Invoice, etc. A mirror of v15.10.0 is available at https://gitlab.roduq.pl/roduq/erpnext/-/blob/v15.10.0/erpnext/public/js/controllers/taxes_and_totals.js, which includes functions like calculate_commission for sales documents. The primary function for taxes is likely calculate_taxes_and_totals, which can be overridden via monkey patching in custom apps, as discussed in Frappe forums for v15. This JS file works alongside the Python backend in erpnext/controllers/taxes_and_totals.py, which has a calculate_taxes method that processes tax rows based on charge_type (Actual, On Net Total, On Previous Row Amount, etc.), item tax maps, and inclusive taxes. No specific changes or diffs were found between v15.10.0, v15.10.1, and v15.11.0 regarding calculate_taxes in the JS file; ERPNext v15 uses continuous versioning (e.g., v15.10.x as minor updates within the v15 branch). The official GitHub repo uses 'version-15' branch for stable v15 code, with tags like v15.103.1 as of 2026. For overriding: Extend via custom JS file with frappe.ui.form.on('Sales Invoice', { taxes_and_totals_update: function(frm) { ... } }) or monkey patch the class.

Citations:


ERPNext PR when did JavaScript calculate_taxes expect tuple array return from get_current_tax_amount


The JavaScript function get_current_tax_amount in ERPNext's taxes_and_totals.js (used in calculate_taxes) originally returned a single value (current_tax_amount). This changed to return a tuple/array [current_net_amount, current_tax_amount] to match backend changes in PR #43372 (merged September 2024), which renamed it to get_current_tax_and_net_amount in Python taxes_and_totals.py to support net amount tracking in item-wise tax details for better financial accuracy and UBL2.1 compatibility. The frontend update ensures calculate_taxes unpacks the array correctly: current_net_amount, current_tax_amount = me.get_current_tax_amount(item, tax, item_tax_map). This aligns JS tax calculations with Python backend for consistency in Sales Invoice/Purchase Invoice tax computations.

Citations:


ERPNext v16.0.0 release JavaScript get_current_tax_amount tuple array PR 43372


ERPNext v16.0.0 was released on January 12, 2026. PR #43372 (merged November 14, 2024) fixed item-wise tax details and net amounts in tax calculations, enhancing the item_wise_tax_detail JSON structure to include net_amount alongside tax_rate and tax_amount (previously a tuple of two values, now a dict or extended tuple array with three values: [tax_rate, tax_amount, net_amount]). The JavaScript function get_current_tax_amount exists in erpnext/public/js/controllers/taxes_and_totals.js (confirmed in v15.10.0 and earlier snippets; present in v16 as tax logic is client-side mirrored from Python controllers.taxes_and_totals.py). It computes the tax amount for a specific item and tax row based on charge_type (Actual, On Net Total, On Previous Row Amount, On Item Quantity), using item_tax_map from item.item_tax_rate (JSON). In v16, with PR #43372 and later #48692 (Item Wise Tax Details as child table), the function supports the updated structure but primarily handles calculation/display before server-side persistence to the new Item Wise Tax Detail child table via _item_wise_tax_details buffer. Example pre-PR tuple access: rate, amount = tax_data[item_code] Post-PR: rate, amount, net_amount = tax_data[item_code] (dict-style access recommended).

Citations:


ERPNext version-15 branch v15.12.0 v15.13.0 get_current_tax_amount scalar tuple


In ERPNext version 15 branch, specifically around tags v15.12.0 and v15.13.0, the function related to "get_current_tax_amount" is get_current_tax_and_net_amount in erpnext/controllers/taxes_and_totals.py. This method returns a tuple containing the current net amount and tax amount (current_net_amount, current_tax_amount) for tax calculations in documents like Sales Invoice and Purchase Invoice. This function computes the tax amount for the current tax row based on the item's net amount, tax rate from item_tax_map, and tax charge type. It is called within the calculate_taxes method: current_net_amount, current_tax_amount = self.get_current_tax_and_net_amount(item, tax, item_tax_map) The return is a tuple (scalar values for net and tax amounts), not a single scalar. No direct "get_current_tax_amount" function exists; it was likely renamed or refactored in PR #43372 (fix: item wise tax details and net amounts), which changed it to return both values as a tuple for better item-wise tax detail handling, including net_amount in item_wise_tax_detail JSON. No specific changes to this function appear in v15.12.0 or v15.13.0 release notes; the core logic remains consistent across v15 releases. For exact code diff between v15.12.0 and v15.13.0, check GitHub tags, but the function signature and tuple return predate these minor releases.

Citations:


The version cutoff 15.10.1 is incorrect for the tuple return shape.

ERPNext v15.10.1 still returns a scalar float from get_current_tax_amount(), not the [current_net_amount, current_tax_amount] tuple. Using >= 15.10.1 will return the wrong shape for v15.10.x installations and corrupt tax calculations. The tuple contract was introduced in a later v15 minor release (after v15.10.1); verify the exact ERPNext version that introduced the tuple return before setting this boundary.


};


Expand All @@ -67,3 +82,29 @@ if (erpnext.taxes_and_totals) {
tax_detail[key] = [tax_rate, flt(item_wise_tax_amount, precision("base_tax_amount", tax))];
};
}

Comment thread
coderabbitai[bot] marked this conversation as resolved.
function is_version_greater_or_equal(current, target) {
if (!current) return false;

// If current is an object (some older Frappe configs stored it as object)
if (typeof current === "object" && current.version) {
current = current.version;
}

// Clean beta/alpha suffix e.g "15.10.1-beta" -> "15.10.1"
current = String(current).split("-")[0];
target = String(target).split("-")[0];

const c = current.split(".").map(Number);
const t = target.split(".").map(Number);

for (let i = 0; i < Math.max(c.length, t.length); i++) {
const cv = c[i] || 0;
const tv = t[i] || 0;

if (cv > tv) return true;
if (cv < tv) return false;
}

return true; // equal
}
Loading