[wsgi] [minor] fixed flags
diff --git a/selling/page/sales_funnel/__init__.py b/selling/page/sales_funnel/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/selling/page/sales_funnel/__init__.py
diff --git a/selling/page/sales_funnel/sales_funnel.css b/selling/page/sales_funnel/sales_funnel.css
new file mode 100644
index 0000000..89e904f
--- /dev/null
+++ b/selling/page/sales_funnel/sales_funnel.css
@@ -0,0 +1,3 @@
+.funnel-wrapper {
+	margin: 15px;
+}
\ No newline at end of file
diff --git a/selling/page/sales_funnel/sales_funnel.js b/selling/page/sales_funnel/sales_funnel.js
new file mode 100644
index 0000000..e2c3a98
--- /dev/null
+++ b/selling/page/sales_funnel/sales_funnel.js
@@ -0,0 +1,189 @@
+// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd.
+// License: GNU General Public License v3. See license.txt
+
+wn.pages['sales-funnel'].onload = function(wrapper) { 
+	wn.ui.make_app_page({
+		parent: wrapper,
+		title: 'Sales Funnel',
+		single_column: true
+	});
+	
+	wrapper.crm_funnel = new erpnext.CRMFunnel(wrapper);
+	
+	wrapper.appframe.add_module_icon("Selling", "sales-funnel", function() {
+		wn.set_route("selling-home");
+	});
+}
+
+erpnext.CRMFunnel = Class.extend({
+	init: function(wrapper) {
+		var me = this;
+		// 0 setTimeout hack - this gives time for canvas to get width and height
+		setTimeout(function() { 
+			me.setup(wrapper);
+			me.get_data();
+		}, 0);
+	},
+	
+	setup: function(wrapper) {
+		var me = this;
+		
+		this.elements = { 
+			layout: $(wrapper).find(".layout-main"),
+			from_date: wrapper.appframe.add_date("From Date"),
+			to_date: wrapper.appframe.add_date("To Date"),
+			refresh_btn: wrapper.appframe.add_button("Refresh", 
+				function() { me.get_data(); }, "icon-refresh"),
+		};
+		
+		this.elements.no_data = $('<div class="alert alert-warning">No Data</div>')
+			.toggle(false)
+			.appendTo(this.elements.layout);
+		
+		this.elements.funnel_wrapper = $('<div class="funnel-wrapper text-center"></div>')
+			.appendTo(this.elements.layout);
+		
+		this.options = {
+			from_date: wn.datetime.add_months(wn.datetime.get_today(), -1),
+			to_date: wn.datetime.get_today()
+		};
+		
+		// set defaults and bind on change
+		$.each(this.options, function(k, v) { 
+			me.elements[k].val(wn.datetime.str_to_user(v)); 
+			me.elements[k].on("change", function() {
+				me.options[k] = wn.datetime.user_to_str($(this).val());
+				me.get_data();
+			});
+		});
+		
+		// bind refresh
+		this.elements.refresh_btn.on("click", function() {
+			me.get_data(this);
+		});
+		
+		// bind resize
+		$(window).resize(function() {
+			me.render();
+		});
+	},
+	
+	get_data: function(btn) {
+		var me = this;
+		wn.call({
+			module: "selling",
+			page: "crm_funnel",
+			method: "get_funnel_data",
+			args: {
+				from_date: this.options.from_date,
+				to_date: this.options.to_date
+			},
+			btn: btn,
+			callback: function(r) {
+				if(!r.exc) {
+					me.options.data = r.message;
+					me.render();
+				}
+			}
+		});
+	},
+	
+	render: function() {
+		var me = this;
+		this.prepare();
+		
+		var context = this.elements.context,
+			x_start = 0.0,
+			x_end = this.options.width,
+			x_mid = (x_end - x_start) / 2.0,
+			y = 0,
+			y_old = 0.0;
+		
+		if(this.options.total_value === 0) {
+			this.elements.no_data.toggle(true);
+			return;
+		}
+		
+		this.options.data.forEach(function(d) {
+			context.fillStyle = d.color;
+			context.strokeStyle = d.color;
+			me.draw_triangle(x_start, x_mid, x_end, y, me.options.height);
+			
+			y_old = y;
+
+			// new y
+			y = y + d.height;
+			
+			// new x
+			var half_side = (me.options.height - y) / Math.sqrt(3);
+			x_start = x_mid - half_side;
+			x_end = x_mid + half_side;
+			
+			var y_mid = y_old + (y - y_old) / 2.0;
+			
+			me.draw_legend(x_mid, y_mid, me.options.width, me.options.height, d.value + " - " + d.title);
+		});
+	},
+	
+	prepare: function() {
+		var me = this;
+		
+		this.elements.no_data.toggle(false);
+		
+		// calculate width and height options
+		this.options.width = $(this.elements.funnel_wrapper).width() * 2.0 / 3.0;
+		this.options.height = (Math.sqrt(3) * this.options.width) / 2.0;
+		
+		// calculate total weightage
+		// as height decreases, area decreases by the square of the reduction
+		// hence, compensating by squaring the index value
+		this.options.total_weightage = this.options.data.reduce(
+			function(prev, curr, i) { return prev + Math.pow(i+1, 2) * curr.value; }, 0.0);
+		
+		// calculate height for each data
+		$.each(this.options.data, function(i, d) {
+			d.height = me.options.height * d.value * Math.pow(i+1, 2) / me.options.total_weightage;
+		});
+		
+		this.elements.canvas = $('<canvas></canvas>')
+			.appendTo(this.elements.funnel_wrapper.empty())
+			.attr("width", $(this.elements.funnel_wrapper).width())
+			.attr("height", this.options.height);
+		
+		this.elements.context = this.elements.canvas.get(0).getContext("2d");
+	},
+	
+	draw_triangle: function(x_start, x_mid, x_end, y, height) {
+		var context = this.elements.context;
+		context.beginPath();
+		context.moveTo(x_start, y);
+		context.lineTo(x_end, y);
+		context.lineTo(x_mid, height);
+		context.lineTo(x_start, y);
+		context.closePath();
+		context.fill();
+	},
+	
+	draw_legend: function(x_mid, y_mid, width, height, title) {
+		var context = this.elements.context;
+		
+		// draw line
+		context.beginPath();
+		context.moveTo(x_mid, y_mid);
+		context.lineTo(width, y_mid);
+		context.closePath();
+		context.stroke();
+		
+		// draw circle
+		context.beginPath();
+		context.arc(width, y_mid, 5, 0, Math.PI * 2, false);
+		context.closePath();
+		context.fill();
+		
+		// draw text
+		context.fillStyle = "black";
+		context.textBaseline = "middle";
+		context.font = "1.1em sans-serif";
+		context.fillText(title, width + 20, y_mid);
+	}
+});
\ No newline at end of file
diff --git a/selling/page/sales_funnel/sales_funnel.py b/selling/page/sales_funnel/sales_funnel.py
new file mode 100644
index 0000000..be0aebe
--- /dev/null
+++ b/selling/page/sales_funnel/sales_funnel.py
@@ -0,0 +1,33 @@
+# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd.
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import webnotes
+
+@webnotes.whitelist()
+def get_funnel_data(from_date, to_date):
+	active_leads = webnotes.conn.sql("""select count(*) from `tabLead`
+		where (`modified` between %s and %s)
+		and status != "Do Not Contact" """, (from_date, to_date))[0][0]
+		
+	active_leads += webnotes.conn.sql("""select count(distinct customer) from `tabContact`
+		where (`modified` between %s and %s)
+		and status != "Passive" """, (from_date, to_date))[0][0]
+	
+	opportunities = webnotes.conn.sql("""select count(*) from `tabOpportunity`
+		where docstatus = 1 and (`modified` between %s and %s)
+		and status != "Lost" """, (from_date, to_date))[0][0]
+	
+	quotations = webnotes.conn.sql("""select count(*) from `tabQuotation`
+		where docstatus = 1 and (`modified` between %s and %s)
+		and status != "Lost" """, (from_date, to_date))[0][0]
+	
+	sales_orders = webnotes.conn.sql("""select count(*) from `tabQuotation`
+		where docstatus = 1 and (`modified` between %s and %s)""", (from_date, to_date))[0][0]
+	
+	return [
+		{ "title": "Active Leads / Customers", "value": active_leads, "color": "#B03B46" },
+		{ "title": "Opportunities", "value": opportunities, "color": "#F09C00" },
+		{ "title": "Quotations", "value": quotations, "color": "#006685" },
+		{ "title": "Sales Orders", "value": sales_orders, "color": "#00AD65" }
+	]
diff --git a/selling/page/sales_funnel/sales_funnel.txt b/selling/page/sales_funnel/sales_funnel.txt
new file mode 100644
index 0000000..b841f20
--- /dev/null
+++ b/selling/page/sales_funnel/sales_funnel.txt
@@ -0,0 +1,33 @@
+[
+ {
+  "creation": "2013-10-04 13:17:18", 
+  "docstatus": 0, 
+  "modified": "2013-10-04 13:17:18", 
+  "modified_by": "Administrator", 
+  "owner": "Administrator"
+ }, 
+ {
+  "doctype": "Page", 
+  "icon": "icon-filter", 
+  "module": "Selling", 
+  "name": "__common__", 
+  "page_name": "sales-funnel", 
+  "standard": "Yes", 
+  "title": "Sales Funnel"
+ }, 
+ {
+  "doctype": "Page Role", 
+  "name": "__common__", 
+  "parent": "sales-funnel", 
+  "parentfield": "roles", 
+  "parenttype": "Page", 
+  "role": "Sales Manager"
+ }, 
+ {
+  "doctype": "Page", 
+  "name": "sales-funnel"
+ }, 
+ {
+  "doctype": "Page Role"
+ }
+]
\ No newline at end of file
diff --git a/selling/page/selling_home/selling_home.js b/selling/page/selling_home/selling_home.js
index 9697ccf..5dd33e6 100644
--- a/selling/page/selling_home/selling_home.js
+++ b/selling/page/selling_home/selling_home.js
@@ -155,6 +155,10 @@
 				"label":wn._("Sales Analytics"),
 				page: "sales-analytics"
 			},
+			{
+				"label":wn._("Sales Funnel"),
+				page: "sales-funnel"
+			},
 		]
 	},
 	{