fix: Reverse Charge booking logic and validation

This commit is contained in:
Deepesh Garg
2021-04-09 21:36:48 +05:30
parent ac61a86021
commit b5ea536530
7 changed files with 110 additions and 280 deletions

View File

@@ -1,196 +1,82 @@
{ {
"allow_copy": 0, "actions": [],
"allow_guest_to_view": 0, "creation": "2018-01-02 15:48:58.768352",
"allow_import": 0, "doctype": "DocType",
"allow_rename": 0, "editable_grid": 1,
"beta": 0, "engine": "InnoDB",
"creation": "2018-01-02 15:48:58.768352", "field_order": [
"custom": 0, "company",
"docstatus": 0, "cgst_account",
"doctype": "DocType", "sgst_account",
"document_type": "", "igst_account",
"editable_grid": 1, "cess_account",
"engine": "InnoDB", "is_reverse_charge_account"
],
"fields": [ "fields": [
{ {
"allow_bulk_edit": 0, "columns": 1,
"allow_on_submit": 0, "fieldname": "company",
"bold": 0, "fieldtype": "Link",
"collapsible": 0, "in_list_view": 1,
"columns": 0, "label": "Company",
"fieldname": "company", "options": "Company",
"fieldtype": "Link", "reqd": 1
"hidden": 0, },
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Company",
"length": 0,
"no_copy": 0,
"options": "Company",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{ {
"allow_bulk_edit": 0, "columns": 2,
"allow_on_submit": 0, "fieldname": "cgst_account",
"bold": 0, "fieldtype": "Link",
"collapsible": 0, "in_list_view": 1,
"columns": 0, "label": "CGST Account",
"fieldname": "cgst_account", "options": "Account",
"fieldtype": "Link", "reqd": 1
"hidden": 0, },
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "CGST Account",
"length": 0,
"no_copy": 0,
"options": "Account",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{ {
"allow_bulk_edit": 0, "columns": 2,
"allow_on_submit": 0, "fieldname": "sgst_account",
"bold": 0, "fieldtype": "Link",
"collapsible": 0, "in_list_view": 1,
"columns": 0, "label": "SGST Account",
"fieldname": "sgst_account", "options": "Account",
"fieldtype": "Link", "reqd": 1
"hidden": 0, },
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "SGST Account",
"length": 0,
"no_copy": 0,
"options": "Account",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{ {
"allow_bulk_edit": 0, "columns": 2,
"allow_on_submit": 0, "fieldname": "igst_account",
"bold": 0, "fieldtype": "Link",
"collapsible": 0, "in_list_view": 1,
"columns": 0, "label": "IGST Account",
"fieldname": "igst_account", "options": "Account",
"fieldtype": "Link", "reqd": 1
"hidden": 0, },
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "IGST Account",
"length": 0,
"no_copy": 0,
"options": "Account",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{ {
"allow_bulk_edit": 0, "columns": 2,
"allow_on_submit": 0, "fieldname": "cess_account",
"bold": 0, "fieldtype": "Link",
"collapsible": 0, "in_list_view": 1,
"columns": 0, "label": "CESS Account",
"fieldname": "cess_account", "options": "Account"
"fieldtype": "Link", },
"hidden": 0, {
"ignore_user_permissions": 0, "columns": 1,
"ignore_xss_filter": 0, "default": "0",
"in_filter": 0, "fieldname": "is_reverse_charge_account",
"in_global_search": 0, "fieldtype": "Check",
"in_list_view": 1, "in_list_view": 1,
"in_standard_filter": 0, "label": "Is Reverse Charge Account"
"label": "CESS Account",
"length": 0,
"no_copy": 0,
"options": "Account",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
} }
], ],
"has_web_view": 0, "index_web_pages_for_search": 1,
"hide_heading": 0, "istable": 1,
"hide_toolbar": 0, "links": [],
"idx": 0, "modified": "2021-04-09 12:30:25.889993",
"image_view": 0, "modified_by": "Administrator",
"in_create": 0, "module": "Accounts",
"is_submittable": 0, "name": "GST Account",
"issingle": 0, "owner": "Administrator",
"istable": 1, "permissions": [],
"max_attachments": 0, "quick_entry": 1,
"modified": "2018-01-02 15:52:22.335988", "sort_field": "modified",
"modified_by": "Administrator", "sort_order": "DESC",
"module": "Accounts", "track_changes": 1
"name": "GST Account",
"name_case": "",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0
} }

View File

@@ -260,7 +260,7 @@ doc_events = {
}, },
"Purchase Invoice": { "Purchase Invoice": {
"validate": [ "validate": [
"erpnext.regional.india.utils.update_grand_total_for_rcm", "erpnext.regional.india.utils.validate_reverse_charge_transaction",
"erpnext.regional.united_arab_emirates.utils.update_grand_total_for_rcm", "erpnext.regional.united_arab_emirates.utils.update_grand_total_for_rcm",
"erpnext.regional.united_arab_emirates.utils.validate_returns" "erpnext.regional.united_arab_emirates.utils.validate_returns"
] ]
@@ -406,7 +406,6 @@ regional_overrides = {
'erpnext.controllers.taxes_and_totals.get_regional_round_off_accounts': 'erpnext.regional.india.utils.get_regional_round_off_accounts', 'erpnext.controllers.taxes_and_totals.get_regional_round_off_accounts': 'erpnext.regional.india.utils.get_regional_round_off_accounts',
'erpnext.hr.utils.calculate_annual_eligible_hra_exemption': 'erpnext.regional.india.utils.calculate_annual_eligible_hra_exemption', 'erpnext.hr.utils.calculate_annual_eligible_hra_exemption': 'erpnext.regional.india.utils.calculate_annual_eligible_hra_exemption',
'erpnext.hr.utils.calculate_hra_exemption_for_period': 'erpnext.regional.india.utils.calculate_hra_exemption_for_period', 'erpnext.hr.utils.calculate_hra_exemption_for_period': 'erpnext.regional.india.utils.calculate_hra_exemption_for_period',
'erpnext.accounts.doctype.purchase_invoice.purchase_invoice.make_regional_gl_entries': 'erpnext.regional.india.utils.make_regional_gl_entries',
'erpnext.controllers.accounts_controller.validate_einvoice_fields': 'erpnext.regional.india.e_invoice.utils.validate_einvoice_fields' 'erpnext.controllers.accounts_controller.validate_einvoice_fields': 'erpnext.regional.india.e_invoice.utils.validate_einvoice_fields'
}, },
'United Arab Emirates': { 'United Arab Emirates': {

View File

@@ -758,4 +758,4 @@ erpnext.patches.v13_0.rename_membership_settings_to_non_profit_settings
erpnext.patches.v12_0.create_taxable_value_field erpnext.patches.v12_0.create_taxable_value_field
erpnext.patches.v12_0.add_company_link_to_einvoice_settings erpnext.patches.v12_0.add_company_link_to_einvoice_settings
erpnext.patches.v13_0.update_payment_terms_outstanding erpnext.patches.v13_0.update_payment_terms_outstanding
erpnext.patches.v12_0.create_itc_reversal_custom_fields #1 erpnext.patches.v12_0.create_itc_reversal_custom_fields #1

View File

@@ -27,6 +27,11 @@ def execute():
fetch_from='company_address.gstin', fetch_from='company_address.gstin',
depends_on="eval:doc.voucher_type=='Reversal Of ITC'", depends_on="eval:doc.voucher_type=='Reversal Of ITC'",
mandatory_depends_on="eval:doc.voucher_type=='Reversal Of ITC'") mandatory_depends_on="eval:doc.voucher_type=='Reversal Of ITC'")
],
'Purchase Invoice': [
dict(fieldname='eligibility_for_itc', label='Eligibility For ITC',
fieldtype='Select', insert_after='reason_for_issuing_document', print_hide=1,
options='Input Service Distributor\nImport Of Service\nImport Of Capital Goods\nITC on Reverse Charge\nIneligible\nAll Other ITC', default="All Other ITC")
] ]
} }

