feat: GSTR1 json format download  (#16887)

* feat: download gstr-1 b2b in json in its appropriate json format

* fix: download file as raw type

* feat: added logic to convert export gst data to its json

* code commonify and logic for b2cl added

* fix: Codacy and typo fixes

* fix: B2B json creation fixes

* fix: Filter customer by category

* fix: Fetch fp from to date
diff --git a/erpnext/regional/report/gstr_1/gstr_1.js b/erpnext/regional/report/gstr_1/gstr_1.js
index 9246aa6..9682768 100644
--- a/erpnext/regional/report/gstr_1/gstr_1.js
+++ b/erpnext/regional/report/gstr_1/gstr_1.js
@@ -4,7 +4,7 @@
 frappe.query_reports["GSTR-1"] = {
 	"filters": [
 		{
-			"fieldname":"company",
+			"fieldname": "company",
 			"label": __("Company"),
 			"fieldtype": "Link",
 			"options": "Company",
@@ -12,22 +12,22 @@
 			"default": frappe.defaults.get_user_default("Company")
 		},
 		{
-			"fieldname":"company_address",
+			"fieldname": "company_address",
 			"label": __("Address"),
 			"fieldtype": "Link",
 			"options": "Address",
-			"get_query": function() {
+			"get_query": function () {
 				var company = frappe.query_report.get_filter_value('company');
 				if (company) {
 					return {
 						"query": 'frappe.contacts.doctype.address.address.address_query',
-						"filters": { link_doctype: 'Company', link_name: company}
+						"filters": { link_doctype: 'Company', link_name: company }
 					};
 				}
 			}
 		},
 		{
-			"fieldname":"from_date",
+			"fieldname": "from_date",
 			"label": __("From Date"),
 			"fieldtype": "Date",
 			"reqd": 1,
@@ -35,19 +35,34 @@
 			"width": "80"
 		},
 		{
-			"fieldname":"to_date",
+			"fieldname": "to_date",
 			"label": __("To Date"),
 			"fieldtype": "Date",
 			"reqd": 1,
 			"default": frappe.datetime.get_today()
 		},
 		{
-			"fieldname":"type_of_business",
+			"fieldname": "type_of_business",
 			"label": __("Type of Business"),
 			"fieldtype": "Select",
 			"reqd": 1,
-			"options": ["B2B", "B2C Large", "B2C Small","CDNR", "EXPORT"],
+			"options": ["B2B", "B2C Large", "B2C Small", "CDNR", "EXPORT"],
 			"default": "B2B"
 		}
-	]
+	],
+	onload: function (report) {
+
+		report.page.add_inner_button(__("Download as Json"), function () {
+			var filters = report.get_values();
+
+			const args = {
+				cmd: 'erpnext.regional.report.gstr_1.gstr_1.get_json',
+				data: report.data,
+				report_name: report.report_name,
+				filters: filters
+			};
+
+			open_url_post(frappe.request.url, args);
+		});
+	}
 }
diff --git a/erpnext/regional/report/gstr_1/gstr_1.py b/erpnext/regional/report/gstr_1/gstr_1.py
index b01abce..24bd6cf 100644
--- a/erpnext/regional/report/gstr_1/gstr_1.py
+++ b/erpnext/regional/report/gstr_1/gstr_1.py
@@ -4,9 +4,10 @@
 from __future__ import unicode_literals
 import frappe, json
 from frappe import _
-from frappe.utils import flt, formatdate
+from frappe.utils import flt, formatdate, now_datetime, getdate
 from datetime import date
 from six import iteritems
+from erpnext.regional.doctype.gstr_3b_report.gstr_3b_report import get_period
 
 def execute(filters=None):
 	return Gstr1Report(filters).run()
@@ -38,7 +39,7 @@
 			shipping_bill_date,
 			reason_for_issuing_document
 		"""
-		self.customer_type = "Company" if self.filters.get("type_of_business") ==  "B2B" else "Individual"
+		# self.customer_type = "Company" if self.filters.get("type_of_business") ==  "B2B" else "Individual"
 
 	def run(self):
 		self.get_columns()
@@ -113,9 +114,14 @@
 				if self.filters.get(opts[0]):
 					conditions += opts[1]
 
-		customers = frappe.get_all("Customer", filters={"customer_type": self.customer_type})
+		# customers = frappe.get_all("Customer", filters={"customer_type": self.customer_type})
 
 		if self.filters.get("type_of_business") ==  "B2B":
+			customers = frappe.get_all("Customer",
+				filters={
+					"gst_category": ["in", ["Registered Regular", "Deemed Export", "SEZ"]]
+			})
+
 			conditions += """ and ifnull(gst_category, '') != 'Overseas' and is_return != 1
 				and customer in ({0})""".format(", ".join([frappe.db.escape(c.name) for c in customers]))
 
@@ -124,6 +130,11 @@
 			if not b2c_limit:
 				frappe.throw(_("Please set B2C Limit in GST Settings."))
 
+		customers = frappe.get_all("Customer",
+			filters={
+				"gst_category": ["in", ["Unregistered"]]
+		})
+
 		if self.filters.get("type_of_business") ==  "B2C Large":
 			conditions += """ and SUBSTR(place_of_supply, 1, 2) != SUBSTR(company_gstin, 1, 2)
 				and grand_total > {0} and is_return != 1 and customer in ({1})""".\
@@ -494,3 +505,158 @@
 				}
 			]
 		self.columns = self.invoice_columns + self.tax_columns + self.other_columns
+
+@frappe.whitelist()
+def get_json():
+	data = frappe._dict(frappe.local.form_dict)
+
+	del data["cmd"]
+	if "csrf_token" in data:
+		del data["csrf_token"]
+
+	filters = json.loads(data["filters"])
+	report_data = json.loads(data["data"])
+	report_name = data["report_name"]
+	gstin = get_company_gstin_number(filters["company"])
+
+	fp = "%02d%s" % (getdate(filters["to_date"]).month, getdate(filters["to_date"]).year)
+
+	gst_json = {"gstin": "", "version": "GST2.2.9",
+		"hash": "hash", "gstin": gstin, "fp": fp}
+
+	res = {}
+	if filters["type_of_business"] == "B2B":
+		for item in report_data:
+			res.setdefault(item["customer_gstin"], {}).setdefault(item["invoice_number"],[]).append(item)
+
+		out = get_b2b_json(res, gstin)
+		gst_json["b2b"] = out
+	elif filters["type_of_business"] == "B2C Large":
+		for item in report_data:
+			res.setdefault(item["place_of_supply"], []).append(item)
+
+		out = get_b2cl_json(res, gstin)
+		gst_json["b2cl"] = out
+	elif filters["type_of_business"] == "EXPORT":
+		for item in report_data:
+			res.setdefault(item["export_type"], []).append(item)
+
+		out = get_export_json(res)
+		gst_json["exp"] = out
+
+	download_json_file(report_name, filters["type_of_business"], gst_json)
+
+def get_b2b_json(res, gstin):
+	inv_type, out = {"Registered Regular": "R", "Deemed Export": "DE", "URD": "URD", "SEZ": "SEZ"}, []
+	for gst_in in res:
+		b2b_item, inv = {"ctin": gst_in, "inv": []}, []
+		if not gst_in: continue
+
+		for number, invoice in iteritems(res[gst_in]):
+			inv_item = get_basic_invoice_detail(invoice[0])
+			inv_item["pos"] = "%02d" % int(invoice[0]["place_of_supply"].split('-')[0])
+			inv_item["rchrg"] = invoice[0]["reverse_charge"]
+			inv_item["inv_typ"] = inv_type.get(invoice[0].get("gst_category", ""),"")
+
+			if inv_item["pos"]=="00": continue
+			inv_item["itms"] = []
+
+			for item in invoice:
+				inv_item["itms"].append(get_rate_and_tax_details(item, gstin))
+
+			inv.append(inv_item)
+
+		if not inv: continue
+		b2b_item["inv"] = inv
+		out.append(b2b_item)
+
+	return out
+
+def get_b2cl_json(res, gstin):
+	out = []
+	for pos in res:
+		b2cl_item, inv = {"pos": "%02d" % int(pos.split('-')[0]), "inv": []}, []
+
+		for row in res[pos]:
+			inv_item = get_basic_invoice_detail(row)
+			if row.get("sale_from_bonded_wh"):
+				inv_item["inv_typ"] = "CBW"
+
+			inv_item["itms"] = [get_rate_and_tax_details(row, gstin)]
+
+			inv.append(inv_item)
+
+		b2cl_item["inv"] = inv
+		out.append(b2cl_item)
+
+	return out
+
+def get_export_json(res):
+	out = []
+	for exp_type in res:
+		exp_item, inv = {"exp_typ": exp_type, "inv": []}, []
+
+		for row in res[exp_type]:
+			inv_item = get_basic_invoice_detail(row)
+			inv_item["itms"] = [{
+				"txval": flt(row["taxable_value"], 2),
+				"rt": row["rate"] or 0,
+				"iamt": 0,
+				"csamt": 0
+			}]
+
+			inv.append(inv_item)
+
+		exp_item["inv"] = inv
+		out.append(exp_item)
+
+	return out
+
+def get_basic_invoice_detail(row):
+	return {
+		"inum": row["invoice_number"],
+		"idt": getdate(row["posting_date"]).strftime('%d-%m-%Y'),
+		"val": flt(row["invoice_value"], 2)
+	}
+
+def get_rate_and_tax_details(row, gstin):
+	itm_det = {"txval": flt(row["taxable_value"], 2),
+		"rt": row["rate"],
+		"csamt": (flt(row.get("cess_amount"), 2) or 0)
+	}
+
+	# calculate rate
+	num = 1 if not row["rate"] else "%d%02d" % (row["rate"], 1)
+	rate = row.get("rate") or 0
+
+	# calculate tax amount added
+	tax = flt((row["taxable_value"]*rate)/100.0, 2)
+	frappe.errprint([tax, tax/2])
+	if row.get("customer_gstin") and gstin[0:2] == row["customer_gstin"][0:2]:
+		itm_det.update({"camt": flt(tax/2.0, 2), "samt": flt(tax/2.0, 2)})
+	else:
+		itm_det.update({"iamt": tax})
+
+	return {"num": int(num), "itm_det": itm_det}
+
+def get_company_gstin_number(company):
+	filters = [
+		["is_your_company_address", "=", 1],
+		["Dynamic Link", "link_doctype", "=", "Company"],
+		["Dynamic Link", "link_name", "=", company],
+		["Dynamic Link", "parenttype", "=", "Address"],
+	]
+
+	gstin = frappe.get_all("Address", filters=filters, fields=["gstin"])
+
+	if gstin:
+		return gstin[0]["gstin"]
+	else:
+		frappe.throw(_("No GST No. found for the Company."))
+
+def download_json_file(filename, report_type, data):
+	''' download json content in a file '''
+	frappe.response['filename'] = frappe.scrub("{0} {1}".format(filename, report_type)) + '.json'
+	frappe.response['filecontent'] = json.dumps(data)
+	frappe.response['content_type'] = 'application/json'
+	frappe.response['type'] = 'download'