text: // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
  // MIT License. See license.txt
  
  import DataTable from 'frappe-datatable';
  frappe.provide("frappe.report_dump");
  
  $.extend(frappe.report_dump, {
  	data: {},
  	last_modified: {},
  	with_data: function(doctypes, callback) {
  		var pre_loaded = Object.keys(frappe.report_dump.last_modified);
  		return frappe.call({
  			method: "frappe.desk.report_dump.get_data",
  			type: "GET",
  			args: {
  				doctypes: doctypes,
  				last_modified: frappe.report_dump.last_modified
  			},
  			freeze: true,
  			callback: function(r) {
  				// creating map of data from a list
  				$.each(r.message, function(doctype, doctype_data) {
  					frappe.report_dump.set_data(doctype, doctype_data);
  				});
  
  				// reverse map names
  				$.each(r.message, function(doctype, doctype_data) {
  					// only if not pre-loaded
  					if(!in_list(pre_loaded, doctype)) {
  						if(doctype_data.links) {
  							$.each(frappe.report_dump.data[doctype], function(row_idx, row) {
  								$.each(doctype_data.links, function(link_key, link) {
  									if(frappe.report_dump.data[link[0]][row[link_key]]) {
  										row[link_key] = frappe.report_dump.data[link[0]][row[link_key]][link[1]];
  									} else {
  										row[link_key] = null;
  									}
  								});
  							});
  						}
  					}
  				});
  
  				callback();
  			}
  		});
  	},
  	set_data: function(doctype, doctype_data) {
  		var data = [];
  		var replace_dict = {};
  		var make_row = function(d) {
  			var row = {};
  			$.each(doctype_data.columns, function(idx, col) {
  				row[col] = d[idx];
  			});
  			row.id = row.name;
  			row.doctype = doctype;
  			return row;
  		};
  		if(frappe.report_dump.last_modified[doctype]) {
  			// partial loading, make a name dict
  			$.each(doctype_data.data, function(i, d) {
  				var row = make_row(d);
  				replace_dict[row.name] = row;
  			});
  
  			// replace old data
  			$.each(frappe.report_dump.data[doctype], function(i, d) {
  				if(replace_dict[d.name]) {
  					data.push(replace_dict[d.name]);
  					delete replace_dict[d.name];
  				} else if(doctype_data.modified_names.indexOf(d.name)!==-1) {
  					// if modified but not in replace_dict, then assume it as cancelled
  					// don't push in data
  				} else {
  					data.push(d);
  				}
  			});
  
  			// add new records
  			$.each(replace_dict, function(name, d) {
  				data.push(d);
  			});
  		} else {
  
  			// first loading
  			$.each(doctype_data.data, function(i, d) {
  				data.push(make_row(d));
  			});
  		}
  		frappe.report_dump.last_modified[doctype] = doctype_data.last_modified;
  		frappe.report_dump.data[doctype] = data;
  	}
  });
  
  frappe.provide("frappe.views");
  frappe.views.GridReport = Class.extend({
  	init: function(opts) {
  		this.filter_inputs = {};
  		this.preset_checks = [];
  		this.tree_grid = {show: false};
  		$.extend(this, opts);
  
  		this.wrapper = $('<div class="grid-report"></div>').appendTo(this.page.main);
  		this.page.main.find(".page").css({"padding-top": "0px"});
  
  		if(this.filters) {
  			this.make_filters();
  		}
  		this.make_waiting();
  
  		this.get_data_and_refresh();
  	},
  	bind_show: function() {
  		// bind show event to reset cur_report_grid
  		// and refresh filters from url
  		// this must be called after init
  		// because "frappe.container.page" will only be set
  		// once "load" event is over.
  
  		var me = this;
  		$(this.page).bind('show', function() {
  			// reapply filters on show
  			frappe.cur_grid_report = me;
  			me.get_data_and_refresh();
  		});
  
  	},
  	get_data_and_refresh: function() {
  		var me = this;
  		this.get_data(function() {
  			me.apply_filters_from_route();
  			me.refresh();
  		});
  	},
  	get_data: function(callback) {
  		var me = this;
  
  		frappe.report_dump.with_data(this.doctypes, function() {
  			if(!me.setup_filters_done) {
  				me.setup_filters();
  				me.setup_filters_done = true;
  			}
  			callback();
  		});
  	},
  	setup_filters: function() {
  		var me = this;
  
  		$.each(me.filter_inputs, function(i, v) {
  			var opts = v.get(0).opts;
  			if(opts.fieldtype == "Select" && in_list(me.doctypes, opts.link)) {
  				$(v).add_options(frappe.report_dump.data[opts.link].map(d => d.name));
  			} else if(opts.fieldtype == "Link" && in_list(me.doctypes, opts.link)) {
  				opts.list = frappe.report_dump.data[opts.link].map(d => d.name);
  				me.set_autocomplete(v, opts.list);
  			}
  		});
  
  		// refresh
  		this.page.set_primary_action(__("Refresh"), function() {
  			me.get_data(function() {
  				me.refresh();
  			});
  		});
  
  		// reset filters button
  		if (this.filter_inputs) {
  			this.page.add_menu_item(__("Reset Filters"), function() {
  				me.init_filter_values();
  				me.refresh();
  			}, true);
  		}
  
  		this.page.add_menu_item(__("Print"), function() {
  			frappe.ui.get_print_settings(false, function(print_settings) {
  				frappe.render_grid({grid: me.grid, title: me.page.title, print_settings: print_settings, report: me});
  			});
  
  		}, true);
  
  		// range
  		this.filter_inputs.range && this.filter_inputs.range.on("change", function() {
  			me.refresh();
  		});
  	},
  	set_filter: function(key, value) {
  		var filters = this.filter_inputs[key];
  		if(filters) {
  			var opts = filters.get(0).opts;
  			if(opts.fieldtype === "Check") {
  				filters.prop("checked", cint(value) ? true : false);
  			} if(opts.fieldtype=="Date") {
  				filters.val(frappe.datetime.str_to_user(value));
  			} else {
  				filters.val(value);
  			}
  		} else {
  			frappe.msgprint(__("Invalid Filter: {0}", [key]));
  		}
  	},
  	set_autocomplete: function($filter, list) {
  		var me = this;
  		new Awesomplete($filter.get(0), {
  			list: list
  		});
  		$filter.on("awesomplete-select", function(e) {
  			var value = e.originalEvent.text.value;
  			$filter.val(value);
  			me.refresh();
  		});
  	},
  	init_filter_values: function() {
  		$.each(this.filter_inputs, function(key, filter) {
  			var opts = filter.get(0).opts;
  			if(frappe.sys_defaults[key]) {
  				filter.val(frappe.sys_defaults[key]);
  			} else if(opts.fieldtype=='Select') {
  				filter.get(0).selectedIndex = 0;
  			} else if(opts.fieldtype=='Data') {
  				filter.val("");
  			} else if(opts.fieldtype=="Link") {
  				filter.val("");
  			}
  		});
  
  		this.set_default_values();
  	},
  
  	set_default_values: function() {
  		var values = {
  			from_date: frappe.datetime.str_to_user(frappe.sys_defaults.year_start_date),
  			to_date: frappe.datetime.str_to_user(frappe.sys_defaults.year_end_date)
  		};
  
  		var me = this;
  		$.each(values, function(i, v) {
  			if(me.filter_inputs[i] && !me.filter_inputs[i].val())
  				me.filter_inputs[i].val(v);
  		});
  	},
  
  	make_filters: function() {
  		var me = this;
  		$.each(this.filters, function(i, v) {
  			v.fieldname = v.fieldname || v.label.replace(/ /g, '_').toLowerCase();
  			var input = null;
  			if(v.fieldtype=='Select') {
  				input = me.page.add_select(v.label, v.options || [v.default_value]);
  			} else if(v.fieldtype=="Link") {
  				input = me.page.add_data(v.label);
  				new Awesomplete(input.get(0), {
  					list: v.list || []
  				});
  			} else if(v.fieldtype==='Button' && v.label===__("Refresh")) {
  				input = me.page.set_primary_action(v.label, null, v.icon);
  			} else if(v.fieldtype==='Button') {
  				input = me.page.add_menu_item(v.label, null, true);
  			} else if(v.fieldtype==='Date') {
  				input = me.page.add_date(v.label);
  			} else if(v.fieldtype==='Label') {
  				input = me.page.add_label(v.label);
  			} else if(v.fieldtype==='Data') {
  				input = me.page.add_data(v.label);
  			} else if(v.fieldtype==='Check') {
  				input = me.page.add_check(v.label);
  			}
  
  			if(input) {
  				input && (input.get(0).opts = v);
  				if(v.cssClass) {
  					input.addClass(v.cssClass);
  				}
  				input.keypress(function(e) {
  					if(e.which==13) {
  						me.refresh();
  					}
  				});
  			}
  			me.filter_inputs[v.fieldname] = input;
  		});
  	},
  	make_waiting: function() {
  		this.waiting = frappe.messages.waiting(this.wrapper, __("Loading Report")+"...");
  	},
  	load_filter_values: function() {
  		var me = this;
  		$.each(this.filter_inputs, function(i, f) {
  			var opts = f.get(0).opts;
  			if(opts.fieldtype=='Check') {
  				me[opts.fieldname] = f.prop('checked') ? 1 : 0;
  			} else if(opts.fieldtype!='Button') {
  				me[opts.fieldname] = f.val();
  				if(opts.fieldtype=="Date") {
  					me[opts.fieldname] = frappe.datetime.user_to_str(me[opts.fieldname]);
  				} else if (opts.fieldtype == "Select") {
  					me[opts.fieldname+'_default'] = opts.default_value;
  				}
  			}
  		});
  
  		if(this.filter_inputs.from_date && this.filter_inputs.to_date && (this.to_date < this.from_date)) {
  			frappe.msgprint(__("From Date must be before To Date"));
  			return;
  		}
  
  	},
  
  	make_name_map: function(data, key) {
  		var map = {};
  		key = key || "name";
  		$.each(data, function(i, v) {
  			map[v[key]] = v;
  		});
  		return map;
  	},
  
  	reset_item_values: function(item) {
  		var me = this;
  		$.each(this.columns, function(i, col) {
  			if (col.formatter==me.currency_formatter) {
  				item[col.id] = 0.0;
  			}
  		});
  	},
  
  	round_item_values: function(item) {
  		var me = this;
  		$.each(this.columns, function(i, col) {
  			if (col.formatter==me.currency_formatter) {
  				item[col.id] = flt(item[col.id], frappe.defaults.get_default("float_precision") || 3);
  			}
  		});
  	},
  
  	round_off_data: function() {
  		var me = this;
  		$.each(this.data, function(i, d) {
  			me.round_item_values(d);
  		});
  	},
  
  	refresh: function() {
  		this.waiting.toggle(false);
  		if(!this.grid_wrapper)
  			this.make();
  		// this.show_zero = $('.show-zero input:checked').length;
  		this.load_filter_values();
  		this.setup_columns();
  		this.setup_dataview_columns();
  		this.apply_link_formatters();
  		this.prepare_data();
  		this.round_off_data();
  		this.prepare_data_view();
  		// chart might need prepared data
  		frappe.show_alert("Updated", 2);
  		this.render();
  		this.setup_chart && this.setup_chart();
  	},
  	setup_dataview_columns: function() {
  		this.columns = this.columns.filter(col => !col.hidden);
  		this.datatable_columns = this.columns.map(column => {
  			return Object.assign(column, {
  				format: (value, row, column, data) => {
  					return column.formatter ?
  						column.formatter(row, {}, value, column, data) :
  						value || '';
  				}
  			});
  		});
  	},
  	make: function() {
  		// chart wrapper
  		this.chart_area = $('<div class="chart" style="padding-bottom: 1px"></div>').appendTo(this.wrapper);
  
  		this.page.add_menu_item(__("Export"), () =>  this.export(), true);
  
  		// grid wrapper
  		this.grid_wrapper = $("<div style='height: 500px;'>")
  			.appendTo(this.wrapper);
  		this.id = frappe.dom.set_unique_id(this.grid_wrapper.get(0));
  
  		this.bind_show();
  
  		frappe.cur_grid_report = this;
  		$(this.wrapper).trigger('make');
  
  	},
  	apply_filters_from_route: function() {
  		var me = this;
  		if(frappe.route_options) {
  			$.each(frappe.route_options, function(key, value) {
  				me.set_filter(key, value);
  			});
  			frappe.route_options = null;
  		} else {
  			this.init_filter_values();
  		}
  		this.set_default_values();
  
  		$(this.wrapper).trigger('apply_filters_from_route');
  	},
  	options: {
  		editable: false,
  		enableColumnReorder: false
  	},
  	render: function() {
  		this.datatable = new DataTable('#' + this.id, {
  			columns: this.datatable_columns,
  			data: this.data,
  			layout: 'fixed',
  			inlineFilters: true,
  			treeView: true,
  			checkboxColumn: true,
  			checkedRowStatus: false,
  			events: {
  				onCheckRow: (row) => {
  					const rowIndex = row.meta.rowIndex;
  					const checked = this.datatable.rowmanager.checkMap[rowIndex];
  					const data = this.datatable.datamanager.getData(rowIndex);
  					data.checked = Boolean(checked);
  
  					this.setup_chart && this.setup_chart();
  				}
  			}
  		});
  
  		this.data.forEach((d, i) => {
  			if (d.checked) {
  				this.datatable.rowmanager.checkRow(i, true);
  			}
  		});
  	},
  	prepare_data_view: function() {
  	},
  	export: function() {
  		frappe.tools.downloadify(frappe.slickgrid_tools.get_view_data(this.columns, this.dataView),
  			["Report Manager", "System Manager"], this.title);
  		return false;
  	},
  	apply_filters: function(item) {
  		// generic filter: apply filter functiions
  		// from all filter_inputs
  		var filters = this.filter_inputs;
  		if(item._show) return true;
  
  		for (var i in filters) {
  			if(!this.apply_filter(item, i)) {
  				return false;
  			}
  		}
  
  		return true;
  	},
  	apply_filter: function(item, fieldname) {
  		var filter = this.filter_inputs[fieldname].get(0);
  		if(filter.opts.filter) {
  			if(!filter.opts.filter(this[filter.opts.fieldname], item, filter.opts, this)) {
  				return false;
  			}
  		}
  		return true;
  	},
  	apply_zero_filter: function(val, item, opts, me) {
  		// show only non-zero values
  		if(!me.show_zero) {
  			for(var i=0, j=me.columns.length; i<j; i++) {
  				var col = me.columns[i];
  				if(col.formatter==me.currency_formatter && !col.hidden) {
  					if(flt(item[col.field]) > 0.001 ||  flt(item[col.field]) < -0.001) {
  						return true;
  					}
  				}
  			}
  			return false;
  		}
  		return true;
  	},
  	show_zero_check: function() {
  		var me = this;
  		this.wrapper.bind('make', function() {
  			me.wrapper.find('.show-zero').toggle(true).find('input').click(function(){
  				me.refresh();
  			});
  		});
  	},
  	is_default: function(fieldname) {
  		return this[fieldname]==this[fieldname + "_default"];
  	},
  	date_formatter: function(row, cell, value) {
  		return frappe.datetime.str_to_user(value);
  	},
  	currency_formatter: function(row, cell, value, columnDef, dataContext) {
  		if (isNaN(value)) value = '';
  		return repl('<div style="text-align: right; %(_style)s">%(value)s</div>', {
  			_style: dataContext._style || "",
  			value: ((value==null || value==="") ? "" : format_number(value))
  		});
  	},
  	text_formatter: function(row, cell, value, columnDef, dataContext) {
  		return repl('<span style="%(_style)s" title="%(esc_value)s">%(value)s</span>', {
  			_style: dataContext._style || "",
  			esc_value: cstr(value).replace(/"/g, '\\"'),
  			value: cstr(value)
  		});
  	},
  	check_formatter: function(row, cell, value, columnDef, dataContext) {
  		return repl('<input type="checkbox" data-id="%(id)s" \
  			class="chart-check" %(checked)s>', {
  				"id": dataContext.id,
  				"checked": dataContext.checked ? 'checked="checked"' : ""
  			});
  	},
  	apply_link_formatters: function() {
  		$.each(this.columns, function(i, col) {
  			if(col.link_formatter) {
  				col.formatter = function(row, cell, value, columnDef, dataContext, for_print) {
  					// added link and open button to links
  					// link_formatter must have
  					// filter_input, open_btn (true / false), doctype (will be eval'd)
  					if(!value) return "";
  
  					if(for_print) {
  						return value;
  					}
  
  					var me = frappe.cur_grid_report;
  
  					if(dataContext._show) {
  						return repl('<span style="%(_style)s">%(value)s</span>', {
  							_style: dataContext._style || "",
  							value: value
  						});
  					}
  
  					// make link to add a filter
  					var html;
  					var link_formatter = me.dataview_columns[cell].link_formatter;
  					if (link_formatter.filter_input) {
  						html = repl('<a href="#" \
  							onclick="frappe.cur_grid_report.set_filter(\'%(col_name)s\', \'%(value)s\'); \
  								frappe.cur_grid_report.refresh(); return false;">\
  							%(value)s</a>', {
  								value: value,
  								col_name: link_formatter.filter_input,
  								page_name: frappe.container.page.page_name
  							});
  					} else {
  						html = value;
  					}
  
  					// make icon to open form
  					if(link_formatter.open_btn) {
  						var doctype = link_formatter.doctype
  							? eval(link_formatter.doctype)
  							: dataContext.doctype;
  						html += me.get_link_open_icon(doctype, value);
  					}
  					return html;
  				};
  			}
  		});
  	},
  	get_link_open_icon: function(doctype, name) {
  		return repl(' <a href="#Form/%(doctype)s/%(name)s">\
  			<i class="fa fa-share" style="cursor: pointer;"></i></a>', {
  				doctype: doctype,
  				name: encodeURIComponent(name)
  			});
  	},
  	make_date_range_columns: function() {
  		this.columns = [];
  
  		var me = this;
  		var range = this.filter_inputs.range.val();
  		this.from_date = frappe.datetime.user_to_str(this.filter_inputs.from_date.val());
  		this.to_date = frappe.datetime.user_to_str(this.filter_inputs.to_date.val());
  		var date_diff = frappe.datetime.get_diff(this.to_date, this.from_date);
  
  		me.column_map = {};
  		me.last_date = null;
  
  		var add_column = function(date) {
  			me.columns.push({
  				id: date,
  				name: frappe.datetime.str_to_user(date),
  				field: date,
  				formatter: me.currency_formatter,
  				width: 100
  			});
  		};
  
  		var build_columns = function(condition) {
  			// add column for each date range
  			for(var i=0; i <= date_diff; i++) {
  				var date = frappe.datetime.add_days(me.from_date, i);
  				if(!condition) condition = () => true;
  
  				if(condition(date)) add_column(date);
  				me.last_date = date;
  
  				if(me.columns.length) {
  					me.column_map[date] = me.columns[me.columns.length-1];
  				}
  			}
  		};
  
  		// make columns for all date ranges
  		if(range=='Daily') {
  			build_columns();
  		} else if(range=='Weekly') {
  			build_columns(function(date) {
  				if(!me.last_date) return true;
  				return !(frappe.datetime.get_diff(date, me.from_date) % 7);
  			});
  		} else if(range=='Monthly') {
  			build_columns(function(date) {
  				if(!me.last_date) return true;
  				return frappe.datetime.str_to_obj(me.last_date).getMonth() != frappe.datetime.str_to_obj(date).getMonth();
  			});
  		} else if(range=='Quarterly') {
  			build_columns(function(date) {
  				if(!me.last_date) return true;
  				return frappe.datetime.str_to_obj(date).getDate()==1 && in_list([0,3,6,9], frappe.datetime.str_to_obj(date).getMonth());
  			});
  		} else if(range=='Yearly') {
  			build_columns(function(date) {
  				if(!me.last_date) return true;
  				return $.map(frappe.report_dump.data['Fiscal Year'], function(v) {
  					return date==v.year_start_date ? true : null;
  				}).length;
  			});
  
  		}
  
  		// set label as last date of period
  		$.each(this.columns, function(i, col) {
  			col.name = me.columns[i+1]
  				? frappe.datetime.str_to_user(frappe.datetime.add_days(me.columns[i+1].id, -1))
  				: frappe.datetime.str_to_user(me.to_date);
  		});
  	},
  	trigger_refresh_on_change: function(filters) {
  		var me = this;
  		$.each(filters, function(i, f) {
  			me.filter_inputs[f] && me.filter_inputs[f].on("change", function() {
  				me.refresh();
  			});
  		});
  	}
  });
  
  frappe.views.GridReportWithPlot = frappe.views.GridReport.extend({
  	setup_chart: function() {
  		if (in_list(["Daily", "Weekly"], this.filter_inputs.range.val())) {
  			this.chart_area.toggle(false);
  			return;
  		} else {
  			this.chart_area.toggle(true);
  		}
  		var chart_data = this.get_chart_data ? this.get_chart_data() : null;
  
  		const parent = this.wrapper.find('.chart')[0];
  		this.chart = new Chart(parent, {
  			height: 200,
  			data: chart_data,
  			type: 'line'
  		});
  	},
  
  	get_chart_data: function() {
  		var me = this;
  		var plottable_cols = [];
  		$.each(me.columns, function(idx, col) {
  			if(col.formatter==me.currency_formatter && !col.hidden && col.plot!==false) {
  				plottable_cols.push(col.field);
  			}
  		});
  
  		var data = {
  			labels: plottable_cols,
  			datasets: []
  		};
  
  		$.each(this.data, function(i, item) {
  			if (item.checked) {
  				let dataset = {};
  				dataset.name = item.name;
  				dataset.values = [];
  				$.each(plottable_cols, function(idx, col) {
  					dataset.values.push(item[col]);
  				});
  				data["datasets"].push(dataset);
  			}
  		});
  		return data;
  	}
  });
  
  
  frappe.views.TreeGridReport = frappe.views.GridReportWithPlot.extend({
  	make_transaction_list: function(parent_doctype, doctype) {
  		var me = this;
  		var tmap = {};
  		$.each(frappe.report_dump.data[doctype], function(i, v) {
  			if(!tmap[v.parent]) tmap[v.parent] = [];
  			tmap[v.parent].push(v);
  		});
  		if (!this.tl) this.tl = {};
  		if (!this.tl[parent_doctype]) this.tl[parent_doctype] = [];
  
  		$.each(frappe.report_dump.data[parent_doctype], function(i, parent) {
  			if(tmap[parent.name]) {
  				$.each(tmap[parent.name], function(i, d) {
  					me.tl[parent_doctype].push($.extend(copy_dict(parent), d));
  				});
  			}
  		});
  	},
  	add_tree_grid_events: function() {
  		var me = this;
  		this.grid.onClick.subscribe(function(e, args) {
  			if ($(e.target).hasClass("toggle")) {
  				var item = me.dataView.getItem(args.row);
  				if (item) {
  					if (!item._collapsed) {
  						item._collapsed = true;
  					} else {
  						item._collapsed = false;
  					}
  
  					me.dataView.updateItem(item.id, item);
  				}
  				e.stopImmediatePropagation();
  			}
  		});
  	},
  	tree_formatter: function(row, cell, value, columnDef, dataContext) {
  		var me = frappe.cur_grid_report;
  		var data = me.data;
  		var spacer = "<span style='display:inline-block;height:1px;width:" +
  			(15 * dataContext["indent"]) + "px'></span>";
  		var idx = me.dataView.getIdxById(dataContext.id);
  		var link = me.tree_grid.formatter(dataContext);
  
  		if(dataContext.doctype) {
  			link += me.get_link_open_icon(dataContext.doctype, dataContext.name);
  		}
  
  		if (data[idx + 1] && data[idx + 1].indent > data[idx].indent) {
  			if (dataContext._collapsed) {
  				return spacer + " <span class='toggle expand'></span>&nbsp;" + link;
  			} else {
  				return spacer + " <span class='toggle collapse'></span>&nbsp;" + link;
  			}
  		} else {
  			return spacer + " <span class='toggle'></span>&nbsp;" + link;
  		}
  	},
  	tree_dataview_filter: function(item) {
  		var me = frappe.cur_grid_report;
  		if(!me.apply_filters(item)) return false;
  
  		var parent = item[me.tree_grid.parent_field];
  		while (parent) {
  			if (me.item_by_name[parent]._collapsed) {
  				return false;
  			}
  			parent = me.parent_map[parent];
  		}
  		return true;
  	},
  	prepare_tree: function(item_dt, group_dt) {
  		var group_data = frappe.report_dump.data[group_dt];
  		var item_data = frappe.report_dump.data[item_dt];
  
  		// prepare map with child in respective group
  		var me = this;
  		var item_group_map = {};
  		var group_ids = group_data.map(v => v.id);
  
  		$.each(item_data, function(i, item) {
  			var parent = item[me.tree_grid.parent_field];
  			if(!item_group_map[parent]) item_group_map[parent] = [];
  			if(group_ids.indexOf(item.name)==-1) {
  				item_group_map[parent].push(item);
  			} else {
  				frappe.msgprint(__("Ignoring Item {0}, because a group exists with the same name!", [item.name.bold()]));
  			}
  		});
  
  		// arrange items besides their parent item groups
  		var items = [];
  		$.each(group_data, function(i, group){
  			group.is_group = true;
  			items.push(group);
  			items = items.concat(item_group_map[group.name] || []);
  		});
  		return items;
  	},
  	set_indent: function() {
  		var me = this;
  		$.each(this.data, function(i, d) {
  			var indent = 0;
  			var parent = me.parent_map[d.name];
  			if(parent) {
  				while(parent) {
  					indent++;
  					parent = me.parent_map[parent];
  				}
  			}
  			d.indent = indent;
  		});
  	},
  
  	export: function() {
  		var msgbox =	frappe.msgprint($.format('<p>{0}</p>\
  			<p><input type="checkbox" name="with_groups" checked="checked"> {1}</p>\
  			<p><input type="checkbox" name="with_ledgers" checked="checked"> {2}</p>\
  			<p><button class="btn btn-primary"> {3}</button>', [
  				__('Select To Download:'),
  				__('With Groups'),
  				__('With Ledgers'),
  				__('Download')
  			]));
  
  		var me = this;
  
  		$(msgbox.body).find("button").click(function() {
  			var with_groups = $(msgbox.body).find("[name='with_groups']").prop("checked");
  			var with_ledgers = $(msgbox.body).find("[name='with_ledgers']").prop("checked");
  
  			var data = frappe.slickgrid_tools.get_view_data(me.columns, me.data,
  				function(row, item) {
  					if(with_groups) {
  						// add row
  						for(var i=0; i<item.indent; i++) row[0] = "   " + row[0];
  					}
  					if(with_groups && (item.is_group == 1 || item.is_group)) {
  						return true;
  					}
  					if(with_ledgers && (item.is_group != 1 && !item.is_group)) {
  						return true;
  					}
  
  					return false;
  				});
  
  			frappe.tools.downloadify(data, ["Report Manager", "System Manager"], me.title);
  			return false;
  		});
  
  		return false;
  	},
  
  });
  