fix(e-invoicing): validations & tax calculation fixes (#25314)
* fix: GST on freight charge in e-invoicing
* feat(india): bulk e-invoice generation (#24969)
* fix: cannot fetch e invoice settings
* fix: patch condition (#25301)
* fix: patch condition
* fix: except einvoice loading error seperately
* fix: json.loads error
Co-authored-by: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com>
diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py
index ddcedd5..9d23369 100644
--- a/erpnext/regional/india/utils.py
+++ b/erpnext/regional/india/utils.py
@@ -2,7 +2,7 @@
import frappe, re, json
from frappe import _
import erpnext
-from frappe.utils import cstr, flt, date_diff, nowdate, round_based_on_smallest_currency_fraction, money_in_words, getdate
+from frappe.utils import cstr, flt, cint, date_diff, nowdate, round_based_on_smallest_currency_fraction, money_in_words, getdate
from erpnext.regional.india import states, state_numbers
from erpnext.controllers.taxes_and_totals import get_itemised_tax, get_itemised_taxable_amount
from erpnext.controllers.accounts_controller import get_taxes_and_charges
@@ -41,24 +41,25 @@
return
if len(doc.gstin) != 15:
- frappe.throw(_("Invalid GSTIN! A GSTIN must have 15 characters."))
+ frappe.throw(_("A GSTIN must have 15 characters."), title=_("Invalid GSTIN"))
if gst_category and gst_category == 'UIN Holders':
if not GSTIN_UIN_FORMAT.match(doc.gstin):
- frappe.throw(_("Invalid GSTIN! The input you've entered doesn't match the GSTIN format for UIN Holders or Non-Resident OIDAR Service Providers"))
+ frappe.throw(_("The input you've entered doesn't match the GSTIN format for UIN Holders or Non-Resident OIDAR Service Providers"),
+ title=_("Invalid GSTIN"))
else:
if not GSTIN_FORMAT.match(doc.gstin):
- frappe.throw(_("Invalid GSTIN! The input you've entered doesn't match the format of GSTIN."))
+ frappe.throw(_("The input you've entered doesn't match the format of GSTIN."), title=_("Invalid GSTIN"))
validate_gstin_check_digit(doc.gstin)
set_gst_state_and_state_number(doc)
if not doc.gst_state:
- frappe.throw(_("Please Enter GST state"))
+ frappe.throw(_("Please enter GST state"), title=_("Invalid State"))
if doc.gst_state_number != doc.gstin[:2]:
- frappe.throw(_("Invalid GSTIN! First 2 digits of GSTIN should match with State number {0}.")
- .format(doc.gst_state_number))
+ frappe.throw(_("First 2 digits of GSTIN should match with State number {0}.")
+ .format(doc.gst_state_number), title=_("Invalid GSTIN"))
def validate_pan_for_india(doc, method):
if doc.get('country') != 'India' or not doc.pan:
@@ -830,3 +831,48 @@
account_list.extend(gst_account_list)
return account_list
+
+def update_taxable_values(doc, method):
+ country = frappe.get_cached_value('Company', doc.company, 'country')
+
+ if country != 'India':
+ return
+
+ gst_accounts = get_gst_accounts(doc.company)
+
+ # Only considering sgst account to avoid inflating taxable value
+ gst_account_list = gst_accounts.get('sgst_account', []) + gst_accounts.get('sgst_account', []) \
+ + gst_accounts.get('igst_account', [])
+
+ additional_taxes = 0
+ total_charges = 0
+ item_count = 0
+ considered_rows = []
+
+ for tax in doc.get('taxes'):
+ prev_row_id = cint(tax.row_id) - 1
+ if tax.account_head in gst_account_list and prev_row_id not in considered_rows:
+ if tax.charge_type == 'On Previous Row Amount':
+ additional_taxes += doc.get('taxes')[prev_row_id].tax_amount_after_discount_amount
+ considered_rows.append(prev_row_id)
+ if tax.charge_type == 'On Previous Row Total':
+ additional_taxes += doc.get('taxes')[prev_row_id].base_total - doc.base_net_total
+ considered_rows.append(prev_row_id)
+
+ for item in doc.get('items'):
+ if doc.apply_discount_on == 'Grand Total' and doc.discount_amount:
+ proportionate_value = item.base_amount if doc.base_total else item.qty
+ total_value = doc.base_total if doc.base_total else doc.total_qty
+ else:
+ proportionate_value = item.base_net_amount if doc.base_net_total else item.qty
+ total_value = doc.base_net_total if doc.base_net_total else doc.total_qty
+
+ applicable_charges = flt(flt(proportionate_value * (flt(additional_taxes) / flt(total_value)),
+ item.precision('taxable_value')))
+ item.taxable_value = applicable_charges + proportionate_value
+ total_charges += applicable_charges
+ item_count += 1
+
+ if total_charges != additional_taxes:
+ diff = additional_taxes - total_charges
+ doc.get('items')[item_count - 1].taxable_value += diff