Block negative stock in perpetual inventory
diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py
index f0890dd..7280322 100644
--- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py
+++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py
@@ -14,6 +14,9 @@
 		frappe.db.set_default("auto_accounting_for_stock", self.auto_accounting_for_stock)
 
 		if cint(self.auto_accounting_for_stock):
+			if cint(frappe.db.get_value("Stock Settings", None, "allow_negative_stock")):
+				frappe.throw(_("Negative stock is not allowed in case of Perpetual Inventory, please disable it from Stock Settings"))
+
 			# set default perpetual account in company
 			for company in frappe.db.sql("select name from tabCompany"):
 				frappe.get_doc("Company", company[0]).save()
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index a2bf78c..31f7113 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -7,7 +7,7 @@
 from frappe.utils import cint, cstr, flt
 from frappe import _, msgprint, throw
 from erpnext.accounts.party import get_party_account, get_due_date
-from erpnext.controllers.stock_controller import update_gl_entries_after
+from erpnext.controllers.stock_controller import update_gl_entries_after, block_negative_stock
 from frappe.model.mapper import get_mapped_doc
 
 from erpnext.controllers.selling_controller import SellingController
@@ -456,8 +456,8 @@
 
 		self.make_sl_entries(sl_entries)
 
-	def make_gl_entries(self, repost_future_gle=True):
-		gl_entries = self.get_gl_entries()
+	def make_gl_entries(self, repost_future_gle=True, allow_negative_stock=False):
+		gl_entries = self.get_gl_entries(allow_negative_stock=allow_negative_stock)
 
 		if gl_entries:
 			from erpnext.accounts.general_ledger import make_gl_entries
@@ -476,7 +476,7 @@
 					items, warehouses = self.get_items_and_warehouses()
 					update_gl_entries_after(self.posting_date, self.posting_time, warehouses, items)
 
-	def get_gl_entries(self, warehouse_account=None):
+	def get_gl_entries(self, warehouse_account=None, allow_negative_stock=False):
 		from erpnext.accounts.general_ledger import merge_similar_entries
 
 		gl_entries = []
@@ -485,7 +485,7 @@
 
 		self.make_tax_gl_entries(gl_entries)
 
-		self.make_item_gl_entries(gl_entries)
+		self.make_item_gl_entries(gl_entries, allow_negative_stock)
 
 		# merge gl entries before adding pos entries
 		gl_entries = merge_similar_entries(gl_entries)
@@ -520,7 +520,7 @@
 					})
 				)
 
-	def make_item_gl_entries(self, gl_entries):
+	def make_item_gl_entries(self, gl_entries, allow_negative_stock=False):
 		# income account gl entries
 		for item in self.get("entries"):
 			if flt(item.base_amount):
@@ -537,7 +537,7 @@
 		# 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()
+			gl_entries += super(SalesInvoice, self).get_gl_entries(allow_negative_stock=allow_negative_stock)
 
 	def make_pos_gl_entries(self, gl_entries):
 		if cint(self.is_pos) and self.cash_bank_account and self.paid_amount:
diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py
index 2114768..073ef8a 100644
--- a/erpnext/accounts/general_ledger.py
+++ b/erpnext/accounts/general_ledger.py
@@ -97,8 +97,7 @@
 
 		for entry in gl_map:
 			if entry.account in aii_accounts:
