feat: Group by item wise sales register (#20251)

* fix: Group by sales register report

* fix: Remove group-by item

* fix: Grand total query fix

* fix: Remove debug param

* fix: Code cleanup and commonification

* fix: Add group by functionality to purchase register

* fix: Changes in total row display

* fix: Minor fixes

* fix: Add total row

* fix: Do not calculate total for rate
diff --git a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.js b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.js
index e936a17..f88906a 100644
--- a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.js
+++ b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.js
@@ -34,6 +34,20 @@
 			"label": __("Mode of Payment"),
 			"fieldtype": "Link",
 			"options": "Mode of Payment"
+		},
+		{
+			"label": __("Group By"),
+			"fieldname": "group_by",
+			"fieldtype": "Select",
+			"options": ["Supplier", "Item Group", "Item", "Invoice"]
 		}
-	]
+	],
+	"formatter": function(value, row, column, data, default_formatter) {
+		value = default_formatter(value, row, column, data);
+		if (data && data.bold) {
+			value = value.bold();
+
+		}
+		return value;
+	}
 }
diff --git a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py
index 380b208..8b6359c 100644
--- a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py
+++ b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py
@@ -5,7 +5,9 @@
 import frappe, erpnext
 from frappe import _
 from frappe.utils import flt
-from erpnext.accounts.report.item_wise_sales_register.item_wise_sales_register import get_tax_accounts
+from erpnext.accounts.report.item_wise_sales_register.item_wise_sales_register import (get_tax_accounts,
+	get_grand_total, add_total_row, get_display_value, get_group_by_and_display_fields, add_sub_total_row,
+	get_group_by_conditions)
 
 def execute(filters=None):
 	return _execute(filters)
@@ -13,7 +15,7 @@
 def _execute(filters=None, additional_table_columns=None, additional_query_columns=None):
 	if not filters: filters = {}
 	filters.update({"from_date": filters.get("date_range")[0], "to_date": filters.get("date_range")[1]})
-	columns = get_columns(additional_table_columns)
+	columns = get_columns(additional_table_columns, filters)
 
 	company_currency = erpnext.get_company_currency(filters.company)
 
@@ -23,16 +25,16 @@
 		itemised_tax, tax_columns = get_tax_accounts(item_list, columns, company_currency,
 			doctype="Purchase Invoice", tax_doctype="Purchase Taxes and Charges")
 
-	columns.append({
-		"fieldname": "currency",
-		"label": _("Currency"),
-		"fieldtype": "Data",
-		"width": 80
-	})
-
 	po_pr_map = get_purchase_receipts_against_purchase_order(item_list)
 
 	data = []
+	total_row_map = {}
+	skip_total_row = 0
+	prev_group_by_value = ''
+
+	if filters.get('group_by'):
+		grand_total = get_grand_total(filters, 'Purchase Invoice')
+
 	for d in item_list:
 		if not d.stock_qty:
 			continue
@@ -44,51 +46,243 @@
 			purchase_receipt = ", ".join(po_pr_map.get(d.po_detail, []))
 
 		expense_account = d.expense_account or aii_account_map.get(d.company)
-		row = [d.item_code, d.item_name, d.item_group, d.description, d.parent, d.posting_date, d.supplier,
-			d.supplier_name]
+
+		row = {
+			'item_code': d.item_code,
+			'item_name': d.item_name,
+			'item_group': d.item_group,
+			'description': d.description,
+			'invoice': d.parent,
+			'posting_date': d.posting_date,
+			'customer': d.supplier,
+			'customer_name': d.supplier_name
+		}
 
 		if additional_query_columns:
 			for col in additional_query_columns:
-				row.append(d.get(col))
+				row.update({
+					col: d.get(col)
+				})
 
-		row += [
-			d.credit_to, d.mode_of_payment, d.project, d.company, d.purchase_order,
-			purchase_receipt, expense_account, d.stock_qty, d.stock_uom, d.base_net_amount / d.stock_qty, d.base_net_amount
-		]
+		row.update({
+			'credit_to': d.credit_to,
+			'mode_of_payment': d.mode_of_payment,
+			'project': d.project,
+			'company': d.company,
+			'purchase_order': d.purchase_order,
+			'purchase_receipt': d.purchase_receipt,
+			'expense_account': expense_account,
+			'stock_qty': d.stock_qty,
+			'stock_uom': d.stock_uom,
+			'rate': d.base_net_amount / d.stock_qty,
+			'amount': d.base_net_amount
+		})
 
 		total_tax = 0
 		for tax in tax_columns:
 			item_tax = itemised_tax.get(d.name, {}).get(tax, {})
