Merge pull request #27279 from deepeshgarg007/gstr_1_cdnr_unregistered_json_develop

feat: CDNR Unreg json generation
diff --git a/erpnext/accounts/doctype/payment_entry/regional/india.js b/erpnext/accounts/doctype/payment_entry/regional/india.js
new file mode 100644
index 0000000..abb3445
--- /dev/null
+++ b/erpnext/accounts/doctype/payment_entry/regional/india.js
@@ -0,0 +1,29 @@
+frappe.ui.form.on("Payment Entry", {
+	company: function(frm) {
+		frappe.call({
+			'method': 'frappe.contacts.doctype.address.address.get_default_address',
+			'args': {
+				'doctype': 'Company',
+				'name': frm.doc.company
+			},
+			'callback': function(r) {
+				frm.set_value('company_address', r.message);
+			}
+		});
+	},
+
+	party: function(frm) {
+		if (frm.doc.party_type == "Customer" && frm.doc.party) {
+			frappe.call({
+				'method': 'frappe.contacts.doctype.address.address.get_default_address',
+				'args': {
+					'doctype': 'Customer',
+					'name': frm.doc.party
+				},
+				'callback': function(r) {
+					frm.set_value('customer_address', r.message);
+				}
+			});
+		}
+	}
+});
\ No newline at end of file
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index 2f7e930..5b6e1ee 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -287,6 +287,7 @@
 		]
 	},
 	"Payment Entry": {
+		"validate": "erpnext.regional.india.utils.update_place_of_supply",
 		"on_submit": ["erpnext.regional.create_transaction_log", "erpnext.accounts.doctype.payment_request.payment_request.update_payment_req_status", "erpnext.accounts.doctype.dunning.dunning.resolve_dunning"],
 		"on_trash": "erpnext.regional.check_deletion_permission"
 	},
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index f7f3ddf..a03f90e 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -302,4 +302,5 @@
 erpnext.patches.v14_0.delete_einvoicing_doctypes
 erpnext.patches.v13_0.set_operation_time_based_on_operating_cost
 erpnext.patches.v13_0.validate_options_for_data_field
+erpnext.patches.v13_0.create_gst_payment_entry_fields
 erpnext.patches.v14_0.delete_shopify_doctypes
diff --git a/erpnext/patches/v13_0/create_gst_payment_entry_fields.py b/erpnext/patches/v13_0/create_gst_payment_entry_fields.py
new file mode 100644
index 0000000..334c9d2
--- /dev/null
+++ b/erpnext/patches/v13_0/create_gst_payment_entry_fields.py
@@ -0,0 +1,31 @@
+# Copyright (c) 2021, Frappe and Contributors
+# License: GNU General Public License v3. See license.txt
+
+import frappe
+from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
+
+def execute():
+	frappe.reload_doc('accounts', 'doctype', 'advance_taxes_and_charges')
+	frappe.reload_doc('accounts', 'doctype', 'payment_entry')
+
+	custom_fields = {
+		'Payment Entry': [
+			dict(fieldname='gst_section', label='GST Details', fieldtype='Section Break', insert_after='deductions',
+				print_hide=1, collapsible=1),
+			dict(fieldname='company_address', label='Company Address', fieldtype='Link', insert_after='gst_section',
+				print_hide=1, options='Address'),
+			dict(fieldname='company_gstin', label='Company GSTIN',
+				fieldtype='Data', insert_after='company_address',
+				fetch_from='company_address.gstin', print_hide=1, read_only=1),
+			dict(fieldname='place_of_supply', label='Place of Supply',
+				fieldtype='Data', insert_after='company_gstin',
+				print_hide=1, read_only=1),
+			dict(fieldname='customer_address', label='Customer Address', fieldtype='Link', insert_after='place_of_supply',
+				print_hide=1, options='Address', depends_on = 'eval:doc.party_type == "Customer"'),
+			dict(fieldname='customer_gstin', label='Customer GSTIN',
+				fieldtype='Data', insert_after='customer_address',
+				fetch_from='customer_address.gstin', print_hide=1, read_only=1)
+		]
+	}
+
+	create_custom_fields(custom_fields, update=True)
\ No newline at end of file
diff --git a/erpnext/regional/india/setup.py b/erpnext/regional/india/setup.py
index 4db5551..963c407 100644
--- a/erpnext/regional/india/setup.py
+++ b/erpnext/regional/india/setup.py
@@ -450,6 +450,26 @@
 		}
 	]
 