View File

@@ -183,6 +183,7 @@ class GSTR3BReport(Document):
itc_type_map = { itc_type_map = {
'IMPG': 'Import Of Capital Goods', 'IMPG': 'Import Of Capital Goods',
'IMPS': 'Import Of Service', 'IMPS': 'Import Of Service',
'ISRC': 'ITC on Reverse Charge',
'ISD': 'Input Service Distributor', 'ISD': 'Input Service Distributor',
'OTH': 'All Other ITC' 'OTH': 'All Other ITC'
} }
@@ -198,7 +199,7 @@ class GSTR3BReport(Document):
itc_type = 'All Other ITC' itc_type = 'All Other ITC'
gst_category = ['Unregistered', 'Overseas'] gst_category = ['Unregistered', 'Overseas']
else: else:
gst_category = ['Unregistered', 'Overseas', 'Registered Regular'] gst_category = ['Overseas', 'Registered Regular']
reverse_charge = ["N", "Y"] reverse_charge = ["N", "Y"]
for account_head in self.account_heads: for account_head in self.account_heads:

View File

@@ -190,7 +190,7 @@ def make_custom_fields(update=True):
purchase_invoice_itc_fields = [ purchase_invoice_itc_fields = [
dict(fieldname='eligibility_for_itc', label='Eligibility For ITC', dict(fieldname='eligibility_for_itc', label='Eligibility For ITC',
fieldtype='Select', insert_after='reason_for_issuing_document', print_hide=1, fieldtype='Select', insert_after='reason_for_issuing_document', print_hide=1,
options='Input Service Distributor\nImport Of Service\nImport Of Capital Goods\nIneligible\nAll Other ITC', default="All Other ITC"), options='Input Service Distributor\nImport Of Service\nImport Of Capital Goods\nITC on Reverse Charge\nIneligible\nAll Other ITC', default="All Other ITC"),
dict(fieldname='itc_integrated_tax', label='Availed ITC Integrated Tax', dict(fieldname='itc_integrated_tax', label='Availed ITC Integrated Tax',
fieldtype='Data', insert_after='eligibility_for_itc', print_hide=1), fieldtype='Data', insert_after='eligibility_for_itc', print_hide=1),
dict(fieldname='itc_central_tax', label='Availed ITC Central Tax', dict(fieldname='itc_central_tax', label='Availed ITC Central Tax',

View File

@@ -674,10 +674,17 @@ def validate_state_code(state_code, address):
return int(state_code) return int(state_code)
@frappe.whitelist() @frappe.whitelist()
def get_gst_accounts(company, account_wise=False): def get_gst_accounts(company, account_wise=False, only_reverse_charge=0, only_non_reverse_charge=0):
filters={"parent": "GST Settings", "company": company}
if only_reverse_charge:
filters.update({'is_reverse_charge_account': 1})
elif only_non_reverse_charge:
filters.update({'is_reverse_charge_account': 0})
gst_accounts = frappe._dict() gst_accounts = frappe._dict()
gst_settings_accounts = frappe.get_all("GST Account", gst_settings_accounts = frappe.get_all("GST Account",
filters={"parent": "GST Settings", "company": company}, filters=filters,
fields=["cgst_account", "sgst_account", "igst_account", "cess_account"]) fields=["cgst_account", "sgst_account", "igst_account", "cess_account"])
if not gst_settings_accounts and not frappe.flags.in_test: if not gst_settings_accounts and not frappe.flags.in_test:
@@ -692,105 +699,37 @@ def get_gst_accounts(company, account_wise=False):
return gst_accounts return gst_accounts
def update_grand_total_for_rcm(doc, method): def validate_reverse_charge_transaction(doc, method):
country = frappe.get_cached_value('Company', doc.company, 'country') country = frappe.get_cached_value('Company', doc.company, 'country')
if country != 'India': if country != 'India':
return return
gst_tax, base_gst_tax = get_gst_tax_amount(doc) base_gst_tax = 0
base_reverse_charge_booked = 0
if not base_gst_tax:
return
if doc.reverse_charge == 'Y': if doc.reverse_charge == 'Y':
doc.taxes_and_charges_added -= gst_tax gst_accounts = get_gst_accounts(doc.company, only_reverse_charge=1)
doc.total_taxes_and_charges -= gst_tax reverse_charge_accounts = gst_accounts.get('cgst_account') + gst_accounts.get('sgst_account') \
doc.base_taxes_and_charges_added -= base_gst_tax + gst_accounts.get('igst_account')
doc.base_total_taxes_and_charges -= base_gst_tax
update_totals(gst_tax, base_gst_tax, doc) gst_accounts = get_gst_accounts(doc.company, only_non_reverse_charge=1)
non_reverse_charge_accounts = gst_accounts.get('cgst_account') + gst_accounts.get('sgst_account') \
def update_totals(gst_tax, base_gst_tax, doc):
doc.base_grand_total -= base_gst_tax
doc.grand_total -= gst_tax
if doc.meta.get_field("rounded_total"):
if doc.is_rounded_total_disabled():
doc.outstanding_amount = doc.grand_total
else:
doc.rounded_total = round_based_on_smallest_currency_fraction(doc.grand_total,
doc.currency, doc.precision("rounded_total"))
doc.rounding_adjustment += flt(doc.rounded_total - doc.grand_total,
doc.precision("rounding_adjustment"))
doc.outstanding_amount = doc.rounded_total or doc.grand_total
doc.in_words = money_in_words(doc.grand_total, doc.currency)
doc.base_in_words = money_in_words(doc.base_grand_total, erpnext.get_company_currency(doc.company))
doc.set_payment_schedule()
def make_regional_gl_entries(gl_entries, doc):
country = frappe.get_cached_value('Company', doc.company, 'country')
if country != 'India':
return gl_entries
gst_tax, base_gst_tax = get_gst_tax_amount(doc)
if not base_gst_tax:
return gl_entries
if doc.reverse_charge == 'Y':
gst_accounts = get_gst_accounts(doc.company)
gst_account_list = gst_accounts.get('cgst_account') + gst_accounts.get('sgst_account') \
+ gst_accounts.get('igst_account') + gst_accounts.get('igst_account')
for tax in doc.get('taxes'): for tax in doc.get('taxes'):
if tax.category not in ("Total", "Valuation and Total"): if tax.account_head in non_reverse_charge_accounts and tax.add_deduct_tax == 'Add':
continue
dr_or_cr = "credit" if tax.add_deduct_tax == "Add" else "debit"
if flt(tax.base_tax_amount_after_discount_amount) and tax.account_head in gst_account_list:
account_currency = get_account_currency(tax.account_head)
gl_entries.append(doc.get_gl_dict(
{
"account": tax.account_head,
"cost_center": tax.cost_center,
"posting_date": doc.posting_date,
"against": doc.supplier,
dr_or_cr: tax.base_tax_amount_after_discount_amount,
dr_or_cr + "_in_account_currency": tax.base_tax_amount_after_discount_amount \
if account_currency==doc.company_currency \
else tax.tax_amount_after_discount_amount
}, account_currency, item=tax)
)
return gl_entries
def get_gst_tax_amount(doc):
gst_accounts = get_gst_accounts(doc.company)
gst_account_list = gst_accounts.get('cgst_account', []) + gst_accounts.get('sgst_account', []) \
+ gst_accounts.get('igst_account', [])
base_gst_tax = 0
gst_tax = 0
for tax in doc.get('taxes'):
if tax.category not in ("Total", "Valuation and Total"):
continue
if flt(tax.base_tax_amount_after_discount_amount) and tax.account_head in gst_account_list:
if tax.add_deduct_tax == "Add":
base_gst_tax += tax.base_tax_amount_after_discount_amount base_gst_tax += tax.base_tax_amount_after_discount_amount
gst_tax += tax.tax_amount_after_discount_amount elif tax.account_head in reverse_charge_accounts and tax.add_deduct_tax == 'Deduct':
else: base_reverse_charge_booked += tax.base_tax_amount_after_discount_amount
base_gst_tax -= tax.base_tax_amount_after_discount_amount
gst_tax -= tax.tax_amount_after_discount_amount
return gst_tax, base_gst_tax if base_gst_tax != base_reverse_charge_booked:
msg = _("Booked reverse charge is not equal to applied tax amount")
msg += "<br>"
msg += _("Please refer {gst_document_link} to learn more about how to setup and create reverse charge invoice").format(
gst_document_link='<a href="https://docs.erpnext.com/docs/user/manual/en/regional/india/gst-setup">GST Documentation</a>')
frappe.throw(msg)
@frappe.whitelist() @frappe.whitelist()
def get_regional_round_off_accounts(company, account_list): def get_regional_round_off_accounts(company, account_list):