-			row += [item_tax.get("tax_rate", 0), item_tax.get("tax_amount", 0)]
+			row.update({
+				frappe.scrub(tax + ' Rate'): item_tax.get("tax_rate", 0),
+				frappe.scrub(tax + ' Amount'): item_tax.get("tax_amount", 0),
+			})
 			total_tax += flt(item_tax.get("tax_amount"))
 
-		row += [total_tax, d.base_net_amount + total_tax, company_currency]
+		row.update({
+			'total_tax': total_tax,
+			'total': d.base_net_amount + total_tax,
+			'currency': company_currency
+		})
+
+		if filters.get('group_by'):
+			row.update({'percent_gt': flt(row['total']/grand_total) * 100})
+			group_by_field, subtotal_display_field = get_group_by_and_display_fields(filters)
+			data, prev_group_by_value = add_total_row(data, filters, prev_group_by_value, d, total_row_map,
+				group_by_field, subtotal_display_field, grand_total, tax_columns)
+			add_sub_total_row(row, total_row_map, d.get(group_by_field, ''), tax_columns)
 
 		data.append(row)
 
-	return columns, data
+	if filters.get('group_by'):
+		total_row = total_row_map.get(prev_group_by_value or d.get('item_name'))
+		total_row['percent_gt'] = flt(total_row['total']/grand_total * 100)
+		data.append(total_row)
+		data.append({})
+		add_sub_total_row(total_row, total_row_map, 'total_row', tax_columns)
+		data.append(total_row_map.get('total_row'))
+		skip_total_row = 1
+
+	return columns, data, None, None, None, skip_total_row
 
 
-def get_columns(additional_table_columns):
-	columns = [
-		_("Item Code") + ":Link/Item:120", _("Item Name") + "::120",
-		_("Item Group") + ":Link/Item Group:100", "Description::150", _("Invoice") + ":Link/Purchase Invoice:120",
-		_("Posting Date") + ":Date:80", _("Supplier") + ":Link/Supplier:120",
-		"Supplier Name::120"
-	]
+def get_columns(additional_table_columns, filters):
+
+	columns = []
+
+	if filters.get('group_by') != ('Item'):
+		columns.extend(
+			[
+				{
+					'label': _('Item Code'),
+					'fieldname': 'item_code',
+					'fieldtype': 'Link',
+					'options': 'Item',
+					'width': 120
+				},
+				{
+					'label': _('Item Name'),
+					'fieldname': 'item_name',
+					'fieldtype': 'Data',
+					'width': 120
+				}
+			]
+		)
+
+	if filters.get('group_by') not in ('Item', 'Item Group'):
+		columns.extend([
+			{
+				'label': _('Item Group'),
+				'fieldname': 'item_group',
+				'fieldtype': 'Link',
+				'options': 'Item Group',
+				'width': 120
+			}
+		])
+
+	columns.extend([
+		{
+			'label': _('Description'),
+			'fieldname': 'description',
+			'fieldtype': 'Data',
+			'width': 150
+		},
+		{
+			'label': _('Invoice'),
+			'fieldname': 'invoice',
+			'fieldtype': 'Link',
+			'options': 'Purchase Invoice',
+			'width': 120
+		},
+		{
+			'label': _('Posting Date'),
+			'fieldname': 'posting_date',
+			'fieldtype': 'Date',
+			'width': 120
+		}
+	])
+
+	if filters.get('group_by') != 'Supplier':
+		columns.extend([
+			{
+				'label': _('Supplier'),
+				'fieldname': 'supplier',
+				'fieldtype': 'Link',
+				'options': 'Supplier',
+				'width': 120
+			},
+			{
+				'label': _('Supplier Name'),
+				'fieldname': 'supplier_name',
+				'fieldtype': 'Data',
+				'width': 120
+			}
+		])
 
 	if additional_table_columns:
 		columns += additional_table_columns
 
 	columns += [
-		 "Payable Account:Link/Account:120",
-		_("Mode of Payment") + ":Link/Mode of Payment:80", _("Project") + ":Link/Project:80",
-		_("Company") + ":Link/Company:100", _("Purchase Order") + ":Link/Purchase Order:100",
-		_("Purchase Receipt") + ":Link/Purchase Receipt:100", _("Expense Account") + ":Link/Account:140",
-		_("Stock Qty") + ":Float:120", _("Stock UOM") + "::100",
-		_("Rate") + ":Currency/currency:120", _("Amount") + ":Currency/currency:120"
+		{
+			'label': _('Payable Account'),
+			'fieldname': 'credit_to',
+			'fieldtype': 'Link',
+			'options': 'Account',
+			'width': 80
+		},
+		{
+			'label': _('Mode Of Payment'),
+			'fieldname': 'mode_of_payment',
+			'fieldtype': 'Data',
+			'width': 120
+		},
+		{
+			'label': _('Project'),
+			'fieldname': 'project',
+			'fieldtype': 'Link',
+			'options': 'Project',
+			'width': 80
+		},
+		{
+			'label': _('Company'),
+			'fieldname': 'company',
+			'fieldtype': 'Link',
+			'options': 'Company',
+			'width': 80
+		},
+		{
+			'label': _('Purchase Order'),
+			'fieldname': 'purchase_order',
+			'fieldtype': 'Link',
+			'options': 'Purchase Order',
+			'width': 100
+		},
+		{
+			'label': _("Purchase Receipt"),
+			'fieldname': 'Purchase Receipt',
+			'fieldtype': 'Link',
+			'options': 'Purchase Receipt',
+			'width': 100
+		},
+		{
+			'label': _('Expense Account'),
+			'fieldname': 'expense_account',
+			'fieldtype': 'Link',
+			'options': 'Account',
+			'width': 100
+		},
+		{
+			'label': _('Stock Qty'),
+			'fieldname': 'stock_qty',
+			'fieldtype': 'Float',
+			'width': 100
+		},
+		{
+			'label': _('Stock UOM'),
+			'fieldname': 'stock_uom',
+			'fieldtype': 'Link',
+			'options': 'UOM',
+			'width': 100
+		},
+		{
+			'label': _('Rate'),
+			'fieldname': 'rate',
+			'fieldtype': 'Float',
+			'options': 'currency',
+			'width': 100
+		},
+		{
+			'label': _('Amount'),
+			'fieldname': 'amount',
+			'fieldtype': 'Currency',
+			'options': 'currency',
+			'width': 100
+		},
+		{
+			'fieldname': 'currency',
+			'label': _('Currency'),
+			'fieldtype': 'Currency',
+			'width': 80,
+			'hidden': 1
+		}
 	]
 