+	payment_entry_fields = [
+		dict(fieldname='gst_section', label='GST Details', fieldtype='Section Break', insert_after='deductions',
+			print_hide=1, collapsible=1),
+		dict(fieldname='company_address', label='Company Address', fieldtype='Link', insert_after='gst_section',
+			print_hide=1, options='Address'),
+		dict(fieldname='company_gstin', label='Company GSTIN',
+			fieldtype='Data', insert_after='company_address',
+			fetch_from='company_address.gstin', print_hide=1, read_only=1),
+		dict(fieldname='place_of_supply', label='Place of Supply',
+			fieldtype='Data', insert_after='company_gstin',
+			print_hide=1, read_only=1),
+		dict(fieldname='gst_column_break', fieldtype='Column Break',
+				insert_after='place_of_supply'),
+		dict(fieldname='customer_address', label='Customer Address', fieldtype='Link', insert_after='gst_column_break',
+			print_hide=1, options='Address', depends_on = 'eval:doc.party_type == "Customer"'),
+		dict(fieldname='customer_gstin', label='Customer GSTIN',
+			fieldtype='Data', insert_after='customer_address',
+			fetch_from='customer_address.gstin', print_hide=1, read_only=1)
+	]
+
 	custom_fields = {
 		'Address': [
 			dict(fieldname='gstin', label='Party GSTIN', fieldtype='Data',
@@ -464,6 +484,7 @@
 		'Purchase Receipt': purchase_invoice_gst_fields,
 		'Sales Invoice': sales_invoice_gst_category + invoice_gst_fields + sales_invoice_shipping_fields + sales_invoice_gst_fields + si_ewaybill_fields,
 		'Delivery Note': sales_invoice_gst_fields + ewaybill_fields + sales_invoice_shipping_fields + delivery_note_gst_category,
+		'Payment Entry': payment_entry_fields,
 		'Journal Entry': journal_entry_fields,
 		'Sales Order': sales_invoice_gst_fields,
 		'Tax Category': inter_state_gst_field,
diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py
index 635a1a1..bf06d4a 100644
--- a/erpnext/regional/india/utils.py
+++ b/erpnext/regional/india/utils.py
@@ -767,6 +767,15 @@
 		if tax.account_head in gst_accounts.get('cess_account', []):
 			doc.itc_cess_amount += flt(tax.base_tax_amount_after_discount_amount)
 
+def update_place_of_supply(doc, method):
+	country = frappe.get_cached_value('Company', doc.company, 'country')
+	if country != 'India':
+		return
+
+	address = frappe.db.get_value("Address", doc.get('customer_address'), ["gst_state", "gst_state_number"], as_dict=1)
+	if address and address.gst_state and address.gst_state_number:
+		doc.place_of_supply = cstr(address.gst_state_number) + "-" + cstr(address.gst_state)
+
 @frappe.whitelist()
 def get_regional_round_off_accounts(company, account_list):
 	country = frappe.get_cached_value('Company', company, 'country')
diff --git a/erpnext/regional/report/gstr_1/gstr_1.js b/erpnext/regional/report/gstr_1/gstr_1.js
index 444f5db..ef2bdb6 100644
--- a/erpnext/regional/report/gstr_1/gstr_1.js
+++ b/erpnext/regional/report/gstr_1/gstr_1.js
@@ -51,7 +51,9 @@
 				{ "value": "B2C Large", "label": __("B2C(Large) Invoices - 5A, 5B") },
 				{ "value": "B2C Small", "label": __("B2C(Small) Invoices - 7") },
 				{ "value": "CDNR-REG", "label": __("Credit/Debit Notes (Registered) - 9B") },
-				{ "value": "EXPORT", "label": __("Export Invoice - 6A") }
+				{ "value": "CDNR-UNREG", "label": __("Credit/Debit Notes (Unregistered) - 9B") },
+				{ "value": "EXPORT", "label": __("Export Invoice - 6A") },
+				{ "value": "Advances", "label": __("Tax Liability (Advances Received) - 11A(1), 11A(2)") }
 			],
 			"default": "B2B"
 		}
diff --git a/erpnext/regional/report/gstr_1/gstr_1.py b/erpnext/regional/report/gstr_1/gstr_1.py
index 7c4731e..e3e09ef 100644
--- a/erpnext/regional/report/gstr_1/gstr_1.py
+++ b/erpnext/regional/report/gstr_1/gstr_1.py
@@ -54,26 +54,45 @@
 			self.get_invoice_items()
 			self.get_items_based_on_tax_rate()
 			self.invoice_fields = [d["fieldname"] for d in self.invoice_columns]
-			self.get_data()
+		
+		self.get_data()
 
 		return self.columns, self.data
 
 	def get_data(self):
 		if self.filters.get("type_of_business") in  ("B2C Small", "B2C Large"):
 			self.get_b2c_data()
-		else:
+		elif self.filters.get("type_of_business") == "Advances":
+			self.get_advance_data()
+		elif self.invoices:
 			for inv, items_based_on_rate in self.items_based_on_tax_rate.items():
 				invoice_details = self.invoices.get(inv)
 				for rate, items in items_based_on_rate.items():
 					row, taxable_value = self.get_row_data_for_invoice(inv, invoice_details, rate, items)
 
-					if self.filters.get("type_of_business") == "CDNR-REG":
+					if self.filters.get("type_of_business") in ("CDNR-REG", "CDNR-UNREG"):
 						row.append("Y" if invoice_details.posting_date <= date(2017, 7, 1) else "N")
 						row.append("C" if invoice_details.is_return else "D")
 
 					if taxable_value:
 						self.data.append(row)
 
+	def get_advance_data(self):
+		advances_data = {}
+		advances = self.get_advance_entries()
+		for entry in advances:
+			# only consider IGST and SGST so as to avoid duplication of taxable amount
+			if entry.account_head in self.gst_accounts.igst_account or \
+				entry.account_head in self.gst_accounts.sgst_account:
+				advances_data.setdefault((entry.place_of_supply, entry.rate), [0.0, 0.0])
+				advances_data[(entry.place_of_supply, entry.rate)][0] += (entry.amount * 100 / entry.rate)
+			elif entry.account_head in self.gst_accounts.cess_account:
+				advances_data[(entry.place_of_supply, entry.rate)][1] += entry.amount
+
+		for key, value in advances_data.items():
+			row= [key[0], key[1], value[0], value[1]]
+			self.data.append(row)
+
 	def get_b2c_data(self):
 		b2cs_output = {}
 
@@ -110,7 +129,7 @@
 	def get_row_data_for_invoice(self, invoice, invoice_details, tax_rate, items):
 		row = []
 		for fieldname in self.invoice_fields:
-			if self.filters.get("type_of_business") == "CDNR-REG" and fieldname == "invoice_value":
+			if self.filters.get("type_of_business") in ("CDNR-REG", "CDNR-UNREG") and fieldname == "invoice_value":
 				row.append(abs(invoice_details.base_rounded_total) or abs(invoice_details.base_grand_total))
 			elif fieldname == "invoice_value":
 				row.append(invoice_details.base_rounded_total or invoice_details.base_grand_total)
@@ -171,6 +190,16 @@
 		for d in invoice_data:
 			self.invoices.setdefault(d.invoice_number, d)
 
+	def get_advance_entries(self):
+		return frappe.db.sql("""
+			SELECT SUM(a.base_tax_amount) as amount, a.account_head, a.rate, p.place_of_supply
+			FROM `tabPayment Entry` p, `tabAdvance Taxes and Charges` a
+			WHERE p.docstatus = 1
+			AND p.name = a.parent
+			AND posting_date between %s and %s
+			GROUP BY a.account_head, p.place_of_supply, a.rate
+		""", (self.filters.get('from_date'), self.filters.get('to_date')), as_dict=1)
+
 	def get_conditions(self):
 		conditions = ""
 
@@ -202,6 +231,12 @@
 		elif self.filters.get("type_of_business") == "CDNR-REG":
 			conditions += """ AND (is_return = 1 OR is_debit_note = 1) AND IFNULL(gst_category, '') in ('Registered Regular', 'Deemed Export', 'SEZ')"""
 
+		elif self.filters.get("type_of_business") == "CDNR-UNREG":
+			b2c_limit = frappe.db.get_single_value('GST Settings', 'b2c_limit')
+			conditions += """ AND ifnull(SUBSTR(place_of_supply, 1, 2),'') != ifnull(SUBSTR(company_gstin, 1, 2),'')
+				AND ABS(grand_total) > {0} AND (is_return = 1 OR is_debit_note = 1)
+				AND IFNULL(gst_category, '') in ('Unregistered', 'Overseas')""".format(flt(b2c_limit))
+
 		elif self.filters.get("type_of_business") ==  "EXPORT":
 			conditions += """ AND is_return !=1 and gst_category = 'Overseas' """
 
@@ -507,6 +542,84 @@
 					"width": 80
 				}
 			]
