Merge pull request #16411 from adityahase/dashboard

feat: Accounting Dashboard
diff --git a/erpnext/accounts/dashboard_chart_source/__init__.py b/erpnext/accounts/dashboard_chart_source/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/accounts/dashboard_chart_source/__init__.py
diff --git a/erpnext/accounts/dashboard_chart_source/account_balance_timeline/__init__.py b/erpnext/accounts/dashboard_chart_source/account_balance_timeline/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/accounts/dashboard_chart_source/account_balance_timeline/__init__.py
diff --git a/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.js b/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.js
new file mode 100644
index 0000000..eebd2db
--- /dev/null
+++ b/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.js
@@ -0,0 +1,45 @@
+frappe.dashboard_chart_sources["Account Balance Timeline"] = {
+	method_path: "erpnext.accounts.dashboard_chart_source.account_balance_timeline.account_balance_timeline.get",
+	filters: [
+		{
+			fieldname: "company",
+			label: __("Company"),
+			fieldtype: "Link",
+			options: "Company",
+			default: frappe.defaults.get_user_default("Company"),
+			reqd: 1
+		},
+		{
+			fieldname: "account",
+			label: __("Account"),
+			fieldtype: "Link",
+			options: "Account",
+			reqd: 1
+		},
+		{
+			fieldname: "timespan",
+			label: __("Period"),
+			fieldtype: "Select",
+			options: [
+				{value: "Last Year", label: __("Last Year")},
+				{value: "Last Quarter", label: __("Last Quarter")},
+				{value: "Last Month", label: __("Last Month")},
+				{value: "Last Week", label: __("Last Week")}
+			],
+			reqd: 1
+		},
+		{
+			fieldname: "timegrain",
+			label: __("Periodicity"),
+			fieldtype: "Select",
+			options: [
+				{value: "Quarterly", label: __("Quarterly")},
+				{value: "Monthly", label: __("Monthly")},
+				{value: "Weekly", label: __("Weekly")},
+				{value: "Daily", label: __("Daily")}
+			],
+			reqd: 1
+		},
+	],
+	is_time_series: true
+};
\ No newline at end of file
diff --git a/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.json b/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.json
new file mode 100644
index 0000000..b7ea601
--- /dev/null
+++ b/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.json
@@ -0,0 +1,13 @@
+{
+ "config": "{\n    \"method_path\": \"erpnext.accounts.dashboard_chart_source.account_balance_timeline.account_balance_timeline.get\",\n\t\"filters\": [\n\t\t{\n\t\t\t\"fieldname\": \"company\",\n\t\t\t\"label\": \"Company\",\n\t\t\t\"fieldtype\": \"Link\",\n\t\t\t\"options\": \"Company\",\n\t\t\t\"reqd\": 1\n\t\t},\n\t\t{\n\t\t\t\"fieldname\": \"account\",\n\t\t\t\"label\": \"Account\",\n\t\t\t\"fieldtype\": \"Link\",\n\t\t\t\"options\": \"Account\",\n\t\t\t\"reqd\": 1\n\t\t},\n\t\t{\n\t\t\t\"fieldname\": \"timespan\",\n\t\t\t\"label\": \"Period\",\n\t\t\t\"fieldtype\": \"Select\",\n\t\t\t\"options\": [\n\t\t\t\t{\"value\": \"Last Year\", \"label\": \"Last Year\"},\n\t\t\t\t{\"value\": \"Last Quarter\", \"label\": \"Last Quarter\"},\n\t\t\t\t{\"value\": \"Last Month\", \"label\": \"Last Month\"},\n\t\t\t\t{\"value\": \"Last Week\", \"label\": \"Last Week\"}\n\t\t\t],\n\t\t\t\"reqd\": 1\n\t\t},\n\t\t{\n\t\t\t\"fieldname\": \"timegrain\",\n\t\t\t\"label\": \"Periodicity\",\n\t\t\t\"fieldtype\": \"Select\",\n\t\t\t\"options\": [\n\t\t\t\t{\"value\": \"Quarterly\", \"label\": \"Quarterly\"},\n\t\t\t\t{\"value\": \"Monthly\", \"label\": \"Monthly\"},\n\t\t\t\t{\"value\": \"Weekly\", \"label\": \"Weekly\"},\n\t\t\t\t{\"value\": \"Daily\", \"label\": \"Daily\"}\n\t\t\t],\n\t\t\t\"reqd\": 1\n\t\t}\n\t],\n\t\"is_time_series\": true\n}\n",
+ "creation": "2019-02-06 07:57:10.377718",
+ "docstatus": 0,
+ "doctype": "Dashboard Chart Source",
+ "idx": 0,
+ "modified": "2019-03-15 16:14:26.505986",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Account Balance Timeline",
+ "owner": "Administrator",
+ "source_name": "Account Balance Timeline"
+}
\ No newline at end of file
diff --git a/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.py b/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.py
new file mode 100644
index 0000000..f98a236
--- /dev/null
+++ b/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.py
@@ -0,0 +1,100 @@
+# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+from itertools import groupby
+from operator import itemgetter
+import frappe
+from frappe.core.page.dashboard.dashboard import cache_source
+from frappe.utils import add_to_date, date_diff, getdate, nowdate
+from erpnext.accounts.report.general_ledger.general_ledger import execute
+
+@frappe.whitelist()
+@cache_source
+def get(filters=None):
+	timespan = filters.get("timespan")
+	timegrain = filters.get("timegrain")
+	account = filters.get("account")
+	company = filters.get("company")
+
+	from_date = get_from_date_from_timespan(timespan)
+	to_date = nowdate()
+	filters = frappe._dict({
+		"company": company,
+		"from_date": from_date,
+		"to_date": to_date,
+		"account": account,
+		"group_by": "Group by Voucher (Consolidated)"
+	})
+	report_results = execute(filters=filters)[1]
+
+	interesting_fields = ["posting_date", "balance"]
+
+	_results = []
+	for row in report_results[1:-2]:
+		_results.append([row[key] for key in interesting_fields])
+
+	_results = add_opening_balance(from_date, _results, report_results[0])
+	grouped_results = groupby(_results, key=itemgetter(0))
+	results = [list(values)[-1] for key, values in grouped_results]
+	results = add_missing_dates(results, from_date, to_date)
+	results = granulate_results(results, from_date, to_date, timegrain)
+
+	return {
+		"labels": [result[0] for result in results],
+		"datasets": [{
+			"name": account,
+			"values": [result[1] for result in results]
+		}]
+	}
+
+def get_from_date_from_timespan(timespan):
+	days = months = years = 0
+	if "Last Week" == timespan:
+		days = -7
+	if "Last Month" == timespan:
+		months = -1
+	elif "Last Quarter" == timespan:
+		months = -3
+	elif "Last Year" == timespan:
+		years = -1
+	return add_to_date(None, years=years, months=months, days=days,
+		as_string=True, as_datetime=True)
+
+
+def add_opening_balance(from_date, _results, opening):
+	if not _results or (_results[0][0] != getdate(from_date)):
+		_results.insert(0, [from_date, opening.balance])
+	return _results
+
+def add_missing_dates(incomplete_results, from_date, to_date):
+	day_count = date_diff(to_date, from_date)
+
+	results_dict = dict(incomplete_results)
+	last_balance = incomplete_results[0][1]
+	results = []
+	for date in (add_to_date(getdate(from_date), days=n) for n in range(day_count + 1)):
+		if date in results_dict:
+			last_balance = results_dict[date]
+		results.append([date, last_balance])
+	return results
+
+def get_dates_from_timegrain(from_date, to_date, timegrain):
+	days = months = years = 0
+	if "Daily" == timegrain:
+		days = 1
+	elif "Weekly" == timegrain:
+		days = 7
+	elif "Monthly" == timegrain:
+		months = 1
+	elif "Quarterly" == timegrain:
+		months = 3
+
+	dates = [from_date]
+	while dates[-1] <= to_date:
+		dates.append(add_to_date(dates[-1], years=years, months=months, days=days))
+	return dates
+
+def granulate_results(incomplete_results, from_date, to_date, timegrain):
+	dates = set(get_dates_from_timegrain(getdate(from_date), getdate(to_date), timegrain))
+	return list(filter(lambda x: x[0] in dates,incomplete_results))