+	if filters.get('group_by'):
+		columns.append({
+			'label': _('% Of Grand Total'),
+			'fieldname': 'percent_gt',
+			'fieldtype': 'Float',
+			'width': 80
+		})
+
 	return columns
 
 def get_conditions(filters):
@@ -103,6 +297,11 @@
 			if filters.get(opts[0]):
 				conditions += opts[1]
 
+	if not filters.get("group_by"):
+		conditions += "ORDER BY `tabPurchase Invoice`.posting_date desc, `tabPurchase Invoice Item`.item_code desc"
+	else:
+		conditions += get_group_by_conditions(filters, 'Purchase Invoice')
+
 	return conditions
 
 def get_items(filters, additional_query_columns):
@@ -129,7 +328,6 @@
 		from `tabPurchase Invoice`, `tabPurchase Invoice Item`
 		where `tabPurchase Invoice`.name = `tabPurchase Invoice Item`.`parent` and
 		`tabPurchase Invoice`.docstatus = 1 %s %s
-		order by `tabPurchase Invoice`.posting_date desc, `tabPurchase Invoice Item`.item_code desc
 	""".format(additional_query_columns) % (conditions, match_conditions), filters, as_dict=1)
 
 def get_aii_accounts():
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 9dd9b3f..8a9c76f 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
@@ -4,48 +4,62 @@
 frappe.query_reports["Item-wise Sales Register"] = {
 	"filters": [
 		{
-			"fieldname":"date_range",
+			"fieldname": "date_range",
 			"label": __("Date Range"),
 			"fieldtype": "DateRange",
 			"default": [frappe.datetime.add_months(frappe.datetime.get_today(),-1), frappe.datetime.get_today()],
 			"reqd": 1
 		},
 		{
-			"fieldname":"customer",
+			"fieldname": "customer",
 			"label": __("Customer"),
 			"fieldtype": "Link",
 			"options": "Customer"
 		},
 		{
-			"fieldname":"company",
+			"fieldname": "company",
 			"label": __("Company"),
 			"fieldtype": "Link",
 			"options": "Company",
 			"default": frappe.defaults.get_user_default("Company")
 		},
 		{
-			"fieldname":"mode_of_payment",
+			"fieldname": "mode_of_payment",
 			"label": __("Mode of Payment"),
 			"fieldtype": "Link",
 			"options": "Mode of Payment"
 		},
 		{
-			"fieldname":"warehouse",
+			"fieldname": "warehouse",
 			"label": __("Warehouse"),
 			"fieldtype": "Link",
 			"options": "Warehouse"
 		},
 		{
-			"fieldname":"brand",
+			"fieldname": "brand",
 			"label": __("Brand"),
 			"fieldtype": "Link",
 			"options": "Brand"
 		},
 		{
-			"fieldname":"item_group",
+			"fieldname": "item_group",
 			"label": __("Item Group"),
 			"fieldtype": "Link",
 			"options": "Item Group"
+		},
+		{
+			"label": __("Group By"),
+			"fieldname": "group_by",
+			"fieldtype": "Select",
+			"options": ["Customer Group", "Customer", "Item Group", "Item", "Territory", "Invoice"]
 		}
-	]
+	],
+	"formatter": function(value, row, column, data, default_formatter) {
+		value = default_formatter(value, row, column, data);
+		if (data && data.bold) {
+			value = value.bold();
+
+		}
+		return value;
+	}
 }
diff --git a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py
index 9da89ff..2cc2db6 100644
--- a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py
+++ b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py
@@ -4,7 +4,7 @@
 from __future__ import unicode_literals
 import frappe, erpnext
 from frappe import _
-from frappe.utils import flt
+from frappe.utils import flt, cstr
 from frappe.model.meta import get_field_precision
 from frappe.utils.xlsxutils import handle_html
 from erpnext.accounts.report.sales_register.sales_register import get_mode_of_payments
@@ -15,23 +15,25 @@
 def _execute(filters=None, additional_table_columns=None, additional_query_columns=None):
 	if not filters: filters = {}
 	filters.update({"from_date": filters.get("date_range") and filters.get("date_range")[0], "to_date": filters.get("date_range") and filters.get("date_range")[1]})
-	columns = get_columns(additional_table_columns)
+	columns = get_columns(additional_table_columns, filters)
 
 	company_currency = frappe.get_cached_value('Company',  filters.get("company"),  "default_currency")
 
 	item_list = get_items(filters, additional_query_columns)
 	if item_list:
 		itemised_tax, tax_columns = get_tax_accounts(item_list, columns, company_currency)
-	columns.append({
-		"fieldname": "currency",
-		"label": _("Currency"),
-		"fieldtype": "Data",
-		"width": 80
-	})
+
 	mode_of_payments = get_mode_of_payments(set([d.parent for d in item_list]))
 	so_dn_map = get_delivery_notes_against_sales_order(item_list)
 
 	data = []
+	total_row_map = {}
+	skip_total_row = 0
+	prev_group_by_value = ''
+
+	if filters.get('group_by'):
+		grand_total = get_grand_total(filters, 'Sales Invoice')
+
 	for d in item_list:
 		delivery_note = None
 		if d.delivery_note:
@@ -42,57 +44,285 @@
 		if not delivery_note and d.update_stock:
 			delivery_note = d.parent
 
-		row = [d.item_code, d.item_name, d.item_group, d.description, d.parent, d.posting_date, d.customer, d.customer_name]
+		row = {
+			'item_code': d.item_code,
+			'item_name': d.item_name,
+			'item_group': d.item_group,
+			'description': d.description,
+			'invoice': d.parent,
+			'posting_date': d.posting_date,
+			'customer': d.customer,
+			'customer_name': d.customer_name,
+			'customer_group': d.customer_group,
+		}
 
 		if additional_query_columns:
 			for col in additional_query_columns:
-				row.append(d.get(col))
+				row.update({
+					col: d.get(col)
+				})
 
-		row += [
-			d.customer_group, d.debit_to, ", ".join(mode_of_payments.get(d.parent, [])),
-			d.territory, d.project, d.company, d.sales_order,
-			delivery_note, d.income_account, d.cost_center, d.stock_qty, d.stock_uom
-		]
+		row.update({
+			'debit_to': d.debit_to,
+			'mode_of_payment': ", ".join(mode_of_payments.get(d.parent, [])),
+			'territory': d.territory,
+			'project': d.project,
+			'company': d.company,
+			'sales_order': d.sales_order,
+			'delivery_note': d.delivery_note,
+			'income_account': d.income_account,
+			'cost_center': d.cost_center,
+			'stock_qty': d.stock_qty,
+			'stock_uom': d.stock_uom
+		})
 
 		if d.stock_uom != d.uom and d.stock_qty:
-			row += [(d.base_net_rate * d.qty)/d.stock_qty, d.base_net_amount]
+			row.update({
+				'rate': (d.base_net_rate * d.qty)/d.stock_qty,
+				'amount': d.base_net_amount
+			})
 		else:
-			row += [d.base_net_rate, d.base_net_amount]
+			row.update({
+				'rate': d.base_net_rate,
+				'amount': d.base_net_amount
+			})
 
 		total_tax = 0
 		for tax in tax_columns:
 			item_tax = itemised_tax.get(d.name, {}).get(tax, {})
-			row += [item_tax.get("tax_rate", 0), item_tax.get("tax_amount", 0)]
+			row.update({
+				frappe.scrub(tax + ' Rate'): item_tax.get("tax_rate", 0),
+				frappe.scrub(tax + ' Amount'): item_tax.get("tax_amount", 0),
+			})
 			total_tax += flt(item_tax.get("tax_amount"))
 
-		row += [total_tax, d.base_net_amount + total_tax, company_currency]
+		row.update({
+			'total_tax': total_tax,
+			'total': d.base_net_amount + total_tax,
+			'currency': company_currency
+		})
+
+		if filters.get('group_by'):
+			row.update({'percent_gt': flt(row['total']/grand_total) * 100})
+			group_by_field, subtotal_display_field = get_group_by_and_display_fields(filters)
+			data, prev_group_by_value = add_total_row(data, filters, prev_group_by_value, d, total_row_map,
+				group_by_field, subtotal_display_field, grand_total, tax_columns)
+			add_sub_total_row(row, total_row_map, d.get(group_by_field, ''), tax_columns)
 
 		data.append(row)
 
-	return columns, data
+	if filters.get('group_by'):
+		total_row = total_row_map.get(prev_group_by_value or d.get('item_name'))
+		total_row['percent_gt'] = flt(total_row['total']/grand_total * 100)
+		data.append(total_row)
+		data.append({})
+		add_sub_total_row(total_row, total_row_map, 'total_row', tax_columns)
+		data.append(total_row_map.get('total_row'))
+		skip_total_row = 1
+	
+	return columns, data, None, None, None, skip_total_row
 
-def get_columns(additional_table_columns):
-	columns = [
-		_("Item Code") + ":Link/Item:120", _("Item Name") + "::120",
-		_("Item Group") + ":Link/Item Group:100", "Description::150", _("Invoice") + ":Link/Sales Invoice:120",
-		_("Posting Date") + ":Date:80", _("Customer") + ":Link/Customer:120",
-		_("Customer Name") + "::120"]
+def get_columns(additional_table_columns, filters):
+	columns = []
+
+	if filters.get('group_by') != ('Item'):
+		columns.extend(
+			[
+				{
+					'label': _('Item Code'),
+					'fieldname': 'item_code',
+					'fieldtype': 'Link',
+					'options': 'Item',
+					'width': 120
+				},
+				{
+					'label': _('Item Name'),
+					'fieldname': 'item_name',
+					'fieldtype': 'Data',
+					'width': 120
+				}
+			]
+		)
+
+	if filters.get('group_by') not in ('Item', 'Item Group'):
+		columns.extend([
+			{
+				'label': _('Item Group'),
+				'fieldname': 'item_group',
+				'fieldtype': 'Link',
+				'options': 'Item Group',
+				'width': 120
+			}
+		])
+
+	columns.extend([
+		{
+			'label': _('Description'),
+			'fieldname': 'description',
+			'fieldtype': 'Data',
+			'width': 150
+		},
+		{
+			'label': _('Invoice'),
+			'fieldname': 'invoice',
+			'fieldtype': 'Link',
+			'options': 'Sales Invoice',
+			'width': 120
+		},
+		{
+			'label': _('Posting Date'),
+			'fieldname': 'posting_date',
+			'fieldtype': 'Date',
+			'width': 120
+		}
+	])
+
+	if filters.get('group_by') != 'Customer':
+		columns.extend([
+			{
+				'label': _('Customer Group'),
+				'fieldname': 'customer_group',
+				'fieldtype': 'Link',
+				'options': 'Customer Group',
+				'width': 120
+			}
+		])
+
+	if filters.get('group_by') not in ('Customer', 'Customer Group'):
+		columns.extend([
+			{
+				'label': _('Customer'),
+				'fieldname': 'customer',
+				'fieldtype': 'Link',
+				'options': 'Customer',
+				'width': 120
+			},
+			{
+				'label': _('Customer Name'),
+				'fieldname': 'customer_name',
+				'fieldtype': 'Data',
+				'width': 120
+			}
+		])
 
 	if additional_table_columns:
 		columns += additional_table_columns
 
 	columns += [
-		_("Customer Group") + ":Link/Customer Group:120",
-		_("Receivable Account") + ":Link/Account:120",
-		_("Mode of Payment") + "::120", _("Territory") + ":Link/Territory:80",
-		_("Project") + ":Link/Project:80", _("Company") + ":Link/Company:100",
-		_("Sales Order") + ":Link/Sales Order:100", _("Delivery Note") + ":Link/Delivery Note:100",
-		_("Income Account") + ":Link/Account:140", _("Cost Center") + ":Link/Cost Center:140",
-		_("Stock Qty") + ":Float:120", _("Stock UOM") + "::100",
-		_("Rate") + ":Currency/currency:120",
-		_("Amount") + ":Currency/currency:120"
+		{
+			'label': _('Receivable Account'),
+			'fieldname': 'debit_to',
+			'fieldtype': 'Link',
+			'options': 'Account',
+			'width': 80
+		},
+		{
+			'label': _('Mode Of Payment'),
+			'fieldname': 'mode_of_payment',
+			'fieldtype': 'Data',
+			'width': 120
+		}
 	]
 
+	if filters.get('group_by') != 'Terriotory':
+		columns.extend([
+			{
+				'label': _("Territory"),
+				'fieldname': 'territory',
+				'fieldtype': 'Link',
+				'options': 'Territory',
+				'width': 80
+			}
+		])
+
+
+	columns += [
+		{
+			'label': _('Project'),
+			'fieldname': 'project',
+			'fieldtype': 'Link',
+			'options': 'Project',
+			'width': 80
+		},
+		{
+			'label': _('Company'),
+			'fieldname': 'company',
+			'fieldtype': 'Link',
+			'options': 'Company',
+			'width': 80
+		},
+		{
+			'label': _('Sales Order'),
+			'fieldname': 'sales_order',
+			'fieldtype': 'Link',
+			'options': 'Sales Order',
+			'width': 100
+		},
+		{
+			'label': _("Delivery Note"),
+			'fieldname': 'delivery_note',
+			'fieldtype': 'Link',
+			'options': 'Delivery Note',
+			'width': 100
+		},
+		{
+			'label': _('Income Account'),
+			'fieldname': 'income_account',
+			'fieldtype': 'Link',
+			'options': 'Account',
+			'width': 100
+		},
+		{
+			'label': _("Cost Center"),
+			'fieldname': 'cost_center',
+			'fieldtype': 'Link',
+			'options': 'Cost Center',
+			'width': 100
+		},
+		{
+			'label': _('Stock Qty'),
+			'fieldname': 'stock_qty',
+			'fieldtype': 'Float',
+			'width': 100
+		},
+		{
+			'label': _('Stock UOM'),
+			'fieldname': 'stock_uom',
+			'fieldtype': 'Link',
+			'options': 'UOM',
+			'width': 100
+		},
+		{
+			'label': _('Rate'),
+			'fieldname': 'rate',
+			'fieldtype': 'Float',
+			'options': 'currency',
+			'width': 100
+		},
+		{
+			'label': _('Amount'),
+			'fieldname': 'amount',
+			'fieldtype': 'Currency',
+			'options': 'currency',
+			'width': 100
+		},
+		{
+			'fieldname': 'currency',
+			'label': _('Currency'),
+			'fieldtype': 'Currency',
+			'width': 80,
+			'hidden': 1
+		}
+	]
+
+	if filters.get('group_by'):
+		columns.append({
+			'label': _('% Of Grand Total'),
+			'fieldname': 'percent_gt',
+			'fieldtype': 'Float',
+			'width': 80
+		})
+
 	return columns
 
 def get_conditions(filters):
@@ -112,24 +342,32 @@
 				and ifnull(`tabSales Invoice Payment`.mode_of_payment, '') = %(mode_of_payment)s)"""
 
 	if filters.get("warehouse"):
