GSTR1 for B2B (#12296)
diff --git a/erpnext/accounts/doctype/gst_account/__init__.py b/erpnext/accounts/doctype/gst_account/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/accounts/doctype/gst_account/__init__.py
diff --git a/erpnext/accounts/doctype/gst_account/gst_account.json b/erpnext/accounts/doctype/gst_account/gst_account.json
new file mode 100644
index 0000000..7067338
--- /dev/null
+++ b/erpnext/accounts/doctype/gst_account/gst_account.json
@@ -0,0 +1,196 @@
+{
+ "allow_copy": 0,
+ "allow_guest_to_view": 0,
+ "allow_import": 0,
+ "allow_rename": 0,
+ "beta": 0,
+ "creation": "2018-01-02 15:48:58.768352",
+ "custom": 0,
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "fields": [
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "company",
+ "fieldtype": "Link",
+ "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,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "cgst_account",
+ "fieldtype": "Link",
+ "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,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "sgst_account",
+ "fieldtype": "Link",
+ "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,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "igst_account",
+ "fieldtype": "Link",
+ "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,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "cess_account",
+ "fieldtype": "Link",
+ "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": "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,
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "idx": 0,
+ "image_view": 0,
+ "in_create": 0,
+ "is_submittable": 0,
+ "issingle": 0,
+ "istable": 1,
+ "max_attachments": 0,
+ "modified": "2018-01-02 15:52:22.335988",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "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
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/gst_account/gst_account.py b/erpnext/accounts/doctype/gst_account/gst_account.py
new file mode 100644
index 0000000..d784849
--- /dev/null
+++ b/erpnext/accounts/doctype/gst_account/gst_account.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.model.document import Document
+
+class GSTAccount(Document):
+ pass
diff --git a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.js b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.js
index b57a7fc..6a8cc6a 100644
--- a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.js
+++ b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.js
@@ -1,7 +1,7 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
-frappe.query_reports["Item-wise Sales Register"] = frappe.query_reports["Sales Register"] = {
+frappe.query_reports["Item-wise Sales Register"] = {
"filters": [
{
"fieldname":"from_date",
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index 6265e1a..ebc1921 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -203,6 +203,9 @@
},
'Address': {
'validate': 'erpnext.regional.india.utils.validate_gstin_for_india'
+ },
+ 'Sales Invoice': {
+ 'validate': 'erpnext.regional.india.utils.set_place_of_supply'
}
}
diff --git a/erpnext/regional/doctype/gst_settings/gst_settings.json b/erpnext/regional/doctype/gst_settings/gst_settings.json
index 67084b4..ebd0865 100644
--- a/erpnext/regional/doctype/gst_settings/gst_settings.json
+++ b/erpnext/regional/doctype/gst_settings/gst_settings.json
@@ -48,6 +48,35 @@
"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
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
"fieldname": "gstin_email_sent_on",
"fieldtype": "Date",
"hidden": 0,
@@ -71,6 +100,66 @@
"search_index": 0,
"set_only_once": 0,
"unique": 0
+ },
+ {
+ "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
+ },
+ {
+ "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
}
],
"has_web_view": 0,
@@ -83,7 +172,7 @@
"issingle": 1,
"istable": 0,
"max_attachments": 0,
- "modified": "2017-09-29 14:39:15.625952",
+ "modified": "2018-01-02 15:53:13.489144",
"modified_by": "Administrator",
"module": "Regional",
"name": "GST Settings",
diff --git a/erpnext/regional/doctype/gst_settings/test_gst_settings.py b/erpnext/regional/doctype/gst_settings/test_gst_settings.py
new file mode 100644
index 0000000..d118dee
--- /dev/null
+++ b/erpnext/regional/doctype/gst_settings/test_gst_settings.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+import frappe
+import unittest
+
+class TestGSTSettings(unittest.TestCase):
+ pass
diff --git a/erpnext/regional/india/setup.py b/erpnext/regional/india/setup.py
index 4ef9b11..7143bd3 100644
--- a/erpnext/regional/india/setup.py
+++ b/erpnext/regional/india/setup.py
@@ -123,8 +123,8 @@
fieldtype='Data', insert_after='shipping_address',
options='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,
- options='shipping_address_name.gst_state_number', read_only=0),
+ 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)
diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py
index 2cd71a5..10a9c97 100644
--- a/erpnext/regional/india/utils.py
+++ b/erpnext/regional/india/utils.py
@@ -60,6 +60,14 @@
return hsn_tax, hsn_taxable_amount
+def set_place_of_supply(doc, method):
+ if not hasattr(doc, 'customer_gstin'):
+ return
+
+ address_name = doc.shipping_address_name or doc.customer_address
+ address = frappe.db.get_value("Address", address_name, ["gst_state", "gst_state_number"], as_dict=1)
+ doc.place_of_supply = address.gst_state_number + "-" + address.gst_state
+
# don't remove this function it is used in tests
def test_method():
'''test function'''
diff --git a/erpnext/regional/report/gstr_1/__init__.py b/erpnext/regional/report/gstr_1/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/regional/report/gstr_1/__init__.py
diff --git a/erpnext/regional/report/gstr_1/gstr_1.js b/erpnext/regional/report/gstr_1/gstr_1.js
new file mode 100644
index 0000000..9437786
--- /dev/null
+++ b/erpnext/regional/report/gstr_1/gstr_1.js
@@ -0,0 +1,38 @@
+// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
+// License: GNU General Public License v3. See license.txt
+
+frappe.query_reports["GSTR-1"] = {
+ "filters": [
+ {
+ "fieldname":"company",
+ "label": __("Company"),
+ "fieldtype": "Link",
+ "options": "Company",
+ "reqd": 1,
+ "default": frappe.defaults.get_user_default("Company")
+ },
+ {
+ "fieldname":"from_date",
+ "label": __("From Date"),
+ "fieldtype": "Date",
+ "reqd": 1,
+ "default": frappe.datetime.add_months(frappe.datetime.get_today(), -3),
+ "width": "80"
+ },
+ {
+ "fieldname":"to_date",
+ "label": __("To Date"),
+ "fieldtype": "Date",
+ "reqd": 1,
+ "default": frappe.datetime.get_today()
+ },
+ {
+ "fieldname":"type_of_business",
+ "label": __("Type of Business"),
+ "fieldtype": "Select",
+ "reqd": 1,
+ "options": ["B2B", "B2C Large", "B2C Small"],
+ "default": "B2B"
+ }
+ ]
+}
diff --git a/erpnext/regional/report/gstr_1/gstr_1.json b/erpnext/regional/report/gstr_1/gstr_1.json
new file mode 100644
index 0000000..a71d89e
--- /dev/null
+++ b/erpnext/regional/report/gstr_1/gstr_1.json
@@ -0,0 +1,30 @@
+{
+ "add_total_row": 0,
+ "apply_user_permissions": 1,
+ "creation": "2018-01-02 15:54:41.424225",
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "idx": 0,
+ "is_standard": "Yes",
+ "letter_head": "test",
+ "modified": "2018-01-02 17:56:15.379347",
+ "modified_by": "Administrator",
+ "module": "Regional",
+ "name": "GSTR-1",
+ "owner": "Administrator",
+ "ref_doctype": "GL Entry",
+ "report_name": "GSTR-1",
+ "report_type": "Script Report",
+ "roles": [
+ {
+ "role": "Accounts User"
+ },
+ {
+ "role": "Accounts Manager"
+ },
+ {
+ "role": "Auditor"
+ }
+ ]
+}
\ 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
new file mode 100644
index 0000000..6dd872a
--- /dev/null
+++ b/erpnext/regional/report/gstr_1/gstr_1.py
@@ -0,0 +1,161 @@
+# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe, json
+from frappe import _
+
+def execute(filters=None):
+ columns, data = get_columns(filters), get_data(filters)
+ return columns, data
+
+def get_columns(filters):
+ return [
+ "GSTIN/UIN of Recipient::150",
+ "Receiver Name::120",
+ "Invoice Number:Link/Sales Invoice:120",
+ "Invoice date:Date:120",
+ "Invoice Value:Currency:120",
+ "Place of Supply::120",
+ "Reverse Charge::120",
+ "Invoice Type::120",
+ "E-Commerce GSTIN::120",
+ "Rate:Int:80",
+ "Taxable Value:Currency:120",
+ "Cess Amount:Currency:120"
+ ]
+
+def get_data(filters):
+ gst_accounts = get_gst_accounts(filters)
+ invoices = get_invoice_data(filters)
+ invoice_items = get_invoice_items(invoices)
+ items_based_on_tax_rate, invoice_cess = get_items_based_on_tax_rate(invoices.keys(), gst_accounts)
+
+ data = []
+ for inv, items_based_on_rate in items_based_on_tax_rate.items():
+ invoice_details = invoices.get(inv)
+ for rate, items in items_based_on_rate.items():
+ row = [
+ invoice_details.customer_gstin,
+ invoice_details.customer_name,
+ inv,
+ invoice_details.posting_date,
+ invoice_details.base_rounded_total or invoice_details.base_grand_total,
+ invoice_details.place_of_supply,
+ invoice_details.reverse_charge,
+ invoice_details.invoice_type,
+ invoice_details.ecommerce_gstin,
+ rate,
+ sum([net_amount for item_code, net_amount in invoice_items.get(inv).items()
+ if item_code in items]),
+ invoice_cess.get(inv)
+ ]
+ data.append(row)
+
+ return data
+
+def get_gst_accounts(filters):
+ gst_accounts = frappe._dict()
+ gst_settings_accounts = frappe.get_list("GST Account",
+ filters={"parent": "GST Settings", "company": filters.company},
+ fields=["cgst_account", "sgst_account", "igst_account", "cess_account"])
+
+ if not gst_settings_accounts:
+ frappe.throw(_("Please set GST Accounts in GST Settings"))
+
+ for d in gst_settings_accounts:
+ for acc, val in d.items():
+ gst_accounts.setdefault(acc, []).append(val)
+
+ return gst_accounts
+
+def get_invoice_data(filters):
+ invoices = frappe._dict()
+ conditions = get_conditions(filters)
+ match_conditions = frappe.build_match_conditions("Sales Invoice")
+
+ if match_conditions:
+ match_conditions = " and {0} ".format(match_conditions)
+
+ invoice_data = frappe.db.sql("""
+ select
+ `tabSales Invoice`.name,
+ `tabSales Invoice`.customer_name,
+ `tabSales Invoice`.posting_date,
+ `tabSales Invoice`.base_grand_total,
+ `tabSales Invoice`.base_rounded_total,
+ `tabSales Invoice`.customer_gstin,
+ `tabSales Invoice`.place_of_supply,
+ `tabSales Invoice`.ecommerce_gstin,
+ `tabSales Invoice`.reverse_charge,
+ `tabSales Invoice`.invoice_type
+ from `tabSales Invoice`
+ where `tabSales Invoice`.docstatus = 1 %s %s
+ order by `tabSales Invoice`.posting_date desc
+ """ % (conditions, match_conditions), filters, as_dict=1)
+
+ for d in invoice_data:
+ invoices.setdefault(d.name, d)
+ return invoices
+
+def get_conditions(filters):
+ conditions = ""
+
+ for opts in (("company", " and company=%(company)s"),
+ ("from_date", " and `tabSales Invoice`.posting_date>=%(from_date)s"),
+ ("to_date", " and `tabSales Invoice`.posting_date<=%(to_date)s")):
+ if filters.get(opts[0]):
+ conditions += opts[1]
+
+ return conditions
+
+def get_invoice_items(invoices):
+ invoice_items = frappe._dict()
+ items = frappe.db.sql("""
+ select item_code, parent, base_net_amount
+ from `tabSales Invoice Item`
+ where parent in (%s)
+ """ % (', '.join(['%s']*len(invoices))), tuple(invoices), as_dict=1)
+
+ for d in items:
+ invoice_items.setdefault(d.parent, {}).setdefault(d.item_code, d.base_net_amount)
+ return invoice_items
+
+def get_items_based_on_tax_rate(invoices, gst_accounts):
+ tax_details = frappe.db.sql("""
+ select
+ parent, account_head, item_wise_tax_detail, base_tax_amount_after_discount_amount
+ from `tabSales Taxes and Charges`
+ where
+ parenttype = 'Sales Invoice' and docstatus = 1
+ and parent in (%s)
+ and tax_amount_after_discount_amount > 0
+ order by account_head
+ """ % (', '.join(['%s']*len(invoices))), tuple(invoices))
+
+ items_based_on_tax_rate = {}
+ invoice_cess = frappe._dict()
+
+ for parent, account, item_wise_tax_detail, tax_amount in tax_details:
+ if account in gst_accounts.cess_account:
+ invoice_cess.setdefault(parent, tax_amount)
+ else:
+ if item_wise_tax_detail:
+ try:
+ item_wise_tax_detail = json.loads(item_wise_tax_detail)
+ cgst_or_sgst = False
+ if account in gst_accounts.cgst_account or account in gst_accounts.sgst_account:
+ cgst_or_sgst = True
+
+ for item_code, tax_amounts in item_wise_tax_detail.items():
+ tax_rate = tax_amounts[0]
+ if cgst_or_sgst:
+ tax_rate *= 2
+
+ rate_based_dict = items_based_on_tax_rate.setdefault(parent, {}).setdefault(tax_rate, [])
+ if item_code not in rate_based_dict:
+ rate_based_dict.append(item_code)
+
+ except ValueError:
+ continue
+ return items_based_on_tax_rate, invoice_cess
\ No newline at end of file
diff --git a/erpnext/regional/report/gstr_1/utils.py b/erpnext/regional/report/gstr_1/utils.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/regional/report/gstr_1/utils.py