+		elif self.filters.get("type_of_business") == "CDNR-UNREG":
+			self.invoice_columns = [
+				{
+					"fieldname": "customer_name",
+					"label": "Receiver Name",
+					"fieldtype": "Data",
+					"width": 120
+				},
+				{
+					"fieldname": "return_against",
+					"label": "Issued Against",
+					"fieldtype": "Link",
+					"options": "Sales Invoice",
+					"width": 120
+				},
+				{
+					"fieldname": "posting_date",
+					"label": "Note Date",
+					"fieldtype": "Date",
+					"width": 120
+				},
+				{
+					"fieldname": "invoice_number",
+					"label": "Note Number",
+					"fieldtype": "Link",
+					"options": "Sales Invoice",
+					"width":120
+				},
+				{
+					"fieldname": "export_type",
+					"label": "Export Type",
+					"fieldtype": "Data",
+					"hidden": 1
+				},
+				{
+					"fieldname": "reason_for_issuing_document",
+					"label": "Reason For Issuing document",
+					"fieldtype": "Data",
+					"width": 140
+				},
+				{
+					"fieldname": "place_of_supply",
+					"label": "Place Of Supply",
+					"fieldtype": "Data",
+					"width": 120
+				},
+				{
+					"fieldname": "gst_category",
+					"label": "GST Category",
+					"fieldtype": "Data"
+				},
+				{
+					"fieldname": "invoice_value",
+					"label": "Invoice Value",
+					"fieldtype": "Currency",
+					"width": 120
+				}
+			]
+			self.other_columns = [
+				{
+					"fieldname": "cess_amount",
+					"label": "Cess Amount",
+					"fieldtype": "Currency",
+					"width": 100
+				},
+				{
+					"fieldname": "pre_gst",
+					"label": "PRE GST",
+					"fieldtype": "Data",
+					"width": 80
+				},
+				{
+					"fieldname": "document_type",
+					"label": "Document Type",
+					"fieldtype": "Data",
+					"width": 80
+				}
+			]
 		elif self.filters.get("type_of_business") ==  "B2C Small":
 			self.invoice_columns = [
 				{
@@ -582,6 +695,25 @@
 					"width": 120
 				}
 			]