-		conditions +=  """ and exists(select name from `tabSales Invoice Item`
-			 where parent=`tabSales Invoice`.name
-			 	and ifnull(`tabSales Invoice Item`.warehouse, '') = %(warehouse)s)"""
+		conditions +=  """and ifnull(`tabSales Invoice Item`.warehouse, '') = %(warehouse)s"""
 
 
 	if filters.get("brand"):
-		conditions +=  """ and exists(select name from `tabSales Invoice Item`
-			 where parent=`tabSales Invoice`.name
-			 	and ifnull(`tabSales Invoice Item`.brand, '') = %(brand)s)"""
+		conditions +=  """and ifnull(`tabSales Invoice Item`.brand, '') = %(brand)s"""
 
 	if filters.get("item_group"):
-		conditions +=  """ and exists(select name from `tabSales Invoice Item`
-			 where parent=`tabSales Invoice`.name
-			 	and ifnull(`tabSales Invoice Item`.item_group, '') = %(item_group)s)"""
+		conditions +=  """and ifnull(`tabSales Invoice Item`.item_group, '') = %(item_group)s"""
 
+	if not filters.get("group_by"):
+		conditions += "ORDER BY `tabSales Invoice`.posting_date desc, `tabSales Invoice Item`.item_group desc"
+	else:
+		conditions += get_group_by_conditions(filters, 'Sales Invoice')
 
 	return conditions
 
