Merge pull request #4025 from anandpdoshi/split-emails

use the new split_emails method to split emails by comma. Merge this after Communication CC is merged.
diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
index b283c8f..09bd7d2 100644
--- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
+++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
@@ -79,6 +79,7 @@
 				`tabGL Entry`
 			where
 				party_type = %(party_type)s and party = %(party)s
+				and voucher_type != "Journal Entry"
 				and account = %(account)s and {dr_or_cr} > 0 {cond}
 			group by voucher_type, voucher_no
 		""".format(**{
diff --git a/erpnext/accounts/doctype/payment_tool/payment_tool.js b/erpnext/accounts/doctype/payment_tool/payment_tool.js
index dcf657a..0b4fb2d 100644
--- a/erpnext/accounts/doctype/payment_tool/payment_tool.js
+++ b/erpnext/accounts/doctype/payment_tool/payment_tool.js
@@ -25,8 +25,14 @@
 	});
 
 	frm.set_query("against_voucher_type", "vouchers", function() {
+		if (frm.doc.party_type=="Customer") {
+			var doctypes = ["Sales Order", "Sales Invoice", "Journal Entry"];
+		} else {
+			var doctypes = ["Purchase Order", "Purchase Invoice", "Journal Entry"];
+		}
+
 		return {
-			filters: {"name": ["in", ["Sales Invoice", "Purchase Invoice", "Journal Entry", "Sales Order", "Purchase Order"]]}
+			filters: { "name": ["in", doctypes] }
 		};
 	});
 });
@@ -135,6 +141,7 @@
 					c.against_voucher_no = d.voucher_no;
 					c.total_amount = d.invoice_amount;
 					c.outstanding_amount = d.outstanding_amount;
+					c.payment_amount = d.outstanding_amount;
 				});
 			}
 			refresh_field("vouchers");
@@ -145,25 +152,39 @@
 });
 
 // validate against_voucher_type
-frappe.ui.form.on("Payment Tool Detail", "against_voucher_type", function(frm) {
-	erpnext.payment_tool.validate_against_voucher(frm);
+frappe.ui.form.on("Payment Tool Detail", "against_voucher_type", function(frm, cdt, cdn) {
+	var row = frappe.model.get_doc(cdt, cdn);
+	erpnext.payment_tool.validate_against_voucher(frm, row);
 });
 
-erpnext.payment_tool.validate_against_voucher = function(frm) {
-	$.each(frm.doc.vouchers || [], function(i, row) {
+erpnext.payment_tool.validate_against_voucher = function(frm, row) {
+	var _validate = function(i, row) {
+		if (!row.against_voucher_type) {
+			return;
+		}
+
 		if(frm.doc.party_type=="Customer"
 			&& !in_list(["Sales Order", "Sales Invoice", "Journal Entry"], row.against_voucher_type)) {
 				frappe.model.set_value(row.doctype, row.name, "against_voucher_type", "");
-				frappe.throw(__("Against Voucher Type must be one of Sales Order, Sales Invoice or Journal Entry"))
+				frappe.msgprint(__("Against Voucher Type must be one of Sales Order, Sales Invoice or Journal Entry"));
+				return false;
 			}
 
 		if(frm.doc.party_type=="Supplier"
 			&& !in_list(["Purchase Order", "Purchase Invoice", "Journal Entry"], row.against_voucher_type)) {
 				frappe.model.set_value(row.doctype, row.name, "against_voucher_type", "");
-				frappe.throw(__("Against Voucher Type must be one of Purchase Order, Purchase Invoice or Journal Entry"))
+				frappe.msgprint(__("Against Voucher Type must be one of Purchase Order, Purchase Invoice or Journal Entry"));
+				return false;
 			}
 
-	});
+	}
+
+	if (row) {
+		_validate(0, row);
+	} else {
+		$.each(frm.doc.vouchers || [], _validate);
+	}
+
 }
 
 // validate against_voucher_type
@@ -178,14 +199,16 @@
 		args: {
 			"against_voucher_type": row.against_voucher_type,
 			"against_voucher_no": row.against_voucher_no,
-			"party_account": self.party_account,
-			"company": self.company
+			"party_account": frm.doc.party_account,
+			"company": frm.doc.company
 		},
 		callback: function(r) {
 			if(!r.exc) {
 				$.each(r.message, function(k, v) {
 					frappe.model.set_value(cdt, cdn, k, v);
 				});
+
+				frappe.model.set_value(cdt, cdn, "payment_amount", r.message.outstanding_amount);
 			}
 		}
 	});
@@ -208,7 +231,7 @@
 		} else {
 			if(row.payment_amount < 0)
 				msgprint(__("Row {0}: Payment amount can not be negative", [row.idx]));
-			else if(row.payment_amount >= row.outstanding_amount)
+			else if(row.payment_amount > row.outstanding_amount)
 				msgprint(__("Row {0}: Payment Amount cannot be greater than Outstanding Amount", [__(row.idx)]));
 
 			frappe.model.set_value(row.doctype, row.name, "payment_amount", 0.0);
diff --git a/erpnext/accounts/doctype/payment_tool/payment_tool.py b/erpnext/accounts/doctype/payment_tool/payment_tool.py
index 924fd1e..eedf69f 100644
--- a/erpnext/accounts/doctype/payment_tool/payment_tool.py
+++ b/erpnext/accounts/doctype/payment_tool/payment_tool.py
@@ -58,7 +58,7 @@
 		frappe.throw(_("No permission to use Payment Tool"), frappe.PermissionError)
 
 	args = json.loads(args)
-	
+
 	party_account_currency = frappe.db.get_value("Account", args.get("party_account"), "account_currency")
 	company_currency = frappe.db.get_value("Company", args.get("company"), "default_currency")
 
@@ -71,18 +71,18 @@
 
 	# Get all outstanding sales /purchase invoices
 	outstanding_invoices = get_outstanding_invoices(amount_query, args.get("party_account"),
-		args.get("party_type"), args.get("party"))
+		args.get("party_type"), args.get("party"), with_journal_entry=False)
 
 	# Get all SO / PO which are not fully billed or aginst which full advance not paid
-	orders_to_be_billed = get_orders_to_be_billed(args.get("party_type"), args.get("party"), 
+	orders_to_be_billed = get_orders_to_be_billed(args.get("party_type"), args.get("party"),
 		party_account_currency, company_currency)
 	return outstanding_invoices + orders_to_be_billed
 
 def get_orders_to_be_billed(party_type, party, party_account_currency, company_currency):
 	voucher_type = 'Sales Order' if party_type == "Customer" else 'Purchase Order'
-	
+
 	ref_field = "base_grand_total" if party_account_currency == company_currency else "grand_total"
-	
+
 	orders = frappe.db.sql("""
 		select
 			name as voucher_no,
