Merge remote-tracking branch 'frappe/develop' into wip-4.1
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice_list.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice_list.js
index 5592bc5..42c80b4 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice_list.js
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice_list.js
@@ -7,8 +7,10 @@
 	add_columns: [{"content":"Percent Paid", width:"10%", type:"bar-graph",
 		label: "Payment Received"}],
 	prepare_data: function(data) {
-		data["Percent Paid"] =  (data.docstatus===1 && flt(data.grand_total))
-			? (((flt(data.grand_total) - flt(data.outstanding_amount)) / flt(data.grand_total)) * 100)
-			: 0;
+		if (data.docstatus === 1) {
+			data["Percent Paid"] =  flt(data.grand_total)
+				? (((flt(data.grand_total) - flt(data.outstanding_amount)) / flt(data.grand_total)) * 100)
+				: 100.0;
+		}
 	}
 };
diff --git a/erpnext/accounts/page/financial_analytics/financial_analytics.js b/erpnext/accounts/page/financial_analytics/financial_analytics.js
index 52298bb..4574390 100644
--- a/erpnext/accounts/page/financial_analytics/financial_analytics.js
+++ b/erpnext/accounts/page/financial_analytics/financial_analytics.js
@@ -206,11 +206,11 @@
 		if(me.pl_or_bs=='Balance Sheet') {
 			$.each(me.data, function(i, ac) {
 				if((ac.rgt - ac.lft)==1 && ac.report_type=='Balance Sheet') {
-					var opening = 0;
+					var opening = flt(ac["opening_dr"]) - flt(ac["opening_cr"]);
 					//if(opening) throw opening;
 					$.each(me.columns, function(i, col) {
 						if(col.formatter==me.currency_formatter) {
-							if(col.balance_type=="Dr") {
+							if(col.balance_type=="Dr" && !in_list(["opening_dr", "opening_cr"], col.field)) {
 								opening = opening + flt(ac[col.date + "_dr"]) -
 									flt(ac[col.date + "_cr"]);
 								me.set_debit_or_credit(ac, col.date, opening);
diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py
index accaeb4..afccdfa 100644
--- a/erpnext/controllers/buying_controller.py
+++ b/erpnext/controllers/buying_controller.py
@@ -52,9 +52,8 @@
 			validate_warehouse_company(w, self.company)
 
 	def validate_stock_or_nonstock_items(self):
-		if not self.get_stock_items():
-			tax_for_valuation = [d.account_head for d in
-				self.get("other_charges")
+		if self.meta.get_field("other_charges") and not self.get_stock_items():
+			tax_for_valuation = [d.account_head for d in self.get("other_charges")
 				if d.category in ["Valuation", "Valuation and Total"]]
 			if tax_for_valuation:
 				frappe.throw(_("Tax Category can not be 'Valuation' or 'Valuation and Total' as all items are non-stock items"))
diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py
index a30cde7..789e7a3 100644
--- a/erpnext/controllers/queries.py
+++ b/erpnext/controllers/queries.py
@@ -141,7 +141,7 @@
 			concat(substr(tabItem.description, 1, 40), "..."), description) as decription
 		from tabItem
 		where tabItem.docstatus < 2
-			and (ifnull(tabItem.end_of_life, '') = '' or tabItem.end_of_life > %(today)s)
+			and (tabItem.end_of_life is null or tabItem.end_of_life > %(today)s)
 			and (tabItem.`{key}` LIKE %(txt)s
 				or tabItem.item_name LIKE %(txt)s)
 			{fcond} {mcond}
@@ -236,13 +236,21 @@
 				'page_len': page_len})
 
 def get_account_list(doctype, txt, searchfield, start, page_len, filters):
-	if isinstance(filters, dict):
-		if not filters.get("group_or_ledger"):
-			filters["group_or_ledger"] = "Ledger"
-	elif isinstance(filters, list):
-		if "group_or_ledger" not in [d[0] for d in filters]:
-			filters.append(["Account", "group_or_ledger", "=", "Ledger"])
+	filter_list = []
 
-	return frappe.widgets.reportview.execute("Account", filters = filters,
+	if isinstance(filters, dict):
+		for key, val in filters.items():
+			if isinstance(val, (list, tuple)):
+				filter_list.append([doctype, key, val[0], val[1]])
+			else:
+				filter_list.append([doctype, key, "=", val])
+
+	if "group_or_ledger" not in [d[1] for d in filter_list]:
+		filter_list.append(["Account", "group_or_ledger", "=", "Ledger"])
+
+	if searchfield and txt:
+		filter_list.append([doctype, searchfield, "like", "%%%s%%" % txt])
+
+	return frappe.widgets.reportview.execute("Account", filters = filter_list,
 		fields = ["name", "parent_account"],
 		limit_start=start, limit_page_length=page_len, as_list=True)
diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py
index df320dc..33f03b6 100644
--- a/erpnext/controllers/selling_controller.py
+++ b/erpnext/controllers/selling_controller.py
@@ -19,7 +19,7 @@
 		if frappe.db.get_value('Sales Email Settings', None, 'extract_emails'):
 			return frappe.db.get_value('Sales Email Settings', None, 'email_id')
 		else:
-			return frappe.session.user
+			return comm.sender or frappe.session.user
 
 	def set_missing_values(self, for_validate=False):
 		super(SellingController, self).set_missing_values(for_validate)
diff --git a/erpnext/hr/doctype/salary_manager/salary_manager.py b/erpnext/hr/doctype/salary_manager/salary_manager.py
index dcc1665..7d962e3 100644
--- a/erpnext/hr/doctype/salary_manager/salary_manager.py
+++ b/erpnext/hr/doctype/salary_manager/salary_manager.py
@@ -128,7 +128,7 @@
 		for ss in ss_list:
 			ss_obj = frappe.get_doc("Salary Slip",ss[0])
 			try:
-				frappe.db.set(ss_obj, 'email_check', cint(self.send_mail))
+				frappe.db.set(ss_obj, 'email_check', cint(self.send_email))
 				if cint(self.send_email) == 1:
 					ss_obj.send_mail_funct()
 
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 8755837..761b63c 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -58,3 +58,6 @@
 erpnext.patches.v4_0.create_custom_fields_for_india_specific_fields
 erpnext.patches.v4_0.save_default_letterhead
 erpnext.patches.v4_0.update_custom_print_formats_for_renamed_fields
+erpnext.patches.v4_0.update_other_charges_in_custom_purchase_print_formats
+erpnext.patches.v4_0.create_price_list_if_missing
+execute:frappe.db.sql("update `tabItem` set end_of_life=null where end_of_life='0000-00-00'") #2014-06-16
diff --git a/erpnext/patches/v4_0/apply_user_permissions.py b/erpnext/patches/v4_0/apply_user_permissions.py
index 36c7781..7dae02f 100644
--- a/erpnext/patches/v4_0/apply_user_permissions.py
+++ b/erpnext/patches/v4_0/apply_user_permissions.py
@@ -27,7 +27,9 @@
 	# save employees to run on_update events
 	for employee in frappe.db.sql_list("""select name from `tabEmployee` where docstatus < 2"""):
 		try:
-			frappe.get_doc("Employee", employee).save()
+			emp = frappe.get_doc("Employee", employee)
+			emp.ignore_mandatory = True
+			emp.save()
 		except EmployeeUserDisabledError:
 			pass
 
diff --git a/erpnext/patches/v4_0/create_price_list_if_missing.py b/erpnext/patches/v4_0/create_price_list_if_missing.py
new file mode 100644
index 0000000..f65b7cb
--- /dev/null
+++ b/erpnext/patches/v4_0/create_price_list_if_missing.py
@@ -0,0 +1,35 @@
+# 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 frappe
+from frappe import _
+from frappe.utils.nestedset import get_root_of
+
+def execute():
+	# setup not complete
+	if not frappe.db.sql("""select name from tabCompany limit 1"""):
+		return
+
+	if "shopping_cart" in frappe.get_installed_apps():
+		frappe.reload_doc("shopping_cart", "doctype", "shopping_cart_settings")
+
+	if not frappe.db.sql("select name from `tabPrice List` where buying=1"):
+		create_price_list(_("Standard Buying"), buying=1)
+
+	if not frappe.db.sql("select name from `tabPrice List` where selling=1"):
+		create_price_list(_("Standard Selling"), selling=1)
+
+def create_price_list(pl_name, buying=0, selling=0):
+	price_list = frappe.get_doc({
+		"doctype": "Price List",
+		"price_list_name": pl_name,
+		"enabled": 1,
+		"buying": buying,
+		"selling": selling,
+		"currency": frappe.db.get_default("currency"),
+		"valid_for_territories": [{
+			"territory": get_root_of("Territory")
+		}]
+	})
+	price_list.insert()
diff --git a/erpnext/patches/v4_0/split_email_settings.py b/erpnext/patches/v4_0/split_email_settings.py
index 05d9bc5..1b8a0c6 100644
--- a/erpnext/patches/v4_0/split_email_settings.py
+++ b/erpnext/patches/v4_0/split_email_settings.py
@@ -28,6 +28,7 @@
 
 		outgoing_email_settings.set(to_fieldname, email_settings.get(from_fieldname))
 
+	outgoing_email_settings._fix_numeric_types()
 	outgoing_email_settings.save()
 
 def map_support_email_settings(email_settings):
@@ -47,6 +48,7 @@
 
 		support_email_settings.set(to_fieldname, email_settings.get(from_fieldname))
 
+	support_email_settings._fix_numeric_types()
 	support_email_settings.save()
 
 def get_email_settings():
diff --git a/erpnext/patches/v4_0/update_other_charges_in_custom_purchase_print_formats.py b/erpnext/patches/v4_0/update_other_charges_in_custom_purchase_print_formats.py
new file mode 100644
index 0000000..c0f9ee0
--- /dev/null
+++ b/erpnext/patches/v4_0/update_other_charges_in_custom_purchase_print_formats.py
@@ -0,0 +1,12 @@
+# 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 frappe
+import re
+
+def execute():
+	for name, html in frappe.db.sql("""select name, html from `tabPrint Format`
+		where standard = 'No' and html like '%%purchase\\_tax\\_details%%'"""):
+			html = re.sub(r"\bpurchase_tax_details\b", "other_charges", html)
+			frappe.db.set_value("Print Format", name, "html", html)
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.js b/erpnext/stock/doctype/delivery_note/delivery_note.js
index fb94b88..10fe650 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.js
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.js
@@ -118,10 +118,10 @@
 }
 
 cur_frm.cscript['Make Packing Slip'] = function() {
-	n = frappe.model.make_new_doc_and_get_name('Packing Slip');
-	ps = locals["Packing Slip"][n];
-	ps.delivery_note = cur_frm.doc.name;
-	loaddoc('Packing Slip', n);
+	frappe.model.open_mapped_doc({
+		method: "erpnext.stock.doctype.delivery_note.delivery_note.make_packing_slip",
+		frm: cur_frm
+	})
 }
 
 var set_print_hide= function(doc, cdt, cdn){
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py
index da7dd7a..bbc9f81 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.py
@@ -68,13 +68,14 @@
 		self.validate_for_items()
 		self.validate_warehouse()
 		self.validate_uom_is_integer("stock_uom", "qty")
-		self.update_current_stock()
 		self.validate_with_previous_doc()
 
 		from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
 		make_packing_list(self, 'delivery_note_details')
 
-		self.status = 'Draft'
+		self.update_current_stock()
+
+		if not self.status: self.status = 'Draft'
 		if not self.installation_status: self.installation_status = 'Not Installed'
 
 	def validate_with_previous_doc(self):
@@ -133,14 +134,17 @@
 
 
 	def update_current_stock(self):
-		for d in self.get('delivery_note_details'):
-			bin = frappe.db.sql("select actual_qty from `tabBin` where item_code = %s and warehouse = %s", (d.item_code, d.warehouse), as_dict = 1)
-			d.actual_qty = bin and flt(bin[0]['actual_qty']) or 0
+		if self._action != "update_after_submit":
+			for d in self.get('delivery_note_details'):
+				d.actual_qty = frappe.db.get_value("Bin", {"item_code": d.item_code,
+					"warehouse": d.warehouse}, "actual_qty")
 
-		for d in self.get('packing_details'):
-			bin = frappe.db.sql("select actual_qty, projected_qty from `tabBin` where item_code =	%s and warehouse = %s", (d.item_code, d.warehouse), as_dict = 1)
-			d.actual_qty = bin and flt(bin[0]['actual_qty']) or 0
-			d.projected_qty = bin and flt(bin[0]['projected_qty']) or 0
+			for d in self.get('packing_details'):
+				bin_qty = frappe.db.get_value("Bin", {"item_code": d.item_code,
+					"warehouse": d.warehouse}, ["actual_qty", "projected_qty"], as_dict=True)
+				if bin_qty:
+					d.actual_qty = flt(bin_qty.actual_qty)
+					d.projected_qty = flt(bin_qty.projected_qty)
 
 	def on_submit(self):
 		self.validate_packed_qty()
@@ -346,3 +350,19 @@
 	}, target_doc)
 
 	return doclist
+
+@frappe.whitelist()
+def make_packing_slip(source_name, target_doc=None):
+	doclist = get_mapped_doc("Delivery Note", source_name, 	{
+		"Delivery Note": {
+			"doctype": "Packing Slip",
+			"field_map": {
+				"name": "delivery_note"
+			},
+			"validation": {
+				"docstatus": ["=", 0]
+			}
+		}
+	}, target_doc)
+
+	return doclist
diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py
index f8f0d09..9951fc8 100644
--- a/erpnext/stock/doctype/material_request/material_request.py
+++ b/erpnext/stock/doctype/material_request/material_request.py
@@ -48,7 +48,7 @@
 
 	def validate_schedule_date(self):
 		for d in self.get('indent_details'):
-			if d.schedule_date < self.transaction_date:
+			if d.schedule_date and d.schedule_date < self.transaction_date:
 				frappe.throw(_("Expected Date cannot be before Material Request Date"))
 
 	# Validate
diff --git a/erpnext/stock/doctype/packing_slip/packing_slip.js b/erpnext/stock/doctype/packing_slip/packing_slip.js
index acdd27e..9788290 100644
--- a/erpnext/stock/doctype/packing_slip/packing_slip.js
+++ b/erpnext/stock/doctype/packing_slip/packing_slip.js
@@ -18,7 +18,7 @@
 
 cur_frm.cscript.onload_post_render = function(doc, cdt, cdn) {
 	if(doc.delivery_note && doc.__islocal) {
-			cur_frm.cscript.get_items(doc, cdt, cdn);
+		cur_frm.cscript.get_items(doc, cdt, cdn);
 	}
 }
 
diff --git a/erpnext/stock/doctype/price_list/price_list.py b/erpnext/stock/doctype/price_list/price_list.py
index d992488..a4ff250 100644
--- a/erpnext/stock/doctype/price_list/price_list.py
+++ b/erpnext/stock/doctype/price_list/price_list.py
@@ -51,6 +51,7 @@
 
 			if self.name == b.get(price_list_fieldname):
 				b.set(price_list_fieldname, None)
+				b.ignore_permissions = True
 				b.save()
 
 		for module in ["Selling", "Buying"]:
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index ac81f88..d8164f7 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -70,6 +70,9 @@
 
 	def set_transfer_qty(self):
 		for item in self.get("mtn_details"):
+			if not flt(item.qty):
+				frappe.throw(_("Row {0}: Qty is mandatory").format(item.idx))
+
 			item.transfer_qty = flt(item.qty * item.conversion_factor, self.precision("transfer_qty", item))
 
 	def validate_item(self):
@@ -191,6 +194,11 @@
 
 		raw_material_cost = 0.0
 
+		if not self.posting_date or not self.posting_time:
+			frappe.throw(_("Posting date and posting time is mandatory"))
+
+		allow_negative_stock = frappe.db.get_value("Stock Settings", None, "allow_negative_stock")
+
 		for d in self.get('mtn_details'):
 			args = frappe._dict({
 				"item_code": d.item_code,
@@ -200,13 +208,21 @@
 				"qty": d.s_warehouse and -1*d.transfer_qty or d.transfer_qty,
 				"serial_no": d.serial_no
 			})
+
 			# get actual stock at source warehouse
 			d.actual_qty = get_previous_sle(args).get("qty_after_transaction") or 0
 
+			if d.s_warehouse and not allow_negative_stock and d.actual_qty <= d.transfer_qty:
+				frappe.throw(_("""Row {0}: Qty not avalable in warehouse {1} on {2} {3}.
+					Available Qty: {4}, Transfer Qty: {5}""").format(d.idx, d.s_warehouse,
+					self.posting_date, self.posting_time, d.actual_qty, d.transfer_qty))
+
 			# get incoming rate
 			if not d.bom_no:
-				if not flt(d.incoming_rate):
-					d.incoming_rate = self.get_incoming_rate(args)
+				if not flt(d.incoming_rate) or d.s_warehouse or self.purpose == "Sales Return":
+					incoming_rate = self.get_incoming_rate(args)
+					if incoming_rate:
+						d.incoming_rate = incoming_rate
 
 				d.amount = flt(d.transfer_qty) * flt(d.incoming_rate)
 				raw_material_cost += flt(d.amount)
diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py
index 9c251b8..cdf5aa1 100644
--- a/erpnext/stock/get_item_details.py
+++ b/erpnext/stock/get_item_details.py
@@ -255,7 +255,7 @@
 
 	args = frappe._dict(args)
 	out = frappe._dict()
-	if not args.get("item_code"): return
+	if args.get("doctype") == "Material Request" or not args.get("item_code"): return out
 
 	if not args.get("item_group") or not args.get("brand"):
 		args.item_group, args.brand = frappe.db.get_value("Item",