+def get_group_by_conditions(filters, doctype):
+	if filters.get("group_by") == 'Invoice':
+		return "ORDER BY `tab{0} Item`.parent desc".format(doctype)
+	elif filters.get("group_by") == 'Item':
+		return "ORDER BY `tab{0} Item`.`item_code`".format(doctype)
+	elif filters.get("group_by") == 'Item Group':
+		return "ORDER BY `tab{0} Item`.{1}".format(doctype, frappe.scrub(filters.get('group_by')))
+	elif filters.get("group_by") in ('Customer', 'Customer Group', 'Territory', 'Supplier'):
+		return "ORDER BY `tab{0}`.{1}".format(doctype, frappe.scrub(filters.get('group_by')))
+
 def get_items(filters, additional_query_columns):
 	conditions = get_conditions(filters)
 	match_conditions = frappe.build_match_conditions("Sales Invoice")
@@ -156,9 +394,8 @@
 			`tabSales Invoice`.update_stock, `tabSales Invoice Item`.uom, `tabSales Invoice Item`.qty {0}
 		from `tabSales Invoice`, `tabSales Invoice Item`
 		where `tabSales Invoice`.name = `tabSales Invoice Item`.parent
-			and `tabSales Invoice`.docstatus = 1 %s %s
-		order by `tabSales Invoice`.posting_date desc, `tabSales Invoice Item`.item_code desc
-		""".format(additional_query_columns or '') % (conditions, match_conditions), filters, as_dict=1)
+			and `tabSales Invoice`.docstatus = 1 {1} {2}
+		""".format(additional_query_columns or '', conditions, match_conditions), filters, as_dict=1) #nosec
 
 def get_delivery_notes_against_sales_order(item_list):
 	so_dn_map = frappe._dict()