@@ -115,7 +115,7 @@
 	party_account_currency = frappe.db.get_value("Account", party_account, "account_currency")
 	company_currency = frappe.db.get_value("Company", company, "default_currency")
 	ref_field = "base_grand_total" if party_account_currency == company_currency else "grand_total"
-	
+
 	if against_voucher_type in ["Sales Order", "Purchase Order"]:
 		select_cond = "{0} as total_amount, ifnull({0}, 0) - ifnull(advance_paid, 0) as outstanding_amount"\
 			.format(ref_field)
diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
index 20ecbe9..21bd570 100644
--- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
+++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
@@ -75,7 +75,9 @@
 		voucher_details = self.get_voucher_details(args.get("party_type"))
 
 		future_vouchers = self.get_entries_after(self.filters.report_date, args.get("party_type"))
-
+		
+		company_currency = frappe.db.get_value("Company", self.filters.get("company"), "default_currency")
+		
 		data = []
 		for gle in self.get_entries_till(self.filters.report_date, args.get("party_type")):
 			if self.is_receivable_or_payable(gle, dr_or_cr, future_vouchers):
@@ -115,8 +117,13 @@
 						row += [self.get_territory(gle.party)]
 					if args.get("party_type") == "Supplier":
 						row += [self.get_supplier_type(gle.party)]
+						
+					if self.filters.get(scrub(args.get("party_type"))):
+						row.append(gle.account_currency)
+					else:
+						row.append(company_currency)
 
-					row += [gle.account_currency, gle.remarks]
+					row.append(gle.remarks)
 					data.append(row)
 
 		return data
diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py
index 89ff6cf..e1bb9ea 100644
--- a/erpnext/accounts/utils.py
+++ b/erpnext/accounts/utils.py
@@ -100,7 +100,7 @@
 	if party_type and party:
 		cond.append("""gle.party_type = "%s" and gle.party = "%s" """ %
 			(party_type.replace('"', '\\"'), party.replace('"', '\\"')))
