Sales / Purchase Return redesigned via negative DN / SI / PR / PI
diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py
index 3d306fb..edee122 100644
--- a/erpnext/accounts/doctype/gl_entry/gl_entry.py
+++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py
@@ -4,7 +4,7 @@
 from __future__ import unicode_literals
 import frappe
 
-from frappe.utils import flt, fmt_money, getdate, formatdate, cstr
+from frappe.utils import flt, fmt_money, getdate, formatdate, cstr, cint
 from frappe import _
 
 from frappe.model.document import Document
@@ -139,9 +139,9 @@
 		if against_voucher_amount < 0:
 			bal = -bal
 
-	# Validation : Outstanding can not be negative
-	if bal < 0 and not on_cancel:
-		frappe.throw(_("Outstanding for {0} cannot be less than zero ({1})").format(against_voucher, fmt_money(bal)))
+		# Validation : Outstanding can not be negative for JV
+		if bal < 0 and not on_cancel:
+			frappe.throw(_("Outstanding for {0} cannot be less than zero ({1})").format(against_voucher, fmt_money(bal)))
 
 	# Update outstanding amt on against voucher
 	if against_voucher_type in ["Sales Invoice", "Purchase Invoice"]:
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
index 07dbf72..548abb7 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
@@ -25,6 +25,9 @@
 				frappe.boot.doctype_icons["Journal Entry"]);
 
 		if(doc.docstatus==1) {
+			cur_frm.add_custom_button(__('Make Purchase Return'), this.make_purchase_return,
+				frappe.boot.doctype_icons["Purchase Invoice"]);
+			
 			cur_frm.add_custom_button(__('View Ledger'), function() {
 				frappe.route_options = {
 					"voucher_no": doc.name,
@@ -109,7 +112,14 @@
 		$.each(this.frm.doc["items"] || [], function(i, row) {
 			if(row.purchase_receipt) frappe.model.clear_doc("Purchase Receipt", row.purchase_receipt)
 		})
-	}
+	},
+	
+	make_purchase_return: function() {
+		frappe.model.open_mapped_doc({
+			method: "erpnext.accounts.doctype.purchase_invoice.purchase_invoice.make_purchase_return",
+			frm: cur_frm
+		})
+	},
 });
 
 cur_frm.script_manager.make(erpnext.accounts.PurchaseInvoice);
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
index 69b0708..c579716 100755
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
@@ -12,7 +12,7 @@
    "no_copy": 1, 
    "oldfieldname": "naming_series", 
    "oldfieldtype": "Select", 
-   "options": "PINV-", 
+   "options": "PINV-\nPINV-RET-", 
    "permlevel": 0, 
    "print_hide": 1, 
    "read_only": 0, 
