Payment against negative invoice, negative outstanding allocation and much more
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js
index bb84eee..230d728 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.js
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js
@@ -193,6 +193,34 @@
 			}, "icon-table");
 		}
 	},
+	
+	payment_type: function(frm) {
+		if(frm.doc.payment_type == "Internal Transfer") {
+			$.each(["party", "party_balance", "paid_from", "paid_to", 
+				"references", "total_allocated_amount"], function(i, field) {
+					frm.set_value(field, null);
+			})
+		} else {
+			if(!frm.doc.party)
+				frm.set_value("party_type", frm.doc.payment_type=="Receive" ? "Customer" : "Supplier");
+			else 
+				frm.events.party(frm);
+			
+			if(frm.doc.mode_of_payment)
+				frm.events.mode_of_payment(frm);
+		}
+	},
+	
+	party_type: function(frm) {
+		if(frm.doc.party) {
+			$.each(["party", "party_balance", "paid_from", "paid_to", 
+				"paid_from_account_currency", "paid_from_account_balance",
+				"paid_to_account_currency", "paid_to_account_balance",
+				"references", "total_allocated_amount"], function(i, field) {
+					frm.set_value(field, null);
+			})
+		}
+	},
 		
 	party: function(frm) {
 		if(frm.doc.payment_type && frm.doc.party_type && frm.doc.party) {
@@ -227,18 +255,6 @@
 			});
 		}
 	},