-			
+
 	if account or (party_type and party):
 		if in_account_currency:
 			select_field = "sum(ifnull(debit_in_account_currency, 0)) - sum(ifnull(credit_in_account_currency, 0))"
@@ -202,7 +202,7 @@
 			select cost_center, balance, against_account, is_advance, account_type, exchange_rate
 			from `tabJournal Entry Account` where name = %s
 		""", d['voucher_detail_no'], as_dict=True)
-		
+
 		# new entry with balance amount
 		ch = jv_obj.append("accounts")
 		ch.account = d['account']
@@ -392,7 +392,7 @@
 	# Amount should be credited
 	return flt(stock_rbnb) + flt(sys_bal)
 
-def get_outstanding_invoices(amount_query, account, party_type, party):
+def get_outstanding_invoices(amount_query, account, party_type, party, with_journal_entry=True):
 	all_outstanding_vouchers = []
 	outstanding_voucher_list = frappe.db.sql("""
 		select
@@ -420,6 +420,9 @@
 		payment_amount = -1*payment_amount[0][0] if payment_amount else 0
 		precision = frappe.get_precision("Sales Invoice", "outstanding_amount")
 
+		if not with_journal_entry and d.voucher_type=="Journal Entry":
+			continue
+
 		if d.invoice_amount > payment_amount:
 
 			all_outstanding_vouchers.append({
diff --git a/erpnext/controllers/item_variant.py b/erpnext/controllers/item_variant.py
index 4edf52b..38536cf 100644
--- a/erpnext/controllers/item_variant.py
+++ b/erpnext/controllers/item_variant.py
@@ -60,8 +60,8 @@
 			if not (is_in_range and is_incremental):
 				frappe.throw(_("Value for Attribute {0} must be within the range of {1} to {2} in the increments of {3}")\
 					.format(attribute, from_range, to_range, increment), InvalidItemAttributeValueError)
-
-		elif value not in attribute_values[attribute]:
+				
+		elif value not in attribute_values.get(attribute, []):
 			frappe.throw(_("Value {0} for Attribute {1} does not exist in the list of valid Item Attribute Values").format(
 				value, attribute))
 
diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py
index 804dc4d..785e7aa 100644
--- a/erpnext/setup/doctype/company/company.py
+++ b/erpnext/setup/doctype/company/company.py
@@ -207,12 +207,14 @@
 		frappe.defaults.clear_default("company", value=self.name)
 
 		# clear default accounts, warehouses from item
-		for f in ["default_warehouse", "website_warehouse"]:
-			frappe.db.sql("""update tabItem set %s=NULL where %s in (%s)"""
-				% (f, f, ', '.join(['%s']*len(warehouses))), tuple(warehouses))
+		if warehouses:
+			
+			for f in ["default_warehouse", "website_warehouse"]:
+				frappe.db.sql("""update tabItem set %s=NULL where %s in (%s)"""
+					% (f, f, ', '.join(['%s']*len(warehouses))), tuple(warehouses))
 
-		frappe.db.sql("""delete from `tabItem Reorder` where warehouse in (%s)"""
-			% ', '.join(['%s']*len(warehouses)), tuple(warehouses))
+			frappe.db.sql("""delete from `tabItem Reorder` where warehouse in (%s)"""
+				% ', '.join(['%s']*len(warehouses)), tuple(warehouses))
 
 		for f in ["income_account", "expense_account"]:
 			frappe.db.sql("""update tabItem set %s=NULL where %s in (%s)"""
diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py
index d8d21ff..6eadb83 100644
--- a/erpnext/stock/doctype/item/item.py
+++ b/erpnext/stock/doctype/item/item.py
@@ -341,10 +341,10 @@
 					frappe.throw(_("Please specify Attribute Value for attribute {0}").format(d.attribute))
 				args[d.attribute] = d.attribute_value
 
-			if self.get("__islocal"):
+			if self.variant_of:
 				# test this during insert because naming is based on item_code and we cannot use condition like self.name != variant
 				variant = get_variant(self.variant_of, args)
-				if variant:
+				if variant and self.get("__islocal"):
 					frappe.throw(_("Item variant {0} exists with same attributes").format(variant), ItemVariantExistsError)
 
 def validate_end_of_life(item_code, end_of_life=None, verbose=1):