Merge branch 'develop' into tcs_calculation
diff --git a/erpnext/regional/doctype/gst_settings/gst_settings.json b/erpnext/regional/doctype/gst_settings/gst_settings.json
index 98c33ad..95b930c 100644
--- a/erpnext/regional/doctype/gst_settings/gst_settings.json
+++ b/erpnext/regional/doctype/gst_settings/gst_settings.json
@@ -1,222 +1,86 @@
{
- "allow_copy": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "beta": 0,
- "creation": "2017-06-27 15:09:01.318003",
- "custom": 0,
- "docstatus": 0,
- "doctype": "DocType",
- "document_type": "",
- "editable_grid": 1,
- "engine": "InnoDB",
+ "actions": [],
+ "creation": "2017-06-27 15:09:01.318003",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "gst_summary",
+ "column_break_2",
+ "round_off_gst_values",
+ "gstin_email_sent_on",
+ "section_break_4",
+ "gst_accounts",
+ "b2c_limit"
+ ],
"fields": [
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "gst_summary",
- "fieldtype": "HTML",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "GST Summary",
- "length": 0,
- "no_copy": 0,
- "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
- },
+ "fieldname": "gst_summary",
+ "fieldtype": "HTML",
+ "label": "GST Summary",
+ "show_days": 1,
+ "show_seconds": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "column_break_2",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "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
- },
+ "fieldname": "column_break_2",
+ "fieldtype": "Column Break",
+ "show_days": 1,
+ "show_seconds": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "gstin_email_sent_on",
- "fieldtype": "Date",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "GSTIN Email Sent On",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
+ "fieldname": "gstin_email_sent_on",
+ "fieldtype": "Date",
+ "label": "GSTIN Email Sent On",
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "section_break_4",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "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
- },
+ "fieldname": "section_break_4",
+ "fieldtype": "Section Break",
+ "show_days": 1,
+ "show_seconds": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "gst_accounts",
- "fieldtype": "Table",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "GST Accounts",
- "length": 0,
- "no_copy": 0,
- "options": "GST 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
- },
+ "fieldname": "gst_accounts",
+ "fieldtype": "Table",
+ "label": "GST Accounts",
+ "options": "GST Account",
+ "show_days": 1,
+ "show_seconds": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "default": "250000",
- "description": "Set Invoice Value for B2C. B2CL and B2CS calculated based on this invoice value.",
- "fieldname": "b2c_limit",
- "fieldtype": "Data",
- "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": "B2C Limit",
- "length": 0,
- "no_copy": 0,
- "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
+ "default": "250000",
+ "description": "Set Invoice Value for B2C. B2CL and B2CS calculated based on this invoice value.",
+ "fieldname": "b2c_limit",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "B2C Limit",
+ "reqd": 1,
+ "show_days": 1,
+ "show_seconds": 1
+ },
+ {
+ "default": "0",
+ "description": "Enabling this option will round off individual GST components in all the Invoices",
+ "fieldname": "round_off_gst_values",
+ "fieldtype": "Check",
+ "label": "Round Off GST Values",
+ "show_days": 1,
+ "show_seconds": 1
}
- ],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
- "idx": 0,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 0,
- "issingle": 1,
- "istable": 0,
- "max_attachments": 0,
- "modified": "2018-02-14 08:14:15.375181",
- "modified_by": "Administrator",
- "module": "Regional",
- "name": "GST Settings",
- "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
-}
\ No newline at end of file
+ ],
+ "index_web_pages_for_search": 1,
+ "issingle": 1,
+ "links": [],
+ "modified": "2021-01-28 17:19:47.969260",
+ "modified_by": "Administrator",
+ "module": "Regional",
+ "name": "GST Settings",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+ }
\ No newline at end of file
diff --git a/erpnext/regional/doctype/gstr_3b_report/test_gstr_3b_report.py b/erpnext/regional/doctype/gstr_3b_report/test_gstr_3b_report.py
index 8174da2..023b4ed 100644
--- a/erpnext/regional/doctype/gstr_3b_report/test_gstr_3b_report.py
+++ b/erpnext/regional/doctype/gstr_3b_report/test_gstr_3b_report.py
@@ -14,8 +14,20 @@
test_dependencies = ["Territory", "Customer Group", "Supplier Group", "Item"]
class TestGSTR3BReport(unittest.TestCase):
- def test_gstr_3b_report(self):
+ def setUp(self):
+ frappe.set_user("Administrator")
+ frappe.db.sql("delete from `tabSales Invoice` where company='_Test Company GST'")
+ frappe.db.sql("delete from `tabPurchase Invoice` where company='_Test Company GST'")
+ frappe.db.sql("delete from `tabGSTR 3B Report` where company='_Test Company GST'")
+
+ make_company()
+ make_item("Milk", properties = {"is_nil_exempt": 1, "standard_rate": 0.000000})
+ set_account_heads()
+ make_customers()
+ make_suppliers()
+
+ def test_gstr_3b_report(self):
month_number_mapping = {
1: "January",
2: "February",
@@ -31,17 +43,6 @@
12: "December"
}
- frappe.set_user("Administrator")
-
- frappe.db.sql("delete from `tabSales Invoice` where company='_Test Company GST'")
- frappe.db.sql("delete from `tabPurchase Invoice` where company='_Test Company GST'")
- frappe.db.sql("delete from `tabGSTR 3B Report` where company='_Test Company GST'")
-
- make_company()
- make_item("Milk", properties = {"is_nil_exempt": 1, "standard_rate": 0.000000})
- set_account_heads()
- make_customers()
- make_suppliers()
make_sales_invoice()
create_purchase_invoices()
@@ -67,6 +68,42 @@
self.assertEqual(output["itc_elg"]["itc_avl"][4]["samt"], 22.50)
self.assertEqual(output["itc_elg"]["itc_avl"][4]["camt"], 22.50)
+ def test_gst_rounding(self):
+ gst_settings = frappe.get_doc('GST Settings')
+ gst_settings.round_off_gst_values = 1
+ gst_settings.save()
+
+ current_country = frappe.flags.country
+ frappe.flags.country = 'India'
+
+ si = create_sales_invoice(company="_Test Company GST",
+ customer = '_Test GST Customer',
+ currency = 'INR',
+ warehouse = 'Finished Goods - _GST',
+ debit_to = 'Debtors - _GST',
+ income_account = 'Sales - _GST',
+ expense_account = 'Cost of Goods Sold - _GST',
+ cost_center = 'Main - _GST',
+ rate=216,
+ do_not_save=1
+ )
+
+ si.append("taxes", {
+ "charge_type": "On Net Total",
+ "account_head": "IGST - _GST",
+ "cost_center": "Main - _GST",
+ "description": "IGST @ 18.0",
+ "rate": 18
+ })
+
+ si.save()
+ # Check for 39 instead of 38.88
+ self.assertEqual(si.taxes[0].base_tax_amount_after_discount_amount, 39)
+
+ frappe.flags.country = current_country
+ gst_settings.round_off_gst_values = 1
+ gst_settings.save()
+
def make_sales_invoice():
si = create_sales_invoice(company="_Test Company GST",
customer = '_Test GST Customer',
@@ -145,7 +182,6 @@
si3.submit()
def create_purchase_invoices():
-
pi = make_purchase_invoice(
company="_Test Company GST",
supplier = '_Test Registered Supplier',
@@ -193,7 +229,6 @@
pi1.submit()
def make_suppliers():
-
if not frappe.db.exists("Supplier", "_Test Registered Supplier"):
frappe.get_doc({
"supplier_group": "_Test Supplier Group",
@@ -257,7 +292,6 @@
address.save()
def make_customers():
-
if not frappe.db.exists("Customer", "_Test GST Customer"):
frappe.get_doc({
"customer_group": "_Test Customer Group",
@@ -354,9 +388,9 @@
address.save()
def make_company():
-
if frappe.db.exists("Company", "_Test Company GST"):
return
+
company = frappe.new_doc("Company")
company.company_name = "_Test Company GST"
company.abbr = "_GST"
@@ -388,7 +422,6 @@
address.save()
def set_account_heads():
-
gst_settings = frappe.get_doc("GST Settings")
gst_account = frappe.get_all(
diff --git a/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.py b/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.py
index e8a8ed8..ad60db0 100644
--- a/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.py
+++ b/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.py
@@ -5,12 +5,16 @@
from __future__ import unicode_literals
import frappe
from frappe import _
-from frappe.utils import getdate
+from frappe.utils import getdate, get_link_to_form
from frappe.model.document import Document
from erpnext.accounts.utils import get_fiscal_year
class LowerDeductionCertificate(Document):
def validate(self):
+ self.validate_dates()
+ self.validate_supplier_against_section_code()
+
+ def validate_dates(self):
if getdate(self.valid_upto) < getdate(self.valid_from):
frappe.throw(_("Valid Upto date cannot be before Valid From date"))
@@ -24,3 +28,20 @@
<= fiscal_year.year_end_date):
frappe.throw(_("Valid Upto date not in Fiscal Year {0}").format(frappe.bold(self.fiscal_year)))
+ def validate_supplier_against_section_code(self):
+ duplicate_certificate = frappe.db.get_value('Lower Deduction Certificate', {'supplier': self.supplier, 'section_code': self.section_code}, ['name', 'valid_from', 'valid_upto'], as_dict=True)
+ if duplicate_certificate and self.are_dates_overlapping(duplicate_certificate):
+ certificate_link = get_link_to_form('Lower Deduction Certificate', duplicate_certificate.name)
+ frappe.throw(_("There is already a valid Lower Deduction Certificate {0} for Supplier {1} against Section Code {2} for this time period.")
+ .format(certificate_link, frappe.bold(self.supplier), frappe.bold(self.section_code)))
+
+ def are_dates_overlapping(self,duplicate_certificate):
+ valid_from = duplicate_certificate.valid_from
+ valid_upto = duplicate_certificate.valid_upto
+ if valid_from <= getdate(self.valid_from) <= valid_upto:
+ return True
+ elif valid_from <= getdate(self.valid_upto) <= valid_upto:
+ return True
+ elif getdate(self.valid_from) <= valid_from and valid_upto <= getdate(self.valid_upto):
+ return True
+ return False
\ No newline at end of file
diff --git a/erpnext/regional/india/__init__.py b/erpnext/regional/india/__init__.py
index d6221a8..378b735 100644
--- a/erpnext/regional/india/__init__.py
+++ b/erpnext/regional/india/__init__.py
@@ -20,6 +20,7 @@
'Jharkhand',
'Karnataka',
'Kerala',
+ 'Ladakh',
'Lakshadweep Islands',
'Madhya Pradesh',
'Maharashtra',
@@ -59,6 +60,7 @@
"Jharkhand": "20",
"Karnataka": "29",
"Kerala": "32",
+ "Ladakh": "38",
"Lakshadweep Islands": "31",
"Madhya Pradesh": "23",
"Maharashtra": "27",
@@ -80,4 +82,4 @@
"West Bengal": "19",
}
-number_state_mapping = {v: k for k, v in iteritems(state_numbers)}
\ No newline at end of file
+number_state_mapping = {v: k for k, v in iteritems(state_numbers)}
diff --git a/erpnext/regional/india/e_invoice/einvoice.js b/erpnext/regional/india/e_invoice/einvoice.js
index 9fa94c4..a756b57 100644
--- a/erpnext/regional/india/e_invoice/einvoice.js
+++ b/erpnext/regional/india/e_invoice/einvoice.js
@@ -188,7 +188,6 @@
'fieldname': 'vehicle_no',
'label': 'Vehicle No',
'fieldtype': 'Data',
- 'depends_on': 'eval:(doc.mode_of_transport === "Road")',
'default': frm.doc.vehicle_no
},
{
diff --git a/erpnext/regional/india/e_invoice/utils.py b/erpnext/regional/india/e_invoice/utils.py
index 5f701f2..322fa18 100644
--- a/erpnext/regional/india/e_invoice/utils.py
+++ b/erpnext/regional/india/e_invoice/utils.py
@@ -23,7 +23,7 @@
invalid_doctype = doc.doctype != 'Sales Invoice'
invalid_supply_type = doc.get('gst_category') not in ['Registered Regular', 'SEZ', 'Overseas', 'Deemed Export']
company_transaction = doc.get('billing_address_gstin') == doc.get('company_gstin')
- no_taxes_applied = len(doc.get('taxes', [])) == 0
+ no_taxes_applied = not doc.get('taxes')
if not einvoicing_enabled or invalid_doctype or invalid_supply_type or company_transaction or no_taxes_applied:
return
@@ -37,7 +37,7 @@
elif doc.docstatus == 1 and doc._action == 'submit' and not doc.irn:
frappe.throw(_('You must generate IRN before submitting the document.'), title=_('Missing IRN'))
- elif doc.docstatus == 2 and doc._action == 'cancel' and not doc.irn_cancelled:
+ elif doc.irn and doc.docstatus == 2 and doc._action == 'cancel' and not doc.irn_cancelled:
frappe.throw(_('You must cancel IRN before cancelling the document.'), title=_('Cancel Not Allowed'))
def raise_document_name_too_long_error():
@@ -63,7 +63,7 @@
elif invoice.gst_category == 'Overseas': supply_type = 'EXPWOP'
elif invoice.gst_category == 'Deemed Export': supply_type = 'DEXP'
- if not supply_type:
+ if not supply_type:
rr, sez, overseas, export = bold('Registered Regular'), bold('SEZ'), bold('Overseas'), bold('Deemed Export')
frappe.throw(_('GST category should be one of {}, {}, {}, {}').format(rr, sez, overseas, export),
title=_('Invalid Supply Type'))
@@ -128,7 +128,7 @@
if details:
frappe.local.gstin_cache[key] = details
return details
-
+
if not details:
return GSPConnector.get_gstin_details(gstin)
@@ -160,7 +160,7 @@
item.update(d.as_dict())
item.sr_no = d.idx
- item.description = d.item_name.replace('"', '\\"')
+ item.description = json.dumps(d.item_name)[1:-1]
item.qty = abs(item.qty)
item.discount_amount = 0
@@ -174,7 +174,7 @@
item.serial_no = ""
item = update_item_taxes(invoice, item)
-
+
item.total_value = abs(
item.taxable_value + item.igst_amount + item.sgst_amount +
item.cgst_amount + item.cess_amount + item.cess_nadv_amount + item.other_charges
@@ -232,9 +232,9 @@
invoice_value_details.round_off = invoice.base_rounding_adjustment
invoice_value_details.base_grand_total = abs(invoice.base_rounded_total) or abs(invoice.base_grand_total)
invoice_value_details.grand_total = abs(invoice.rounded_total) or abs(invoice.grand_total)
-
+
invoice_value_details = update_invoice_taxes(invoice, invoice_value_details)
-
+
return invoice_value_details
def update_invoice_taxes(invoice, invoice_value_details):
@@ -251,13 +251,13 @@
if t.account_head in gst_accounts.cess_account:
# using after discount amt since item also uses after discount amt for cess calc
invoice_value_details.total_cess_amt += abs(t.base_tax_amount_after_discount_amount)
-
+
for tax_type in ['igst', 'cgst', 'sgst']:
if t.account_head in gst_accounts[f'{tax_type}_account']:
invoice_value_details[f'total_{tax_type}_amt'] += abs(t.base_tax_amount_after_discount_amount)
else:
invoice_value_details.total_other_charges += abs(t.base_tax_amount_after_discount_amount)
-
+
return invoice_value_details
def get_payment_details(invoice):
@@ -329,23 +329,23 @@
place_of_supply = get_place_of_supply(invoice, invoice.doctype) or invoice.billing_address_gstin
place_of_supply = place_of_supply[:2]
buyer_details.update(dict(place_of_supply=place_of_supply))
-
+
shipping_details = payment_details = prev_doc_details = eway_bill_details = frappe._dict({})
if invoice.shipping_address_name and invoice.customer_address != invoice.shipping_address_name:
if invoice.gst_category == 'Overseas':
shipping_details = get_overseas_address_details(invoice.shipping_address_name)
else:
shipping_details = get_party_details(invoice.shipping_address_name)
-
+
if invoice.is_pos and invoice.base_paid_amount:
payment_details = get_payment_details(invoice)
-
+
if invoice.is_return and invoice.return_against:
prev_doc_details = get_return_doc_reference(invoice)
-
+
if invoice.transporter:
eway_bill_details = get_eway_bill_details(invoice)
-
+
# not yet implemented
dispatch_details = period_details = export_details = frappe._dict({})
@@ -357,7 +357,7 @@
export_details=export_details, eway_bill_details=eway_bill_details
)
einvoice = json.loads(einvoice)
-
+
validations = json.loads(read_json('einv_validation'))
errors = validate_einvoice(validations, einvoice)
if errors:
@@ -419,7 +419,7 @@
errors.append(_('{} {} should be between {} and {}').format(label, value, minimum, maximum))
if pattern_str and not pattern.match(value):
errors.append(field_validation.get('validationMsg'))
-
+
return errors
class RequestFailed(Exception): pass
@@ -452,19 +452,19 @@
else:
credentials = self.e_invoice_settings.credentials[0] if self.e_invoice_settings.credentials else None
return credentials
-
+
def get_seller_gstin(self):
gstin = self.invoice.company_gstin or frappe.db.get_value('Address', self.invoice.company_address, 'gstin')
if not gstin:
frappe.throw(_('Cannot retrieve Company GSTIN. Please select company address with valid GSTIN.'))
return gstin
-
+
def get_auth_token(self):
if time_diff_in_seconds(self.e_invoice_settings.token_expiry, now_datetime()) < 150.0:
self.fetch_auth_token()
-
+
return self.e_invoice_settings.auth_token
-
+
def make_request(self, request_type, url, headers=None, data=None):
if request_type == 'post':
res = make_post_request(url, headers=headers, data=data)
@@ -473,7 +473,7 @@
self.log_request(url, headers, data, res)
return res
-
+
def log_request(self, url, headers, data, res):
headers.update({ 'password': self.credentials.password })
request_log = frappe.get_doc({
@@ -504,7 +504,7 @@
except Exception:
self.log_error(res)
self.raise_error(True)
-
+
def get_headers(self):
return {
'content-type': 'application/json',
@@ -526,7 +526,7 @@
else:
self.log_error(res)
raise RequestFailed
-
+
except RequestFailed:
self.raise_error()
@@ -571,7 +571,7 @@
else:
raise RequestFailed
-
+
except RequestFailed:
errors = self.sanitize_error_message(res.get('message'))
self.raise_error(errors=errors)
@@ -579,7 +579,7 @@
except Exception:
self.log_error(data)
self.raise_error(True)
-
+
def get_irn_details(self, irn):
headers = self.get_headers()
@@ -590,7 +590,7 @@
return res.get('result')
else:
raise RequestFailed
-
+
except RequestFailed:
errors = self.sanitize_error_message(res.get('message'))
self.raise_error(errors=errors)
@@ -598,7 +598,7 @@
except Exception:
self.log_error()
self.raise_error(True)
-
+
def cancel_irn(self, irn, reason, remark):
headers = self.get_headers()
data = json.dumps({
@@ -620,7 +620,7 @@
else:
raise RequestFailed
-
+
except RequestFailed:
errors = self.sanitize_error_message(res.get('message'))
self.raise_error(errors=errors)
@@ -669,7 +669,7 @@
except Exception:
self.log_error(data)
self.raise_error(True)
-
+
def cancel_eway_bill(self, eway_bill, reason, remark):
headers = self.get_headers()
data = json.dumps({
@@ -701,7 +701,7 @@
except Exception:
self.log_error(data)
self.raise_error(True)
-
+
def sanitize_error_message(self, message):
'''
On validation errors, response message looks something like this:
@@ -740,7 +740,7 @@
"Exception:", err_tb
])
frappe.log_error(title=_('E Invoice Request Failed'), message=message)
-
+
def raise_error(self, raise_exception=False, errors=[]):
title = _('E Invoice Request Failed')
if errors:
@@ -753,7 +753,7 @@
raise_exception=raise_exception,
indicator='red'
)
-
+
def set_einvoice_data(self, res):
enc_signed_invoice = res.get('SignedInvoice')
dec_signed_invoice = jwt.decode(enc_signed_invoice, verify=False)['data']
@@ -792,7 +792,7 @@
_file.save()
frappe.db.commit()
self.invoice.qrcode_image = _file.file_url
-
+
def update_invoice(self):
self.invoice.flags.ignore_validate_update_after_submit = True
self.invoice.flags.ignore_validate = True
diff --git a/erpnext/regional/india/gst_state_code_data.json b/erpnext/regional/india/gst_state_code_data.json
index ff88e0f..8481c27 100644
--- a/erpnext/regional/india/gst_state_code_data.json
+++ b/erpnext/regional/india/gst_state_code_data.json
@@ -168,5 +168,10 @@
"state_number": "37",
"state_code": "AD",
"state_name": "Andhra Pradesh (New)"
+ },
+ {
+ "state_number": "38",
+ "state_code": "LA",
+ "state_name": "Ladakh"
}
]
diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py
index e89885f..cb30605 100644
--- a/erpnext/regional/india/utils.py
+++ b/erpnext/regional/india/utils.py
@@ -55,6 +55,14 @@
frappe.throw(_("Invalid GSTIN! First 2 digits of GSTIN should match with State number {0}.")
.format(doc.gst_state_number))
+def validate_pan_for_india(doc, method):
+ if doc.get('country') != 'India' or not doc.pan:
+ return
+
+ p = re.compile("[A-Z]{5}[0-9]{4}[A-Z]{1}")
+ if not p.match(doc.pan):
+ frappe.throw(_("Invalid PAN No. The input you've entered doesn't match the format of PAN."))
+
def validate_tax_category(doc, method):
if doc.get('gst_state') and frappe.db.get_value('Tax Category', {'gst_state': doc.gst_state, 'is_inter_state': doc.is_inter_state}):
if doc.is_inter_state:
@@ -772,3 +780,24 @@
)
return gl_entries
+
+@frappe.whitelist()
+def get_regional_round_off_accounts(company, account_list):
+ country = frappe.get_cached_value('Company', company, 'country')
+
+ if country != 'India':
+ return
+
+ if isinstance(account_list, string_types):
+ account_list = json.loads(account_list)
+
+ if not frappe.db.get_single_value('GST Settings', 'round_off_gst_values'):
+ return
+
+ gst_accounts = get_gst_accounts(company)
+ gst_account_list = gst_accounts.get('cgst_account') + gst_accounts.get('sgst_account') \
+ + gst_accounts.get('igst_account')
+
+ account_list.extend(gst_account_list)
+
+ return account_list
\ No newline at end of file
diff --git a/erpnext/regional/report/gstr_1/gstr_1.py b/erpnext/regional/report/gstr_1/gstr_1.py
index 96dc3f7..09b04ff 100644
--- a/erpnext/regional/report/gstr_1/gstr_1.py
+++ b/erpnext/regional/report/gstr_1/gstr_1.py
@@ -236,6 +236,7 @@
self.cgst_sgst_invoices = []
unidentified_gst_accounts = []
+ unidentified_gst_accounts_invoice = []
for parent, account, item_wise_tax_detail, tax_amount in self.tax_details:
if account in self.gst_accounts.cess_account:
self.invoice_cess.setdefault(parent, tax_amount)
@@ -251,6 +252,7 @@
if not (cgst_or_sgst or account in self.gst_accounts.igst_account):
if "gst" in account.lower() and account not in unidentified_gst_accounts:
unidentified_gst_accounts.append(account)
+ unidentified_gst_accounts_invoice.append(parent)
continue
for item_code, tax_amounts in item_wise_tax_detail.items():
@@ -273,7 +275,7 @@
# Build itemised tax for export invoices where tax table is blank
for invoice, items in iteritems(self.invoice_items):
- if invoice not in self.items_based_on_tax_rate \
+ if invoice not in self.items_based_on_tax_rate and invoice not in unidentified_gst_accounts_invoice \
and frappe.db.get_value(self.doctype, invoice, "export_type") == "Without Payment of Tax":
self.items_based_on_tax_rate.setdefault(invoice, {}).setdefault(0, items.keys())