@@ -155,6 +155,28 @@
    "search_index": 0
   }, 
   {
+   "fieldname": "is_return", 
+   "fieldtype": "Check", 
+   "label": "Is Return", 
+   "no_copy": 1, 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 1, 
+   "read_only": 1
+  }, 
+  {
+   "depends_on": "is_return", 
+   "fieldname": "return_against", 
+   "fieldtype": "Link", 
+   "label": "Return Against Purchase Invoice", 
+   "no_copy": 1, 
+   "options": "Purchase Invoice", 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 1, 
+   "read_only": 1
+  }, 
+  {
    "fieldname": "currency_and_price_list", 
    "fieldtype": "Section Break", 
    "label": "", 
@@ -940,7 +962,7 @@
  "icon": "icon-file-text", 
  "idx": 1, 
  "is_submittable": 1, 
- "modified": "2015-07-03 03:26:32.934540", 
+ "modified": "2015-07-17 14:09:19.666457", 
  "modified_by": "Administrator", 
  "module": "Accounts", 
  "name": "Purchase Invoice", 
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
index 1ac0f5a..b34f845 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -37,14 +37,16 @@
 
 		super(PurchaseInvoice, self).validate()
 
-		self.po_required()
-		self.pr_required()
-		self.validate_supplier_invoice()
+		if not self.is_return:
+			self.po_required()
+			self.pr_required()
+			self.validate_supplier_invoice()
+			self.validate_advance_jv("advances", "purchase_order")
+			
 		self.check_active_purchase_items()
 		self.check_conversion_rate()
 		self.validate_credit_to_acc()
 		self.clear_unallocated_advances("Purchase Invoice Advance", "advances")
-		self.validate_advance_jv("advances", "purchase_order")
 		self.check_for_stopped_status()
 		self.validate_with_previous_doc()
 		self.validate_uom_is_integer("uom", "qty")
@@ -71,8 +73,9 @@
 		super(PurchaseInvoice, self).set_missing_values(for_validate)
 
 	def get_advances(self):
-		super(PurchaseInvoice, self).get_advances(self.credit_to, "Supplier", self.supplier,
-			"Purchase Invoice Advance", "advances", "debit", "purchase_order")
+		if not self.is_return:
+			super(PurchaseInvoice, self).get_advances(self.credit_to, "Supplier", self.supplier,
+				"Purchase Invoice Advance", "advances", "debit", "purchase_order")
 
 	def check_active_purchase_items(self):
 		for d in self.get('items'):
@@ -226,9 +229,11 @@
 
 		# this sequence because outstanding may get -negative
 		self.make_gl_entries()
-		self.update_against_document_in_jv()
-		self.update_prevdoc_status()
-		self.update_billing_status_for_zero_amount_refdoc("Purchase Order")
+		if not self.is_return:
+			self.update_against_document_in_jv()
+			self.update_prevdoc_status()
+			self.update_billing_status_for_zero_amount_refdoc("Purchase Order")
+			
 		self.update_project()
 
 	def make_gl_entries(self):
@@ -358,11 +363,12 @@
 			make_gl_entries(gl_entries, cancel=(self.docstatus == 2))
 
 	def on_cancel(self):
-		from erpnext.accounts.utils import remove_against_link_from_jv
-		remove_against_link_from_jv(self.doctype, self.name, "against_voucher")
+		if not self.is_return:
+			from erpnext.accounts.utils import remove_against_link_from_jv
+			remove_against_link_from_jv(self.doctype, self.name, "against_voucher")
 
-		self.update_prevdoc_status()
-		self.update_billing_status_for_zero_amount_refdoc("Purchase Order")
+			self.update_prevdoc_status()
+			self.update_billing_status_for_zero_amount_refdoc("Purchase Order")
 		self.make_gl_entries_on_cancel()
 		self.update_project()
 
@@ -403,3 +409,8 @@
 				and tabAccount.%(key)s LIKE '%(txt)s'
 				%(mcond)s""" % {'company': filters['company'], 'key': searchfield,
 			'txt': "%%%s%%" % frappe.db.escape(txt), 'mcond':get_match_cond(doctype)})
+
+@frappe.whitelist()
+def make_purchase_return(source_name, target_doc=None):
+	from erpnext.utilities.transaction_base import make_return_doc
+	return make_return_doc("Purchase Invoice", source_name, target_doc)	
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
index 5b2f348..8daf3f6 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
@@ -53,9 +53,6 @@
 				frappe.set_route("query-report", "General Ledger");
 			}, "icon-table");
 
-			// var percent_paid = cint(flt(doc.base_grand_total - doc.outstanding_amount) / flt(doc.base_grand_total) * 100);
-			// cur_frm.dashboard.add_progress(percent_paid + "% Paid", percent_paid);
-
 			if(cint(doc.update_stock)!=1) {
 				// show Make Delivery Note button only if Sales Invoice is not created from Delivery Note
 				var from_delivery_note = false;
@@ -69,9 +66,12 @@
 				}
 			}
 
-			if(doc.outstanding_amount!=0) {
+			if(doc.outstanding_amount!=0 && !cint(doc.is_return)) {
 				cur_frm.add_custom_button(__('Make Payment Entry'), cur_frm.cscript.make_bank_entry, "icon-money");
 			}
+			
+			cur_frm.add_custom_button(__('Make Sales Return'), this.make_sales_return,
+				frappe.boot.doctype_icons["Sales Invoice"]);
 		}
 
 		// Show buttons only when pos view is active
@@ -205,8 +205,14 @@
 
 	items_on_form_rendered: function() {
 		erpnext.setup_serial_no();
+	},
+	
+	make_sales_return: function() {
+		frappe.model.open_mapped_doc({
+			method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.make_sales_return",
+			frm: cur_frm
+		})
 	}
-
 });
 
 // for backward compatibility: combine new and previous states
@@ -283,16 +289,6 @@
 	});
 }
 
-cur_frm.fields_dict.debit_to.get_query = function(doc) {
-	return{
-		filters: {
-			'report_type': 'Balance Sheet',
-			'is_group': 0,
-			'company': doc.company
-		}
-	}
-}
-
 cur_frm.fields_dict.cash_bank_account.get_query = function(doc) {
 	return {
 		filters: [
@@ -399,4 +395,4 @@
 			['Account', 'account_type', '=', 'Receivable']
 		]
 	}
-});
+});
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
index 25dd398..b983d99 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
@@ -21,7 +21,7 @@
    "no_copy": 1, 
    "oldfieldname": "naming_series", 
    "oldfieldtype": "Select", 
-   "options": "SINV-", 
+   "options": "SINV-\nSINV-RET-", 
    "permlevel": 0, 
    "print_hide": 1, 
    "read_only": 0, 
@@ -170,6 +170,28 @@
    "read_only": 0
   }, 
   {
+   "fieldname": "is_return", 
+   "fieldtype": "Check", 
+   "label": "Is Return", 
+   "no_copy": 1, 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 1, 
+   "read_only": 1
+  }, 
+  {
+   "depends_on": "is_return", 
+   "fieldname": "return_against", 
+   "fieldtype": "Link", 
+   "label": "Return Against Sales Invoice", 
+   "no_copy": 1, 
+   "options": "Sales Invoice", 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 1, 
+   "read_only": 1
+  }, 
+  {
    "fieldname": "shipping_address_name", 
    "fieldtype": "Link", 
    "hidden": 1, 
@@ -1252,8 +1274,8 @@
  ], 
  "icon": "icon-file-text", 
  "idx": 1, 
- "is_submittable": 1,
- "modified": "2015-07-09 17:33:28.583808", 
+ "is_submittable": 1, 
+ "modified": "2015-07-17 13:29:36.922418", 
  "modified_by": "Administrator", 
  "module": "Accounts", 
  "name": "Sales Invoice", 
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index 829478d..9129e1f 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -80,14 +80,16 @@
 
 		self.check_prev_docstatus()
 
-		self.update_status_updater_args()
-		self.update_prevdoc_status()
-		self.update_billing_status_for_zero_amount_refdoc("Sales Order")
-		self.check_credit_limit()
+		if not self.is_return:
+			self.update_status_updater_args()
+			self.update_prevdoc_status()
+			self.update_billing_status_for_zero_amount_refdoc("Sales Order")
+			self.check_credit_limit()
+			
 		# this sequence because outstanding may get -ve
 		self.make_gl_entries()
 
-		if not cint(self.is_pos) == 1:
+		if not cint(self.is_pos) == 1 and not self.is_return:
 			self.update_against_document_in_jv()
 
 		self.update_time_log_batch(self.name)
@@ -100,13 +102,15 @@
 			self.update_stock_ledger()
 
 		self.check_stop_sales_order("sales_order")
-
+		
 		from erpnext.accounts.utils import remove_against_link_from_jv
 		remove_against_link_from_jv(self.doctype, self.name, "against_invoice")
-
-		self.update_status_updater_args()
-		self.update_prevdoc_status()
-		self.update_billing_status_for_zero_amount_refdoc("Sales Order")
+		
+		if not self.is_return:
+			self.update_status_updater_args()
+			self.update_prevdoc_status()
+			self.update_billing_status_for_zero_amount_refdoc("Sales Order")
+			
 		self.validate_c_form_on_cancel()
 
 		self.make_gl_entries_on_cancel()
@@ -199,8 +203,9 @@
 				self.set_taxes()
 
 	def get_advances(self):
-		super(SalesInvoice, self).get_advances(self.debit_to, "Customer", self.customer,
-			"Sales Invoice Advance", "advances", "credit", "sales_order")
+		if not self.is_return:
+			super(SalesInvoice, self).get_advances(self.debit_to, "Customer", self.customer,
+				"Sales Invoice Advance", "advances", "credit", "sales_order")
 
 	def get_company_abbr(self):
 		return frappe.db.sql("select abbr from tabCompany where name=%s", self.company)[0][0]
@@ -285,6 +290,8 @@
 
 	def so_dn_required(self):
 		"""check in manage account if sales order / delivery note required or not."""
+		if self.is_return:
+			return
 		dic = {'Sales Order':'so_required','Delivery Note':'dn_required'}
 		for i in dic:
 			if frappe.db.get_value('Selling Settings', None, dic[i]) == 'Yes':
@@ -419,13 +426,16 @@
 	def update_stock_ledger(self):
 		sl_entries = []
 		for d in self.get_item_list():
-			if frappe.db.get_value("Item", d.item_code, "is_stock_item") == "Yes" \
-					and d.warehouse:
+			if frappe.db.get_value("Item", d.item_code, "is_stock_item") == "Yes" and d.warehouse:
+				incoming_rate = 0
+				if cint(self.is_return) and self.return_against and self.docstatus==1:
+					incoming_rate = self.get_incoming_rate_for_sales_return(d.item_code, self.return_against)
+					
 				sl_entries.append(self.get_sl_entries(d, {
 					"actual_qty": -1*flt(d.qty),
-					"stock_uom": frappe.db.get_value("Item", d.item_code, "stock_uom")
+					"stock_uom": frappe.db.get_value("Item", d.item_code, "stock_uom"),
+					"incoming_rate": incoming_rate
 				}))
-
 		self.make_sl_entries(sl_entries)
 
 	def make_gl_entries(self, repost_future_gle=True):
@@ -435,8 +445,7 @@
 			from erpnext.accounts.general_ledger import make_gl_entries
 
 			# if POS and amount is written off, there's no outstanding and hence no need to update it
-			update_outstanding = cint(self.is_pos) and self.write_off_account \
-				and 'No' or 'Yes'
+			update_outstanding = "No" if (cint(self.is_pos) or self.write_off_account) else "Yes"
 
 			make_gl_entries(gl_entries, cancel=(self.docstatus == 2),
 				update_outstanding=update_outstanding, merge_entries=False)
@@ -484,7 +493,7 @@
 					"against": self.against_income_account,
 					"debit": self.base_grand_total,
 					"remarks": self.remarks,
-					"against_voucher": self.name,
+					"against_voucher": self.against_invoice if cint(self.is_return) else self.name,
 					"against_voucher_type": self.doctype
 				})
 			)
@@ -519,7 +528,6 @@
 		# expense account gl entries
 		if cint(frappe.defaults.get_global_default("auto_accounting_for_stock")) \
 				and cint(self.update_stock):
-
 			gl_entries += super(SalesInvoice, self).get_gl_entries()
 
 	def make_pos_gl_entries(self, gl_entries):
@@ -533,7 +541,7 @@
 					"against": self.cash_bank_account,
 					"credit": self.paid_amount,
 					"remarks": self.remarks,
-					"against_voucher": self.name,
+					"against_voucher": self.against_invoice if cint(self.is_return) else self.name,
 					"against_voucher_type": self.doctype,
 				})
 			)
@@ -557,7 +565,7 @@
 					"against": self.write_off_account,
 					"credit": self.write_off_amount,
 					"remarks": self.remarks,
-					"against_voucher": self.name,
+					"against_voucher": self.against_invoice if cint(self.is_return) else self.name,
 					"against_voucher_type": self.doctype,
 				})
 			)
@@ -651,3 +659,9 @@
 	}, target_doc, set_missing_values)
 
 	return doclist
+
+
+@frappe.whitelist()
+def make_sales_return(source_name, target_doc=None):
+	from erpnext.utilities.transaction_base import make_return_doc
+	return make_return_doc("Sales Invoice", source_name, target_doc)
\ No newline at end of file
diff --git a/erpnext/buying/doctype/purchase_common/purchase_common.js b/erpnext/buying/doctype/purchase_common/purchase_common.js
index 1b7d20a..19ad9ab 100644
--- a/erpnext/buying/doctype/purchase_common/purchase_common.js
+++ b/erpnext/buying/doctype/purchase_common/purchase_common.js
@@ -164,8 +164,10 @@
 			frappe.model.round_floats_in(this.frm.doc, ["base_grand_total", "total_advance", "write_off_amount"]);
 			this.frm.doc.total_amount_to_pay = flt(this.frm.doc.base_grand_total - this.frm.doc.write_off_amount,
 				precision("total_amount_to_pay"));
-			this.frm.doc.outstanding_amount = flt(this.frm.doc.total_amount_to_pay - this.frm.doc.total_advance,
-				precision("outstanding_amount"));
+			if (!this.frm.doc.is_return) {
+				this.frm.doc.outstanding_amount = flt(this.frm.doc.total_amount_to_pay - this.frm.doc.total_advance,
+					precision("outstanding_amount"));
+			}
 		}
 	}
 });
diff --git a/erpnext/buying/doctype/purchase_common/purchase_common.py b/erpnext/buying/doctype/purchase_common/purchase_common.py
index 476aa92..1bf6f8f 100644
--- a/erpnext/buying/doctype/purchase_common/purchase_common.py
+++ b/erpnext/buying/doctype/purchase_common/purchase_common.py
@@ -41,8 +41,7 @@
 	def validate_for_items(self, obj):
 		items = []
 		for d in obj.get("items"):
-			# validation for valid qty
-			if flt(d.qty) < 0 or (d.parenttype != 'Purchase Receipt' and not flt(d.qty)):
+			if not d.qty:
 				frappe.throw(_("Please enter quantity for Item {0}").format(d.item_code))
 
 			# udpate with latest quantities
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index 98f2409..d1ce3c6 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -4,12 +4,15 @@
 from __future__ import unicode_literals
 import frappe
 from frappe import _, throw
-from frappe.utils import today, flt, cint
+from frappe.utils import today, flt, cint, format_datetime, get_datetime
 from erpnext.setup.utils import get_company_currency, get_exchange_rate
 from erpnext.accounts.utils import get_fiscal_year, validate_fiscal_year
 from erpnext.utilities.transaction_base import TransactionBase
 from erpnext.controllers.recurring_document import convert_to_recurring, validate_recurring_document
 
+class StockOverReturnError(frappe.ValidationError): pass
+
+
 class AccountsController(TransactionBase):
 	def validate(self):
 		if self.get("_action") and self._action != "update_after_submit":
@@ -17,10 +20,14 @@
 		self.validate_date_with_fiscal_year()
 		if self.meta.get_field("currency"):
 			self.calculate_taxes_and_totals()
-			self.validate_value("base_grand_total", ">=", 0)
+			if not self.meta.get_field("is_return") or not self.is_return:
+				self.validate_value("base_grand_total", ">=", 0)
+			
+			self.validate_return_doc()
 			self.set_total_in_words()
 
-		self.validate_due_date()
+		if not self.is_return:
+			self.validate_due_date()
 
 		if self.meta.get_field("is_recurring"):
 			validate_recurring_document(self)
@@ -51,6 +58,94 @@
 					self.fiscal_year = get_fiscal_year(self.get(fieldname))[0]
 				break
 
+	def validate_return_doc(self):
+		if not self.meta.get_field("is_return") or not self.is_return:
+			return
+			
+		self.validate_return_against()
+		self.validate_returned_items()
+			
+	def validate_return_against(self):
+		if not self.return_against:
+			frappe.throw(_("{0} is mandatory for Return").format(self.meta.get_label("return_against")))
+		else:
+			filters = {"doctype": self.doctype, "docstatus": 1, "company": self.company}
+			if self.meta.get_field("customer"):
+				filters["customer"] = self.customer
+			elif self.meta.get_field("supplier"):
+				filters["supplier"] = self.supplier
+				
+			if not frappe.db.exists(filters):
+					frappe.throw(_("Invalid {0}: {1}")
+						.format(self.meta.get_label("return_against"), self.return_against))
+			else:
+				ref_doc = frappe.get_doc(self.doctype, self.return_against)
+					
+				# validate posting date time
+				return_posting_datetime = "%s %s" % (self.posting_date, self.get("posting_time") or "00:00:00")
+				ref_posting_datetime = "%s %s" % (ref_doc.posting_date, ref_doc.get("posting_time") or "00:00:00")
+				
+				if get_datetime(return_posting_datetime) < get_datetime(ref_posting_datetime):
+					frappe.throw(_("Posting timestamp must be after {0}")
+						.format(datetime_in_user_format(ref_posting_datetime)))
+				
+				# validate same exchange rate
+				if self.conversion_rate != ref_doc.conversion_rate:
+					frappe.throw(_("Exchange Rate must be same as {0} {1} ({2})")
+						.format(self.doctype, self.return_against, ref_doc.conversion_rate))
+						
+				# validate update stock
+				if self.doctype == "Sales Invoice" and self.update_stock \
+					and not frappe.db.get_value("Sales Invoice", self.return_against, "update_stock"):
+						frappe.throw(_("'Update Stock' can not be checked because items are not delivered via {0}")
+							.format(self.return_against))
+					
+	def validate_returned_items(self):
+		valid_items = frappe._dict()
+		for d in frappe.db.sql("""select item_code, sum(qty) as qty, rate from `tab{0} Item` 
+			where parent = %s group by item_code""".format(self.doctype), self.return_against, as_dict=1):
+				valid_items.setdefault(d.item_code, d)
+				
+		already_returned_items = self.get_already_returned_items()
+		
+		items_returned = False
+		for d in self.get("items"):
+			if flt(d.qty) < 0:
+				if d.item_code not in valid_items:
+					frappe.throw(_("Row # {0}: Returned Item {1} does not exists in {2} {3}")
+						.format(d.idx, d.item_code, self.doctype, self.return_against))
+				else:
+					ref = valid_items.get(d.item_code, frappe._dict())
+					already_returned_qty = flt(already_returned_items.get(d.item_code))
+					max_return_qty = flt(ref.qty) - already_returned_qty
+					
+					if already_returned_qty >= ref.qty:
+						frappe.throw(_("Item {0} has already been returned").format(d.item_code), StockOverReturnError)
+					elif abs(d.qty) > max_return_qty:
+						frappe.throw(_("Row # {0}: Cannot return more than {1} for Item {2}")
+							.format(d.idx, ref.qty, d.item_code), StockOverReturnError)
+					elif flt(d.rate) != ref.rate:
+						frappe.throw(_("Row # {0}: Rate must be same as {1} {2}")
+							.format(d.idx, self.doctype, self.return_against))
+					
+				
+				items_returned = True
+				
+		if not items_returned:
+			frappe.throw(_("Atleast one item should be entered with negative quantity in return document"))
+			
+	def get_already_returned_items(self):
+		return frappe._dict(frappe.db.sql("""
+			select 
+				child.item_code, sum(abs(child.qty)) as qty
+			from 
+				`tab{0} Item` child, `tab{1}` par 
+			where 
+				child.parent = par.name and par.docstatus = 1
+				and ifnull(par.is_return, 0) = 1 and par.return_against = %s and child.qty < 0 
+			group by item_code
+		""".format(self.doctype, self.doctype), self.return_against))
+
 	def calculate_taxes_and_totals(self):
 		from erpnext.controllers.taxes_and_totals import calculate_taxes_and_totals
 		calculate_taxes_and_totals(self)
diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py
index 9867973..0b60473 100644
--- a/erpnext/controllers/buying_controller.py
+++ b/erpnext/controllers/buying_controller.py
@@ -26,8 +26,7 @@
 	def validate(self):
 		super(BuyingController, self).validate()
 		if getattr(self, "supplier", None) and not self.supplier_name:
-			self.supplier_name = frappe.db.get_value("Supplier",
-				self.supplier, "supplier_name")
+			self.supplier_name = frappe.db.get_value("Supplier", self.supplier, "supplier_name")
 		self.is_item_table_empty()
 		self.set_qty_as_per_stock_uom()
 		self.validate_stock_or_nonstock_items()
diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py
index b2a9f03..01ef605 100644
--- a/erpnext/controllers/selling_controller.py
+++ b/erpnext/controllers/selling_controller.py
@@ -175,7 +175,7 @@
 				if flt(d.qty) > flt(d.delivered_qty):
 					reserved_qty_for_main_item = flt(d.qty) - flt(d.delivered_qty)
 
-			elif self.doctype == "Delivery Note" and d.against_sales_order:
+			elif self.doctype == "Delivery Note" and d.against_sales_order and not self.is_return:
 				# if SO qty is 10 and there is tolerance of 20%, then it will allow DN of 12.
 				# But in this case reserved qty should only be reduced by 10 and not 12
 
@@ -211,7 +211,7 @@
 					'qty': d.qty,
 					'reserved_qty': reserved_qty_for_main_item,
 					'uom': d.stock_uom,
-                                        'stock_uom': d.stock_uom,
+					'stock_uom': d.stock_uom,
 					'batch_no': cstr(d.get("batch_no")).strip(),
 					'serial_no': cstr(d.get("serial_no")).strip(),
 					'name': d.name
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index 6678007..19440e2 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -216,6 +216,17 @@
 				tuple(item_codes))
 
 		return serialized_items
+		
+	def get_incoming_rate_for_sales_return(self, item_code, against_document):
+		incoming_rate = 0.0
+		if against_document and item_code:
+			incoming_rate = frappe.db.sql("""select abs(ifnull(stock_value_difference, 0) / actual_qty)
+				from `tabStock Ledger Entry`
+				where voucher_type = %s and voucher_no = %s and item_code = %s limit 1""",
+				(self.doctype, against_document, item_code))
+			incoming_rate = incoming_rate[0][0] if incoming_rate else 0.0
+
+		return incoming_rate
 
 def update_gl_entries_after(posting_date, posting_time, for_warehouses=None, for_items=None,
 		warehouse_account=None):
diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py
index e77a9a6..f22b624 100644
--- a/erpnext/controllers/taxes_and_totals.py
+++ b/erpnext/controllers/taxes_and_totals.py
@@ -77,6 +77,9 @@
 			if not self.discount_amount_applied:
 				validate_taxes_and_charges(tax)
 				validate_inclusive_tax(tax, self.doc)
+				
+			if self.doc.meta.get_field("is_return") and self.doc.is_return and tax.charge_type == "Actual":
+				tax.tax_amount = -1 * tax.tax_amount
 
 			tax.item_wise_tax_detail = {}
 			tax_fields = ["total", "tax_amount_after_discount_amount",
@@ -396,13 +399,15 @@
 		# total_advance is only for non POS Invoice
 
 		if self.doc.doctype == "Sales Invoice":
-			self.doc.round_floats_in(self.doc, ["base_grand_total", "total_advance", "write_off_amount", "paid_amount"])
-			total_amount_to_pay = self.doc.base_grand_total - self.doc.write_off_amount
-			self.doc.outstanding_amount = flt(total_amount_to_pay - self.doc.total_advance - self.doc.paid_amount,
-				self.doc.precision("outstanding_amount"))
+			if not self.doc.is_return:
+				self.doc.round_floats_in(self.doc, ["base_grand_total", "total_advance", "write_off_amount", "paid_amount"])
+				total_amount_to_pay = self.doc.base_grand_total - self.doc.write_off_amount
+				self.doc.outstanding_amount = flt(total_amount_to_pay - self.doc.total_advance - self.doc.paid_amount,
+					self.doc.precision("outstanding_amount"))
 		else:
 			self.doc.round_floats_in(self.doc, ["total_advance", "write_off_amount"])
 			self.doc.total_amount_to_pay = flt(self.doc.base_grand_total - self.doc.write_off_amount,
 				self.doc.precision("total_amount_to_pay"))
-			self.doc.outstanding_amount = flt(self.doc.total_amount_to_pay - self.doc.total_advance,
-				self.doc.precision("outstanding_amount"))
+			if not self.doc.is_return:
+				self.doc.outstanding_amount = flt(self.doc.total_amount_to_pay - self.doc.total_advance,
+					self.doc.precision("outstanding_amount"))
diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js
index 4a26d6d..07c2d56 100644
--- a/erpnext/public/js/controllers/taxes_and_totals.js
+++ b/erpnext/public/js/controllers/taxes_and_totals.js
@@ -13,8 +13,9 @@
 			this.apply_discount_amount();
 
 		// Advance calculation applicable to Sales /Purchase Invoice
-		if(in_list(["Sales Invoice", "Purchase Invoice"], this.frm.doc.doctype) && this.frm.doc.docstatus < 2) {
-			this.calculate_total_advance(update_paid_amount);
+		if(in_list(["Sales Invoice", "Purchase Invoice"], this.frm.doc.doctype)
+			 && this.frm.doc.docstatus < 2 && !this.frm.doc.is_return) {
+				 this.calculate_total_advance(update_paid_amount);
 		}
 
 		// Sales person's commission
@@ -93,6 +94,10 @@
 			tax_fields = ["total", "tax_amount_after_discount_amount",
 				"tax_amount_for_current_item", "grand_total_for_current_item",
 				"tax_fraction_for_current_item", "grand_total_fraction_for_current_item"]
+			
+			if (frappe.meta.get_docfield(me.frm.doc.doctype, "is_return") && me.frm.doc.is_return 
+					&& tax.charge_type == "Actual")
+				tax.tax_amount = -1 * tax.tax_amount;
 
 			if (cstr(tax.charge_type) != "Actual" &&
 				!(me.discount_amount_applied && me.frm.doc.apply_discount_on=="Grand Total"))
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index 0a75dad..01e5781 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -46,6 +46,23 @@
 				}
 			});
 		}
+		
+		if(this.frm.fields_dict["return_against"]) {
+			this.frm.set_query("return_against", function(doc) {
+				var filters = {
+					"docstatus": 1,
+					"is_return": 0,
+					"company": doc.company
+				};
+				if (me.frm.fields_dict["customer"] && doc.customer) filters["customer"] = doc.customer;
+				if (me.frm.fields_dict["supplier"] && doc.supplier) filters["supplier"] = doc.supplier;
+	
+				return {
+					filters: filters
+				}
+			});
+		}
+		
 	},
 
 	onload_post_render: function() {
@@ -354,7 +371,8 @@
 	plc_conversion_rate: function() {
 		if(this.frm.doc.price_list_currency === this.get_company_currency()) {
 			this.frm.set_value("plc_conversion_rate", 1.0);
-		} else if(this.frm.doc.price_list_currency === this.frm.doc.currency && 			this.frm.doc.plc_conversion_rate && cint(this.frm.doc.plc_conversion_rate) != 1 &&
+		} else if(this.frm.doc.price_list_currency === this.frm.doc.currency 
+			&& this.frm.doc.plc_conversion_rate && cint(this.frm.doc.plc_conversion_rate) != 1 &&
 			cint(this.frm.doc.plc_conversion_rate) != cint(this.frm.doc.conversion_rate)) {
 				this.frm.set_value("conversion_rate", this.frm.doc.plc_conversion_rate);
 		}
diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js
index f3cd8a7..e8d8fd5 100644
--- a/erpnext/selling/sales_common.js
+++ b/erpnext/selling/sales_common.js
@@ -210,7 +210,7 @@
 		// NOTE:
 		// paid_amount and write_off_amount is only for POS Invoice
 		// total_advance is only for non POS Invoice
-		if(this.frm.doc.doctype == "Sales Invoice" && this.frm.doc.docstatus==0) {
+		if(this.frm.doc.doctype == "Sales Invoice" && this.frm.doc.docstatus==0 && !this.frm.doc.is_return) {
 			frappe.model.round_floats_in(this.frm.doc, ["base_grand_total", "total_advance", "write_off_amount",
 				"paid_amount"]);
 			var total_amount_to_pay = this.frm.doc.base_grand_total - this.frm.doc.write_off_amount
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.js b/erpnext/stock/doctype/delivery_note/delivery_note.js
index 631009f..26adf4e 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.js
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.js
@@ -24,7 +24,9 @@
 			cur_frm.add_custom_button(__('Make Installation Note'), this.make_installation_note);
 
 		if (doc.docstatus==1) {
-
+			cur_frm.add_custom_button(__('Make Sales Return'), this.make_sales_return,
+				frappe.boot.doctype_icons["Delivery Note"]);
+				
 			this.show_stock_ledger();
 			this.show_general_ledger();
 		}
@@ -73,6 +75,13 @@
 			frm: cur_frm
 		});
 	},
+	
+	make_sales_return: function() {
+		frappe.model.open_mapped_doc({
+			method: "erpnext.stock.doctype.delivery_note.delivery_note.make_sales_return",
+			frm: cur_frm
+		})
+	},
 
 	tc_name: function() {
 		this.get_terms();
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.json b/erpnext/stock/doctype/delivery_note/delivery_note.json
index 72a7227..89da480 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.json
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.json
@@ -29,7 +29,7 @@
    "no_copy": 1, 
    "oldfieldname": "naming_series", 
    "oldfieldtype": "Select", 
-   "options": "DN-", 
+   "options": "DN-\nDN-RET-", 
    "permlevel": 0, 
    "print_hide": 1, 
    "read_only": 0, 
@@ -206,6 +206,28 @@
    "width": "100px"
   }, 
   {
+   "fieldname": "is_return", 
+   "fieldtype": "Check", 
+   "label": "Is Return", 
+   "no_copy": 1, 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 1, 
+   "read_only": 1
+  }, 
+  {
+   "depends_on": "is_return", 
+   "fieldname": "return_against", 
+   "fieldtype": "Link", 
+   "label": "Return Against Delivery Note", 
+   "no_copy": 1, 
+   "options": "Delivery Note", 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 1, 
+   "read_only": 1
+  }, 
+  {
    "fieldname": "cusrrency_and_price_list", 
    "fieldtype": "Section Break", 
    "label": "", 
@@ -1070,7 +1092,7 @@
  "idx": 1, 
  "in_create": 0, 
  "is_submittable": 1, 
- "modified": "2015-07-13 05:28:29.814096", 
+ "modified": "2015-07-17 13:29:28.019506", 
  "modified_by": "Administrator", 
  "module": "Stock", 
  "name": "Delivery Note", 
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py
index 90a8a6c..cd501da 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.py
@@ -84,7 +84,7 @@
 
 	def so_required(self):
 		"""check in manage account if sales order required or not"""
-		if frappe.db.get_value("Selling Settings", None, 'so_required') == 'Yes':
+		if not self.is_return and frappe.db.get_value("Selling Settings", None, 'so_required') == 'Yes':
 			 for d in self.get('items'):
 				 if not d.against_sales_order:
 					 frappe.throw(_("Sales Order required for Item {0}").format(d.item_code))
@@ -175,17 +175,15 @@
 		# Check for Approving Authority
 		frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype, self.company, self.base_grand_total, self)
 
-		# update delivered qty in sales order
-		self.update_prevdoc_status()
+		if not self.is_return:
+			# update delivered qty in sales order
+			self.update_prevdoc_status()
 
-		self.check_credit_limit()
+			self.check_credit_limit()
 
-		# create stock ledger entry
 		self.update_stock_ledger()
-
 		self.make_gl_entries()
 
-		# set DN status
 		frappe.db.set(self, 'status', 'Submitted')
 
 
@@ -193,7 +191,8 @@
 		self.check_stop_sales_order("against_sales_order")
 		self.check_next_docstatus()
 
-		self.update_prevdoc_status()
+		if not self.is_return:
+			self.update_prevdoc_status()
 
 		self.update_stock_ledger()
 
@@ -251,9 +250,14 @@
 			if frappe.db.get_value("Item", d.item_code, "is_stock_item") == "Yes" \
 					and d.warehouse and flt(d['qty']):
 				self.update_reserved_qty(d)
-
+				
+				incoming_rate = 0
+				if cint(self.is_return) and self.return_against and self.docstatus==1:
+					incoming_rate = self.get_incoming_rate_for_sales_return(d.item_code, self.return_against)
+					
 				sl_entries.append(self.get_sl_entries(d, {
 					"actual_qty": -1*flt(d['qty']),
+					incoming_rate: incoming_rate
 				}))
 
 		self.make_sl_entries(sl_entries)
@@ -387,3 +391,9 @@
 	}, target_doc)
 
 	return doclist
+
+	
+@frappe.whitelist()
+def make_sales_return(source_name, target_doc=None):
+	from erpnext.utilities.transaction_base import make_return_doc
+	return make_return_doc("Delivery Note", source_name, target_doc)
\ No newline at end of file
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
index fe41b4f..727d38e 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
@@ -34,6 +34,9 @@
 				cur_frm.add_custom_button(__('Make Purchase Invoice'), this.make_purchase_invoice,
 					frappe.boot.doctype_icons["Purchase Invoice"]);
 			}
+			
+			cur_frm.add_custom_button(__('Make Purchase Return'), this.make_purchase_return,
+				frappe.boot.doctype_icons["Purchase Receipt"]);
 
 			this.show_stock_ledger();
 			this.show_general_ledger();
@@ -105,6 +108,13 @@
 			frm: cur_frm
 		})
 	},
+	
+	make_purchase_return: function() {
+		frappe.model.open_mapped_doc({
+			method: "erpnext.stock.doctype.purchase_receipt.purchase_receipt.make_purchase_return",
+			frm: cur_frm
+		})
+	},
 
 	tc_name: function() {
 		this.get_terms();
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
index 6e344b6..c44923a 100755
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
@@ -21,13 +21,14 @@
    "width": "50%"
   }, 
   {
+   "default": "", 
    "fieldname": "naming_series", 
    "fieldtype": "Select", 
    "label": "Series", 
    "no_copy": 1, 
    "oldfieldname": "naming_series", 
    "oldfieldtype": "Select", 
-   "options": "PREC-", 
+   "options": "PREC-\nPREC-RET-", 
    "permlevel": 0, 
    "print_hide": 1, 
    "reqd": 1
@@ -131,6 +132,28 @@
    "width": "100px"
   }, 
   {
+   "fieldname": "is_return", 
+   "fieldtype": "Check", 
+   "label": "Is Return", 
+   "no_copy": 1, 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 1, 
+   "read_only": 1
+  }, 
+  {
+   "depends_on": "is_return", 
+   "fieldname": "return_against", 
+   "fieldtype": "Link", 
+   "label": "Return Against Purchase Receipt", 
+   "no_copy": 1, 
+   "options": "Purchase Receipt", 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 1, 
+   "read_only": 1
+  }, 
+  {
    "fieldname": "currency_and_price_list", 
    "fieldtype": "Section Break", 
    "label": "", 
@@ -854,7 +877,7 @@
  "icon": "icon-truck", 
  "idx": 1, 
  "is_submittable": 1, 
- "modified": "2015-07-13 05:28:27.389559", 
+ "modified": "2015-07-17 13:29:10.298448", 
  "modified_by": "Administrator", 
  "module": "Stock", 
  "name": "Purchase Receipt", 
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
index e782889..31a2f50 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -44,6 +44,7 @@
 		self.set_status()
 		self.po_required()
 		self.validate_with_previous_doc()
+		self.validate_purchase_return()
 		self.validate_rejected_warehouse()
 		self.validate_accepted_rejected_qty()
 		self.validate_inspection()
@@ -60,12 +61,21 @@
 		self.set_landed_cost_voucher_amount()
 		self.update_valuation_rate("items")
 
+
 	def set_landed_cost_voucher_amount(self):
 		for d in self.get("items"):
 			lc_voucher_amount = frappe.db.sql("""select sum(ifnull(applicable_charges, 0))
 				from `tabLanded Cost Item`
 				where docstatus = 1 and purchase_receipt_item = %s""", d.name)
 			d.landed_cost_voucher_amount = lc_voucher_amount[0][0] if lc_voucher_amount else 0.0
+			
+	def validate_purchase_return(self):
+		for d in self.get("items"):
+			print flt(d.rejected_qty)
+			if self.is_return and flt(d.rejected_qty) != 0:
+				frappe.throw(_("Row #{0}: Rejected Qty can not be entered in Purchase Return").format(d.idx))
+				
+			# validate rate with ref PR
 
 	def validate_rejected_warehouse(self):
 		for d in self.get("items"):
@@ -108,7 +118,7 @@
 			self.validate_rate_with_reference_doc([["Purchase Order", "prevdoc_docname", "prevdoc_detail_docname"]])
 
 	def po_required(self):
-		if frappe.db.get_value("Buying Settings", None, "po_required") == 'Yes':
+		if not self.is_return and frappe.db.get_value("Buying Settings", None, "po_required") == 'Yes':
 			 for d in self.get('items'):
 				 if not d.prevdoc_docname:
 					 frappe.throw(_("Purchase Order number required for Item {0}").format(d.item_code))
@@ -123,11 +133,20 @@
 
 				if pr_qty:
 					val_rate_db_precision = 6 if cint(self.precision("valuation_rate", d)) <= 6 else 9
-					sl_entries.append(self.get_sl_entries(d, {
+					rate = flt(d.valuation_rate, val_rate_db_precision)
+					sle = self.get_sl_entries(d, {
 						"actual_qty": flt(pr_qty),
-						"serial_no": cstr(d.serial_no).strip(),
-						"incoming_rate": flt(d.valuation_rate, val_rate_db_precision)
-					}))
+						"serial_no": cstr(d.serial_no).strip()
+					})
+					if self.is_return:
+						sle.update({
+							"outgoing_rate": rate
+						})
+					else:
+						sle.update({
+							"incoming_rate": rate
+						})
+					sl_entries.append(sle)
 
 				if flt(d.rejected_qty) > 0:
 					sl_entries.append(self.get_sl_entries(d, {
@@ -176,7 +195,6 @@
 				"item_code": d.rm_item_code,
 				"warehouse": self.supplier_warehouse,
 				"actual_qty": -1*flt(d.consumed_qty),
-				"incoming_rate": 0
 			}))
 
 	def validate_inspection(self):
@@ -207,17 +225,16 @@
 		# Set status as Submitted
 		frappe.db.set(self, 'status', 'Submitted')
 
-		self.update_prevdoc_status()
-
-		self.update_ordered_qty()
+		if not self.is_return:
+			self.update_prevdoc_status()
+			self.update_ordered_qty()
+			purchase_controller.update_last_purchase_rate(self, 1)
 
 		self.update_stock_ledger()
 
 		from erpnext.stock.doctype.serial_no.serial_no import update_serial_nos_after_submit
 		update_serial_nos_after_submit(self, "items")
 
-		purchase_controller.update_last_purchase_rate(self, 1)
-
 		self.make_gl_entries()
 
 	def check_next_docstatus(self):
@@ -244,12 +261,13 @@
 
 		self.update_stock_ledger()
 
-		self.update_prevdoc_status()
+		if not self.is_return:
+			self.update_prevdoc_status()
 
-		# Must be called after updating received qty in PO
-		self.update_ordered_qty()
+			# Must be called after updating received qty in PO
+			self.update_ordered_qty()
 
-		pc_obj.update_last_purchase_rate(self, 0)
+			pc_obj.update_last_purchase_rate(self, 0)
 
 		self.make_gl_entries_on_cancel()
 
@@ -417,7 +435,7 @@
 			"doctype": "Purchase Invoice",
 			"validation": {
 				"docstatus": ["=", 1],
-			}
+			},
 		},
 		"Purchase Receipt Item": {
 			"doctype": "Purchase Invoice Item",
@@ -449,3 +467,8 @@
 			invoiced_qty_map[pr_detail] += qty
 
 	return invoiced_qty_map
+
+@frappe.whitelist()
+def make_purchase_return(source_name, target_doc=None):
+	from erpnext.utilities.transaction_base import make_return_doc
+	return make_return_doc("Purchase Receipt", source_name, target_doc)
\ No newline at end of file
diff --git a/erpnext/utilities/transaction_base.py b/erpnext/utilities/transaction_base.py
index 50b0319..fd2aaab 100644
--- a/erpnext/utilities/transaction_base.py
+++ b/erpnext/utilities/transaction_base.py
@@ -128,3 +128,36 @@
 				if d.get(f):
 					if cint(d.get(f))!=d.get(f):
 						frappe.throw(_("Quantity cannot be a fraction in row {0}").format(d.idx), UOMMustBeIntegerError)
+
+def make_return_doc(doctype, source_name, target_doc=None):
+	from frappe.model.mapper import get_mapped_doc
+	def set_missing_values(source, target):
+		doc = frappe.get_doc(target)
+		doc.is_return = 1
+		doc.return_against = source.name
+		doc.ignore_pricing_rule = 1
+		doc.run_method("calculate_taxes_and_totals")
+
+	def update_item(source_doc, target_doc, source_parent):
+		target_doc.qty = -1* source_doc.qty
+		if doctype == "Purchase Receipt":
+			target_doc.received_qty = -1* source_doc.qty
+		elif doctype == "Purchase Invoice":
+			target_doc.purchase_receipt = source_doc.purchase_receipt
+			target_doc.pr_detail = source_doc.pr_detail
+
+	doclist = get_mapped_doc(doctype, source_name,	{
+		doctype: {
+			"doctype": doctype,
+			
+			"validation": {
+				"docstatus": ["=", 1],
+			}
+		},
+		doctype +" Item": {
+			"doctype": doctype + " Item",
+			"postprocess": update_item
+		},
+	}, target_doc, set_missing_values)
+
+	return doclist