-	
-	payment_type: function(frm) {
-		if(frm.doc.payment_type == "Internal Transfer") {
-			$.each(["party", "party_balance", "paid_from", "paid_to", 
-				"references", "total_allocated_amount"], function(i, field) {
-					frm.set_value(field, null);
-			})
-		} else {
-			frm.set_value("party_type", frm.doc.payment_type=="Receive" ? "Customer" : "Supplier");
-			frm.events.party(frm);
-		}
-	},
 		
 	mode_of_payment: function(frm) {
 		return  frappe.call({
@@ -279,7 +295,7 @@
 	set_account_currency_and_balance: function(frm, account, currency_field, 
 			balance_field, callback_function) {
 		frappe.call({
-			method: "erpnext.accounts.doctype.payment_entry.payment_entry.get_account_currency_and_balance",
+			method: "erpnext.accounts.doctype.payment_entry.payment_entry.get_account_details",
 			args: {
 				"account": account,
 				"date": frm.doc.posting_date
@@ -289,11 +305,16 @@
 					frm.set_value(currency_field, r.message['account_currency']);
 					frm.set_value(balance_field, r.message['account_balance']);
 					
-					if(frm.doc.payment_type == "Receive" && currency_field=="paid_to_account_currency" 
-						&& !frm.doc.received_amount && frm.doc.paid_amount) {
+					if(frm.doc.payment_type=="Receive" && currency_field=="paid_to_account_currency") {
+						frm.toggle_reqd(["reference_no", "reference_date"], 
+							(r.message['account_type'] == "Bank" ? 1 : 0));
+						if(!frm.doc.received_amount && frm.doc.paid_amount)
 							frm.events.paid_amount(frm);
-					} else if(frm.doc.payment_type == "Pay" && currency_field=="paid_from_account_currency" 
-						&& !frm.doc.paid_amount && frm.doc.received_amount) {
+					} else if(frm.doc.payment_type=="Pay" && currency_field=="paid_from_account_currency") { 
+						frm.toggle_reqd(["reference_no", "reference_date"], 
+							(r.message['account_type'] == "Bank" ? 1 : 0));
+							
+						if(!frm.doc.paid_amount && frm.doc.received_amount)
 							frm.events.received_amount(frm);
 					}
 					
@@ -420,16 +441,22 @@
 			},
 			callback: function(r, rt) {
 				if(r.message) {
-					var total_invoice_outstanding_amount = 0;
+					var total_positive_outstanding = 0;
+					var total_negative_outstanding = 0;
+					
 					$.each(r.message, function(i, d) {
 						var c = frm.add_child("references");
 						c.reference_doctype = d.voucher_type;
 						c.reference_name = d.voucher_no;
 						c.total_amount = d.invoice_amount;
 						c.outstanding_amount = d.outstanding_amount;
-						if(!in_list(["Sales Order", "Purchase Order"], d.voucher_type))
-							total_invoice_outstanding_amount += flt(d.outstanding_amount);
-						
+						if(!in_list(["Sales Order", "Purchase Order"], d.voucher_type)) {
+							if(flt(d.outstanding_amount) > 0)
+								total_positive_outstanding += flt(d.outstanding_amount);
+							else
+								total_negative_outstanding += Math.abs(flt(d.outstanding_amount));
+						}
+							
 						var party_account_currency = frm.doc.payment_type=="Receive" ? 
 							frm.doc.paid_from_account_currency : frm.doc.paid_to_account_currency;
 
@@ -439,33 +466,83 @@
 							c.exchange_rate = 1;
 						}
 						if (in_list(['Sales Invoice', 'Purchase Invoice'], d.reference_doctype)){
-							c.due_date = d.due_date
+							c.due_date = d.due_date;
 						}
 					});
 					
-					if(total_invoice_outstanding_amount > 0) {
-						var party_amt_field = (frm.doc.payment_type=="Receive") ? 
-							"paid_amount" : "received_amount";
-						frm.set_value(party_amt_field, total_invoice_outstanding_amount);
+					if((frm.doc.payment_type=="Receive" && frm.doc.party_type=="Customer") || 
+						(frm.doc.payment_type=="Pay" && frm.doc.party_type=="Supplier")) {
+							if(total_positive_outstanding > total_negative_outstanding)
+								frm.set_value("paid_amount", 
+									total_positive_outstanding - total_negative_outstanding);
+					} else if (total_negative_outstanding && 
+							(total_positive_outstanding < total_negative_outstanding)) {
+						frm.set_value("received_amount", 
+							total_negative_outstanding - total_positive_outstanding);
 					}
-					
 				}
 				
-				frm.events.allocate_party_amount_against_ref_docs(frm, 
+				frm.events.allocate_party_amount_against_ref_docs(frm,
 					(frm.doc.payment_type=="Receive" ? frm.doc.paid_amount : frm.doc.received_amount));
 			}
 		});
 	},
 	
-	allocate_party_amount_against_ref_docs: function(frm, amount) {
+	allocate_party_amount_against_ref_docs: function(frm, paid_amount) {
+		var total_positive_outstanding_including_order = 0;
+		var total_negative_outstanding = 0;
+		
+		$.each(frm.doc.references || [], function(i, row) {
+			if(flt(row.outstanding_amount) > 0)
+				total_positive_outstanding_including_order += flt(row.outstanding_amount);
+			else
+				total_negative_outstanding += Math.abs(flt(row.outstanding_amount));
+		})
+		
+		var allocated_negative_outstanding = 0;
+		if((frm.doc.payment_type=="Receive" && frm.doc.party_type=="Customer") || 
+				(frm.doc.payment_type=="Pay" && frm.doc.party_type=="Supplier")) {
+			if(total_positive_outstanding_including_order > paid_amount) {
+				var remaining_outstanding = total_positive_outstanding_including_order - paid_amount;
+				allocated_negative_outstanding = total_negative_outstanding < remaining_outstanding ? 
+					total_negative_outstanding : remaining_outstanding;
+			}
+		
+			var allocated_positive_outstanding =  paid_amount + allocated_negative_outstanding;
+		} else {
+			if(paid_amount > total_negative_outstanding) {
+				if(total_negative_outstanding == 0) {
+					frappe.msgprint(__("Cannot {0} {1} {2} without any negative outstanding invoice", 
+						[frm.doc.payment_type, 
+							(frm.doc.party_type=="Customer" ? "to" : "from"), frm.doc.party_type]));
+					return false
+				} else {
+					frappe.msgprint(__("Paid Amount cannot be greater than total negative outstanding amount {0}", [total_negative_outstanding]));
+					return false;
+				}
+			} else {
+				allocated_positive_outstanding = total_negative_outstanding - paid_amount;
+				allocated_negative_outstanding = paid_amount + 
+					(total_positive_outstanding_including_order < allocated_positive_outstanding ? 
+						total_positive_outstanding_including_order : allocated_positive_outstanding)
+			}
+		}
+		
 		$.each(frm.doc.references || [], function(i, row) {
 			row.allocated_amount = 0
 			
-			if(amount) {
-				if(row.outstanding_amount >= amount) row.allocated_amount = amount;
+			if(row.outstanding_amount > 0 && allocated_positive_outstanding > 0) {
+				if(row.outstanding_amount >= allocated_positive_outstanding) 
+						row.allocated_amount = allocated_positive_outstanding;
 				else row.allocated_amount = row.outstanding_amount;
-			
-				amount -= flt(row.allocated_amount);
+				
+				allocated_positive_outstanding -= flt(row.allocated_amount);
+			} else if (row.outstanding_amount < 0 && allocated_negative_outstanding) {
+				if(Math.abs(row.outstanding_amount) >= allocated_negative_outstanding)
+					row.allocated_amount = -1*allocated_negative_outstanding;
+				else row.allocated_amount = row.outstanding_amount;
+				
+				allocated_negative_outstanding -= Math.abs(flt(row.allocated_amount));
 			}
 		})
 		frm.refresh_fields()
@@ -476,23 +553,13 @@
 		var total_allocated_amount = base_total_allocated_amount = 0.0;
 		$.each(frm.doc.references || [], function(i, row) {
 			if (row.allocated_amount) {
-				if (flt(row.allocated_amount) <= row.outstanding_amount) {
-					total_allocated_amount += flt(row.allocated_amount);
-					base_total_allocated_amount += flt(flt(row.allocated_amount)*flt(row.exchange_rate), 
-						precision("base_paid_amount"));
-				} else {
-					if(flt(row.allocated_amount) < 0)
-						frappe.throw(__("Row {0}: Allocated amount can not be negative", [row.idx]));
-					else if(flt(row.allocated_amount) > flt(row.outstanding_amount))
-						frappe.throw(__("Row {0}: Allocated Amount cannot be greater than Outstanding Amount",
-							 [__(row.idx)]));
-				
-					frappe.model.set_value(row.doctype, row.name, "allocated_amount", 0.0);
-				}
+				total_allocated_amount += flt(row.allocated_amount);
+				base_total_allocated_amount += flt(flt(row.allocated_amount)*flt(row.exchange_rate), 
+					precision("base_paid_amount"));
 			}
 		});
-		frm.set_value("total_allocated_amount", total_allocated_amount);
-		frm.set_value("base_total_allocated_amount", base_total_allocated_amount);
+		frm.set_value("total_allocated_amount", Math.abs(total_allocated_amount));
+		frm.set_value("base_total_allocated_amount", Math.abs(base_total_allocated_amount));
 					
 		frm.events.set_difference_amount(frm);
 	},
@@ -534,7 +601,11 @@
 	
 	check_mandatory_to_fetch: function(frm) {
 		$.each(["Company", "Party Type", "Party", "payment_type"], function(i, field) {
-			if(!frm.doc[frappe.model.scrub(field)]) frappe.throw(__("Please select {0} first", [field]));
+			if(!frm.doc[frappe.model.scrub(field)]) {
+				frappe.msgprint(__("Please select {0} first", [field]));
+				return false;
+			}
+				
 		});
 	},
 		
@@ -612,6 +683,27 @@
 		frm.events.validate_reference_document(frm, row);
 	},
 	
+	reference_name: function(frm, cdt, cdn) {
+		var row = locals[cdt][cdn];
+		return frappe.call({
+			method: "erpnext.accounts.doctype.payment_entry.payment_entry.get_reference_details",
+			args: {
+				reference_doctype: row.reference_doctype,
+				reference_name: row.reference_name,
+				party_account_currency: frm.doc.payment_type=="Receive" ? 
+					frm.doc.paid_from_account_currency : frm.doc.paid_to_account_currency
+			}, 
+			callback: function(r, rt) {
+				if(r.message) {
+					$.each(r.message, function(field, value) {
+						frappe.model.set_value(cdt, cdn, field, value);
+					})
+					frm.refresh_fields();
+				}
+			}
+		})
+	},
+	
 	allocated_amount: function(frm) {
 		frm.events.set_total_allocated_amount(frm);
 	},
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py
index 7738563..4a5f551 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py
@@ -4,7 +4,7 @@
 
 from __future__ import unicode_literals
 import frappe, json
-from frappe import _, scrub
+from frappe import _, scrub, ValidationError
 from frappe.utils import flt, comma_or, nowdate
 from erpnext.accounts.utils import get_outstanding_invoices, get_account_currency, get_balance_on
 from erpnext.accounts.party import get_party_account
@@ -15,6 +15,7 @@
 
 from erpnext.controllers.accounts_controller import AccountsController
 
+class InvalidPaymentEntry(ValidationError): pass
 
 class PaymentEntry(AccountsController):
 	def setup_party_account_field(self):		
@@ -42,8 +43,9 @@
 		self.validate_reference_documents()
 		self.set_amounts()
 		self.clear_unallocated_reference_document_rows()
-		self.set_title()
+		self.validate_payment_against_negative_invoice()
 		self.validate_transaction_reference()
+		self.set_title()
 		self.set_remarks()
 		
 	def on_submit(self):
@@ -51,6 +53,7 @@
 		self.update_advance_paid()
 		
 	def on_cancel(self):
+		self.setup_party_account_field()
 		self.make_gl_entries(cancel=1)
 		self.update_advance_paid()
 							
@@ -78,12 +81,12 @@
 				self.party_account = party_account
 				
 		if self.paid_from and not (self.paid_from_account_currency or self.paid_from_account_balance):
-			acc = get_account_currency_and_balance(self.paid_from, self.posting_date)
+			acc = get_account_details(self.paid_from, self.posting_date)
 			self.paid_from_account_currency = acc.account_currency
 			self.paid_from_account_balance = acc.account_balance
 				
 		if self.paid_to and not (self.paid_to_account_currency or self.paid_to_account_balance):
-			acc = get_account_currency_and_balance(self.paid_to, self.posting_date)
+			acc = get_account_details(self.paid_to, self.posting_date)
 			self.paid_to_account_currency = acc.account_currency
 			self.paid_to_account_balance = acc.account_balance
 			
@@ -96,24 +99,12 @@
 	def set_missing_ref_details(self):
 		for d in self.get("references"):
 			if d.allocated_amount:
-				if d.reference_doctype != "Journal Entry":
-					ref_doc = frappe.get_doc(d.reference_doctype, d.reference_name)
-				
-					d.due_date = ref_doc.get("due_date")
-
-					if self.party_account_currency == self.company_currency:
-						d.total_amount = ref_doc.base_grand_total
-						d.exchange_rate = 1
-					else:
-						d.total_amount = ref_doc.grand_total
-						d.exchange_rate = ref_doc.get("conversion_rate") or \
-							get_exchange_rate(self.party_account_currency, self.company_currency)
+				ref_details = get_reference_details(d.reference_doctype, 
+					d.reference_name, self.party_account_currency)
 					
-					d.outstanding_amount = ref_doc.get("outstanding_amount") \
-						if d.reference_doctype in ("Sales Invoice", "Purchase Invoice") \
-						else flt(d.total_amount) - flt(ref_doc.advance_paid)
-				elif not d.exchange_rate:
-					d.exchange_rate = get_exchange_rate(self.party_account_currency, self.company_currency)
+				for field, value in ref_details.items():
+					if not d.get(field):
+						d.set(field, value)
 	
 	def validate_party_details(self):
 		if self.party:
@@ -162,6 +153,8 @@
 			valid_reference_doctypes = ("Purchase Order", "Purchase Invoice", "Journal Entry")
 			
 		for d in self.get("references"):
+			if not d.allocated_amount:
+				continue
 			if d.reference_doctype not in valid_reference_doctypes:
 				frappe.throw(_("Reference Doctype must be one of {0}")
 					.format(comma_or(valid_reference_doctypes)))
@@ -172,15 +165,45 @@
 				else:
 					ref_doc = frappe.get_doc(d.reference_doctype, d.reference_name)
 
-					if d.reference_doctype != "Journal Entry" \
-							and self.party != ref_doc.get(scrub(self.party_type)):
-						frappe.throw(_("{0} {1} does not associated with {2} {3}")
-							.format(d.reference_doctype, d.reference_name, self.party_type, self.party))
-					
+					if d.reference_doctype != "Journal Entry":
+						if self.party != ref_doc.get(scrub(self.party_type)):
+							frappe.throw(_("{0} {1} does not associated with {2} {3}")
+								.format(d.reference_doctype, d.reference_name, self.party_type, self.party))
+					else:
+						self.validate_journal_entry()
+								
+					if d.reference_doctype in ("Sales Invoice", "Purchase Invoice"):
+						ref_party_account = ref_doc.debit_to \
+							if self.party_type=="Customer" else ref_doc.credit_to
+						if ref_party_account != self.party_account:
+							frappe.throw(_("{0} {1} does not associated with Party Account {2}")
+								.format(d.reference_doctype, d.reference_name, self.party_account))
+						
 					if ref_doc.docstatus != 1:
 						frappe.throw(_("{0} {1} must be submitted")
 							.format(d.reference_doctype, d.reference_name))
 							
+	def validate_journal_entry(self):
+		for d in self.get("references"):
+			if d.allocated_amount and d.reference_doctype == "Journal Entry":				
+				je_accounts = frappe.db.sql("""select debit, credit from `tabJournal Entry Account`
+					where account = %s and party=%s and docstatus = 1 and parent = %s
+					and (reference_type is null or reference_type in ("", "Sales Order", "Purchase Order"))
+					""", (self.party_account, self.party, d.reference_name), as_dict=True)
+
+				if not je_accounts:
+					frappe.throw(_("Row #{0}: Journal Entry {0} does not have account {1} or already matched against another voucher")
+						.format(d.idx, d.reference_name, self.party_account))
+				else:
+					dr_or_cr = "debit" if self.payment_type == "Receive" else "credit"
+					valid = False
+					for jvd in je_accounts:
+						if flt(jvd[dr_or_cr]) > 0:
+							valid = True
+					if not valid:
+						frappe.throw(_("Against Journal Entry {0} does not have any unmatched {1} entry")
+							.format(d.reference_name, dr_or_cr))
+							
 	def set_amounts(self):
 		self.set_amounts_in_company_currency()
 		self.set_total_allocated_amount()
@@ -201,17 +224,15 @@
 		if self.payment_type == "Internal Transfer":
 			return
 			
-		self.total_allocated_amount, self.base_total_allocated_amount = 0, 0
+		total_allocated_amount, base_total_allocated_amount = 0, 0
 		for d in self.get("references"):
-			if d.allocated_amount:
-				if d.reference_doctype not in ("Sales Order", "Purchase Order") \
-					and d.allocated_amount > d.outstanding_amount:
-						frappe.throw(_("Row #{0}: Allocated amount cannot be greater than outstanding amount")
-							.format(d.idx))
-				
-				self.total_allocated_amount += flt(d.allocated_amount)
-				self.base_total_allocated_amount += flt(flt(d.allocated_amount) * flt(d.exchange_rate), 
+			if d.allocated_amount:				
+				total_allocated_amount += flt(d.allocated_amount)
+				base_total_allocated_amount += flt(flt(d.allocated_amount) * flt(d.exchange_rate), 
 					self.precision("base_paid_amount"))
+					
+		self.total_allocated_amount = abs(total_allocated_amount)
+		self.base_total_allocated_amount = abs(base_total_allocated_amount)
 	
 	def set_unallocated_amount(self):
 		self.unallocated_amount = 0;
@@ -244,6 +265,23 @@
 		frappe.db.sql("""delete from `tabPayment Entry Reference` 
 			where parent = %s and allocated_amount = 0""", self.name)
 			
+	def validate_payment_against_negative_invoice(self):
+		if ((self.payment_type=="Pay" and self.party_type=="Customer") 
+				or (self.payment_type=="Receive" and self.party_type=="Supplier")):
+			total_negative_outstanding = sum([abs(flt(d.outstanding_amount)) 
+				for d in self.get("references") if flt(d.outstanding_amount) < 0])
+			
+			party_amount = self.paid_amount if self.payment_type=="Receive" else self.received_amount
+
+			if not total_negative_outstanding:
+				frappe.throw(_("Cannot {0} {1} {2} without any negative outstanding invoice")
+					.format(self.payment_type, ("to" if self.party_type=="Customer" else "from"), 
+						self.party_type), InvalidPaymentEntry)
+					
+			elif party_amount > total_negative_outstanding:
+				frappe.throw(_("Paid Amount cannot be greater than total negative outstanding amount {0}")
+					.format(total_negative_outstanding), InvalidPaymentEntry)
+			
 	def set_title(self):
 		if self.payment_type in ("Receive", "Pay"):
 			self.title = self.party
@@ -313,6 +351,8 @@
 				"account_currency": self.party_account_currency
 			})
 			
+			dr_or_cr = "credit" if self.party_type == "Customer" else "debit"
+			
 			for d in self.get("references"):
 				gle = party_gl_dict.copy()
 				gle.update({
@@ -323,16 +363,10 @@
 				allocated_amount_in_company_currency = flt(flt(d.allocated_amount) * flt(d.exchange_rate), 
 					self.precision("paid_amount"))	
 				
-				if self.payment_type == "Receive":
-					gle.update({
-						"credit_in_account_currency": d.allocated_amount,
-						"credit": allocated_amount_in_company_currency
-					})
-				elif self.payment_type == "Pay":
-					gle.update({
-						"debit_in_account_currency": d.allocated_amount,
-						"debit": allocated_amount_in_company_currency
-					})
+				gle.update({
+					dr_or_cr + "_in_account_currency": d.allocated_amount,
+					dr_or_cr: allocated_amount_in_company_currency
+				})
 				
 				gl_entries.append(gle)
 				
@@ -341,17 +375,12 @@
 					(self.source_exchange_rate if self.payment_type=="Receive" else self.target_exchange_rate)
 					
 				gle = party_gl_dict.copy()
-				if self.payment_type == "Receive":
-					gle.update({
-						"credit_in_account_currency": self.unallocated_amount,
-						"credit": base_unallocated_amount
-					})
-				elif self.payment_type == "Pay":
-					gle.update({
-						"debit_in_account_currency": self.unallocated_amount,
-						"debit": base_unallocated_amount
-					})
 				
+				gle.update({
+					dr_or_cr + "_in_account_currency": d.unallocated_amount,
+					dr_or_cr: base_unallocated_amount
+				})
+
 				gl_entries.append(gle)
 				
 	def add_bank_gl_entries(self, gl_entries):
@@ -406,27 +435,28 @@
 
 	party_account_currency = get_account_currency(args.get("party_account"))
 	company_currency = frappe.db.get_value("Company", args.get("company"), "default_currency")
+	
+	# Get negative outstanding sales /purchase invoices
+	total_field = "base_grand_total" if party_account_currency == company_currency else "grand_total"
+		
+	negative_outstanding_invoices = get_negative_outstanding_invoices(args.get("party_type"), 
+		args.get("party"), args.get("party_account"), total_field)
 
-	if ((args.get("party_type") == "Customer" and args.get("payment_type") == "Pay")
-		or (args.get("party_type") == "Supplier" and args.get("payment_type") == "Received")):
-
-		frappe.throw(_("Please enter the Reference Documents manually"))
-
-	# Get all outstanding sales /purchase invoices
+	# Get positive outstanding sales /purchase invoices
 	outstanding_invoices = get_outstanding_invoices(args.get("party_type"), args.get("party"), 
 		args.get("party_account"))
-		
+	
 	for d in outstanding_invoices:
 		d["exchange_rate"] = 1
 		if party_account_currency != company_currency \
 			and d.voucher_type in ("Sales Invoice", "Purchase Invoice"):
 				d["exchange_rate"] = frappe.db.get_value(d.voucher_type, d.voucher_no, "conversion_rate")
-	
+
 	# 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"), 
 		party_account_currency, company_currency)
 	
-	return outstanding_invoices + orders_to_be_billed
+	return negative_outstanding_invoices + 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'
@@ -463,8 +493,31 @@
 
 	return order_list
 	
+def get_negative_outstanding_invoices(party_type, party, party_account, total_field):
+	voucher_type = "Sales Invoice" if party_type == "Customer" else "Purchase Invoice"
+	return frappe.db.sql("""
+		select
+			"{voucher_type}" as voucher_type, name as voucher_no, 
+			{total_field} as invoice_amount, outstanding_amount, posting_date, 
+			due_date, conversion_rate as exchange_rate
+		from
+			`tab{voucher_type}`
+		where
+			{party_type} = %s and {party_account} = %s and docstatus = 1 and outstanding_amount < 0
+		order by
+			posting_date, name
+		""".format(**{
+			"total_field": total_field,
+			"voucher_type": voucher_type,
+			"party_type": scrub(party_type),
+			"party_account": "debit_to" if party_type=="Customer" else "credit_to"
+		}), (party, party_account), as_dict = True)
+	
 @frappe.whitelist()
 def get_party_details(company, party_type, party, date):
+	if not frappe.db.exists(party_type, party):
+		frappe.throw(_("Invalid {0}: {1}").format(party_type, party))
+		
 	party_account = get_party_account(party_type, party, company)
 	
 	account_currency = get_account_currency(party_account)
@@ -479,10 +532,11 @@
 	}
 
 @frappe.whitelist()	
-def get_account_currency_and_balance(account, date):
+def get_account_details(account, date):
 	return frappe._dict({
 		"account_currency": get_account_currency(account),
-		"account_balance": get_balance_on(account, date)
+		"account_balance": get_balance_on(account, date),
+		"account_type": frappe.db.get_value("Account", account, "account_type")
 	})
 	
 @frappe.whitelist()
@@ -496,6 +550,34 @@
 				.format(frappe.get_meta("Company").get_label(fieldname), company))
 	
 	return ret
+
+@frappe.whitelist()
+def get_reference_details(reference_doctype, reference_name, party_account_currency):
+	total_amount = outstanding_amount = exchange_rate = None
+	
+	if reference_doctype != "Journal Entry":
+		ref_doc = frappe.get_doc(reference_doctype, reference_name)
+
+		if party_account_currency == ref_doc.company_currency:
+			total_amount = ref_doc.base_grand_total
+			exchange_rate = 1
+		else:
+			total_amount = ref_doc.grand_total
+			exchange_rate = ref_doc.get("conversion_rate") or \
+				get_exchange_rate(party_account_currency, ref_doc.company_currency)
+		
+		outstanding_amount = ref_doc.get("outstanding_amount") \
+			if reference_doctype in ("Sales Invoice", "Purchase Invoice") \
+			else flt(total_amount) - flt(ref_doc.advance_paid)			
+	else:
+		exchange_rate = get_exchange_rate(party_account_currency, ref_doc.company_currency)
+		
+	return frappe._dict({
+		"due_date": ref_doc.get("due_date"),
+		"total_amount": total_amount,
+		"outstanding_amount": outstanding_amount,
+		"exchange_rate": exchange_rate
+	})
 	
 @frappe.whitelist()
 def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=None):
@@ -541,13 +623,13 @@
 	
 	paid_amount = received_amount = 0
 	if party_account_currency == bank.account_currency:
-		paid_amount = received_amount = outstanding_amount
+		paid_amount = received_amount = abs(outstanding_amount)
 	elif payment_type == "Receive":
-		paid_amount = outstanding_amount
+		paid_amount = abs(outstanding_amount)
 		if bank_amount:
 			received_amount = bank_amount
 	else:
-		received_amount = outstanding_amount
+		received_amount = abs(outstanding_amount)
 		if bank_amount:
 			paid_amount = bank_amount
 	
diff --git a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py
index 624f405..8e45b3b 100644
--- a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py
@@ -7,7 +7,7 @@
 import unittest
 from frappe.utils import flt, nowdate
 from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
-from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
+from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry, InvalidPaymentEntry
 from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
 from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
 
@@ -149,6 +149,57 @@
 		
 		self.validate_gl_entries(pe.name, expected_gle)
 		
+	def test_payment_against_negative_sales_invoice(self):
+		pe1 = frappe.new_doc("Payment Entry")
+		pe1.payment_type = "Pay"
+		pe1.company = "_Test Company"
+		pe1.party_type = "Customer"
+		pe1.party = "_Test Customer"
+		pe1.paid_from = "_Test Cash - _TC"
+		pe1.paid_amount = 100
+		pe1.received_amount = 100
+		
+		self.assertRaises(InvalidPaymentEntry, pe1.validate)
+		
+		si1 =  create_sales_invoice()
+		
+		# create full payment entry against si1
+		pe2 = get_payment_entry("Sales Invoice", si1.name, bank_account="_Test Cash - _TC")
+		pe2.insert()
+		pe2.submit()
+		
+		# create return entry against si1
+		create_sales_invoice(is_return=1, return_against=si1.name, qty=-1)
+		si1_outstanding = frappe.db.get_value("Sales Invoice", si1.name, "outstanding_amount")
+		self.assertEqual(si1_outstanding, -100)
+		
+		# pay more than outstanding against si1
+		pe3 = get_payment_entry("Sales Invoice", si1.name, bank_account="_Test Cash - _TC")
+		pe3.paid_amount = pe3.received_amount = 300
+		self.assertRaises(InvalidPaymentEntry, pe3.validate)
+		
+		# pay negative outstanding against si1
+		pe3.paid_to = "Debtors - _TC"
+		pe3.paid_amount = pe3.received_amount = 100
+		
+		pe3.insert()
+		pe3.submit()
+		
+		expected_gle = dict((d[0], d) for d in [
+			["Debtors - _TC", 100, 0, si1.name],
+			["_Test Cash - _TC", 0, 100, None]
+		])
+
+		self.validate_gl_entries(pe3.name, expected_gle)
+
+		outstanding_amount = flt(frappe.db.get_value("Sales Invoice", si1.name, "outstanding_amount"))
+		self.assertEqual(outstanding_amount, 0)
+
+		pe3.cancel()
+		self.assertFalse(self.get_gle(pe3.name))
+
+		outstanding_amount = flt(frappe.db.get_value("Sales Invoice", si1.name, "outstanding_amount"))
+		self.assertEqual(outstanding_amount, -100)
 		
 	def validate_gl_entries(self, voucher_no, expected_gle):
 		gl_entries = self.get_gle(voucher_no)
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
index e919c4b..8aae29e 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
@@ -34,7 +34,7 @@
 		}
 
 		if(!doc.is_return && doc.docstatus==1) {
-			if(doc.outstanding_amount > 0) {
+			if(doc.outstanding_amount != 0) {
 				this.frm.add_custom_button(__('Payment'), this.make_payment_entry, __("Make"));
 				cur_frm.page.set_inner_btn_group_as_primary(__("Make"));
 			}
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
index 95ad98a..4bc634a 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
@@ -65,10 +65,14 @@
 				}
 			}
 
-			if(doc.outstanding_amount>0 && !cint(doc.is_return)) {
-				cur_frm.add_custom_button(__('Payment Request'), this.make_payment_request, __("Make"));
+			if(doc.outstanding_amount!=0 && !cint(doc.is_return)) {
 				cur_frm.add_custom_button(__('Payment'), this.make_payment_entry, __("Make"));
 			}
+			
+			if(doc.outstanding_amount>0 && !cint(doc.is_return)) {
+				cur_frm.add_custom_button(__('Payment Request'), this.make_payment_request, __("Make"));
+			}
+			
 
 		}
 
diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py
index b7fb053..e155d8e 100644
--- a/erpnext/accounts/general_ledger.py
+++ b/erpnext/accounts/general_ledger.py
@@ -160,7 +160,6 @@
 
 	gl_map.append(round_off_gle)
 
-
 def delete_gl_entries(gl_entries=None, voucher_type=None, voucher_no=None,
 		adv_adj=False, update_outstanding="Yes"):
 
@@ -168,8 +167,12 @@
 		check_freezing_date, update_outstanding_amt, validate_frozen_account
 
 	if not gl_entries:
-		gl_entries = frappe.db.sql("""select * from `tabGL Entry`
+		gl_entries = frappe.db.sql("""
+			select account, posting_date, party_type, party, 
+			voucher_type, voucher_no, against_voucher_type, against_voucher
+			from `tabGL Entry`
 			where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no), as_dict=True)
+
 	if gl_entries:
 		check_freezing_date(gl_entries[0]["posting_date"], adv_adj)
 
@@ -180,7 +183,7 @@
 		validate_frozen_account(entry["account"], adv_adj)
 		validate_balance_type(entry["account"], adv_adj)
 		validate_expense_against_budget(entry)
-
+		
 		if entry.get("against_voucher") and update_outstanding == 'Yes':
 			update_outstanding_amt(entry["account"], entry.get("party_type"), entry.get("party"), entry.get("against_voucher_type"),
 				entry.get("against_voucher"), on_cancel=True)
diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py
index f430b99..c65a845 100644
--- a/erpnext/accounts/party.py
+++ b/erpnext/accounts/party.py
@@ -254,7 +254,7 @@
 
 		if doc.default_currency and party_account_currency and company_default_currency:
 			if doc.default_currency != party_account_currency and doc.default_currency != company_default_currency:
-				frappe.throw(_("Billing currency must be equal to either default comapany's currency or party's payble account currency"))
+				frappe.throw(_("Billing currency must be equal to either default comapany's currency or party account currency"))
 
 @frappe.whitelist()
 def get_due_date(posting_date, party_type, party, company):
diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py
index afc4d98..6bd5eb1 100644
--- a/erpnext/accounts/utils.py
+++ b/erpnext/accounts/utils.py
@@ -458,9 +458,8 @@
 			and {dr_or_cr} > 0
 			{condition}
 			and ((voucher_type = 'Journal Entry'
-					and (against_voucher = ''
-						or against_voucher is null))
-				or (voucher_type != 'Journal Entry'))
+					and (against_voucher = '' or against_voucher is null))
+				or (voucher_type not in ('Journal Entry', 'Payment Entry')))
 		group by voucher_type, voucher_no
 		having (invoice_amount - payment_amount) > 0.005
 		order by posting_date, name""".format(
@@ -471,10 +470,9 @@
 			"party_type": party_type,
 			"party": party,
 			"account": account,
-		}, as_dict=True, debug=1)
+		}, as_dict=True)
 
 	for d in invoice_list:
-		print d.voucher_no, d.invoice_amount, d.payment_amount
 		outstanding_invoices.append(frappe._dict({
 			'voucher_no': d.voucher_no,
 			'voucher_type': d.voucher_type,
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index f49b050..1379048 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -995,6 +995,7 @@
 			callback: function(r) {
 				var doclist = frappe.model.sync(r.message);
 				frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
+				// cur_frm.refresh_fields()
 			}
 		});
 	}