-				frappe.throw(_("Account: {0} can only be updated via \
-					Stock Transactions").format(entry.account), StockAccountInvalidTransaction)
+				frappe.throw(_("Account: {0} can only be updated via Stock Transactions").format(entry.account), StockAccountInvalidTransaction)
 
 
 def delete_gl_entries(gl_entries=None, voucher_type=None, voucher_no=None,
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index 5755253..304ded2 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -8,10 +8,10 @@
 import frappe.defaults
 
 from erpnext.controllers.accounts_controller import AccountsController
-from erpnext.accounts.general_ledger import make_gl_entries, delete_gl_entries
+from erpnext.accounts.general_ledger import make_gl_entries, delete_gl_entries, process_gl_map
 
 class StockController(AccountsController):
-	def make_gl_entries(self, repost_future_gle=True):
+	def make_gl_entries(self, repost_future_gle=True, allow_negative_stock=False):
 		if self.docstatus == 2:
 			delete_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
 
@@ -19,16 +19,19 @@
 			warehouse_account = get_warehouse_account()
 
 			if self.docstatus==1:
-				gl_entries = self.get_gl_entries(warehouse_account)
+				gl_entries = self.get_gl_entries(warehouse_account, allow_negative_stock)
 				make_gl_entries(gl_entries)
 
 			if repost_future_gle:
 				items, warehouses = self.get_items_and_warehouses()
-				update_gl_entries_after(self.posting_date, self.posting_time, warehouses, items, warehouse_account)
+				update_gl_entries_after(self.posting_date, self.posting_time, warehouses, items,
+					warehouse_account, allow_negative_stock)
 
 	def get_gl_entries(self, warehouse_account=None, default_expense_account=None,
-			default_cost_center=None):
-		from erpnext.accounts.general_ledger import process_gl_map
+			default_cost_center=None, allow_negative_stock=False):
+
+		block_negative_stock(allow_negative_stock)
+
 		if not warehouse_account:
 			warehouse_account = get_warehouse_account()
 
@@ -46,12 +49,17 @@
 
 						self.check_expense_account(detail)
 
+						stock_value_difference = flt(sle.stock_value_difference, 2)
+						if not stock_value_difference:
+							valuation_rate = get_valuation_rate(sle.item_code, sle.warehouse, sle.posting_date)
+							stock_value_difference = flt(sle.qty)*flt(valuation_rate)
+
 						gl_list.append(self.get_gl_dict({
 							"account": warehouse_account[sle.warehouse],
 							"against": detail.expense_account,
 							"cost_center": detail.cost_center,
 							"remarks": self.get("remarks") or "Accounting Entry for Stock",
-							"debit": flt(sle.stock_value_difference, 2)
+							"debit": stock_value_difference
 						}))
 
 						# to target warehouse / expense account
@@ -60,7 +68,7 @@
 							"against": warehouse_account[sle.warehouse],
 							"cost_center": detail.cost_center,
 							"remarks": self.get("remarks") or "Accounting Entry for Stock",
-							"credit": flt(sle.stock_value_difference, 2)
+							"credit": stock_value_difference
 						}))
 					elif sle.warehouse not in warehouse_with_no_account:
 						warehouse_with_no_account.append(sle.warehouse)
@@ -214,7 +222,8 @@
 
 		return serialized_items
 