@@ -177,6 +414,15 @@
 
 	return so_dn_map
 
+def get_grand_total(filters, doctype):
+
+	return frappe.db.sql(""" SELECT
+		SUM(`tab{0}`.base_grand_total)
+		FROM `tab{0}`
+		WHERE `tab{0}`.docstatus = 1
+		and posting_date between %s and %s
+	""".format(doctype), (filters.get('from_date'), filters.get('to_date')))[0][0] #nosec
+
 def get_deducted_taxes():
 	return frappe.db.sql_list("select name from `tabPurchase Taxes and Charges` where add_deduct_tax = 'Deduct'")
 
@@ -264,9 +510,117 @@
 
 	tax_columns.sort()
 	for desc in tax_columns:
-		columns.append(desc + " Rate:Data:80")
-		columns.append(desc + " Amount:Currency/currency:100")
+		columns.append({
+			'label': _(desc + ' Rate'),
+			'fieldname': frappe.scrub(desc + ' Rate'),
+			'fieldtype': 'Float',
+			'width': 100
+		})
 
-	columns += ["Total Tax:Currency/currency:80", "Total:Currency/currency:100"]
+		columns.append({
+			'label': _(desc + ' Amount'),
+			'fieldname': frappe.scrub(desc + ' Amount'),
+			'fieldtype': 'Currency',
+			'options': 'currency',
+			'width': 100
+		})
+
+	columns += [
+		{
+			'label': _('Total Tax'),
+			'fieldname': 'total_tax',
+			'fieldtype': 'Currency',
+			'options': 'currency',
+			'width': 100
+		},
+		{
+			'label': _('Total'),
+			'fieldname': 'total',
+			'fieldtype': 'Currency',
+			'options': 'currency',
+			'width': 100
+		}
+	]
 
 	return itemised_tax, tax_columns
