Apply GST based on Origin and Place of supply GST Code (#14288)
* Add new gst field in Taxes and Charges template - is_inter_state * Add a patch * Add a regional function to fetch taxes on the basis of GSTin * Add regional function to hooks.py * Fetch taxes for Purchase Invoice on the basis of Supplier GSTIN * Fixes in the setup.py for India region * Set is_inter_state field For the existing Taxes and Charges templates, if an account_head with igst account (which is set in GST Settings) is found, set the checkbox and also check if it doesn't have a cgst account. * Fix as per review comment
This commit is contained in:
parent
5103454642
commit
4fa600a8dd
@ -3,7 +3,7 @@
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
import frappe, erpnext
|
||||
from frappe import _, msgprint, scrub
|
||||
from frappe.defaults import get_user_permissions
|
||||
from frappe.model.utils import get_fetch_values
|
||||
@ -43,12 +43,12 @@ def _get_party_details(party=None, account=None, party_type="Customer", company=
|
||||
party = frappe.get_doc(party_type, party)
|
||||
currency = party.default_currency if party.default_currency else get_company_currency(company)
|
||||
|
||||
out["taxes_and_charges"] = set_taxes(party.name, party_type, posting_date, company, out.customer_group, out.supplier_group)
|
||||
out["payment_terms_template"] = get_pyt_term_template(party.name, party_type, company)
|
||||
set_address_details(out, party, party_type, doctype, company)
|
||||
set_contact_details(out, party, party_type)
|
||||
set_other_values(out, party, party_type)
|
||||
set_price_list(out, party, party_type, price_list)
|
||||
out["taxes_and_charges"] = set_taxes(party.name, party_type, posting_date, company, out.customer_group, out.supplier_group)
|
||||
out["payment_terms_template"] = get_pyt_term_template(party.name, party_type, company)
|
||||
|
||||
if not out.get("currency"):
|
||||
out["currency"] = currency
|
||||
@ -83,6 +83,18 @@ def set_address_details(out, party, party_type, doctype=None, company=None):
|
||||
out.update(get_company_address(company))
|
||||
if out.company_address:
|
||||
out.update(get_fetch_values(doctype, 'company_address', out.company_address))
|
||||
get_regional_address_details(out, doctype, company)
|
||||
|
||||
elif doctype and doctype == "Purchase Invoice":
|
||||
out.update(get_company_address(company))
|
||||
if out.company_address:
|
||||
out["shipping_address"] = out["company_address"]
|
||||
out.update(get_fetch_values(doctype, 'shipping_address', out.shipping_address))
|
||||
get_regional_address_details(out, doctype, company)
|
||||
|
||||
@erpnext.allow_regional
|
||||
def get_regional_address_details(out, doctype, company):
|
||||
pass
|
||||
|
||||
def set_contact_details(out, party, party_type):
|
||||
out.contact_person = get_default_contact(party_type, party.name)
|
||||
|
@ -273,7 +273,8 @@ regional_overrides = {
|
||||
'India': {
|
||||
'erpnext.tests.test_regional.test_method': 'erpnext.regional.india.utils.test_method',
|
||||
'erpnext.controllers.taxes_and_totals.get_itemised_tax_breakup_header': 'erpnext.regional.india.utils.get_itemised_tax_breakup_header',
|
||||
'erpnext.controllers.taxes_and_totals.get_itemised_tax_breakup_data': 'erpnext.regional.india.utils.get_itemised_tax_breakup_data'
|
||||
'erpnext.controllers.taxes_and_totals.get_itemised_tax_breakup_data': 'erpnext.regional.india.utils.get_itemised_tax_breakup_data',
|
||||
'erpnext.accounts.party.get_regional_address_details': 'erpnext.regional.india.utils.get_regional_address_details'
|
||||
},
|
||||
'United Arab Emirates': {
|
||||
'erpnext.controllers.taxes_and_totals.update_itemised_tax_data': 'erpnext.regional.united_arab_emirates.utils.update_itemised_tax_data'
|
||||
|
@ -542,4 +542,5 @@ erpnext.patches.v11_0.refactor_erpnext_shopify
|
||||
erpnext.patches.v11_0.move_item_defaults_to_child_table_for_multicompany
|
||||
erpnext.patches.v11_0.rename_overproduction_percent_field
|
||||
erpnext.patches.v10_0.update_status_in_purchase_receipt
|
||||
erpnext.patches.v11_0.rename_members_with_naming_series #04-06-2018
|
||||
erpnext.patches.v11_0.inter_state_field_for_gst
|
||||
erpnext.patches.v11_0.rename_members_with_naming_series #04-06-2018
|
58
erpnext/patches/v11_0/inter_state_field_for_gst.py
Normal file
58
erpnext/patches/v11_0/inter_state_field_for_gst.py
Normal file
@ -0,0 +1,58 @@
|
||||
import frappe
|
||||
from erpnext.regional.india.setup import make_custom_fields
|
||||
|
||||
def execute():
|
||||
company = frappe.get_all('Company', filters = {'country': 'India'})
|
||||
if not company:
|
||||
return
|
||||
|
||||
make_custom_fields()
|
||||
|
||||
frappe.reload_doc("accounts", "doctype", "sales_taxes_and_charges")
|
||||
frappe.reload_doc("accounts", "doctype", "purchase_taxes_and_charges")
|
||||
frappe.reload_doc("accounts", "doctype", "sales_taxes_and_charges_template")
|
||||
frappe.reload_doc("accounts", "doctype", "purchase_taxes_and_charges_template")
|
||||
|
||||
# set is_inter_state in Taxes And Charges Templates
|
||||
if frappe.db.has_column("Sales Taxes And Charges Template", "is_inter_state") and\
|
||||
frappe.db.has_column("Purchase Taxes And Charges Template", "is_inter_state"):
|
||||
|
||||
igst_accounts = set(frappe.db.sql_list('''SELECT igst_account from `tabGST Account` WHERE parent = "GST Settings"'''))
|
||||
cgst_accounts = set(frappe.db.sql_list('''SELECT cgst_account FROM `tabGST Account` WHERE parenttype = "GST Settings"'''))
|
||||
|
||||
when_then_sales = get_formatted_data("Sales Taxes and Charges", igst_accounts, cgst_accounts)
|
||||
when_then_purchase = get_formatted_data("Purchase Taxes and Charges", igst_accounts, cgst_accounts)
|
||||
|
||||
if when_then_sales:
|
||||
frappe.db.sql('''update `tabSales Taxes and Charges Template`
|
||||
set is_inter_state = Case {when_then} Else 0 End
|
||||
'''.format(when_then=" ".join(when_then_sales)))
|
||||
|
||||
if when_then_purchase:
|
||||
frappe.db.sql('''update `tabPurchase Taxes and Charges Template`
|
||||
set is_inter_state = Case {when_then} Else 0 End
|
||||
'''.format(when_then=" ".join(when_then_purchase)))
|
||||
|
||||
def get_formatted_data(doctype, igst_accounts, cgst_accounts):
|
||||
# fetch all the rows data from child table
|
||||
all_details = frappe.db.sql('''
|
||||
select parent, account_head from `tab{doctype}`
|
||||
where parenttype="{doctype} Template"'''.format(doctype=doctype), as_dict=True)
|
||||
|
||||
# group the data in the form "parent: [list of accounts]""
|
||||
group_detail = {}
|
||||
for i in all_details:
|
||||
if not i['parent'] in group_detail: group_detail[i['parent']] = []
|
||||
for j in all_details:
|
||||
if i['parent']==j['parent']:
|
||||
group_detail[i['parent']].append(j['account_head'])
|
||||
|
||||
# form when_then condition based on - if list of accounts for a document
|
||||
# matches any account in igst_accounts list and not matches any in cgst_accounts list
|
||||
when_then = []
|
||||
for i in group_detail:
|
||||
temp = set(group_detail[i])
|
||||
if not temp.isdisjoint(igst_accounts) and temp.isdisjoint(cgst_accounts):
|
||||
when_then.append('''When name='{name}' Then 1'''.format(name=i))
|
||||
|
||||
return when_then
|
@ -113,10 +113,10 @@ def make_custom_fields():
|
||||
purchase_invoice_gst_fields = [
|
||||
dict(fieldname='supplier_gstin', label='Supplier GSTIN',
|
||||
fieldtype='Data', insert_after='supplier_address',
|
||||
options='supplier_address.gstin', print_hide=1),
|
||||
fetch_from='supplier_address.gstin', print_hide=1),
|
||||
dict(fieldname='company_gstin', label='Company GSTIN',
|
||||
fieldtype='Data', insert_after='shipping_address',
|
||||
options='shipping_address.gstin', print_hide=1),
|
||||
fieldtype='Data', insert_after='shipping_address_display',
|
||||
fetch_from='shipping_address.gstin', print_hide=1),
|
||||
dict(fieldname='place_of_supply', label='Place of Supply',
|
||||
fieldtype='Data', insert_after='shipping_address',
|
||||
print_hide=1, read_only=0),
|
||||
@ -136,16 +136,16 @@ def make_custom_fields():
|
||||
sales_invoice_gst_fields = [
|
||||
dict(fieldname='billing_address_gstin', label='Billing Address GSTIN',
|
||||
fieldtype='Data', insert_after='customer_address',
|
||||
options='customer_address.gstin', print_hide=1),
|
||||
fetch_from='customer_address.gstin', print_hide=1),
|
||||
dict(fieldname='customer_gstin', label='Customer GSTIN',
|
||||
fieldtype='Data', insert_after='shipping_address',
|
||||
options='shipping_address_name.gstin', print_hide=1),
|
||||
fieldtype='Data', insert_after='shipping_address_name',
|
||||
fetch_from='shipping_address_name.gstin', print_hide=1),
|
||||
dict(fieldname='place_of_supply', label='Place of Supply',
|
||||
fieldtype='Data', insert_after='customer_gstin',
|
||||
print_hide=1, read_only=0),
|
||||
dict(fieldname='company_gstin', label='Company GSTIN',
|
||||
fieldtype='Data', insert_after='company_address',
|
||||
options='company_address.gstin', print_hide=1),
|
||||
fetch_from='company_address.gstin', print_hide=1),
|
||||
dict(fieldname='port_code', label='Port Code',
|
||||
fieldtype='Data', insert_after='reason_for_issuing_document', print_hide=1,
|
||||
depends_on="eval:doc.invoice_type=='Export' "),
|
||||
@ -157,6 +157,11 @@ def make_custom_fields():
|
||||
depends_on="eval:doc.invoice_type=='Export' ")
|
||||
]
|
||||
|
||||
inter_state_gst_field = [
|
||||
dict(fieldname='is_inter_state', label='Is Inter State',
|
||||
fieldtype='Check', insert_after='disabled', print_hide=1)
|
||||
]
|
||||
|
||||
custom_fields = {
|
||||
'Address': [
|
||||
dict(fieldname='gstin', label='Party GSTIN', fieldtype='Data',
|
||||
@ -168,7 +173,9 @@ def make_custom_fields():
|
||||
],
|
||||
'Purchase Invoice': invoice_gst_fields + purchase_invoice_gst_fields,
|
||||
'Sales Invoice': invoice_gst_fields + sales_invoice_gst_fields,
|
||||
"Delivery Note": sales_invoice_gst_fields,
|
||||
'Delivery Note': sales_invoice_gst_fields,
|
||||
'Sales Taxes and Charges Template': inter_state_gst_field,
|
||||
'Purchase Taxes and Charges Template': inter_state_gst_field,
|
||||
'Item': [
|
||||
dict(fieldname='gst_hsn_code', label='HSN/SAC',
|
||||
fieldtype='Link', options='GST HSN Code', insert_after='item_group'),
|
||||
|
@ -3,6 +3,7 @@ from frappe import _
|
||||
from frappe.utils import cstr
|
||||
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
|
||||
|
||||
def validate_gstin_for_india(doc, method):
|
||||
if not hasattr(doc, 'gstin'):
|
||||
@ -61,19 +62,48 @@ def get_itemised_tax_breakup_data(doc):
|
||||
|
||||
return hsn_tax, hsn_taxable_amount
|
||||
|
||||
def set_place_of_supply(doc, method):
|
||||
if not frappe.get_meta('Address').has_field('gst_state'): return
|
||||
|
||||
if doc.doctype in ("Sales Invoice", "Delivery Note"):
|
||||
address_name = doc.shipping_address_name or doc.customer_address
|
||||
elif doc.doctype == "Purchase Invoice":
|
||||
address_name = doc.shipping_address or doc.supplier_address
|
||||
|
||||
if address_name:
|
||||
address = frappe.db.get_value("Address", address_name, ["gst_state", "gst_state_number"], as_dict=1)
|
||||
doc.place_of_supply = cstr(address.gst_state_number) + "-" + cstr(address.gst_state)
|
||||
def set_place_of_supply(doc, method=None):
|
||||
doc.place_of_supply = get_place_of_supply(doc, doc.doctype)
|
||||
|
||||
# don't remove this function it is used in tests
|
||||
def test_method():
|
||||
'''test function'''
|
||||
return 'overridden'
|
||||
|
||||
def get_place_of_supply(out, doctype):
|
||||
if not frappe.get_meta('Address').has_field('gst_state'): return
|
||||
|
||||
if doctype in ("Sales Invoice", "Delivery Note"):
|
||||
address_name = out.shipping_address_name or out.customer_address
|
||||
elif doctype == "Purchase Invoice":
|
||||
address_name = out.shipping_address or out.supplier_address
|
||||
|
||||
if address_name:
|
||||
address = frappe.db.get_value("Address", address_name, ["gst_state", "gst_state_number"], as_dict=1)
|
||||
return cstr(address.gst_state_number) + "-" + cstr(address.gst_state)
|
||||
|
||||
def get_regional_address_details(out, doctype, company):
|
||||
|
||||
out.place_of_supply = get_place_of_supply(out, doctype)
|
||||
|
||||
if not out.place_of_supply: return
|
||||
|
||||
if doctype in ("Sales Invoice", "Delivery Note"):
|
||||
master_doctype = "Sales Taxes and Charges Template"
|
||||
if not (out.company_gstin or out.place_of_supply):
|
||||
return
|
||||
else:
|
||||
master_doctype = "Purchase Taxes and Charges Template"
|
||||
if not (out.supplier_gstin or out.place_of_supply):
|
||||
return
|
||||
|
||||
if doctype in ("Sales Invoice", "Delivery Note") and out.company_gstin[:2] != out.place_of_supply[:2]\
|
||||
or (doctype == "Purchase Invoice" and out.supplier_gstin[:2] != out.place_of_supply[:2]):
|
||||
default_tax = frappe.db.get_value(master_doctype, {"company": company, "is_inter_state":1, "disabled":0})
|
||||
else:
|
||||
default_tax = frappe.db.get_value(master_doctype, {"company": company, "disabled":0, "is_default": 1})
|
||||
|
||||
if not default_tax:
|
||||
return
|
||||
out["taxes_and_charges"] = default_tax
|
||||
out.taxes = get_taxes_and_charges(master_doctype, default_tax)
|
||||
|
Loading…
x
Reference in New Issue
Block a user