-def update_gl_entries_after(posting_date, posting_time, for_warehouses=None, for_items=None, warehouse_account=None):
+def update_gl_entries_after(posting_date, posting_time, for_warehouses=None, for_items=None,
+		warehouse_account=None, allow_negative_stock=False):
 	def _delete_gl_entries(voucher_type, voucher_no):
 		frappe.db.sql("""delete from `tabGL Entry`
 			where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no))
@@ -228,12 +237,12 @@
 	for voucher_type, voucher_no in future_stock_vouchers:
 		existing_gle = gle.get((voucher_type, voucher_no), [])
 		voucher_obj = frappe.get_doc(voucher_type, voucher_no)
-		expected_gle = voucher_obj.get_gl_entries(warehouse_account)
+		expected_gle = voucher_obj.get_gl_entries(warehouse_account, allow_negative_stock)
 		if expected_gle:
 			if not existing_gle or not compare_existing_and_expected_gle(existing_gle,
 				expected_gle):
 					_delete_gl_entries(voucher_type, voucher_no)
-					voucher_obj.make_gl_entries(repost_future_gle=False)
+					voucher_obj.make_gl_entries(repost_future_gle=False, allow_negative_stock=allow_negative_stock)
 		else:
 			_delete_gl_entries(voucher_type, voucher_no)
 
@@ -285,3 +294,22 @@
 	warehouse_account = dict(frappe.db.sql("""select master_name, name from tabAccount
 		where account_type = 'Warehouse' and ifnull(master_name, '') != ''"""))
 	return warehouse_account
+
+def block_negative_stock(allow_negative_stock=False):
+	if cint(frappe.defaults.get_global_default("auto_accounting_for_stock")) and not allow_negative_stock:
+		if cint(frappe.db.get_value("Stock Settings", None, "allow_negative_stock")):
+			frappe.throw(_("Negative stock is not allowed in case of Perpetual Inventory, please disable it from Stock Settings"))
+
+def get_valuation_rate(item_code, warehouse, posting_date):
+	last_valuation_rate = frappe.db.sql("""select valuation_rate
+		from `tabStock Ledger Entry`
+		where item_code = %s and warehouse = %s
+		and ifnull(qty_after_transaction, 0) > 0 and posting_date < %s
+		order by posting_date desc limit 1""", (item_code, warehouse, posting_date))
+
+	valuation_rate = flt(last_valuation_rate[0][0]) if last_valuation_rate else 0
+
+	if not valuation_rate:
+		valuation_rate = frappe.db.get_value("Item Price", {"item_code": item_code, "buying": 1}, "price")
+
+	return valuation_rate
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
index 2aa9ab6..8e837e2 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
@@ -203,12 +203,12 @@
 				"posting_time": self.posting_time
 			})
 
-	def get_gl_entries(self, warehouse_account=None):
+	def get_gl_entries(self, warehouse_account=None, allow_negative_stock=False):
 		if not self.cost_center:
 			msgprint(_("Please enter Cost Center"), raise_exception=1)
 
 		return super(StockReconciliation, self).get_gl_entries(warehouse_account,
-			self.expense_account, self.cost_center)
+			self.expense_account, self.cost_center, allow_negative_stock=allow_negative_stock)
 
 	def validate_expense_account(self):
 		if not cint(frappe.defaults.get_global_default("auto_accounting_for_stock")):
diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.py b/erpnext/stock/doctype/stock_settings/stock_settings.py
index b505394..95ace86 100644
--- a/erpnext/stock/doctype/stock_settings/stock_settings.py
+++ b/erpnext/stock/doctype/stock_settings/stock_settings.py
@@ -6,18 +6,20 @@
 from __future__ import unicode_literals
 import frappe
 from frappe import _
-
+from frappe.utils import cint
 from frappe.model.document import Document
 
 class StockSettings(Document):
 
 	def validate(self):
-		for key in ["item_naming_by", "item_group", "stock_uom",
-			"allow_negative_stock"]:
+		if cint(self.allow_negative_stock) and cint(frappe.defaults.get_global_default("auto_accounting_for_stock")):
+			frappe.throw(_("Negative stock is not allowed in case of Perpetual Inventory"))
+
+		for key in ["item_naming_by", "item_group", "stock_uom", "allow_negative_stock"]:
 			frappe.db.set_default(key, self.get(key, ""))
-			
+
 		from erpnext.setup.doctype.naming_series.naming_series import set_by_naming_series
-		set_by_naming_series("Item", "item_code", 
+		set_by_naming_series("Item", "item_code",
 			self.get("item_naming_by")=="Naming Series", hide_name_field=True)
 
 		stock_frozen_limit = 356
@@ -25,3 +27,5 @@
 		if submitted_stock_frozen > stock_frozen_limit:
 			self.stock_frozen_upto_days = stock_frozen_limit
 			frappe.msgprint (_("`Freeze Stocks Older Than` should be smaller than %d days.") %stock_frozen_limit)
+
+
diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py
index b7c2074..e8a84c2 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -36,9 +36,9 @@
 				"is_amended": is_amended
 			})
 			update_bin(args)
+
 		if cancel:
-			delete_cancelled_entry(sl_entries[0].get('voucher_type'),
-				sl_entries[0].get('voucher_no'))
+			delete_cancelled_entry(sl_entries[0].get('voucher_type'), sl_entries[0].get('voucher_no'))
 
 def set_as_cancel(voucher_type, voucher_no):
 	frappe.db.sql("""update `tabStock Ledger Entry` set is_cancelled='Yes',