diff --git a/erpnext/accounts/doctype/gst_account/__init__.py b/erpnext/accounts/doctype/gst_account/__init__.py new file mode 100644 index 0000000000..e69de29bb2 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 0000000000..70673387fe --- /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 0000000000..d7848495a6 --- /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 b57a7fce9d..6a8cc6abf9 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 6265e1af85..ebc1921f12 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -203,6 +203,9 @@ doc_events = { }, '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 67084b45f8..ebd0865f41 100644 --- a/erpnext/regional/doctype/gst_settings/gst_settings.json +++ b/erpnext/regional/doctype/gst_settings/gst_settings.json @@ -42,6 +42,35 @@ "set_only_once": 0, "unique": 0 }, + { + "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 + }, { "allow_bulk_edit": 0, "allow_on_submit": 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 0000000000..d118dee617 --- /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 4ef9b117c1..7143bd3457 100644 --- a/erpnext/regional/india/setup.py +++ b/erpnext/regional/india/setup.py @@ -123,8 +123,8 @@ def make_custom_fields(): 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 2cd71a5bec..10a9c97a56 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -60,6 +60,14 @@ def get_itemised_tax_breakup_data(doc): 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 0000000000..e69de29bb2 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 0000000000..943778661b --- /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 0000000000..a71d89eaee --- /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 0000000000..6dd872a46d --- /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 0000000000..e69de29bb2