Landed cost voucher: allow negative stock while doing cancellation entry for purchase receipts
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index 1535575..7fed736 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -197,9 +197,9 @@
 		sl_dict.update(args)
 		return sl_dict
 
-	def make_sl_entries(self, sl_entries, is_amended=None):
+	def make_sl_entries(self, sl_entries, is_amended=None, allow_negative_stock=False):
 		from erpnext.stock.stock_ledger import make_sl_entries
-		make_sl_entries(sl_entries, is_amended)
+		make_sl_entries(sl_entries, is_amended, allow_negative_stock)
 
 	def make_gl_entries_on_cancel(self):
 		if frappe.db.sql("""select name from `tabGL Entry` where voucher_type=%s
diff --git a/erpnext/stock/doctype/bin/bin.py b/erpnext/stock/doctype/bin/bin.py
index e3269e8..1fb1e2d 100644
--- a/erpnext/stock/doctype/bin/bin.py
+++ b/erpnext/stock/doctype/bin/bin.py
@@ -23,7 +23,7 @@
 			if (not getattr(self, f, None)) or (not self.get(f)):
 				self.set(f, 0.0)
 
-	def update_stock(self, args):
+	def update_stock(self, args, allow_negative_stock=False):
 		self.update_qty(args)
 
 		if args.get("actual_qty") or args.get("voucher_type") == "Stock Reconciliation":
@@ -38,7 +38,7 @@
 				"warehouse": self.warehouse,
 				"posting_date": args.get("posting_date"),
 				"posting_time": args.get("posting_time")
-			})
+			}, allow_negative_stock=allow_negative_stock)
 
 	def update_qty(self, args):
 		# update the stock values (for current quantities)
diff --git a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py
index 7bafcf6..16f0f1c 100644
--- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py
+++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py
@@ -93,13 +93,10 @@
 			# as those fields are allowed to edit after submit
 			pr.save()
 
-			# delete stock ledger entries & gl entries for cancelled state of PR
-
-			frappe.db.sql("""delete from `tabStock Ledger Entry`
-				where voucher_type='Purchase Receipt' and voucher_no=%s""", pr.name)
-
-			frappe.db.sql("""delete from `tabGL Entry`
-				where voucher_type='Purchase Receipt' and voucher_no=%s""", pr.name)
+			# update stock & gl entries for cancelled state of PR
+			pr.docstatus = 2
+			pr.update_stock_ledger(allow_negative_stock=True)
+			pr.make_gl_entries_on_cancel()
 
 			# update stock & gl entries for submit state of PR
 			pr.docstatus = 1
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
index f38ee5d..e04abbb 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -129,7 +129,7 @@
 				 if not d.prevdoc_docname:
 					 frappe.throw(_("Purchase Order number required for Item {0}").format(d.item_code))
 
-	def update_stock_ledger(self):
+	def update_stock_ledger(self, allow_negative_stock=False):
 		sl_entries = []
 		stock_items = self.get_stock_items()
 
@@ -153,7 +153,7 @@
 					}))
 
 		self.bk_flush_supp_wh(sl_entries)
-		self.make_sl_entries(sl_entries)
+		self.make_sl_entries(sl_entries, allow_negative_stock=allow_negative_stock)
 
 	def update_ordered_qty(self):
 		po_map = {}
diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py
index eae1bf6..7bbf8fc 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -14,7 +14,7 @@
 _exceptions = frappe.local('stockledger_exceptions')
 # _exceptions = []
 
-def make_sl_entries(sl_entries, is_amended=None):
+def make_sl_entries(sl_entries, is_amended=None, allow_negative_stock=False):
 	if sl_entries:
 		from erpnext.stock.utils import update_bin
 
@@ -35,7 +35,7 @@
 				"sle_id": sle_id,
 				"is_amended": is_amended
 			})
-			update_bin(args)
+			update_bin(args, allow_negative_stock)
 
 		if cancel:
 			delete_cancelled_entry(sl_entries[0].get('voucher_type'), sl_entries[0].get('voucher_no'))
@@ -58,7 +58,7 @@
 	frappe.db.sql("""delete from `tabStock Ledger Entry`
 		where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no))
 
-def update_entries_after(args, allow_zero_rate=False, verbose=1):
+def update_entries_after(args, allow_zero_rate=False, allow_negative_stock=False, verbose=1):
 	"""
 		update valution rate and qty after transaction
 		from the current time-bucket onwards
@@ -73,6 +73,9 @@
 	if not _exceptions:
 		frappe.local.stockledger_exceptions = []
 
+	if not allow_negative_stock:
+		allow_negative_stock = cint(frappe.db.get_default("allow_negative_stock"))
+
 	previous_sle = get_sle_before_datetime(args)
 
 	qty_after_transaction = flt(previous_sle.get("qty_after_transaction"))
@@ -87,7 +90,7 @@
 	stock_value_difference = 0.0
 
 	for sle in entries_to_fix:
-		if sle.serial_no or not cint(frappe.db.get_default("allow_negative_stock")):
+		if sle.serial_no or not allow_negative_stock:
 			# validate negative stock for serialized items, fifo valuation
 			# or when negative stock is not allowed for moving average
 			if not validate_negative_stock(qty_after_transaction, sle):
diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py
index 889c30c..c08ed7d 100644
--- a/erpnext/stock/utils.py
+++ b/erpnext/stock/utils.py
@@ -52,11 +52,11 @@
 	bin_obj.ignore_permissions = True
 	return bin_obj
 
-def update_bin(args):
+def update_bin(args, allow_negative_stock=False):
 	is_stock_item = frappe.db.get_value('Item', args.get("item_code"), 'is_stock_item')
 	if is_stock_item == 'Yes':
 		bin = get_bin(args.get("item_code"), args.get("warehouse"))
-		bin.update_stock(args)
+		bin.update_stock(args, allow_negative_stock)
 		return bin
 	else:
 		frappe.msgprint(_("Item {0} ignored since it is not a stock item").format(args.get("item_code")))