+		elif self.filters.get("type_of_business") == "Advances":
+			self.invoice_columns = [
+				{
+					"fieldname": "place_of_supply",
+					"label": "Place Of Supply",
+					"fieldtype": "Data",
+					"width": 120
+				}
+			]
+
+			self.other_columns = [
+				{
+						"fieldname": "cess_amount",
+						"label": "Cess Amount",
+						"fieldtype": "Currency",
+						"width": 100
+				}
+			]
+			
 		self.columns = self.invoice_columns + self.tax_columns + self.other_columns
 
 @frappe.whitelist()
@@ -620,12 +752,29 @@
 
 		out = get_export_json(res)
 		gst_json["exp"] = out
-	elif filters["type_of_business"] == 'CDNR-REG':
+	elif filters["type_of_business"] == "CDNR-REG":
 		for item in report_data[:-1]:
 			res.setdefault(item["customer_gstin"], {}).setdefault(item["invoice_number"],[]).append(item)
 
 		out = get_cdnr_reg_json(res, gstin)
 		gst_json["cdnr"] = out
+	elif filters["type_of_business"] == "CDNR-UNREG":
+		for item in report_data[:-1]:
+			res.setdefault(item["invoice_number"],[]).append(item)
+
+		out = get_cdnr_unreg_json(res, gstin)
+		gst_json["cdnur"] = out
+
+	elif filters["type_of_business"] == "Advances":
+		for item in report_data[:-1]:
+			if not item.get("place_of_supply"):
+				frappe.throw(_("""{0} not entered in some entries.
+					Please update and try again""").format(frappe.bold("Place Of Supply")))
+
+			res.setdefault(item["place_of_supply"],[]).append(item)
+
+		out = get_advances_json(res, gstin)
+		gst_json["at"] = out
 
 	return {
 		'report_name': report_name,
@@ -705,6 +854,40 @@
 
 	return out
 
+def get_advances_json(data, gstin):
+	company_state_number = gstin[0:2]
+	out = []
+	for place_of_supply, items in iteritems(data):
+		supply_type = "INTRA" if company_state_number == place_of_supply.split('-')[0] else "INTER"
+		row = {
+			"pos": place_of_supply.split('-')[0],
+			"itms": [],
+			"sply_ty": supply_type
+		}
+
+		for item in items:
+			itms = {
+				'rt': item['rate'],
+				'ad_amount': flt(item.get('taxable_value')),
+				'csamt': flt(item.get('cess_amount'))
+			}
+
+			if supply_type == "INTRA":
+				itms.update({
+					"samt": flt((itms["ad_amount"] * itms["rt"]) / 100),
+					"camt": flt((itms["ad_amount"] * itms["rt"]) / 100),
+					"rt": itms["rt"] * 2
+				})
+			else:
+				itms.update({
+					"iamt": flt((itms["ad_amount"] * itms["rt"]) / 100)
+				})
+
+			row['itms'].append(itms)
+		out.append(row)
+
+	return out
+
 def get_b2cl_json(res, gstin):
 	out = []
 	for pos in res:
@@ -784,6 +967,27 @@
 
 	return out
 
+def get_cdnr_unreg_json(res, gstin):
+	out = []
+
+	for invoice, items in iteritems(res):
+		inv_item = {
+			"nt_num": items[0]["invoice_number"],
+			"nt_dt": getdate(items[0]["posting_date"]).strftime('%d-%m-%Y'),
+			"val": abs(flt(items[0]["invoice_value"])),
+			"ntty": items[0]["document_type"],
+			"pos": "%02d" % int(items[0]["place_of_supply"].split('-')[0]),
+			"typ": get_invoice_type_for_cdnrur(items[0])
+		}
+
+		inv_item["itms"] = []
+		for item in items:
+			inv_item["itms"].append(get_rate_and_tax_details(item, gstin))
+
+		out.append(inv_item)
+
+	return out
+
 def get_invoice_type_for_cdnr(row):
 	if row.get('gst_category') == 'SEZ':
 		if row.get('export_type') == 'WPAY':
@@ -791,12 +995,23 @@
 		else:
 			invoice_type = 'SEWOP'
 	elif row.get('gst_category') == 'Deemed Export':
-		row.invoice_type = 'DE'
+		invoice_type = 'DE'
 	elif row.get('gst_category') == 'Registered Regular':
 		invoice_type = 'R'
 
 	return invoice_type
 
+def get_invoice_type_for_cdnrur(row):
+	if row.get('gst_category') == 'Overseas':
+		if row.get('export_type') == 'WPAY':
+			invoice_type = 'EXPWP'
+		else:
+			invoice_type = 'EXPWOP'
+	elif row.get('gst_category') == 'Unregistered':
+		invoice_type = 'B2CL'
+
+	return invoice_type
+
 def get_basic_invoice_detail(row):
 	return {
 		"inum": row["invoice_number"],
@@ -836,7 +1051,7 @@
 			["Dynamic Link", "link_name", "=", company],
 			["Dynamic Link", "parenttype", "=", "Address"],
 		]
-		gstin = frappe.get_all("Address", filters=filters, pluck="gstin")
+		gstin = frappe.get_all("Address", filters=filters, pluck="gstin", order_by="is_primary_address desc")
 		if gstin and not all_gstins:
 			gstin = gstin[0]