+
+def add_total_row(data, filters, prev_group_by_value, item, total_row_map,
+	group_by_field, subtotal_display_field, grand_total, tax_columns):
+	if prev_group_by_value != item.get(group_by_field, ''):
+		if prev_group_by_value:
+			total_row = total_row_map.get(prev_group_by_value)
+			data.append(total_row)
+			data.append({})
+			add_sub_total_row(total_row, total_row_map, 'total_row', tax_columns)
+
+		prev_group_by_value = item.get(group_by_field, '')
+
+		total_row_map.setdefault(item.get(group_by_field, ''), {
+			subtotal_display_field: get_display_value(filters, group_by_field, item),
+			'stock_qty': 0.0,
+			'amount': 0.0,
+			'bold': 1,
+			'total_tax': 0.0,
+			'total': 0.0,
+			'percent_gt': 0.0
+		})
+
+		total_row_map.setdefault('total_row', {
+			subtotal_display_field: "Total",
+			'stock_qty': 0.0,
+			'amount': 0.0,
+			'bold': 1,
+			'total_tax': 0.0,
+			'total': 0.0,
+			'percent_gt': 0.0
+		})
+
+	return data, prev_group_by_value
+
+def get_display_value(filters, group_by_field, item):
+	if filters.get('group_by') == 'Item':
+		if item.get('item_code') != item.get('item_name'):
+			value =  cstr(item.get('item_code')) + "<br><br>" + \
+			"<span style='font-weight: normal'>" + cstr(item.get('item_name')) + "</span>"
+		else:
+			value =  item.get('item_code', '')
+	elif filters.get('group_by') in ('Customer', 'Supplier'):
+		party = frappe.scrub(filters.get('group_by'))
+		if item.get(party) != item.get(party+'_name'):
+			value = item.get(party) + "<br><br>" + \
+			"<span style='font-weight: normal'>" + item.get(party+'_name') + "</span>"
+		else:
+			value =  item.get(party)
+	else:
+		value = item.get(group_by_field)
+
+	return value
+
+def get_group_by_and_display_fields(filters):
+	if filters.get('group_by') == 'Item':
+		group_by_field = 'item_code'
+		subtotal_display_field = 'invoice'
+	elif filters.get('group_by') == 'Invoice':
+		group_by_field = 'parent'
+		subtotal_display_field = 'item_code'
+	else:
+		group_by_field = frappe.scrub(filters.get('group_by'))
+		subtotal_display_field = 'item_code'
+
+	return group_by_field, subtotal_display_field
+
+def add_sub_total_row(item, total_row_map, group_by_value, tax_columns):
+	total_row = total_row_map.get(group_by_value)
+	total_row['stock_qty'] += item['stock_qty']
+	total_row['amount'] += item['amount']
+	total_row['total_tax'] += item['total_tax']
+	total_row['total'] += item['total']
+	total_row['percent_gt'] += item['percent_gt']
+
+	for tax in tax_columns:
+		total_row.setdefault(frappe.scrub(tax + ' Amount'), 0.0)
+		total_row[frappe.scrub(tax + ' Amount')] += flt(item[frappe.scrub(tax + ' Amount')])
+
+
+
+
diff --git a/erpnext/accounts/report/sales_register/sales_register.py b/erpnext/accounts/report/sales_register/sales_register.py
index afdd31d..2be90bc 100644
--- a/erpnext/accounts/report/sales_register/sales_register.py
+++ b/erpnext/accounts/report/sales_register/sales_register.py
@@ -139,7 +139,7 @@
 
 	columns +=[
 		{
-			'label': _("Custmer Group"),
+			'label': _("Customer Group"),
 			'fieldname': 'customer_group',
 			'fieldtype': 'Link',
 			'options': 'Customer Group',
@@ -175,7 +175,7 @@
 			'label': _("Project"),
 			'fieldname': 'project',
 			'fieldtype': 'Link',
-			'options': 'project',
+			'options': 'Project',
 			'width': 80
 		},
 		{