Merge with 3.3.8
diff --git a/erpnext/accounts/report/accounts_payable/general_ledger/__init__.py b/erpnext/accounts/report/accounts_payable/general_ledger/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/accounts/report/accounts_payable/general_ledger/__init__.py
diff --git a/erpnext/accounts/report/accounts_payable/general_ledger/general_ledger.js b/erpnext/accounts/report/accounts_payable/general_ledger/general_ledger.js
new file mode 100644
index 0000000..7985277
--- /dev/null
+++ b/erpnext/accounts/report/accounts_payable/general_ledger/general_ledger.js
@@ -0,0 +1,51 @@
+// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
+// License: GNU General Public License v3. See license.txt
+
+wn.query_reports["General Ledger"] = {
+	"filters": [
+		{
+			"fieldname":"company",
+			"label": wn._("Company"),
+			"fieldtype": "Link",
+			"options": "Company",
+			"default": wn.defaults.get_user_default("company"),
+			"reqd": 1
+		},
+		{
+			"fieldname":"account",
+			"label": wn._("Account"),
+			"fieldtype": "Link",
+			"options": "Account"
+		},
+		{
+			"fieldname":"voucher_no",
+			"label": wn._("Voucher No"),
+			"fieldtype": "Data",
+		},
+		{
+			"fieldname":"group_by",
+			"label": wn._("Group by"),
+			"fieldtype": "Select",
+			"options": "\nGroup by Account\nGroup by Voucher"
+		},
+		{
+			"fieldtype": "Break",
+		},
+		{
+			"fieldname":"from_date",
+			"label": wn._("From Date"),
+			"fieldtype": "Date",
+			"default": wn.datetime.add_months(wn.datetime.get_today(), -1),
+			"reqd": 1,
+			"width": "60px"
+		},
+		{
+			"fieldname":"to_date",
+			"label": wn._("To Date"),
+			"fieldtype": "Date",
+			"default": wn.datetime.get_today(),
+			"reqd": 1,
+			"width": "60px"
+		}
+	]
+}
\ No newline at end of file
diff --git a/erpnext/accounts/report/accounts_payable/general_ledger/general_ledger.py b/erpnext/accounts/report/accounts_payable/general_ledger/general_ledger.py
new file mode 100644
index 0000000..575ccda
--- /dev/null
+++ b/erpnext/accounts/report/accounts_payable/general_ledger/general_ledger.py
@@ -0,0 +1,111 @@
+# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import webnotes
+from webnotes.utils import flt, add_days
+from webnotes import _
+from erpnext.accounts.utils import get_balance_on
+
+def execute(filters=None):
+	account_details = webnotes.conn.get_value("Account", filters["account"], 
+		["debit_or_credit", "group_or_ledger"], as_dict=True) if filters.get("account") else None
+	validate_filters(filters, account_details)
+	
+	columns = get_columns()
+	data = []
+	if filters.get("group_by"):
+		data += get_grouped_gle(filters)
+	else:
+		data += get_gl_entries(filters)
+		if data:
+			data.append(get_total_row(data))
+
+	if account_details:
+		data = [get_opening_balance_row(filters, account_details.debit_or_credit)] + data + \
+			[get_closing_balance_row(filters, account_details.debit_or_credit)]
+
+	return columns, data
+	
+def validate_filters(filters, account_details):
+	if account_details and account_details.group_or_ledger == "Ledger" \
+			and filters.get("group_by") == "Group by Account":
+		webnotes.throw(_("Can not filter based on Account, if grouped by Account"))
+		
+	if filters.get("voucher_no") and filters.get("group_by") == "Group by Voucher":
+		webnotes.throw(_("Can not filter based on Voucher No, if grouped by Voucher"))
+	
+def get_columns():
+	return ["Posting Date:Date:100", "Account:Link/Account:200", "Debit:Float:100", 
+		"Credit:Float:100", "Voucher Type::120", "Voucher No::160", "Link::20", 
+		"Cost Center:Link/Cost Center:100", "Remarks::200"]
+		
+def get_opening_balance_row(filters, debit_or_credit):
+	opening_balance = get_balance_on(filters["account"], add_days(filters["from_date"], -1))
+	return get_balance_row(opening_balance, debit_or_credit, "Opening Balance")
+	
+def get_closing_balance_row(filters, debit_or_credit):
+	closing_balance = get_balance_on(filters["account"], filters["to_date"])
+	return get_balance_row(closing_balance, debit_or_credit, "Closing Balance")
+	
+def get_balance_row(balance, debit_or_credit, balance_label):
+	if debit_or_credit == "Debit":
+		return ["", balance_label, balance, 0.0, "", "", ""]
+	else:
+		return ["", balance_label, 0.0, balance, "", "", ""]
+		
+def get_gl_entries(filters):
+	gl_entries = webnotes.conn.sql("""select 
+			posting_date, account, debit, credit, voucher_type, voucher_no, cost_center, remarks 
+		from `tabGL Entry`
+		where company=%(company)s 
+			and posting_date between %(from_date)s and %(to_date)s
+			{conditions}
+		order by posting_date, account"""\
+		.format(conditions=get_conditions(filters)), filters, as_list=1)
+		
+	for d in gl_entries:
+		icon = """<a href="%s"><i class="icon icon-share" style="cursor: pointer;"></i></a>""" \
+			% ("/".join(["#Form", d[4], d[5]]),)
+		d.insert(6, icon)
+		
+	return gl_entries
+			
+def get_conditions(filters):
+	conditions = []
+	if filters.get("account"):
+		lft, rgt = webnotes.conn.get_value("Account", filters["account"], ["lft", "rgt"])
+		conditions.append("""account in (select name from tabAccount 
+			where lft>=%s and rgt<=%s and docstatus<2)""" % (lft, rgt))
+	if filters.get("voucher_no"):
+		conditions.append("voucher_no=%(voucher_no)s")
+	
+	return "and {}".format(" and ".join(conditions)) if conditions else ""
+		
+def get_grouped_gle(filters):
+	gle_map = {}
+	gle = get_gl_entries(filters)
+	for d in gle:
+		gle_map.setdefault(d[1 if filters["group_by"]=="Group by Account" else 5], []).append(d)
+		
+	data = []
+	for entries in gle_map.values():
+		subtotal_debit = subtotal_credit = 0.0
+		for entry in entries:
+			data.append(entry)
+			subtotal_debit += flt(entry[2])
+			subtotal_credit += flt(entry[3])
+		
+		data.append(["", "Total", subtotal_debit, subtotal_credit, "", "", ""])
+	
+	if data:
+		data.append(get_total_row(gle))
+	return data
+	
+def get_total_row(gle):
+	total_debit = total_credit = 0.0
+	for d in gle:
+		total_debit += flt(d[2])
+		total_credit += flt(d[3])
+		
+	return ["", "Total Debit/Credit", total_debit, total_credit, "", "", ""]
\ No newline at end of file
diff --git a/erpnext/accounts/report/accounts_payable/general_ledger/general_ledger.txt b/erpnext/accounts/report/accounts_payable/general_ledger/general_ledger.txt
new file mode 100644
index 0000000..ef169db
--- /dev/null
+++ b/erpnext/accounts/report/accounts_payable/general_ledger/general_ledger.txt
@@ -0,0 +1,21 @@
+[
+ {
+  "creation": "2013-12-06 13:22:23", 
+  "docstatus": 0, 
+  "modified": "2013-12-06 13:22:23", 
+  "modified_by": "Administrator", 
+  "owner": "Administrator"
+ }, 
+ {
+  "doctype": "Report", 
+  "is_standard": "Yes", 
+  "name": "__common__", 
+  "ref_doctype": "GL Entry", 
+  "report_name": "General Ledger", 
+  "report_type": "Script Report"
+ }, 
+ {
+  "doctype": "Report", 
+  "name": "General Ledger"
+ }
+]
\ No newline at end of file
diff --git a/erpnext/patches/1311/p07_scheduler_errors_digest.py b/erpnext/patches/1311/p07_scheduler_errors_digest.py
new file mode 100644
index 0000000..4527f18
--- /dev/null
+++ b/erpnext/patches/1311/p07_scheduler_errors_digest.py
@@ -0,0 +1,32 @@
+# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import webnotes
+
+def execute():
+	webnotes.reload_doc("setup", "doctype", "email_digest")
+	
+	from webnotes.profile import get_system_managers
+	system_managers = get_system_managers(only_name=True)
+	if not system_managers: 
+		return
+	
+	# no default company
+	company = webnotes.conn.sql_list("select name from `tabCompany`")
+	if company:
+		company = company[0]
+	if not company:
+		return
+	
+	# scheduler errors digest
+	edigest = webnotes.new_bean("Email Digest")
+	edigest.doc.fields.update({
+		"name": "Scheduler Errors",
+		"company": company,
+		"frequency": "Daily",
+		"enabled": 1,
+		"recipient_list": "\n".join(system_managers),
+		"scheduler_errors": 1
+	})
+	edigest.insert()
diff --git a/erpnext/patches/1311/p08_email_digest_recipients.py b/erpnext/patches/1311/p08_email_digest_recipients.py
new file mode 100644
index 0000000..fad5408
--- /dev/null
+++ b/erpnext/patches/1311/p08_email_digest_recipients.py
@@ -0,0 +1,11 @@
+# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import webnotes
+
+def execute():
+	from webnotes.utils import extract_email_id
+	for name, recipients in webnotes.conn.sql("""select name, recipient_list from `tabEmail Digest`"""):
+		recipients = "\n".join([extract_email_id(r) for r in recipients.split("\n")])
+		webnotes.conn.set_value("Email Digest", name, "recipient_list", recipients)
\ No newline at end of file
diff --git a/erpnext/patches/1312/__init__.py b/erpnext/patches/1312/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/patches/1312/__init__.py
diff --git a/erpnext/patches/1312/p01_delete_old_stock_reports.py b/erpnext/patches/1312/p01_delete_old_stock_reports.py
new file mode 100644
index 0000000..e8d620b
--- /dev/null
+++ b/erpnext/patches/1312/p01_delete_old_stock_reports.py
@@ -0,0 +1,17 @@
+# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+
+def execute():
+	import webnotes, os, shutil
+	from webnotes.utils import get_base_path
+	
+	webnotes.delete_doc('Page', 'stock-ledger')
+	webnotes.delete_doc('Page', 'stock-ageing')
+	webnotes.delete_doc('Page', 'stock-level')
+	webnotes.delete_doc('Page', 'general-ledger')
+	
+	for d in [["stock", "stock_ledger"], ["stock", "stock_ageing"],
+		 	["stock", "stock_level"], ["accounts", "general_ledger"]]:
+		path = os.path.join(get_base_path(), "app", d[0], "page", d[1])
+		if os.path.exists(path):
+			shutil.rmtree(path)
\ No newline at end of file
diff --git a/erpnext/patches/1312/p02_update_item_details_in_item_price.py b/erpnext/patches/1312/p02_update_item_details_in_item_price.py
new file mode 100644
index 0000000..c19988c
--- /dev/null
+++ b/erpnext/patches/1312/p02_update_item_details_in_item_price.py
@@ -0,0 +1,10 @@
+# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import webnotes
+
+def execute():
+	webnotes.conn.sql("""update `tabItem Price` ip INNER JOIN `tabItem` i 
+		ON (ip.item_code = i.name) 
+		set ip.item_name = i.item_name, ip.item_description = i.description""")
\ No newline at end of file
diff --git a/erpnext/stock/report/stock_ageing/__init__.py b/erpnext/stock/report/stock_ageing/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/stock/report/stock_ageing/__init__.py
diff --git a/erpnext/stock/report/stock_ageing/stock_ageing.js b/erpnext/stock/report/stock_ageing/stock_ageing.js
new file mode 100644
index 0000000..f9e84b8
--- /dev/null
+++ b/erpnext/stock/report/stock_ageing/stock_ageing.js
@@ -0,0 +1,40 @@
+// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
+// License: GNU General Public License v3. See license.txt
+
+wn.query_reports["Stock Ageing"] = {
+	"filters": [
+		{
+			"fieldname":"company",
+			"label": wn._("Company"),
+			"fieldtype": "Link",
+			"options": "Company",
+			"default": wn.defaults.get_user_default("company"),
+			"reqd": 1
+		},
+		{
+			"fieldname":"to_date",
+			"label": wn._("To Date"),
+			"fieldtype": "Date",
+			"default": wn.datetime.get_today(),
+			"reqd": 1
+		},
+		{
+			"fieldname":"warehouse",
+			"label": wn._("Warehouse"),
+			"fieldtype": "Link",
+			"options": "Warehouse"
+		},
+		{
+			"fieldname":"item_code",
+			"label": wn._("Item"),
+			"fieldtype": "Link",
+			"options": "Item"
+		},
+		{
+			"fieldname":"brand",
+			"label": wn._("Brand"),
+			"fieldtype": "Link",
+			"options": "Brand"
+		}
+	]
+}
\ No newline at end of file
diff --git a/erpnext/stock/report/stock_ageing/stock_ageing.py b/erpnext/stock/report/stock_ageing/stock_ageing.py
new file mode 100644
index 0000000..1a84f93
--- /dev/null
+++ b/erpnext/stock/report/stock_ageing/stock_ageing.py
@@ -0,0 +1,94 @@
+# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import webnotes
+from webnotes.utils import date_diff
+
+def execute(filters=None):
+	
+	columns = get_columns()
+	item_details = get_fifo_queue(filters)
+	to_date = filters["to_date"]
+	data = []
+	for item, item_dict in item_details.items():
+		fifo_queue = item_dict["fifo_queue"]
+		details = item_dict["details"]
+		if not fifo_queue: continue
+		
+		average_age = get_average_age(fifo_queue, to_date)
+		earliest_age = date_diff(to_date, fifo_queue[0][1])
+		latest_age = date_diff(to_date, fifo_queue[-1][1])
+		
+		data.append([item, details.item_name, details.description, details.item_group, 
+			details.brand, average_age, earliest_age, latest_age, details.stock_uom])
+		
+	return columns, data
+	
+def get_average_age(fifo_queue, to_date):
+	batch_age = age_qty = total_qty = 0.0
+	for batch in fifo_queue:
+		batch_age = date_diff(to_date, batch[1])
+		age_qty += batch_age * batch[0]
+		total_qty += batch[0]
+	
+	return (age_qty / total_qty) if total_qty else 0.0
+	
+def get_columns():
+	return ["Item Code:Link/Item:100", "Item Name::100", "Description::200", 
+		"Item Group:Link/Item Group:100", "Brand:Link/Brand:100", "Average Age:Float:100", 
+		"Earliest:Int:80", "Latest:Int:80", "UOM:Link/UOM:100"]
+		
+def get_fifo_queue(filters):
+	item_details = {}
+	for d in get_stock_ledger_entries(filters):
+		item_details.setdefault(d.name, {"details": d, "fifo_queue": []})
+		fifo_queue = item_details[d.name]["fifo_queue"]
+		if d.actual_qty > 0:
+			fifo_queue.append([d.actual_qty, d.posting_date])
+		else:
+			qty_to_pop = abs(d.actual_qty)
+			while qty_to_pop:
+				batch = fifo_queue[0] if fifo_queue else [0, None]
+				if 0 < batch[0] <= qty_to_pop:
+					# if batch qty > 0 
+					# not enough or exactly same qty in current batch, clear batch
+					qty_to_pop -= batch[0]
+					fifo_queue.pop(0)
+				else:
+					# all from current batch
+					batch[0] -= qty_to_pop
+					qty_to_pop = 0
+
+	return item_details
+	
+def get_stock_ledger_entries(filters):
+	return webnotes.conn.sql("""select 
+			item.name, item.item_name, item_group, brand, description, item.stock_uom, 
+			actual_qty, posting_date
+		from `tabStock Ledger Entry` sle,
+			(select name, item_name, description, stock_uom, brand, item_group
+				from `tabItem` {item_conditions}) item
+		where item_code = item.name and
+			company = %(company)s and
+			posting_date <= %(to_date)s
+			{sle_conditions}
+			order by posting_date, posting_time, sle.name"""\
+		.format(item_conditions=get_item_conditions(filters),
+			sle_conditions=get_sle_conditions(filters)), filters, as_dict=True)
+	
+def get_item_conditions(filters):
+	conditions = []
+	if filters.get("item_code"):
+		conditions.append("item_code=%(item_code)s")
+	if filters.get("brand"):
+		conditions.append("brand=%(brand)s")
+	
+	return "where {}".format(" and ".join(conditions)) if conditions else ""
+	
+def get_sle_conditions(filters):
+	conditions = []
+	if filters.get("warehouse"):
+		conditions.append("warehouse=%(warehouse)s")
+	
+	return "and {}".format(" and ".join(conditions)) if conditions else ""
\ No newline at end of file
diff --git a/erpnext/stock/report/stock_ageing/stock_ageing.txt b/erpnext/stock/report/stock_ageing/stock_ageing.txt
new file mode 100644
index 0000000..b88ebce
--- /dev/null
+++ b/erpnext/stock/report/stock_ageing/stock_ageing.txt
@@ -0,0 +1,21 @@
+[
+ {
+  "creation": "2013-12-02 17:09:31", 
+  "docstatus": 0, 
+  "modified": "2013-12-02 17:09:31", 
+  "modified_by": "Administrator", 
+  "owner": "Administrator"
+ }, 
+ {
+  "doctype": "Report", 
+  "is_standard": "Yes", 
+  "name": "__common__", 
+  "ref_doctype": "Item", 
+  "report_name": "Stock Ageing", 
+  "report_type": "Script Report"
+ }, 
+ {
+  "doctype": "Report", 
+  "name": "Stock Ageing"
+ }
+]
\ No newline at end of file
diff --git a/erpnext/stock/report/stock_projected_qty/__init__.py b/erpnext/stock/report/stock_projected_qty/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/stock/report/stock_projected_qty/__init__.py
diff --git a/erpnext/stock/report/stock_projected_qty/stock_projected_qty.js b/erpnext/stock/report/stock_projected_qty/stock_projected_qty.js
new file mode 100644
index 0000000..8c25e5d
--- /dev/null
+++ b/erpnext/stock/report/stock_projected_qty/stock_projected_qty.js
@@ -0,0 +1,31 @@
+// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
+// License: GNU General Public License v3. See license.txt
+
+wn.query_reports["Stock Projected Qty"] = {
+	"filters": [
+		{
+			"fieldname":"company",
+			"label": wn._("Company"),
+			"fieldtype": "Link",
+			"options": "Company"
+		},
+		{
+			"fieldname":"warehouse",
+			"label": wn._("Warehouse"),
+			"fieldtype": "Link",
+			"options": "Warehouse"
+		},
+		{
+			"fieldname":"item_code",
+			"label": wn._("Item"),
+			"fieldtype": "Link",
+			"options": "Item"
+		},
+		{
+			"fieldname":"brand",
+			"label": wn._("Brand"),
+			"fieldtype": "Link",
+			"options": "Brand"
+		}
+	]
+}
\ No newline at end of file
diff --git a/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py b/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py
new file mode 100644
index 0000000..d335ebf
--- /dev/null
+++ b/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py
@@ -0,0 +1,50 @@
+# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import webnotes
+
+def execute(filters=None):
+	columns = get_columns()
+		
+	data = webnotes.conn.sql("""select 
+			item.name, item.item_name, description, item_group, brand, warehouse, item.stock_uom, 
+			actual_qty, planned_qty, indented_qty, ordered_qty, reserved_qty, 
+			projected_qty, item.re_order_level, item.re_order_qty
+		from `tabBin` bin, 
+			(select name, company from tabWarehouse 
+				{warehouse_conditions}) wh,
+			(select name, item_name, description, stock_uom, item_group, 
+				brand, re_order_level, re_order_qty 
+				from `tabItem` {item_conditions}) item
+		where item_code = item.name and warehouse = wh.name
+		order by item.name, wh.name"""\
+		.format(item_conditions=get_item_conditions(filters),
+			warehouse_conditions=get_warehouse_conditions(filters)), filters)
+	
+	return columns, data
+	
+def get_columns():
+	return ["Item Code:Link/Item:140", "Item Name::100", "Description::200", 
+		"Item Group:Link/Item Group:100", "Brand:Link/Brand:100", "Warehouse:Link/Warehouse:120", 
+		"UOM:Link/UOM:100", "Actual Qty:Float:100", "Planned Qty:Float:100", 
+		"Requested Qty:Float:110", "Ordered Qty:Float:100", "Reserved Qty:Float:100", 
+		"Projected Qty:Float:100", "Reorder Level:Float:100", "Reorder Qty:Float:100"]
+	
+def get_item_conditions(filters):
+	conditions = []
+	if filters.get("item_code"):
+		conditions.append("name=%(item_code)s")
+	if filters.get("brand"):
+		conditions.append("brand=%(brand)s")
+	
+	return "where {}".format(" and ".join(conditions)) if conditions else ""
+	
+def get_warehouse_conditions(filters):
+	conditions = []
+	if filters.get("company"):
+		conditions.append("company=%(company)s")
+	if filters.get("warehouse"):
+		conditions.append("name=%(warehouse)s")
+		
+	return "where {}".format(" and ".join(conditions)) if conditions else ""
\ No newline at end of file
diff --git a/erpnext/stock/report/stock_projected_qty/stock_projected_qty.txt b/erpnext/stock/report/stock_projected_qty/stock_projected_qty.txt
new file mode 100644
index 0000000..1998f7a
--- /dev/null
+++ b/erpnext/stock/report/stock_projected_qty/stock_projected_qty.txt
@@ -0,0 +1,22 @@
+[
+ {
+  "creation": "2013-12-04 18:21:56", 
+  "docstatus": 0, 
+  "modified": "2013-12-04 18:21:56", 
+  "modified_by": "Administrator", 
+  "owner": "Administrator"
+ }, 
+ {
+  "add_total_row": 1, 
+  "doctype": "Report", 
+  "is_standard": "Yes", 
+  "name": "__common__", 
+  "ref_doctype": "Item", 
+  "report_name": "Stock Projected Qty", 
+  "report_type": "Script Report"
+ }, 
+ {
+  "doctype": "Report", 
+  "name": "Stock Projected Qty"
+ }
+]
\ No newline at end of file