Merge pull request #26473 from deepeshgarg007/bootstrapped_gst_setup_develop

feat(India): Bootstrapped GST Setup
This commit is contained in:
Deepesh Garg 2021-07-14 16:44:12 +05:30 committed by GitHub
commit 13fffeb97b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 542 additions and 105 deletions

View File

@ -2118,9 +2118,9 @@ def make_sales_invoice_for_ewaybill():
if not gst_account: if not gst_account:
gst_settings.append("gst_accounts", { gst_settings.append("gst_accounts", {
"company": "_Test Company", "company": "_Test Company",
"cgst_account": "CGST - _TC", "cgst_account": "Output Tax CGST - _TC",
"sgst_account": "SGST - _TC", "sgst_account": "Output Tax SGST - _TC",
"igst_account": "IGST - _TC", "igst_account": "Output Tax IGST - _TC",
}) })
gst_settings.save() gst_settings.save()
@ -2137,7 +2137,7 @@ def make_sales_invoice_for_ewaybill():
si.append("taxes", { si.append("taxes", {
"charge_type": "On Net Total", "charge_type": "On Net Total",
"account_head": "CGST - _TC", "account_head": "Output Tax CGST - _TC",
"cost_center": "Main - _TC", "cost_center": "Main - _TC",
"description": "CGST @ 9.0", "description": "CGST @ 9.0",
"rate": 9 "rate": 9
@ -2145,7 +2145,7 @@ def make_sales_invoice_for_ewaybill():
si.append("taxes", { si.append("taxes", {
"charge_type": "On Net Total", "charge_type": "On Net Total",
"account_head": "SGST - _TC", "account_head": "Output Tax SGST - _TC",
"cost_center": "Main - _TC", "cost_center": "Main - _TC",
"description": "SGST @ 9.0", "description": "SGST @ 9.0",
"rate": 9 "rate": 9

View File

@ -788,7 +788,7 @@ def get_children(doctype, parent, company, is_root=False):
return acc return acc
def create_payment_gateway_account(gateway, payment_channel="Email"): def create_payment_gateway_account(gateway, payment_channel="Email"):
from erpnext.setup.setup_wizard.operations.company_setup import create_bank_account from erpnext.setup.setup_wizard.operations.install_fixtures import create_bank_account
company = frappe.db.get_value("Global Defaults", None, "default_company") company = frappe.db.get_value("Global Defaults", None, "default_company")
if not company: if not company:

View File

@ -7,16 +7,21 @@ import frappe
import unittest import unittest
from erpnext.erpnext_integrations.doctype.mpesa_settings.mpesa_settings import process_balance_info, verify_transaction from erpnext.erpnext_integrations.doctype.mpesa_settings.mpesa_settings import process_balance_info, verify_transaction
from erpnext.accounts.doctype.pos_invoice.test_pos_invoice import create_pos_invoice from erpnext.accounts.doctype.pos_invoice.test_pos_invoice import create_pos_invoice
from erpnext.erpnext_integrations.utils import create_mode_of_payment
class TestMpesaSettings(unittest.TestCase): class TestMpesaSettings(unittest.TestCase):
def setUp(self):
# create payment gateway in setup
create_mpesa_settings(payment_gateway_name="_Test")
create_mpesa_settings(payment_gateway_name="_Account Balance")
create_mpesa_settings(payment_gateway_name="Payment")
def tearDown(self): def tearDown(self):
frappe.db.sql('delete from `tabMpesa Settings`') frappe.db.sql('delete from `tabMpesa Settings`')
frappe.db.sql('delete from `tabIntegration Request` where integration_request_service = "Mpesa"') frappe.db.sql('delete from `tabIntegration Request` where integration_request_service = "Mpesa"')
def test_creation_of_payment_gateway(self): def test_creation_of_payment_gateway(self):
create_mpesa_settings(payment_gateway_name="_Test") mode_of_payment = create_mode_of_payment('Mpesa-_Test', payment_type="Phone")
mode_of_payment = frappe.get_doc("Mode of Payment", "Mpesa-_Test")
self.assertTrue(frappe.db.exists("Payment Gateway Account", {'payment_gateway': "Mpesa-_Test"})) self.assertTrue(frappe.db.exists("Payment Gateway Account", {'payment_gateway': "Mpesa-_Test"}))
self.assertTrue(mode_of_payment.name) self.assertTrue(mode_of_payment.name)
self.assertEqual(mode_of_payment.type, "Phone") self.assertEqual(mode_of_payment.type, "Phone")
@ -47,7 +52,6 @@ class TestMpesaSettings(unittest.TestCase):
integration_request.delete() integration_request.delete()
def test_processing_of_callback_payload(self): def test_processing_of_callback_payload(self):
create_mpesa_settings(payment_gateway_name="Payment")
mpesa_account = frappe.db.get_value("Payment Gateway Account", {"payment_gateway": 'Mpesa-Payment'}, "payment_account") mpesa_account = frappe.db.get_value("Payment Gateway Account", {"payment_gateway": 'Mpesa-Payment'}, "payment_account")
frappe.db.set_value("Account", mpesa_account, "account_currency", "KES") frappe.db.set_value("Account", mpesa_account, "account_currency", "KES")
frappe.db.set_value("Customer", "_Test Customer", "default_currency", "KES") frappe.db.set_value("Customer", "_Test Customer", "default_currency", "KES")
@ -90,7 +94,6 @@ class TestMpesaSettings(unittest.TestCase):
pos_invoice.delete() pos_invoice.delete()
def test_processing_of_multiple_callback_payload(self): def test_processing_of_multiple_callback_payload(self):
create_mpesa_settings(payment_gateway_name="Payment")
mpesa_account = frappe.db.get_value("Payment Gateway Account", {"payment_gateway": 'Mpesa-Payment'}, "payment_account") mpesa_account = frappe.db.get_value("Payment Gateway Account", {"payment_gateway": 'Mpesa-Payment'}, "payment_account")
frappe.db.set_value("Account", mpesa_account, "account_currency", "KES") frappe.db.set_value("Account", mpesa_account, "account_currency", "KES")
frappe.db.set_value("Mpesa Settings", "Payment", "transaction_limit", "500") frappe.db.set_value("Mpesa Settings", "Payment", "transaction_limit", "500")
@ -141,7 +144,6 @@ class TestMpesaSettings(unittest.TestCase):
pos_invoice.delete() pos_invoice.delete()
def test_processing_of_only_one_succes_callback_payload(self): def test_processing_of_only_one_succes_callback_payload(self):
create_mpesa_settings(payment_gateway_name="Payment")
mpesa_account = frappe.db.get_value("Payment Gateway Account", {"payment_gateway": 'Mpesa-Payment'}, "payment_account") mpesa_account = frappe.db.get_value("Payment Gateway Account", {"payment_gateway": 'Mpesa-Payment'}, "payment_account")
frappe.db.set_value("Account", mpesa_account, "account_currency", "KES") frappe.db.set_value("Account", mpesa_account, "account_currency", "KES")
frappe.db.set_value("Mpesa Settings", "Payment", "transaction_limit", "500") frappe.db.set_value("Mpesa Settings", "Payment", "transaction_limit", "500")
@ -202,6 +204,7 @@ def create_mpesa_settings(payment_gateway_name="Express"):
doc = frappe.get_doc(dict( #nosec doc = frappe.get_doc(dict( #nosec
doctype="Mpesa Settings", doctype="Mpesa Settings",
sandbox=1,
payment_gateway_name=payment_gateway_name, payment_gateway_name=payment_gateway_name,
consumer_key="5sMu9LVI1oS3oBGPJfh3JyvLHwZOdTKn", consumer_key="5sMu9LVI1oS3oBGPJfh3JyvLHwZOdTKn",
consumer_secret="VI1oS3oBGPJfh3JyvLHw", consumer_secret="VI1oS3oBGPJfh3JyvLHw",

View File

@ -52,7 +52,8 @@ def create_mode_of_payment(gateway, payment_type="General"):
"payment_gateway": gateway "payment_gateway": gateway
}, ['payment_account']) }, ['payment_account'])
if not frappe.db.exists("Mode of Payment", gateway) and payment_gateway_account: mode_of_payment = frappe.db.exists("Mode of Payment", gateway)
if not mode_of_payment and payment_gateway_account:
mode_of_payment = frappe.get_doc({ mode_of_payment = frappe.get_doc({
"doctype": "Mode of Payment", "doctype": "Mode of Payment",
"mode_of_payment": gateway, "mode_of_payment": gateway,
@ -66,6 +67,10 @@ def create_mode_of_payment(gateway, payment_type="General"):
}) })
mode_of_payment.insert(ignore_permissions=True) mode_of_payment.insert(ignore_permissions=True)
return mode_of_payment
elif mode_of_payment:
return frappe.get_doc("Mode of Payment", mode_of_payment)
def get_tracking_url(carrier, tracking_number): def get_tracking_url(carrier, tracking_number):
# Return the formatted Tracking URL. # Return the formatted Tracking URL.
tracking_url = '' tracking_url = ''

View File

@ -72,7 +72,8 @@ class TestExpenseClaim(unittest.TestCase):
def test_expense_claim_gl_entry(self): def test_expense_claim_gl_entry(self):
payable_account = get_payable_account(company_name) payable_account = get_payable_account(company_name)
taxes = generate_taxes() taxes = generate_taxes()
expense_claim = make_expense_claim(payable_account, 300, 200, company_name, "Travel Expenses - _TC4", do_not_submit=True, taxes=taxes) expense_claim = make_expense_claim(payable_account, 300, 200, company_name, "Travel Expenses - _TC4",
do_not_submit=True, taxes=taxes)
expense_claim.submit() expense_claim.submit()
gl_entries = frappe.db.sql("""select account, debit, credit gl_entries = frappe.db.sql("""select account, debit, credit
@ -82,7 +83,7 @@ class TestExpenseClaim(unittest.TestCase):
self.assertTrue(gl_entries) self.assertTrue(gl_entries)
expected_values = dict((d[0], d) for d in [ expected_values = dict((d[0], d) for d in [
['CGST - _TC4',18.0, 0.0], ['Output Tax CGST - _TC4',18.0, 0.0],
[payable_account, 0.0, 218.0], [payable_account, 0.0, 218.0],
["Travel Expenses - _TC4", 200.0, 0.0] ["Travel Expenses - _TC4", 200.0, 0.0]
]) ])
@ -145,7 +146,7 @@ def generate_taxes():
parent_account = frappe.db.get_value('Account', parent_account = frappe.db.get_value('Account',
{'company': company_name, 'is_group':1, 'account_type': 'Tax'}, {'company': company_name, 'is_group':1, 'account_type': 'Tax'},
'name') 'name')
account = create_account(company=company_name, account_name="CGST", account_type="Tax", parent_account=parent_account) account = create_account(company=company_name, account_name="Output Tax CGST", account_type="Tax", parent_account=parent_account)
return {'taxes':[{ return {'taxes':[{
"account_head": account, "account_head": account,
"rate": 0, "rate": 0,

View File

@ -147,7 +147,7 @@ erpnext.setup.slides_settings = [
} }
// Validate bank name // Validate bank name
if(me.values.bank_account){ if(me.values.bank_account) {
frappe.call({ frappe.call({
async: false, async: false,
method: "erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts.validate_bank_account", method: "erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts.validate_bank_account",

View File

@ -19,6 +19,21 @@ class GSTSettings(Document):
from tabAddress where country = "India" and ifnull(gstin, '')!='' ''') from tabAddress where country = "India" and ifnull(gstin, '')!='' ''')
self.set_onload('data', data) self.set_onload('data', data)
def validate(self):
# Validate duplicate accounts
self.validate_duplicate_accounts()
def validate_duplicate_accounts(self):
account_list = []
for account in self.get('gst_accounts'):
for fieldname in ['cgst_account', 'sgst_account', 'igst_account', 'cess_account']:
if account.get(fieldname) in account_list:
frappe.throw(_("Account {0} appears multiple times").format(
frappe.bold(account.get(fieldname))))
if account.get(fieldname):
account_list.append(account.get(fieldname))
@frappe.whitelist() @frappe.whitelist()
def send_reminder(): def send_reminder():
frappe.has_permission('GST Settings', throw=True) frappe.has_permission('GST Settings', throw=True)

View File

@ -46,14 +46,14 @@ class TestGSTR3BReport(unittest.TestCase):
make_sales_invoice() make_sales_invoice()
create_purchase_invoices() create_purchase_invoices()
if frappe.db.exists("GSTR 3B Report", "GSTR3B-March-2019-_Test Address-Billing"): if frappe.db.exists("GSTR 3B Report", "GSTR3B-March-2019-_Test Address GST-Billing"):
report = frappe.get_doc("GSTR 3B Report", "GSTR3B-March-2019-_Test Address-Billing") report = frappe.get_doc("GSTR 3B Report", "GSTR3B-March-2019-_Test Address GST-Billing")
report.save() report.save()
else: else:
report = frappe.get_doc({ report = frappe.get_doc({
"doctype": "GSTR 3B Report", "doctype": "GSTR 3B Report",
"company": "_Test Company GST", "company": "_Test Company GST",
"company_address": "_Test Address-Billing", "company_address": "_Test Address GST-Billing",
"year": getdate().year, "year": getdate().year,
"month": month_number_mapping.get(getdate().month) "month": month_number_mapping.get(getdate().month)
}).insert() }).insert()
@ -89,7 +89,7 @@ class TestGSTR3BReport(unittest.TestCase):
si.append("taxes", { si.append("taxes", {
"charge_type": "On Net Total", "charge_type": "On Net Total",
"account_head": "IGST - _GST", "account_head": "Output Tax IGST - _GST",
"cost_center": "Main - _GST", "cost_center": "Main - _GST",
"description": "IGST @ 18.0", "description": "IGST @ 18.0",
"rate": 18 "rate": 18
@ -117,7 +117,7 @@ def make_sales_invoice():
si.append("taxes", { si.append("taxes", {
"charge_type": "On Net Total", "charge_type": "On Net Total",
"account_head": "IGST - _GST", "account_head": "Output Tax IGST - _GST",
"cost_center": "Main - _GST", "cost_center": "Main - _GST",
"description": "IGST @ 18.0", "description": "IGST @ 18.0",
"rate": 18 "rate": 18
@ -138,7 +138,7 @@ def make_sales_invoice():
si1.append("taxes", { si1.append("taxes", {
"charge_type": "On Net Total", "charge_type": "On Net Total",
"account_head": "IGST - _GST", "account_head": "Output Tax IGST - _GST",
"cost_center": "Main - _GST", "cost_center": "Main - _GST",
"description": "IGST @ 18.0", "description": "IGST @ 18.0",
"rate": 18 "rate": 18
@ -159,7 +159,7 @@ def make_sales_invoice():
si2.append("taxes", { si2.append("taxes", {
"charge_type": "On Net Total", "charge_type": "On Net Total",
"account_head": "IGST - _GST", "account_head": "Output Tax IGST - _GST",
"cost_center": "Main - _GST", "cost_center": "Main - _GST",
"description": "IGST @ 18.0", "description": "IGST @ 18.0",
"rate": 18 "rate": 18
@ -195,7 +195,7 @@ def create_purchase_invoices():
pi.append("taxes", { pi.append("taxes", {
"charge_type": "On Net Total", "charge_type": "On Net Total",
"account_head": "CGST - _GST", "account_head": "Input Tax CGST - _GST",
"cost_center": "Main - _GST", "cost_center": "Main - _GST",
"description": "CGST @ 9.0", "description": "CGST @ 9.0",
"rate": 9 "rate": 9
@ -203,7 +203,7 @@ def create_purchase_invoices():
pi.append("taxes", { pi.append("taxes", {
"charge_type": "On Net Total", "charge_type": "On Net Total",
"account_head": "SGST - _GST", "account_head": "Input Tax SGST - _GST",
"cost_center": "Main - _GST", "cost_center": "Main - _GST",
"description": "SGST @ 9.0", "description": "SGST @ 9.0",
"rate": 9 "rate": 9
@ -410,10 +410,10 @@ def make_company():
company.country = "India" company.country = "India"
company.insert() company.insert()
if not frappe.db.exists('Address', '_Test Address-Billing'): if not frappe.db.exists('Address', '_Test Address GST-Billing'):
address = frappe.get_doc({ address = frappe.get_doc({
"address_title": "_Test Address GST",
"address_line1": "_Test Address Line 1", "address_line1": "_Test Address Line 1",
"address_title": "_Test Address",
"address_type": "Billing", "address_type": "Billing",
"city": "_Test City", "city": "_Test City",
"state": "Test State", "state": "Test State",
@ -444,9 +444,9 @@ def set_account_heads():
if not gst_account: if not gst_account:
gst_settings.append("gst_accounts", { gst_settings.append("gst_accounts", {
"company": "_Test Company GST", "company": "_Test Company GST",
"cgst_account": "CGST - _GST", "cgst_account": "Output Tax CGST - _GST",
"sgst_account": "SGST - _GST", "sgst_account": "Output Tax SGST - _GST",
"igst_account": "IGST - _GST", "igst_account": "Output Tax IGST - _GST"
}) })
gst_settings.save() gst_settings.save()

View File

@ -25,6 +25,7 @@ def setup_company_independent_fixtures(patch=False):
frappe.enqueue('erpnext.regional.india.setup.add_hsn_sac_codes', now=frappe.flags.in_test) frappe.enqueue('erpnext.regional.india.setup.add_hsn_sac_codes', now=frappe.flags.in_test)
create_gratuity_rule() create_gratuity_rule()
add_print_formats() add_print_formats()
update_accounts_settings_for_taxes()
def add_hsn_sac_codes(): def add_hsn_sac_codes():
if frappe.flags.in_test and frappe.flags.created_hsn_codes: if frappe.flags.in_test and frappe.flags.created_hsn_codes:
@ -680,7 +681,7 @@ def make_custom_fields(update=True):
def make_fixtures(company=None): def make_fixtures(company=None):
docs = [] docs = []
company = company.name if company else frappe.db.get_value("Global Defaults", None, "default_company") company = company or frappe.db.get_value("Global Defaults", None, "default_company")
set_salary_components(docs) set_salary_components(docs)
set_tds_account(docs, company) set_tds_account(docs, company)
@ -698,6 +699,53 @@ def make_fixtures(company=None):
# create records for Tax Withholding Category # create records for Tax Withholding Category
set_tax_withholding_category(company) set_tax_withholding_category(company)
def update_regional_tax_settings(country, company):
# Will only add default GST accounts if present
input_account_names = ['Input Tax CGST', 'Input Tax SGST', 'Input Tax IGST']
output_account_names = ['Output Tax CGST', 'Output Tax SGST', 'Output Tax IGST']
rcm_accounts = ['Input Tax CGST RCM', 'Input Tax SGST RCM', 'Input Tax IGST RCM']
gst_settings = frappe.get_single('GST Settings')
existing_account_list = []
for account in gst_settings.get('gst_accounts'):
for key in ['cgst_account', 'sgst_account', 'igst_account']:
existing_account_list.append(account.get(key))
gst_accounts = frappe._dict(frappe.get_all("Account",
{'company': company, 'account_name': ('in', input_account_names +
output_account_names + rcm_accounts)}, ['account_name', 'name'], as_list=1))
add_accounts_in_gst_settings(company, input_account_names, gst_accounts,
existing_account_list, gst_settings)
add_accounts_in_gst_settings(company, output_account_names, gst_accounts,
existing_account_list, gst_settings)
add_accounts_in_gst_settings(company, rcm_accounts, gst_accounts,
existing_account_list, gst_settings, is_reverse_charge=1)
gst_settings.save()
def add_accounts_in_gst_settings(company, account_names, gst_accounts,
existing_account_list, gst_settings, is_reverse_charge=0):
accounts_not_added = 1
for account in account_names:
# Default Account Added does not exists
if not gst_accounts.get(account):
accounts_not_added = 0
# Check if already added in GST Settings
if gst_accounts.get(account) in existing_account_list:
accounts_not_added = 0
if accounts_not_added:
gst_settings.append('gst_accounts', {
'company': company,
'cgst_account': gst_accounts.get(account_names[0]),
'sgst_account': gst_accounts.get(account_names[1]),
'igst_account': gst_accounts.get(account_names[2]),
'is_reverse_charge_account': is_reverse_charge
})
def set_salary_components(docs): def set_salary_components(docs):
docs.extend([ docs.extend([
{'doctype': 'Salary Component', 'salary_component': 'Professional Tax', {'doctype': 'Salary Component', 'salary_component': 'Professional Tax',
@ -731,12 +779,13 @@ def set_tax_withholding_category(company):
docs = get_tds_details(accounts, fiscal_year) docs = get_tds_details(accounts, fiscal_year)
for d in docs: for d in docs:
try: if not frappe.db.exists("Tax Withholding Category", d.get("name")):
doc = frappe.get_doc(d) doc = frappe.get_doc(d)
doc.flags.ignore_validate = True
doc.flags.ignore_permissions = True doc.flags.ignore_permissions = True
doc.flags.ignore_mandatory = True doc.flags.ignore_mandatory = True
doc.insert() doc.insert()
except frappe.DuplicateEntryError: else:
doc = frappe.get_doc("Tax Withholding Category", d.get("name")) doc = frappe.get_doc("Tax Withholding Category", d.get("name"))
if accounts: if accounts:
@ -749,11 +798,12 @@ def set_tax_withholding_category(company):
doc.append("rates", d.get('rates')[0]) doc.append("rates", d.get('rates')[0])
doc.flags.ignore_permissions = True doc.flags.ignore_permissions = True
doc.flags.ignore_validate = True
doc.flags.ignore_mandatory = True doc.flags.ignore_mandatory = True
doc.flags.ignore_links = True
doc.save() doc.save()
def set_tds_account(docs, company): def set_tds_account(docs, company):
abbr = frappe.get_value("Company", company, "abbr")
parent_account = frappe.db.get_value("Account", filters = {"account_name": "Duties and Taxes", "company": company}) parent_account = frappe.db.get_value("Account", filters = {"account_name": "Duties and Taxes", "company": company})
if parent_account: if parent_account:
docs.extend([ docs.extend([
@ -912,7 +962,6 @@ def get_tds_details(accounts, fiscal_year):
] ]
def create_gratuity_rule(): def create_gratuity_rule():
# Standard Indain Gratuity Rule # Standard Indain Gratuity Rule
if not frappe.db.exists("Gratuity Rule", "Indian Standard Gratuity Rule"): if not frappe.db.exists("Gratuity Rule", "Indian Standard Gratuity Rule"):
rule = frappe.new_doc("Gratuity Rule") rule = frappe.new_doc("Gratuity Rule")
@ -930,3 +979,7 @@ def create_gratuity_rule():
rule.flags.ignore_mandatory = True rule.flags.ignore_mandatory = True
rule.save() rule.save()
def update_accounts_settings_for_taxes():
if frappe.db.count('Company') == 1:
frappe.db.set_value('Accounts Settings', None, "add_taxes_from_item_tax_template", 0)

View File

@ -11,7 +11,7 @@
"is_standard": "Yes", "is_standard": "Yes",
"json": "{}", "json": "{}",
"letter_head": "Logo", "letter_head": "Logo",
"modified": "2021-03-12 12:36:48.689413", "modified": "2021-03-13 12:36:48.689413",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Regional", "module": "Regional",
"name": "E-Invoice Summary", "name": "E-Invoice Summary",

View File

@ -110,7 +110,7 @@ class Company(NestedSet):
self.create_default_warehouses() self.create_default_warehouses()
if frappe.flags.country_change: if frappe.flags.country_change:
install_country_fixtures(self.name) install_country_fixtures(self.name, self.country)
self.create_default_tax_template() self.create_default_tax_template()
if not frappe.db.get_value("Department", {"company": self.name}): if not frappe.db.get_value("Department", {"company": self.name}):
@ -440,16 +440,15 @@ def get_name_with_abbr(name, company):
return " - ".join(parts) return " - ".join(parts)
def install_country_fixtures(company): def install_country_fixtures(company, country):
company_doc = frappe.get_doc("Company", company) path = frappe.get_app_path('erpnext', 'regional', frappe.scrub(country))
path = frappe.get_app_path('erpnext', 'regional', frappe.scrub(company_doc.country))
if os.path.exists(path.encode("utf-8")): if os.path.exists(path.encode("utf-8")):
try: try:
module_name = "erpnext.regional.{0}.setup.setup".format(frappe.scrub(company_doc.country)) module_name = "erpnext.regional.{0}.setup.setup".format(frappe.scrub(country))
frappe.get_attr(module_name)(company_doc, False) frappe.get_attr(module_name)(company, False)
except Exception as e: except Exception as e:
frappe.log_error() frappe.log_error()
frappe.throw(_("Failed to setup defaults for country {0}. Please contact support@erpnext.com").format(frappe.bold(company_doc.country))) frappe.throw(_("Failed to setup defaults for country {0}. Please contact support@erpnext.com").format(frappe.bold(country)))
def update_company_current_month_sales(company): def update_company_current_month_sales(company):

View File

@ -1164,33 +1164,292 @@
}, },
"India": { "India": {
"tax_categories": [
{
"title": "In-State",
"is_inter_state": 0,
"gst_state": ""
},
{
"title": "Out-State",
"is_inter_state": 1,
"gst_state": ""
},
{
"title": "Reverse Charge In-State",
"is_inter_state": 0,
"gst_state": ""
},
{
"title": "Reverse Charge Out-State",
"is_inter_state": 1,
"gst_state": ""
},
{
"title": "Registered Composition",
"is_inter_state": 0,
"gst_state": ""
}
],
"chart_of_accounts": { "chart_of_accounts": {
"*": { "*": {
"item_tax_templates": [ "item_tax_templates": [
{ {
"title": "In State GST", "title": "GST 9%",
"taxes": [ "taxes": [
{ {
"tax_type": { "tax_type": {
"account_name": "SGST", "account_name": "Output Tax SGST",
"tax_rate": 9.00 "tax_rate": 9.00
} }
}, },
{ {
"tax_type": { "tax_type": {
"account_name": "CGST", "account_name": "Output Tax CGST",
"tax_rate": 9.00 "tax_rate": 9.00
} }
},
{
"tax_type": {
"account_name": "Output Tax IGST",
"tax_rate": 18.00
}
},
{
"tax_type": {
"account_name": "Input Tax SGST",
"tax_rate": 9.00,
"root_type": "Asset"
}
},
{
"tax_type": {
"account_name": "Input Tax CGST",
"tax_rate": 9.00,
"root_type": "Asset"
}
},
{
"tax_type": {
"account_name": "Input Tax IGST",
"tax_rate": 18.00,
"root_type": "Asset"
}
},
{
"tax_type": {
"account_name": "Input Tax SGST RCM",
"tax_rate": 9.00,
"root_type": "Asset"
}
},
{
"tax_type": {
"account_name": "Input Tax CGST RCM",
"tax_rate": 9.00,
"root_type": "Asset"
}
},
{
"tax_type": {
"account_name": "Input Tax IGST RCM",
"tax_rate": 18.00,
"root_type": "Asset"
}
} }
] ]
}, },
{ {
"title": "Out of State GST", "title": "GST 5%",
"taxes": [ "taxes": [
{ {
"tax_type": { "tax_type": {
"account_name": "IGST", "account_name": "Output Tax SGST",
"tax_rate": 18.00 "tax_rate": 2.5
}
},
{
"tax_type": {
"account_name": "Output Tax CGST",
"tax_rate": 2.5
}
},
{
"tax_type": {
"account_name": "Output Tax IGST",
"tax_rate": 5.0
}
},
{
"tax_type": {
"account_name": "Input Tax SGST",
"tax_rate": 2.5,
"root_type": "Asset"
}
},
{
"tax_type": {
"account_name": "Input Tax CGST",
"tax_rate": 2.5,
"root_type": "Asset"
}
},
{
"tax_type": {
"account_name": "Input Tax IGST",
"tax_rate": 5.0,
"root_type": "Asset"
}
},
{
"tax_type": {
"account_name": "Input Tax SGST RCM",
"tax_rate": 2.50,
"root_type": "Asset"
}
},
{
"tax_type": {
"account_name": "Input Tax CGST RCM",
"tax_rate": 2.50,
"root_type": "Asset"
}
},
{
"tax_type": {
"account_name": "Input Tax IGST RCM",
"tax_rate": 5.00,
"root_type": "Asset"
}
}
]
},
{
"title": "GST 12%",
"taxes": [
{
"tax_type": {
"account_name": "Output Tax SGST",
"tax_rate": 6.0
}
},
{
"tax_type": {
"account_name": "Output Tax CGST",
"tax_rate": 6.0
}
},
{
"tax_type": {
"account_name": "Output Tax IGST",
"tax_rate": 12.0
}
},
{
"tax_type": {
"account_name": "Input Tax SGST",
"tax_rate": 6.0,
"root_type": "Asset"
}
},
{
"tax_type": {
"account_name": "Input Tax CGST",
"tax_rate": 6.0,
"root_type": "Asset"
}
},
{
"tax_type": {
"account_name": "Input Tax IGST",
"tax_rate": 12.0,
"root_type": "Asset"
}
},
{
"tax_type": {
"account_name": "Input Tax SGST RCM",
"tax_rate": 6.00,
"root_type": "Asset"
}
},
{
"tax_type": {
"account_name": "Input Tax CGST RCM",
"tax_rate": 6.00,
"root_type": "Asset"
}
},
{
"tax_type": {
"account_name": "Input Tax IGST RCM",
"tax_rate": 12.00,
"root_type": "Asset"
}
}
]
},
{
"title": "GST 28%",
"taxes": [
{
"tax_type": {
"account_name": "Output Tax SGST",
"tax_rate": 14.0
}
},
{
"tax_type": {
"account_name": "Output Tax CGST",
"tax_rate": 14.0
}
},
{
"tax_type": {
"account_name": "Output Tax IGST",
"tax_rate": 28.0
}
},
{
"tax_type": {
"account_name": "Input Tax SGST",
"tax_rate": 14.0,
"root_type": "Asset"
}
},
{
"tax_type": {
"account_name": "Input Tax CGST",
"tax_rate": 14.0,
"root_type": "Asset"
}
},
{
"tax_type": {
"account_name": "Input Tax IGST",
"tax_rate": 28.0,
"root_type": "Asset"
}
},
{
"tax_type": {
"account_name": "Input Tax SGST RCM",
"tax_rate": 14.00,
"root_type": "Asset"
}
},
{
"tax_type": {
"account_name": "Input Tax CGST RCM",
"tax_rate": 14.00,
"root_type": "Asset"
}
},
{
"tax_type": {
"account_name": "Input Tax IGST RCM",
"tax_rate": 28.00,
"root_type": "Asset"
} }
} }
] ]
@ -1229,35 +1488,116 @@
] ]
} }
], ],
"sales_tax_templates": [
{
"title": "Output GST In-state",
"taxes": [
{
"account_head": {
"account_name": "Output Tax SGST",
"tax_rate": 9.00,
"account_type": "Tax"
}
},
{
"account_head": {
"account_name": "Output Tax CGST",
"tax_rate": 9.00,
"account_type": "Tax"
}
}
],
"tax_category": "In-State"
},
{
"title": "Output GST Out-state",
"taxes": [
{
"account_head": {
"account_name": "Output Tax IGST",
"tax_rate": 18.00,
"account_type": "Tax"
}
}
],
"tax_category": "Out-State"
}
],
"purchase_tax_templates": [
{
"title": "Input GST In-state",
"taxes": [
{
"account_head": {
"account_name": "Input Tax SGST",
"tax_rate": 9.00,
"root_type": "Asset",
"account_type": "Tax"
}
},
{
"account_head": {
"account_name": "Input Tax CGST",
"tax_rate": 9.00,
"root_type": "Asset",
"account_type": "Tax"
}
}
],
"tax_category": "In-State"
},
{
"title": "Input GST Out-state",
"taxes": [
{
"account_head": {
"account_name": "Input Tax IGST",
"tax_rate": 18.00,
"root_type": "Asset",
"account_type": "Tax"
}
}
],
"tax_category": "Out-State"
},
{
"title": "Input GST RCM In-state",
"taxes": [
{
"account_head": {
"account_name": "Input Tax SGST RCM",
"tax_rate": 9.00,
"root_type": "Asset",
"account_type": "Tax"
}
},
{
"account_head": {
"account_name": "Input Tax CGST RCM",
"tax_rate": 9.00,
"root_type": "Asset",
"account_type": "Tax"
}
}
],
"tax_category": "Reverse Charge In-State"
},
{
"title": "Input GST RCM Out-state",
"taxes": [
{
"account_head": {
"account_name": "Input Tax IGST RCM",
"tax_rate": 18.00,
"root_type": "Asset",
"account_type": "Tax"
}
}
],
"tax_category": "Reverse Charge Out-State"
}
],
"*": [ "*": [
{
"title": "In State GST",
"taxes": [
{
"account_head": {
"account_name": "SGST",
"tax_rate": 9.00
}
},
{
"account_head": {
"account_name": "CGST",
"tax_rate": 9.00
}
}
]
},
{
"title": "Out of State GST",
"taxes": [
{
"account_head": {
"account_name": "IGST",
"tax_rate": 18.00
}
}
]
},
{ {
"title": "VAT 5%", "title": "VAT 5%",
"taxes": [ "taxes": [

View File

@ -42,29 +42,6 @@ def enable_shopping_cart(args):
'quotation_series': "QTN-", 'quotation_series': "QTN-",
}).insert() }).insert()
def create_bank_account(args):
if args.get("bank_account"):
company_name = args.get('company_name')
bank_account_group = frappe.db.get_value("Account",
{"account_type": "Bank", "is_group": 1, "root_type": "Asset",
"company": company_name})
if bank_account_group:
bank_account = frappe.get_doc({
"doctype": "Account",
'account_name': args.get("bank_account"),
'parent_account': bank_account_group,
'is_group':0,
'company': company_name,
"account_type": "Bank",
})
try:
return bank_account.insert()
except RootNotEditable:
frappe.throw(_("Bank account cannot be named as {0}").format(args.get("bank_account")))
except frappe.DuplicateEntryError:
# bank account same as a CoA entry
pass
def create_email_digest(): def create_email_digest():
from frappe.utils.user import get_system_managers from frappe.utils.user import get_system_managers
system_managers = get_system_managers(only_name=True) system_managers = get_system_managers(only_name=True)

View File

@ -448,6 +448,8 @@ def install_defaults(args=None):
set_active_domains(args) set_active_domains(args)
update_stock_settings() update_stock_settings()
update_shopping_cart_settings(args) update_shopping_cart_settings(args)
args.update({"set_default": 1})
create_bank_account(args) create_bank_account(args)
def set_global_defaults(args): def set_global_defaults(args):
@ -479,17 +481,17 @@ def update_stock_settings():
stock_settings.save() stock_settings.save()
def create_bank_account(args): def create_bank_account(args):
if not args.bank_account: if not args.get('bank_account'):
return return
company_name = args.company_name company_name = args.get('company_name')
bank_account_group = frappe.db.get_value("Account", bank_account_group = frappe.db.get_value("Account",
{"account_type": "Bank", "is_group": 1, "root_type": "Asset", {"account_type": "Bank", "is_group": 1, "root_type": "Asset",
"company": company_name}) "company": company_name})
if bank_account_group: if bank_account_group:
bank_account = frappe.get_doc({ bank_account = frappe.get_doc({
"doctype": "Account", "doctype": "Account",
'account_name': args.bank_account, 'account_name': args.get('bank_account'),
'parent_account': bank_account_group, 'parent_account': bank_account_group,
'is_group':0, 'is_group':0,
'company': company_name, 'company': company_name,
@ -498,10 +500,13 @@ def create_bank_account(args):
try: try:
doc = bank_account.insert() doc = bank_account.insert()
frappe.db.set_value("Company", args.company_name, "default_bank_account", bank_account.name, update_modified=False) if args.get('set_default'):
frappe.db.set_value("Company", args.get('company_name'), "default_bank_account", bank_account.name, update_modified=False)
return doc
except RootNotEditable: except RootNotEditable:
frappe.throw(_("Bank account cannot be named as {0}").format(args.bank_account)) frappe.throw(_("Bank account cannot be named as {0}").format(args.get('bank_account')))
except frappe.DuplicateEntryError: except frappe.DuplicateEntryError:
# bank account same as a CoA entry # bank account same as a CoA entry
pass pass

View File

@ -26,7 +26,8 @@ def setup_taxes_and_charges(company_name: str, country: str):
if 'chart_of_accounts' not in country_wise_tax: if 'chart_of_accounts' not in country_wise_tax:
country_wise_tax = simple_to_detailed(country_wise_tax) country_wise_tax = simple_to_detailed(country_wise_tax)
from_detailed_data(company_name, country_wise_tax) from_detailed_data(company_name, country_wise_tax.get('chart_of_accounts'))
update_regional_tax_settings(country, company_name)
def simple_to_detailed(templates): def simple_to_detailed(templates):
@ -77,16 +78,15 @@ def simple_to_detailed(templates):
def from_detailed_data(company_name, data): def from_detailed_data(company_name, data):
"""Create Taxes and Charges Templates from detailed data.""" """Create Taxes and Charges Templates from detailed data."""
coa_name = frappe.db.get_value('Company', company_name, 'chart_of_accounts') coa_name = frappe.db.get_value('Company', company_name, 'chart_of_accounts')
coa_data = data.get('chart_of_accounts', {}) tax_templates = data.get(coa_name) or data.get('*')
tax_templates = coa_data.get(coa_name) or coa_data.get('*', {}) sales_tax_templates = tax_templates.get('sales_tax_templates') or tax_templates.get('*')
tax_categories = data.get('tax_categories') purchase_tax_templates = tax_templates.get('purchase_tax_templates') or tax_templates.get('*')
sales_tax_templates = tax_templates.get('sales_tax_templates') or tax_templates.get('*', {}) item_tax_templates = tax_templates.get('item_tax_templates') or tax_templates.get('*')
purchase_tax_templates = tax_templates.get('purchase_tax_templates') or tax_templates.get('*', {}) tax_categories = tax_templates.get('tax_categories')
item_tax_templates = tax_templates.get('item_tax_templates') or tax_templates.get('*', {})
if tax_categories: if tax_categories:
for tax_category in tax_categories: for tax_category in tax_categories:
make_tax_catgory(tax_category) make_tax_category(tax_category)
if sales_tax_templates: if sales_tax_templates:
for template in sales_tax_templates: for template in sales_tax_templates:
@ -101,6 +101,17 @@ def from_detailed_data(company_name, data):
make_item_tax_template(company_name, template) make_item_tax_template(company_name, template)
def update_regional_tax_settings(country, company):
path = frappe.get_app_path('erpnext', 'regional', frappe.scrub(country))
if os.path.exists(path.encode("utf-8")):
try:
module_name = "erpnext.regional.{0}.setup.update_regional_tax_settings".format(frappe.scrub(country))
frappe.get_attr(module_name)(country, company)
except Exception as e:
# Log error and ignore if failed to setup regional tax settings
frappe.log_error()
pass
def make_taxes_and_charges_template(company_name, doctype, template): def make_taxes_and_charges_template(company_name, doctype, template):
template['company'] = company_name template['company'] = company_name
template['doctype'] = doctype template['doctype'] = doctype
@ -130,8 +141,14 @@ def make_taxes_and_charges_template(company_name, doctype, template):
if fieldname not in tax_row: if fieldname not in tax_row:
tax_row[fieldname] = default_value tax_row[fieldname] = default_value
return frappe.get_doc(template).insert(ignore_permissions=True) doc = frappe.get_doc(template)
# Data in country wise json is already pre validated, hence validations can be ignored
# Ingone validations to make doctypes faster
doc.flags.ignore_links = True
doc.flags.ignore_validate = True
doc.insert(ignore_permissions=True)
return doc
def make_item_tax_template(company_name, template): def make_item_tax_template(company_name, template):
"""Create an Item Tax Template. """Create an Item Tax Template.
@ -156,8 +173,24 @@ def make_item_tax_template(company_name, template):
if 'tax_rate' not in tax_row: if 'tax_rate' not in tax_row:
tax_row['tax_rate'] = account_data.get('tax_rate') tax_row['tax_rate'] = account_data.get('tax_rate')
return frappe.get_doc(template).insert(ignore_permissions=True) doc = frappe.get_doc(template)
# Data in country wise json is already pre validated, hence validations can be ignored
# Ingone validations to make doctypes faster
doc.flags.ignore_links = True
doc.flags.ignore_validate = True
doc.insert(ignore_permissions=True)
return doc
def make_tax_category(tax_category):
""" Make tax category based on title if not already created """
doctype = 'Tax Category'
if not frappe.db.exists(doctype, tax_category['title']):
tax_category['doctype'] = doctype
doc = frappe.get_doc(tax_category)
doc.flags.ignore_links = True
doc.flags.ignore_validate = True
doc.insert(ignore_permissions=True)
def get_or_create_account(company_name, account): def get_or_create_account(company_name, account):
""" """
@ -175,8 +208,7 @@ def get_or_create_account(company_name, account):
or_filters={ or_filters={
'account_name': account.get('account_name'), 'account_name': account.get('account_name'),
'account_number': account.get('account_number') 'account_number': account.get('account_number')
} })
)
if existing_accounts: if existing_accounts:
return frappe.get_doc('Account', existing_accounts[0].name) return frappe.get_doc('Account', existing_accounts[0].name)
@ -191,8 +223,11 @@ def get_or_create_account(company_name, account):
account['root_type'] = root_type account['root_type'] = root_type
account['is_group'] = 0 account['is_group'] = 0
return frappe.get_doc(account).insert(ignore_permissions=True, ignore_mandatory=True) doc = frappe.get_doc(account)
doc.flags.ignore_links = True
doc.flags.ignore_validate = True
doc.insert(ignore_permissions=True, ignore_mandatory=True)
return doc
def get_or_create_tax_group(company_name, root_type): def get_or_create_tax_group(company_name, root_type):
# Look for a group account of type 'Tax' # Look for a group account of type 'Tax'
@ -237,7 +272,11 @@ def get_or_create_tax_group(company_name, root_type):
'account_type': 'Tax', 'account_type': 'Tax',
'account_name': account_name, 'account_name': account_name,
'parent_account': root_account.name 'parent_account': root_account.name
}).insert(ignore_permissions=True) })
tax_group_account.flags.ignore_links = True
tax_group_account.flags.ignore_validate = True
tax_group_account.insert(ignore_permissions=True)
tax_group_name = tax_group_account.name tax_group_name = tax_group_account.name