feat: Repost item costing (#24183)

* Repost item valuation (#24031)

* feat: Reposting logic for future finished/transferred item

* feat: added fields to identify needs to recalculate rate while reposting

* refactor: Set rate for outgoing and finished items

* refactor: Arranged fields in Stock Entry item table and added fields to identify finished and scrap item

* refactor: Arranged fields in Stock Entry item table and added fields to identify finished and scrap item

* refactor: Get outgoing rate for purchase return

* refactor: Get incoming rate for sales return

* test: Added tests for reposting valuation of transferred/finished/returned items

* feat: added incoming rate field in DN, SI and Packed Item table

* feat: get incoming rate for returned item

* fix: no error while getting valuation rate in stock entry

* fix: update stock ledger for DN and SI

* feat: update item valuation rate in PR and PI based on supplied items cost

* feat: SLE reposting logic for sales return and subcontracted item with test cases

* feat: update qty in future sle

* feat: repost future sle and gle via Repost Item Valuation

* fix: Skip unwanted function calling while reposting

* fix: repost sle for specific item and warehouse

* test: Modified tests for backdated stock reco

* fix: ignore cancelled sle in few methods

* feat: role allowed to do backdated entry

* feat: Show reposting status on stock valuation related reports

* fix: minor fixes

* fix: fixed sider issues

* fix: serial no fix related to immutable ledger

* fix: Test cases fixes related to perpetual inventory

* fix: Test cases fixed

* fix: Fixed reposting on cancel and test cases

* feat: Restart reposting item valuation

* refactor: Code cleanup using small functions and test case fixes

* fix: minor fixes

* fix: Raise on error while reposting item valuation

* fix: minor fix

* fix: Tests fixed

* fix: skip some validation ig gle made from reposting

* fix: test fixes

* fix: debugging stock and account validation

* fix: debugging stock and account validation

* fix: debugging travis for stock and account sync validation

* fix: debugging travis

* fix: debugging travis

* fix: debugging travis

* fix: removed duplicate field from pos profile
diff --git a/erpnext/stock/doctype/batch/test_batch.py b/erpnext/stock/doctype/batch/test_batch.py
index c2a3d3c..e41f1a8 100644
--- a/erpnext/stock/doctype/batch/test_batch.py
+++ b/erpnext/stock/doctype/batch/test_batch.py
@@ -8,13 +8,8 @@
 
 from erpnext.stock.doctype.batch.batch import get_batch_qty, UnableToSelectBatchError, get_batch_no
 from frappe.utils import cint, flt
-from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory
 
 class TestBatch(unittest.TestCase):
-
-	def setUp(self):
-		set_perpetual_inventory(0)
-
 	def test_item_has_batch_enabled(self):
 		self.assertRaises(ValidationError, frappe.get_doc({
 			"doctype": "Batch",
diff --git a/erpnext/stock/doctype/bin/bin.py b/erpnext/stock/doctype/bin/bin.py
index 7acdec7..ab19b77 100644
--- a/erpnext/stock/doctype/bin/bin.py
+++ b/erpnext/stock/doctype/bin/bin.py
@@ -16,22 +16,30 @@
 	def update_stock(self, args, allow_negative_stock=False, via_landed_cost_voucher=False):
 		'''Called from erpnext.stock.utils.update_bin'''
 		self.update_qty(args)
-
 		if args.get("actual_qty") or args.get("voucher_type") == "Stock Reconciliation":
-			from erpnext.stock.stock_ledger import update_entries_after
+			from erpnext.stock.stock_ledger import update_entries_after, update_qty_in_future_sle
 
 			if not args.get("posting_date"):
 				args["posting_date"] = nowdate()
 
+			if args.get("is_cancelled") and via_landed_cost_voucher:
+				return
+
+			# Reposts only current voucher SL Entries
+			# Updates valuation rate, stock value, stock queue for current transaction
 			update_entries_after({
 				"item_code": self.item_code,
 				"warehouse": self.warehouse,
 				"posting_date": args.get("posting_date"),
 				"posting_time": args.get("posting_time"),
+				"voucher_type": args.get("voucher_type"),
 				"voucher_no": args.get("voucher_no"),
-				"sle_id": args.sle_id
+				"sle_id": args.name
 			}, allow_negative_stock=allow_negative_stock, via_landed_cost_voucher=via_landed_cost_voucher)
 
+			# Update qty_after_transaction in future SLEs of this item and warehouse
+			update_qty_in_future_sle(args)
+
 	def update_qty(self, args):
 		# update the stock values (for current quantities)
 		if args.get("voucher_type")=="Stock Reconciliation":
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py
index 3f3407e..1a6a555 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.py
@@ -217,6 +217,7 @@
 		# because updating reserved qty in bin depends upon updated delivered qty in SO
 		self.update_stock_ledger()
 		self.make_gl_entries()
+		self.repost_future_sle_and_gle()
 
 	def on_cancel(self):
 		super(DeliveryNote, self).on_cancel()
@@ -234,7 +235,8 @@
 		self.cancel_packing_slips()
 
 		self.make_gl_entries_on_cancel()
-		self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry')
+		self.repost_future_sle_and_gle()
+		self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry', 'Repost Item Valuation')
 
 	def check_credit_limit(self):
 		from erpnext.selling.doctype.customer.customer import check_credit_limit
diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
index 6b4663a..559f8be 100644
--- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
@@ -10,8 +10,7 @@
 from frappe.utils import cint, nowdate, nowtime, cstr, add_days, flt, today
 from erpnext.stock.stock_ledger import get_previous_sle
 from erpnext.accounts.utils import get_balance_on
-from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt \
-	import get_gl_entries, set_perpetual_inventory
+from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import get_gl_entries
 from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_invoice, make_delivery_trip
 from erpnext.stock.doctype.stock_entry.test_stock_entry \
 	import make_stock_entry, make_serialized_item, get_qty_after_transaction
@@ -24,9 +23,6 @@
 from erpnext.stock.doctype.item.test_item import create_item
 
 class TestDeliveryNote(unittest.TestCase):
-	def setUp(self):
-		set_perpetual_inventory(0)
-
 	def test_over_billing_against_dn(self):
 		frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 1)
 
@@ -43,7 +39,6 @@
 
 	def test_delivery_note_no_gl_entry(self):
 		company = frappe.db.get_value('Warehouse', '_Test Warehouse - _TC', 'company')
-		set_perpetual_inventory(0, company)
 		make_stock_entry(target="_Test Warehouse - _TC", qty=5, basic_rate=100)
 
 		stock_queue = json.loads(get_previous_sle({
diff --git a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json
index 7b47187..4bbf3de 100644
--- a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json
+++ b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json
@@ -56,6 +56,7 @@
   "base_net_rate",
   "base_net_amount",
   "billed_amt",
+  "incoming_rate",
   "item_weight_details",
   "weight_per_unit",
   "total_weight",
@@ -732,16 +733,22 @@
    "depends_on": "returned_qty",
    "fieldname": "returned_qty",
    "fieldtype": "Float",
-   "label": "Returned Qty in Stock UOM",
+   "label": "Returned Qty in Stock UOM"
+  },
+  {
+   "fieldname": "incoming_rate",
+   "fieldtype": "Currency",
+   "label": "Incoming Rate",
    "no_copy": 1,
    "print_hide": 1,
    "read_only": 1
   }
  ],
  "idx": 1,
+ "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2020-07-31 20:12:43.054342",
+ "modified": "2020-12-07 19:59:27.119856",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Delivery Note Item",
diff --git a/erpnext/stock/doctype/item/test_records.json b/erpnext/stock/doctype/item/test_records.json
index 9ca887c..8f437b1 100644
--- a/erpnext/stock/doctype/item/test_records.json
+++ b/erpnext/stock/doctype/item/test_records.json
@@ -458,5 +458,15 @@
     "item_tax_template": "_Test Item Tax Template 1"
    }
   ]
+ },
+ {
+  "description": "_Test",
+  "doctype": "Item",
+  "is_stock_item": 1,
+  "item_code": "138-CMS Shoe",
+  "item_group": "_Test Item Group",
+  "item_name": "138-CMS Shoe",
+  "stock_uom": "_Test UOM",
+  "gst_hsn_code": "999800"
  }
 ]
diff --git a/erpnext/stock/doctype/item_alternative/test_item_alternative.py b/erpnext/stock/doctype/item_alternative/test_item_alternative.py
index f045e4f..d5700fe 100644
--- a/erpnext/stock/doctype/item_alternative/test_item_alternative.py
+++ b/erpnext/stock/doctype/item_alternative/test_item_alternative.py
@@ -12,11 +12,9 @@
 from erpnext.manufacturing.doctype.work_order.test_work_order import make_wo_order_test_record
 from erpnext.buying.doctype.purchase_order.purchase_order import make_purchase_receipt, make_rm_stock_entry
 import unittest
-from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory
 
 class TestItemAlternative(unittest.TestCase):
 	def setUp(self):
-		set_perpetual_inventory(0)
 		make_items()
 
 	def test_alternative_item_for_subcontract_rm(self):
diff --git a/erpnext/stock/doctype/landed_cost_taxes_and_charges/landed_cost_taxes_and_charges.json b/erpnext/stock/doctype/landed_cost_taxes_and_charges/landed_cost_taxes_and_charges.json
index 0cc243d..64331c7 100644
--- a/erpnext/stock/doctype/landed_cost_taxes_and_charges/landed_cost_taxes_and_charges.json
+++ b/erpnext/stock/doctype/landed_cost_taxes_and_charges/landed_cost_taxes_and_charges.json
@@ -1,4 +1,5 @@
 {
+ "actions": [],
  "creation": "2014-07-11 11:51:00.453717",
  "doctype": "DocType",
  "editable_grid": 1,
@@ -31,16 +32,19 @@
    "reqd": 1
   },
   {
+   "depends_on": "eval:cint(erpnext.is_perpetual_inventory_enabled(parent.company))",
    "fieldname": "expense_account",
    "fieldtype": "Link",
    "in_list_view": 1,
    "label": "Expense Account",
+   "mandatory_depends_on": "eval:cint(erpnext.is_perpetual_inventory_enabled(parent.company))",
    "options": "Account",
-   "reqd": 1
+   "print_hide": 1
   }
  ],
  "istable": 1,
- "modified": "2019-09-30 18:28:32.070655",
+ "links": [],
+ "modified": "2020-12-04 00:22:14.373312",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Landed Cost Taxes and Charges",
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 bc3d326..9ec6b89 100644
--- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py
+++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py
@@ -77,9 +77,9 @@
 		company_currency = erpnext.get_company_currency(self.company)
 		for account in self.taxes:
 			if get_account_currency(account.expense_account) != company_currency:
-				frappe.throw(msg=_(""" Row {0}: Expense account currency should be same as company's default currency.
-					Please select expense account with account currency as {1}""")
-					.format(account.idx, frappe.bold(company_currency)), title=_("Invalid Account Currency"))
+				frappe.throw(_("Row {}: Expense account currency should be same as company's default currency.").format(account.idx)
+					+ _("Please select expense account with account currency as {}.").format(frappe.bold(company_currency)),
+					title=_("Invalid Account Currency"))
 
 	def set_total_taxes_and_charges(self):
 		self.total_taxes_and_charges = sum([flt(d.amount) for d in self.get("taxes")])
@@ -121,7 +121,7 @@
 			doc.set_landed_cost_voucher_amount()
 
 			# set valuation amount in pr item
-			doc.update_valuation_rate("items")
+			doc.update_valuation_rate(reset_outgoing_rate=False)
 
 			# db_update will update and save landed_cost_voucher_amount and voucher_amount in PR
 			for item in doc.get("items"):
@@ -143,6 +143,7 @@
 			doc.docstatus = 1
 			doc.update_stock_ledger(allow_negative_stock=True, via_landed_cost_voucher=True)
 			doc.make_gl_entries()
+			doc.repost_future_sle_and_gle()
 
 	def validate_asset_qty_and_status(self, receipt_document_type, receipt_document):
 		for item in self.get('items'):
@@ -152,14 +153,13 @@
 				docs = frappe.db.get_all('Asset', filters={ receipt_document_type: item.receipt_document,
 					'item_code': item.item_code }, fields=['name', 'docstatus'])
 				if not docs or len(docs) != item.qty:
-					frappe.throw(_('There are not enough asset created or linked to {0}. \
-						Please create or link {1} Assets with respective document.').format(item.receipt_document, item.qty))
+					frappe.throw(_('There are not enough asset created or linked to {0}.').format(item.receipt_document)
+						+ _('Please create or link {0} Assets with respective document.').format(item.qty))
 				if docs:
 					for d in docs:
 						if d.docstatus == 1:
-							frappe.throw(_('{2} <b>{0}</b> has submitted Assets.\
-								Remove Item <b>{1}</b> from table to continue.').format(
-									item.receipt_document, item.item_code, item.receipt_document_type))
+							frappe.throw(_('{0} {1} has submitted Assets. Remove Item {2} from table to continue.')
+								.format(item.receipt_document_type, frappe.bold(item.receipt_document), frappe.bold(item.item_code)))
 
 	def update_rate_in_serial_no_for_non_asset_items(self, receipt_document):
 		for item in receipt_document.get("items"):
diff --git a/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py b/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py
index 3f2c5da..b97213e 100644
--- a/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py
+++ b/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py
@@ -7,7 +7,7 @@
 import frappe
 from frappe.utils import flt
 from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt \
-	import set_perpetual_inventory, get_gl_entries, test_records as pr_test_records, make_purchase_receipt
+	import get_gl_entries, test_records as pr_test_records, make_purchase_receipt
 from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
 from erpnext.accounts.doctype.account.test_account import get_inventory_account
 
@@ -27,7 +27,7 @@
 			},
 			fieldname=["qty_after_transaction", "stock_value"], as_dict=1)
 
-		submit_landed_cost_voucher("Purchase Receipt", pr.name, pr.company)
+		create_landed_cost_voucher("Purchase Receipt", pr.name, pr.company)
 
 		pr_lc_value = frappe.db.get_value("Purchase Receipt Item", {"parent": pr.name}, "landed_cost_voucher_amount")
 		self.assertEqual(pr_lc_value, 25.0)
@@ -89,7 +89,7 @@
 			},
 			fieldname=["qty_after_transaction", "stock_value"], as_dict=1)
 
-		submit_landed_cost_voucher("Purchase Invoice", pi.name, pi.company)
+		create_landed_cost_voucher("Purchase Invoice", pi.name, pi.company)
 
 		pi_lc_value = frappe.db.get_value("Purchase Invoice Item", {"parent": pi.name},
 			"landed_cost_voucher_amount")
@@ -137,7 +137,7 @@
 
 		serial_no_rate = frappe.db.get_value("Serial No", "SN001", "purchase_rate")
 
-		submit_landed_cost_voucher("Purchase Receipt", pr.name, pr.company)
+		create_landed_cost_voucher("Purchase Receipt", pr.name, pr.company)
 
 		serial_no = frappe.db.get_value("Serial No", "SN001",
 			["warehouse", "purchase_rate"], as_dict=1)
@@ -160,7 +160,7 @@
 			})
 		pr.submit()
 
-		lcv = submit_landed_cost_voucher("Purchase Receipt", pr.name, pr.company, 123.22)
+		lcv = create_landed_cost_voucher("Purchase Receipt", pr.name, pr.company, 123.22)
 
 		self.assertEqual(lcv.items[0].applicable_charges, 41.07)
 		self.assertEqual(lcv.items[2].applicable_charges, 41.08)
@@ -236,7 +236,7 @@
 	return lcv
 
 
-def submit_landed_cost_voucher(receipt_document_type, receipt_document, company, charges=50):
+def create_landed_cost_voucher(receipt_document_type, receipt_document, company, charges=50):
 	ref_doc = frappe.get_doc(receipt_document_type, receipt_document)
 
 	lcv = frappe.new_doc("Landed Cost Voucher")
diff --git a/erpnext/stock/doctype/material_request/test_material_request.py b/erpnext/stock/doctype/material_request/test_material_request.py
index 19924b1..0a29fa0 100644
--- a/erpnext/stock/doctype/material_request/test_material_request.py
+++ b/erpnext/stock/doctype/material_request/test_material_request.py
@@ -12,9 +12,6 @@
 from erpnext.stock.doctype.item.test_item import create_item
 
 class TestMaterialRequest(unittest.TestCase):
-	def setUp(self):
-		erpnext.set_perpetual_inventory(0)
-
 	def test_make_purchase_order(self):
 		mr = frappe.copy_doc(test_records[0]).insert()
 
diff --git a/erpnext/stock/doctype/packed_item/packed_item.json b/erpnext/stock/doctype/packed_item/packed_item.json
index 2ac5c42..f1d7f8c 100644
--- a/erpnext/stock/doctype/packed_item/packed_item.json
+++ b/erpnext/stock/doctype/packed_item/packed_item.json
@@ -1,4 +1,5 @@
 {
+ "actions": [],
  "creation": "2013-02-22 01:28:00",
  "doctype": "DocType",
  "editable_grid": 1,
@@ -14,6 +15,7 @@
   "target_warehouse",
   "column_break_9",
   "qty",
+  "uom",
   "section_break_9",
   "serial_no",
   "column_break_11",
@@ -23,7 +25,7 @@
   "actual_qty",
   "projected_qty",
   "column_break_16",
-  "uom",
+  "incoming_rate",
   "page_break",
   "prevdoc_doctype",
   "parent_detail_docname"
@@ -199,11 +201,21 @@
    "no_copy": 1,
    "print_hide": 1,
    "read_only": 1
+  },
+  {
+   "fieldname": "incoming_rate",
+   "fieldtype": "Currency",
+   "label": "Incoming Rate",
+   "no_copy": 1,
+   "print_hide": 1,
+   "read_only": 1
   }
  ],
  "idx": 1,
+ "index_web_pages_for_search": 1,
  "istable": 1,
- "modified": "2019-11-26 20:09:59.400960",
+ "links": [],
+ "modified": "2020-09-24 09:25:13.050151",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Packed Item",
@@ -212,4 +224,4 @@
  "sort_field": "modified",
  "sort_order": "DESC",
  "track_changes": 1
-}
+}
\ No newline at end of file
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
index 97e0fa7..226064b 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -181,6 +181,7 @@
 		update_serial_nos_after_submit(self, "items")
 
 		self.make_gl_entries()
+		self.repost_future_sle_and_gle()
 
 	def check_next_docstatus(self):
 		submit_rv = frappe.db.sql("""select t1.name
@@ -209,7 +210,8 @@
 		# because updating ordered qty in bin depends upon updated ordered qty in PO
 		self.update_stock_ledger()
 		self.make_gl_entries_on_cancel()
-		self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry')
+		self.repost_future_sle_and_gle()
+		self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry', 'Repost Item Valuation')
 		self.delete_auto_created_batches()
 
 	def get_current_stock(self):
diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
index 9b8eeed..83012d3 100644
--- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
@@ -9,14 +9,15 @@
 from frappe.utils import cint, flt, cstr, today, random_string, add_days
 from erpnext.stock.doctype.purchase_receipt.purchase_receipt import make_purchase_invoice
 from erpnext.stock.doctype.item.test_item import create_item
-from erpnext import set_perpetual_inventory
 from erpnext.stock.doctype.serial_no.serial_no import SerialNoDuplicateError
 from erpnext.accounts.doctype.account.test_account import get_inventory_account
 from erpnext.stock.doctype.item.test_item import make_item
 from six import iteritems
+from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
+
+
 class TestPurchaseReceipt(unittest.TestCase):
 	def setUp(self):
-		set_perpetual_inventory(0)
 		frappe.db.set_value("Buying Settings", None, "allow_multiple_items", 1)
 
 	def test_reverse_purchase_receipt_sle(self):
@@ -112,6 +113,8 @@
 
 		self.assertFalse(get_gl_entries("Purchase Receipt", pr.name))
 
+		pr.cancel()
+
 	def test_batched_serial_no_purchase(self):
 		item = frappe.db.exists("Item", {'item_name': 'Batched Serialized Item'})
 		if not item:
@@ -183,22 +186,30 @@
 
 		rm_supp_cost = sum([d.amount for d in pr.get("supplied_items")])
 		self.assertEqual(pr.get("items")[0].rm_supp_cost, flt(rm_supp_cost, 2))
+		
+		pr.cancel()
 
 	def test_subcontracting_gle_fg_item_rate_zero(self):
 		from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
-		set_perpetual_inventory()
 		frappe.db.set_value("Buying Settings", None, "backflush_raw_materials_of_subcontract_based_on", "BOM")
-		make_stock_entry(item_code="_Test Item", target="Work In Progress - TCP1", qty=100, basic_rate=100, company="_Test Company with perpetual inventory")
-		make_stock_entry(item_code="_Test Item Home Desktop 100", target="Work In Progress - TCP1",
+
+		se1 = make_stock_entry(item_code="_Test Item", target="Work In Progress - TCP1",
 			qty=100, basic_rate=100, company="_Test Company with perpetual inventory")
+
+		se2 = make_stock_entry(item_code="_Test Item Home Desktop 100", target="Work In Progress - TCP1",
+			qty=100, basic_rate=100, company="_Test Company with perpetual inventory")
+
 		pr = make_purchase_receipt(item_code="_Test FG Item", qty=10, rate=0, is_subcontracted="Yes",
-			company="_Test Company with perpetual inventory", warehouse='Stores - TCP1', supplier_warehouse='Work In Progress - TCP1')
+			company="_Test Company with perpetual inventory", warehouse='Stores - TCP1',
+			supplier_warehouse='Work In Progress - TCP1')
 
 		gl_entries = get_gl_entries("Purchase Receipt", pr.name)
 
 		self.assertFalse(gl_entries)
 
-		set_perpetual_inventory(0)
+		pr.cancel()
+		se1.cancel()
+		se2.cancel()
 
 	def test_subcontracting_over_receipt(self):
 		"""
@@ -216,13 +227,13 @@
 		item_code = "_Test Subcontracted FG Item 1"
 		make_subcontracted_item(item_code=item_code)
 
-		po = create_purchase_order(item_code=item_code, qty=1,
+		po = create_purchase_order(item_code=item_code, qty=1, include_exploded_items=0,
 			is_subcontracted="Yes", supplier_warehouse="_Test Warehouse 1 - _TC")
 
 		#stock raw materials in a warehouse before transfer
-		make_stock_entry(target="_Test Warehouse - _TC",
-			item_code = "Test Extra Item 1", qty=1, basic_rate=100)
-		make_stock_entry(target="_Test Warehouse - _TC",
+		se1 = make_stock_entry(target="_Test Warehouse - _TC",
+			item_code = "Test Extra Item 1", qty=10, basic_rate=100)
+		se2 = make_stock_entry(target="_Test Warehouse - _TC",
 			item_code = "_Test FG Item", qty=1, basic_rate=100)
 		rm_items = [
 			{
@@ -254,6 +265,13 @@
 		pr1.submit()
 		self.assertRaises(frappe.ValidationError, pr2.submit)
 
+		pr1.cancel()
+		se.cancel()
+		se1.cancel()
+		se2.cancel()
+		po.reload()
+		po.cancel()
+
 	def test_serial_no_supplier(self):
 		pr = make_purchase_receipt(item_code="_Test Serialized Item With Series", qty=1)
 		self.assertEqual(frappe.db.get_value("Serial No", pr.get("items")[0].serial_no, "supplier"),
@@ -284,6 +302,8 @@
 			self.assertEqual(frappe.db.get_value("Serial No", serial_no, "warehouse"),
 				pr.get("items")[0].rejected_warehouse)
 
+		pr.cancel()
+
 	def test_purchase_return_partial(self):
 		pr = make_purchase_receipt(company="_Test Company with perpetual inventory",
 			warehouse = "Stores - TCP1", supplier_warehouse = "Work in Progress - TCP1")
@@ -371,6 +391,9 @@
 		self.assertEqual(pr.per_returned, 100)
 		self.assertEqual(pr.status, 'Return Issued')
 
+		return_pr.cancel()
+		pr.cancel()
+
 	def test_purchase_return_for_rejected_qty(self):
 		from erpnext.stock.doctype.warehouse.test_warehouse import get_warehouse
 
@@ -388,6 +411,9 @@
 
 		self.assertEqual(actual_qty, -2)
 
+		return_pr.cancel()
+		pr.cancel()
+
 
 	def test_purchase_return_for_serialized_items(self):
 		def _check_serial_no_values(serial_no, field_values):
@@ -415,6 +441,10 @@
 			"delivery_document_no": return_pr.name
 		})
 
+		return_pr.cancel()
+		pr.reload()
+		pr.cancel()
+
 	def test_purchase_return_for_multi_uom(self):
 		item_code = "_Test Purchase Return For Multi-UOM"
 		if not frappe.db.exists('Item', item_code):
@@ -431,6 +461,9 @@
 
 		self.assertEqual(abs(return_pr.items[0].stock_qty), 1.0)
 
+		return_pr.cancel()
+		pr.cancel()
+
 	def test_closed_purchase_receipt(self):
 		from erpnext.stock.doctype.purchase_receipt.purchase_receipt import update_purchase_receipt_status
 
@@ -440,6 +473,9 @@
 		update_purchase_receipt_status(pr.name, "Closed")
 		self.assertEqual(frappe.db.get_value("Purchase Receipt", pr.name, "status"), "Closed")
 
+		pr.reload()
+		pr.cancel()
+
 	def test_pr_billing_status(self):
 		# PO -> PR1 -> PI and PO -> PI and PO -> PR2
 		from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
@@ -482,6 +518,16 @@
 		self.assertEqual(pr2.per_billed, 80)
 		self.assertEqual(pr2.status, "To Bill")
 
+		pr2.cancel()
+		pi2.reload()
+		pi2.cancel()
+		pi1.reload()
+		pi1.cancel()
+		pr1.reload()
+		pr1.cancel()
+		po.reload()
+		po.cancel()
+
 	def test_serial_no_against_purchase_receipt(self):
 		from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
 
@@ -509,6 +555,8 @@
 		self.assertEqual(serial_no, frappe.db.get_value("Serial No",
 			{"purchase_document_type": "Purchase Receipt", "purchase_document_no": new_pr_doc.name}, "name"))
 
+		new_pr_doc.cancel()
+
 	def test_not_accept_duplicate_serial_no(self):
 		from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
 		from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
@@ -519,16 +567,19 @@
 			item_code = item.name
 
 		serial_no = random_string(5)
-		make_purchase_receipt(item_code=item_code, qty=1, serial_no=serial_no)
-		create_delivery_note(item_code=item_code, qty=1, serial_no=serial_no)
+		pr1 = make_purchase_receipt(item_code=item_code, qty=1, serial_no=serial_no)
+		dn = create_delivery_note(item_code=item_code, qty=1, serial_no=serial_no)
 
-		pr = make_purchase_receipt(item_code=item_code, qty=1, serial_no=serial_no, do_not_submit=True)
-		self.assertRaises(SerialNoDuplicateError, pr.submit)
+		pr2 = make_purchase_receipt(item_code=item_code, qty=1, serial_no=serial_no, do_not_submit=True)
+		self.assertRaises(SerialNoDuplicateError, pr2.submit)
 
 		se = make_stock_entry(item_code=item_code, target="_Test Warehouse - _TC", qty=1,
 			serial_no=serial_no, basic_rate=100, do_not_submit=True)
 		self.assertRaises(SerialNoDuplicateError, se.submit)
 
+		dn.cancel()
+		pr1.cancel()
+
 	def test_auto_asset_creation(self):
 		asset_item = "Test Asset Item"
 
@@ -549,7 +600,7 @@
 						'company_name': '_Test Company',
 						'fixed_asset_account': '_Test Fixed Asset - _TC',
 						'accumulated_depreciation_account': '_Test Accumulated Depreciations - _TC',
-						'depreciation_expense_account': '_Test Depreciation - _TC'
+						'depreciation_expense_account': '_Test Depreciations - _TC'
 					}]
 				}).insert()
 
@@ -568,6 +619,8 @@
 		location = frappe.db.get_value('Asset', assets[0].name, 'location')
 		self.assertEquals(location, "Test Location")
 
+		pr.cancel()
+
 	def test_purchase_return_with_submitted_asset(self):
 		from erpnext.stock.doctype.purchase_receipt.purchase_receipt import make_purchase_return
 
@@ -594,6 +647,9 @@
 
 		pr_return.submit()
 
+		pr_return.cancel()
+		pr.cancel()
+
 	def test_purchase_receipt_cost_center(self):
 		from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
 		cost_center = "_Test Cost Center for BS Account - TCP1"
@@ -605,7 +661,8 @@
 				'location_name': 'Test Location'
 			}).insert()
 
-		pr = make_purchase_receipt(cost_center=cost_center, company="_Test Company with perpetual inventory", warehouse = "Stores - TCP1", supplier_warehouse = "Work in Progress - TCP1")
+		pr = make_purchase_receipt(cost_center=cost_center, company="_Test Company with perpetual inventory",
+			warehouse = "Stores - TCP1", supplier_warehouse = "Work in Progress - TCP1")
 
 		stock_in_hand_account = get_inventory_account(pr.company, pr.get("items")[0].warehouse)
 		gl_entries = get_gl_entries("Purchase Receipt", pr.name)
@@ -623,6 +680,8 @@
 		for i, gle in enumerate(gl_entries):
 			self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center)
 
+		pr.cancel()
+
 	def test_purchase_receipt_cost_center_with_balance_sheet_account(self):
 		if not frappe.db.exists('Location', 'Test Location'):
 			frappe.get_doc({
@@ -648,6 +707,8 @@
 		for i, gle in enumerate(gl_entries):
 			self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center)
 
+		pr.cancel()
+
 	def test_make_purchase_invoice_from_pr_for_returned_qty(self):
 		from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order, create_pr_against_po
 
@@ -663,6 +724,12 @@
 		pi = make_purchase_invoice(pr.name)
 		self.assertEquals(pi.items[0].qty, 3)
 
+		pr1.cancel()
+		pr.reload()
+		pr.cancel()
+		po.reload()
+		po.cancel()
+
 	def test_make_purchase_invoice_from_pr_with_returned_qty_duplicate_items(self):
 		pr1 = make_purchase_receipt(qty=8, do_not_submit=True)
 		pr1.append("items", {
@@ -689,8 +756,14 @@
 		self.assertEquals(pi2.items[0].qty, 2)
 		self.assertEquals(pi2.items[1].qty, 1)
 
+		pr2.cancel()
+		pi1.cancel()
+		pr1.reload()
+		pr1.cancel()
+
 	def test_stock_transfer_from_purchase_receipt(self):
-		pr1 = make_purchase_receipt(warehouse = 'Work In Progress - TCP1', company="_Test Company with perpetual inventory")
+		pr1 = make_purchase_receipt(warehouse = 'Work In Progress - TCP1',
+			company="_Test Company with perpetual inventory")
 
 		pr = make_purchase_receipt(company="_Test Company with perpetual inventory",
 			warehouse = "Stores - TCP1", do_not_save=1)
@@ -713,18 +786,20 @@
 		for sle in sl_entries:
 			self.assertEqual(expected_sle[sle.warehouse], sle.actual_qty)
 
-	def test_stock_transfer_from_purchase_receipt_with_valuation(self):
-		warehouse = frappe.get_doc('Warehouse', 'Work In Progress - TCP1')
-		warehouse.account = '_Test Account Stock In Hand - TCP1'
-		warehouse.save()
+		pr.cancel()
+		pr1.cancel()
 
-		pr1 = make_purchase_receipt(warehouse = 'Work In Progress - TCP1',
+	def test_stock_transfer_from_purchase_receipt_with_valuation(self):
+		create_warehouse("_Test Warehouse for Valuation", company="_Test Company with perpetual inventory",
+			properties={"account": '_Test Account Stock In Hand - TCP1'})
+
+		pr1 = make_purchase_receipt(warehouse = '_Test Warehouse for Valuation - TCP1',
 			company="_Test Company with perpetual inventory")
 
 		pr = make_purchase_receipt(company="_Test Company with perpetual inventory",
 			warehouse = "Stores - TCP1", do_not_save=1)
 
-		pr.items[0].from_warehouse = 'Work In Progress - TCP1'
+		pr.items[0].from_warehouse = '_Test Warehouse for Valuation - TCP1'
 		pr.supplier_warehouse = ''
 
 
@@ -749,7 +824,7 @@
 		]
 
 		expected_sle = {
-			'Work In Progress - TCP1': -5,
+			'_Test Warehouse for Valuation - TCP1': -5,
 			'Stores - TCP1': 5
 		}
 
@@ -761,60 +836,9 @@
 			self.assertEqual(gle.debit, expected_gle[i][1])
 			self.assertEqual(gle.credit, expected_gle[i][2])
 
-		warehouse.account = ''
-		warehouse.save()
+		pr.cancel()
+		pr1.cancel()
 
-	def test_backdated_purchase_receipt(self):
-		# make purchase receipt for default company
-		make_purchase_receipt(company="_Test Company 4", warehouse="Stores - _TC4")
-
-		# try to make another backdated PR
-		posting_date = add_days(today(), -1)
-		pr = make_purchase_receipt(company="_Test Company 4", warehouse="Stores - _TC4",
-			do_not_submit=True)
-
-		pr.set_posting_time = 1
-		pr.posting_date = posting_date
-		pr.save()
-
-		self.assertRaises(frappe.ValidationError, pr.submit)
-
-		# make purchase receipt for other company backdated
-		pr = make_purchase_receipt(company="_Test Company 5", warehouse="Stores - _TC5",
-			do_not_submit=True)
-
-		pr.set_posting_time = 1
-		pr.posting_date = posting_date
-		pr.submit()
-
-		# Allowed to submit for other company's PR
-		self.assertEqual(pr.docstatus, 1)
-
-	def test_backdated_purchase_receipt_for_same_company_different_warehouse(self):
-			# make purchase receipt for default company
-		make_purchase_receipt(company="_Test Company 4", warehouse="Stores - _TC4")
-
-		# try to make another backdated PR
-		posting_date = add_days(today(), -1)
-		pr = make_purchase_receipt(company="_Test Company 4", warehouse="Stores - _TC4",
-			do_not_submit=True)
-
-		pr.set_posting_time = 1
-		pr.posting_date = posting_date
-		pr.save()
-
-		self.assertRaises(frappe.ValidationError, pr.submit)
-
-		# make purchase receipt for other company backdated
-		pr = make_purchase_receipt(company="_Test Company 4", warehouse="Finished Goods - _TC4",
-			do_not_submit=True)
-
-		pr.set_posting_time = 1
-		pr.posting_date = posting_date
-		pr.submit()
-
-		# Allowed to submit for other company's PR
-		self.assertEqual(pr.docstatus, 1)
 
 	def test_subcontracted_pr_for_multi_transfer_batches(self):
 		from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
@@ -877,6 +901,12 @@
 
 		update_backflush_based_on("BOM")
 
+		pr.delete()
+		se.cancel()
+		ste2.cancel()
+		ste1.cancel()
+		po.cancel()
+
 def get_sl_entries(voucher_type, voucher_no):
 	return frappe.db.sql(""" select actual_qty, warehouse, stock_value_difference
 		from `tabStock Ledger Entry` where voucher_type=%s and voucher_no=%s
@@ -972,6 +1002,8 @@
 	pr.posting_date = args.posting_date or today()
 	if args.posting_time:
 		pr.posting_time = args.posting_time
+	if args.posting_date or args.posting_time:
+		pr.set_posting_time = 1
 	pr.company = args.company or "_Test Company"
 	pr.supplier = args.supplier or "_Test Supplier"
 	pr.is_subcontracted = args.is_subcontracted or "No"
diff --git a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
index 84c64aa..871b255 100644
--- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
+++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
@@ -866,7 +866,7 @@
  "idx": 1,
  "istable": 1,
  "links": [],
- "modified": "2020-11-02 10:00:38.204294",
+ "modified": "2020-12-07 10:00:38.204294",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Purchase Receipt Item",
diff --git a/erpnext/stock/doctype/repost_item_valuation/__init__.py b/erpnext/stock/doctype/repost_item_valuation/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/stock/doctype/repost_item_valuation/__init__.py
diff --git a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.js b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.js
new file mode 100644
index 0000000..e429cd5
--- /dev/null
+++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.js
@@ -0,0 +1,52 @@
+// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Repost Item Valuation', {
+	setup: function(frm) {
+		frm.set_query("warehouse", () => {
+			let filters = {
+				'is_group': 0
+			};
+			if (frm.doc.company) filters['company'] = frm.doc.company;
+			return {filters: filters};
+		});
+
+		frm.set_query("voucher_type", () => {
+			return {
+				filters: {
+					name: ['in', ['Purchase Receipt', 'Purchase Invoice', 'Delivery Note',
+						'Sales Invoice', 'Stock Entry', 'Stock Reconciliation']]
+				}
+			};
+		});
+
+		if (frm.doc.company) {
+			frm.set_query("voucher_no", () => {
+				return {
+					filters: {
+						company: frm.doc.company
+					}
+				};
+			});
+		}
+	},
+	refresh: function(frm) {
+		if (frm.doc.status == "Failed") {
+			frm.add_custom_button(__('Restart'), function () {
+				frm.trigger("restart_reposting");
+			}).addClass("btn-primary");
+		}
+	},
+
+	restart_reposting: function(frm) {
+		frappe.call({
+			method: "restart_reposting",
+			doc: frm.doc,
+			callback: function(r) {
+				if (!r.exc) {
+					frm.refresh();
+				}
+			}
+		});
+	}
+});
diff --git a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.json b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.json
new file mode 100644
index 0000000..071fc86
--- /dev/null
+++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.json
@@ -0,0 +1,215 @@
+{
+ "actions": [],
+ "autoname": "REPOST-ITEM-VAL-.######",
+ "creation": "2020-10-22 22:27:07.742161",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "based_on",
+  "voucher_type",
+  "voucher_no",
+  "item_code",
+  "warehouse",
+  "posting_date",
+  "posting_time",
+  "column_break_5",
+  "status",
+  "company",
+  "allow_negative_stock",
+  "via_landed_cost_voucher",
+  "allow_zero_rate",
+  "amended_from",
+  "error_section",
+  "error_log"
+ ],
+ "fields": [
+  {
+   "depends_on": "eval:doc.based_on=='Item and Warehouse'",
+   "fieldname": "item_code",
+   "fieldtype": "Link",
+   "label": "Item Code",
+   "mandatory_depends_on": "eval:doc.based_on=='Item and Warehouse'",
+   "options": "Item"
+  },
+  {
+   "depends_on": "eval:doc.based_on=='Item and Warehouse'",
+   "fieldname": "warehouse",
+   "fieldtype": "Link",
+   "label": "Warehouse",
+   "mandatory_depends_on": "eval:doc.based_on=='Item and Warehouse'",
+   "options": "Warehouse"
+  },
+  {
+   "fetch_from": "voucher_no.posting_date",
+   "fieldname": "posting_date",
+   "fieldtype": "Date",
+   "label": "Posting Date",
+   "reqd": 1
+  },
+  {
+   "fetch_from": "voucher_no.posting_time",
+   "fieldname": "posting_time",
+   "fieldtype": "Time",
+   "label": "Posting Time"
+  },
+  {
+   "default": "Queued",
+   "fieldname": "status",
+   "fieldtype": "Select",
+   "in_list_view": 1,
+   "in_standard_filter": 1,
+   "label": "Status",
+   "no_copy": 1,
+   "options": "Queued\nIn Progress\nCompleted\nFailed",
+   "read_only": 1
+  },
+  {
+   "fieldname": "amended_from",
+   "fieldtype": "Link",
+   "label": "Amended From",
+   "no_copy": 1,
+   "options": "Repost Item Valuation",
+   "print_hide": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "column_break_5",
+   "fieldtype": "Column Break"
+  },
+  {
+   "depends_on": "eval:doc.status=='Failed'",
+   "fieldname": "error_section",
+   "fieldtype": "Section Break",
+   "label": "Error"
+  },
+  {
+   "fieldname": "error_log",
+   "fieldtype": "Long Text",
+   "label": "Error Log",
+   "no_copy": 1,
+   "read_only": 1
+  },
+  {
+   "fetch_from": "warehouse.company",
+   "fieldname": "company",
+   "fieldtype": "Link",
+   "label": "Company",
+   "options": "Company"
+  },
+  {
+   "depends_on": "eval:doc.based_on=='Transaction'",
+   "fieldname": "voucher_type",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "in_standard_filter": 1,
+   "label": "Voucher Type",
+   "mandatory_depends_on": "eval:doc.based_on=='Transaction'",
+   "options": "DocType"
+  },
+  {
+   "depends_on": "eval:doc.based_on=='Transaction'",
+   "fieldname": "voucher_no",
+   "fieldtype": "Dynamic Link",
+   "in_list_view": 1,
+   "in_standard_filter": 1,
+   "label": "Voucher No",
+   "mandatory_depends_on": "eval:doc.based_on=='Transaction'",
+   "options": "voucher_type"
+  },
+  {
+   "default": "Transaction",
+   "fieldname": "based_on",
+   "fieldtype": "Select",
+   "label": "Based On",
+   "options": "Transaction\nItem and Warehouse",
+   "reqd": 1
+  },
+  {
+   "default": "0",
+   "fieldname": "allow_negative_stock",
+   "fieldtype": "Check",
+   "label": "Allow Negative Stock"
+  },
+  {
+   "default": "0",
+   "fieldname": "via_landed_cost_voucher",
+   "fieldtype": "Check",
+   "label": "Via Landed Cost Voucher"
+  },
+  {
+   "default": "0",
+   "fieldname": "allow_zero_rate",
+   "fieldtype": "Check",
+   "label": "Allow Zero Rate"
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "is_submittable": 1,
+ "links": [],
+ "modified": "2020-12-10 07:52:12.476589",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "Repost Item Valuation",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "cancel": 1,
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "System Manager",
+   "share": 1,
+   "submit": 1,
+   "write": 1
+  },
+  {
+   "cancel": 1,
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Stock User",
+   "share": 1,
+   "submit": 1,
+   "write": 1
+  },
+  {
+   "cancel": 1,
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Stock Manager",
+   "share": 1,
+   "submit": 1,
+   "write": 1
+  },
+  {
+   "cancel": 1,
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Accounts User",
+   "share": 1,
+   "submit": 1,
+   "write": 1
+  }
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC"
+}
\ No newline at end of file
diff --git a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
new file mode 100644
index 0000000..a942f2e
--- /dev/null
+++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
@@ -0,0 +1,89 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe, erpnext
+from frappe.model.document import Document
+from frappe.utils import cint
+from erpnext.stock.stock_ledger import repost_future_sle
+from erpnext.accounts.utils import update_gl_entries_after
+
+
+class RepostItemValuation(Document):
+	def validate(self):
+		self.set_status()
+		self.reset_field_values()
+		self.set_company()
+
+	def reset_field_values(self):
+		if self.based_on == 'Transaction':
+			self.item_code = None
+			self.warehouse = None
+		else:
+			self.voucher_type = None
+			self.voucher_no = None
+
+	def set_company(self):
+		if self.voucher_type and self.voucher_no:
+			self.company = frappe.get_cached_value(self.voucher_type, self.voucher_no, "company")
+		elif self.warehouse:
+			self.company = frappe.get_cached_value("Warehouse", self.warehouse, "company")
+	
+	def set_status(self, status=None):
+		if not status:
+			status = 'Queued'
+		self.db_set('status', status)
+
+	def on_submit(self):
+		frappe.enqueue(repost, timeout=1800, queue='long',
+			job_name='repost_sle', now=frappe.flags.in_test, doc=self)
+
+	def restart_reposting(self):
+		self.set_status('Queued')
+		frappe.enqueue(repost, timeout=1800, queue='long',
+			job_name='repost_sle', now=True, doc=self)
+
+def repost(doc):
+	try:
+		doc.set_status('In Progress')
+		frappe.db.commit()
+
+		repost_sl_entries(doc)
+		repost_gl_entries(doc)
+		doc.set_status('Completed')
+	except Exception:
+		frappe.db.rollback()
+		traceback = frappe.get_traceback()
+		frappe.log_error(traceback)
+		frappe.db.set_value(doc.doctype, doc.name, 'error_log', traceback)
+		doc.set_status('Failed')
+		raise
+	finally:
+		frappe.db.commit()
+
+def repost_sl_entries(doc):
+	if doc.based_on == 'Transaction':
+		repost_future_sle(voucher_type=doc.voucher_type, voucher_no=doc.voucher_no,
+			allow_negative_stock=doc.allow_negative_stock, via_landed_cost_voucher=doc.via_landed_cost_voucher)
+	else:
+		repost_future_sle(args=[frappe._dict({
+			"item_code": doc.item_code,
+			"warehouse": doc.warehouse,
+			"posting_date": doc.posting_date,
+			"posting_time": doc.posting_time
+		})], allow_negative_stock=doc.allow_negative_stock, via_landed_cost_voucher=doc.via_landed_cost_voucher)
+
+def repost_gl_entries(doc):
+	if not cint(erpnext.is_perpetual_inventory_enabled(doc.company)):
+		return
+
+	if doc.based_on == 'Transaction':
+		ref_doc = frappe.get_doc(doc.voucher_type, doc.voucher_no)
+		items, warehouses = ref_doc.get_items_and_warehouses()
+	else:
+		items = [doc.item_code]
+		warehouses = [doc.warehouse]
+
+	update_gl_entries_after(doc.posting_date, doc.posting_time,
+		warehouses, items, company=doc.company)
\ No newline at end of file
diff --git a/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py b/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py
new file mode 100644
index 0000000..13ceb68
--- /dev/null
+++ b/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+# import frappe
+import unittest
+
+class TestRepostItemValuation(unittest.TestCase):
+	pass
diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py
index 64bdf3b..2d89996 100644
--- a/erpnext/stock/doctype/serial_no/serial_no.py
+++ b/erpnext/stock/doctype/serial_no/serial_no.py
@@ -134,17 +134,13 @@
 		sle_dict = self.get_stock_ledger_entries(serial_no)
 		if sle_dict:
 			if sle_dict.get("incoming", []):
-				sle_list = [sle for sle in sle_dict["incoming"] if sle.is_cancelled == 0]
-				if sle_list:
-					entries["purchase_sle"] = sle_list[0]
+				entries["purchase_sle"] = sle_dict["incoming"][0]
 
 			if len(sle_dict.get("incoming", [])) - len(sle_dict.get("outgoing", [])) > 0:
 				entries["last_sle"] = sle_dict["incoming"][0]
 			else:
 				entries["last_sle"] = sle_dict["outgoing"][0]
-				sle_list = [sle for sle in sle_dict["outgoing"] if sle.is_cancelled == 0]
-				if sle_list:
-					entries["delivery_sle"] = sle_list[0]
+				entries["delivery_sle"] = sle_dict["outgoing"][0]
 
 		return entries
 
@@ -155,11 +151,12 @@
 
 		for sle in frappe.db.sql("""
 			SELECT voucher_type, voucher_no,
-				posting_date, posting_time, incoming_rate, actual_qty, serial_no, is_cancelled
+				posting_date, posting_time, incoming_rate, actual_qty, serial_no
 			FROM
 				`tabStock Ledger Entry`
 			WHERE
 				item_code=%s AND company = %s
+				AND is_cancelled = 0
 				AND (serial_no = %s
 					OR serial_no like %s
 					OR serial_no like %s
@@ -179,7 +176,7 @@
 
 	def on_trash(self):
 		sl_entries = frappe.db.sql("""select serial_no from `tabStock Ledger Entry`
-			where serial_no like %s and item_code=%s""",
+			where serial_no like %s and item_code=%s and is_cancelled=0""",
 			("%%%s%%" % self.name, self.item_code), as_dict=True)
 
 		# Find the exact match
@@ -229,7 +226,7 @@
 		if serial_nos:
 			frappe.throw(_("Item {0} is not setup for Serial Nos. Column must be blank").format(sle.item_code),
 				SerialNoNotRequiredError)
-	else:
+	elif not sle.is_cancelled:
 		if serial_nos:
 			if cint(sle.actual_qty) != flt(sle.actual_qty):
 				frappe.throw(_("Serial No {0} quantity {1} cannot be a fraction").format(sle.item_code, sle.actual_qty))
@@ -247,10 +244,6 @@
 						"delivery_document_no", "delivery_document_type", "warehouse",
 						"purchase_document_no", "company"], as_dict=1)
 
-					if sr and cint(sle.actual_qty) < 0 and sr.warehouse != sle.warehouse:
-						frappe.throw(_("Cannot cancel {0} {1} because Serial No {2} does not belong to the warehouse {3}")
-							.format(sle.voucher_type, sle.voucher_no, serial_no, sle.warehouse), SerialNoWarehouseError)
-
 					if sr.item_code!=sle.item_code:
 						if not allow_serial_nos_with_different_item(serial_no, sle):
 							frappe.throw(_("Serial No {0} does not belong to Item {1}").format(serial_no,
@@ -277,7 +270,7 @@
 								frappe.throw(_("Serial No {0} does not belong to Batch {1}").format(serial_no,
 									sle.batch_no), SerialNoBatchError)
 
-							if not sr.warehouse:
+							if not sle.is_cancelled and not sr.warehouse:
 								frappe.throw(_("Serial No {0} does not belong to any Warehouse")
 									.format(serial_no), SerialNoWarehouseError)
 
@@ -327,6 +320,12 @@
 		elif cint(sle.actual_qty) < 0 or not item_det.serial_no_series:
 			frappe.throw(_("Serial Nos Required for Serialized Item {0}").format(sle.item_code),
 				SerialNoRequiredError)
+	elif serial_nos:
+		for serial_no in serial_nos:
+			sr = frappe.db.get_value("Serial No", serial_no, ["name", "warehouse"], as_dict=1)
+			if sr and cint(sle.actual_qty) < 0 and sr.warehouse != sle.warehouse:
+				frappe.throw(_("Cannot cancel {0} {1} because Serial No {2} does not belong to the warehouse {3}")
+					.format(sle.voucher_type, sle.voucher_no, serial_no, sle.warehouse))
 
 def validate_material_transfer_entry(sle_doc):
 	sle_doc.update({
@@ -334,7 +333,7 @@
 		"skip_serial_no_validaiton": False
 	})
 
-	if (sle_doc.voucher_type == "Stock Entry" and
+	if (sle_doc.voucher_type == "Stock Entry" and not sle_doc.is_cancelled and
 		frappe.get_cached_value("Stock Entry", sle_doc.voucher_no, "purpose") == "Material Transfer"):
 		if sle_doc.actual_qty < 0:
 			sle_doc.skip_update_serial_no = True
@@ -379,7 +378,7 @@
 		stock_entry = frappe.get_cached_doc("Stock Entry", sle.voucher_no)
 		if stock_entry.purpose in ("Repack", "Manufacture"):
 			for d in stock_entry.get("items"):
-				if d.serial_no and (d.s_warehouse or d.t_warehouse):
+				if d.serial_no and (d.s_warehouse if not sle.is_cancelled else d.t_warehouse):
 					serial_nos = get_serial_nos(d.serial_no)
 					if sle_serial_no in serial_nos:
 						allow_serial_nos = True
@@ -388,7 +387,7 @@
 
 def update_serial_nos(sle, item_det):
 	if sle.skip_update_serial_no: return
-	if not sle.serial_no and cint(sle.actual_qty) > 0 \
+	if not sle.is_cancelled and not sle.serial_no and cint(sle.actual_qty) > 0 \
 			and item_det.has_serial_no == 1 and item_det.serial_no_series:
 		serial_nos = get_auto_serial_nos(item_det.serial_no_series, sle.actual_qty)
 		frappe.db.set(sle, "serial_no", serial_nos)
diff --git a/erpnext/stock/doctype/serial_no/test_serial_no.py b/erpnext/stock/doctype/serial_no/test_serial_no.py
index ab06107..ed70790 100644
--- a/erpnext/stock/doctype/serial_no/test_serial_no.py
+++ b/erpnext/stock/doctype/serial_no/test_serial_no.py
@@ -12,7 +12,6 @@
 from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
 from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
 from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
-from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory
 
 test_dependencies = ["Item"]
 test_records = frappe.get_test_records('Serial No')
@@ -38,8 +37,6 @@
 		self.assertTrue(SerialNoCannotCannotChangeError, sr.save)
 
 	def test_inter_company_transfer(self):
-		set_perpetual_inventory(0, "_Test Company 1")
-		set_perpetual_inventory(0)
 		se = make_serialized_item(target_warehouse="_Test Warehouse - _TC")
 		serial_nos = get_serial_nos(se.get("items")[0].serial_no)
 
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js
index 27fcbb7..98116ec 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.js
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.js
@@ -510,22 +510,31 @@
 
 	calculate_amount: function(frm) {
 		frm.events.calculate_total_additional_costs(frm);
-
-		const total_basic_amount = frappe.utils.sum(
-			(frm.doc.items || []).map(function(i) { return i.t_warehouse ? flt(i.basic_amount) : 0; })
-		);
-
+		let total_basic_amount = 0;
+		if (in_list(["Repack", "Manufacture"], frm.doc.purpose)) {
+			total_basic_amount = frappe.utils.sum(
+				(frm.doc.items || []).map(function(i) {
+					return i.is_finished_item ? flt(i.basic_amount) : 0;
+				})
+			);
+		} else {
+			total_basic_amount = frappe.utils.sum(
+				(frm.doc.items || []).map(function(i) {
+					return i.t_warehouse ? flt(i.basic_amount) : 0;
+				})
+			);
+		}
+		
 		for (let i in frm.doc.items) {
 			let item = frm.doc.items[i];
 
-			if (item.t_warehouse && total_basic_amount) {
+			if (((in_list(["Repack", "Manufacture"], frm.doc.purpose) && item.is_finished_item) || item.t_warehouse) && total_basic_amount) {
 				item.additional_cost = (flt(item.basic_amount) / total_basic_amount) * frm.doc.total_additional_costs;
 			} else {
 				item.additional_cost = 0;
 			}
 
-			item.amount = flt(item.basic_amount + flt(item.additional_cost),
-				precision("amount", item));
+			item.amount = flt(item.basic_amount + flt(item.additional_cost), precision("amount", item));
 
 			if (flt(item.transfer_qty)) {
 				item.valuation_rate = flt(flt(item.basic_rate) + (flt(item.additional_cost) / flt(item.transfer_qty)),
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.json b/erpnext/stock/doctype/stock_entry/stock_entry.json
index 61e0df6..5aed081 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.json
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.json
@@ -644,9 +644,10 @@
  ],
  "icon": "fa fa-file-text",
  "idx": 1,
+ "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2020-08-11 19:10:07.954981",
+ "modified": "2020-09-09 12:59:02.508943",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Stock Entry",
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index 32d7e6e..afdb54c 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -18,7 +18,7 @@
 from frappe.model.mapper import get_mapped_doc
 from erpnext.stock.doctype.serial_no.serial_no import update_serial_nos_after_submit, get_serial_nos
 from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import OpeningEntryAccountError
-
+from erpnext.accounts.general_ledger import process_gl_map
 import json
 
 from six import string_types, itervalues, iteritems
@@ -58,6 +58,7 @@
 		self.validate_warehouse()
 		self.validate_work_order()
 		self.validate_bom()
+		self.mark_finished_and_scrap_items()
 		self.validate_finished_goods()
 		self.validate_with_material_request()
 		self.validate_batch()
@@ -75,13 +76,11 @@
 		else:
 			set_batch_nos(self, 's_warehouse')
 
-		self.set_incoming_rate()
 		self.validate_serialized_batch()
 		self.set_actual_qty()
-		self.calculate_rate_and_amount(update_finished_item_rate=False)
+		self.calculate_rate_and_amount()
 
 	def on_submit(self):
-
 		self.update_stock_ledger()
 
 		update_serial_nos_after_submit(self, "items")
@@ -89,11 +88,15 @@
 		self.validate_purchase_order()
 		if self.purchase_order and self.purpose == "Send to Subcontractor":
 			self.update_purchase_order_supplied_items()
+
 		self.make_gl_entries()
+
+		self.repost_future_sle_and_gle()
 		self.update_cost_in_project()
 		self.validate_reserved_serial_no_consumption()
 		self.update_transferred_qty()
 		self.update_quality_inspection()
+
 		if self.work_order and self.purpose == "Manufacture":
 			self.update_so_in_serial_number()
 
@@ -113,9 +116,10 @@
 		self.update_work_order()
 		self.update_stock_ledger()
 
-		self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry')
+		self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry', 'Repost Item Valuation')
 
 		self.make_gl_entries_on_cancel()
+		self.repost_future_sle_and_gle()
 		self.update_cost_in_project()
 		self.update_transferred_qty()
 		self.update_quality_inspection()
@@ -256,11 +260,10 @@
 
 	def validate_fg_completed_qty(self):
 		if self.purpose == "Manufacture" and self.work_order:
-			production_item = frappe.get_value('Work Order', self.work_order, 'production_item')
-			for item in self.items:
-				if item.item_code == production_item and item.t_warehouse and item.qty != self.fg_completed_qty:
+			for d in self.items:
+				if d.is_finished_item and d.qty != self.fg_completed_qty:
 					frappe.throw(_("Finished product quantity <b>{0}</b> and For Quantity <b>{1}</b> cannot be different")
-						.format(item.qty, self.fg_completed_qty))
+						.format(d.qty, self.fg_completed_qty))
 
 	def validate_difference_account(self):
 		if not cint(erpnext.is_perpetual_inventory_enabled(self.company)):
@@ -382,21 +385,6 @@
 				frappe.throw(_("Stock Entries already created for Work Order ")
 					+ self.work_order + ":" + ", ".join(other_ste), DuplicateEntryForWorkOrderError)
 
-	def set_incoming_rate(self):
-		if self.purpose == "Repack":
-			self.set_basic_rate_for_finished_goods()
-
-		for d in self.items:
-			if d.s_warehouse:
-				args = self.get_args_for_incoming_rate(d)
-				d.basic_rate = get_incoming_rate(args)
-			elif d.allow_zero_valuation_rate and not d.s_warehouse:
-				d.basic_rate = 0.0
-			elif d.t_warehouse and not d.basic_rate:
-				d.basic_rate = get_valuation_rate(d.item_code, d.t_warehouse,
-					self.doctype, self.name, d.allow_zero_valuation_rate,
-					currency=erpnext.get_company_currency(self.company), company=self.company)
-
 	def set_actual_qty(self):
 		allow_negative_stock = cint(frappe.db.get_value("Stock Settings", None, "allow_negative_stock"))
 
@@ -432,57 +420,64 @@
 				d.serial_no = transferred_serial_no
 
 	def get_stock_and_rate(self):
+		"""
+			Updates rate and availability of all the items.
+			Called from Update Rate and Availability button.
+		"""
 		self.set_work_order_details()
 		self.set_transfer_qty()
 		self.set_actual_qty()
 		self.calculate_rate_and_amount()
 
-	def calculate_rate_and_amount(self, force=False,
-			update_finished_item_rate=True, raise_error_if_no_rate=True):
-		self.set_basic_rate(force, update_finished_item_rate, raise_error_if_no_rate)
+	def calculate_rate_and_amount(self, reset_outgoing_rate=True, raise_error_if_no_rate=True):
+		self.set_basic_rate(reset_outgoing_rate, raise_error_if_no_rate)
 		self.distribute_additional_costs()
 		self.update_valuation_rate()
 		self.set_total_incoming_outgoing_value()
 		self.set_total_amount()
 
-	def set_basic_rate(self, force=False, update_finished_item_rate=True, raise_error_if_no_rate=True):
-		"""get stock and incoming rate on posting date"""
-		raw_material_cost = 0.0
-		scrap_material_cost = 0.0
-		fg_basic_rate = 0.0
+	def set_basic_rate(self, reset_outgoing_rate=True, raise_error_if_no_rate=True):
+		"""
+			Set rate for outgoing, scrapped and finished items
+		"""
+		# Set rate for outgoing items
+		outgoing_items_cost = self.set_rate_for_outgoing_items(reset_outgoing_rate)
 
+		# Set basic rate for incoming items
 		for d in self.get('items'):
-			if d.t_warehouse: fg_basic_rate = flt(d.basic_rate)
-			args = self.get_args_for_incoming_rate(d)
+			if d.s_warehouse or d.set_basic_rate_manually: continue
 
-			# get basic rate
-			if not d.bom_no:
-				if (not flt(d.basic_rate) and not d.allow_zero_valuation_rate) or d.s_warehouse or force:
-					basic_rate = flt(get_incoming_rate(args, raise_error_if_no_rate), self.precision("basic_rate", d))
-					if basic_rate > 0:
-						d.basic_rate = basic_rate
+			if d.allow_zero_valuation_rate:
+				d.basic_rate = 0.0
+			elif d.is_finished_item:
+				if self.purpose == "Manufacture":
+					d.basic_rate = self.get_basic_rate_for_manufactured_item(d.transfer_qty, outgoing_items_cost)
+				elif self.purpose == "Repack":
+					d.basic_rate = self.get_basic_rate_for_repacked_items(d.transfer_qty, outgoing_items_cost)
+
+			if not d.basic_rate and not d.allow_zero_valuation_rate:
+				d.basic_rate = get_valuation_rate(d.item_code, d.t_warehouse,
+					self.doctype, self.name, d.allow_zero_valuation_rate,
+					currency=erpnext.get_company_currency(self.company), company=self.company,
+					raise_error_if_no_rate=raise_error_if_no_rate)
+
+			d.basic_rate = flt(d.basic_rate, d.precision("basic_rate"))
+			d.basic_amount = flt(flt(d.transfer_qty) * flt(d.basic_rate), d.precision("basic_amount"))
+
+	def set_rate_for_outgoing_items(self, reset_outgoing_rate=True):
+		outgoing_items_cost = 0.0
+		for d in self.get('items'):
+			if d.s_warehouse:
+				if reset_outgoing_rate:
+					args = self.get_args_for_incoming_rate(d)
+					rate = get_incoming_rate(args)
+					if rate > 0:
+						d.basic_rate = rate
 
 				d.basic_amount = flt(flt(d.transfer_qty) * flt(d.basic_rate), d.precision("basic_amount"))
 				if not d.t_warehouse:
-					raw_material_cost += flt(d.basic_amount)
-
-			# get scrap items basic rate
-			if d.bom_no:
-				if not flt(d.basic_rate) and not d.allow_zero_valuation_rate and \
-					getattr(self, "pro_doc", frappe._dict()).scrap_warehouse == d.t_warehouse:
-					basic_rate = flt(get_incoming_rate(args, raise_error_if_no_rate),
-						self.precision("basic_rate", d))
-					if basic_rate > 0:
-						d.basic_rate = basic_rate
-					d.basic_amount = flt(flt(d.transfer_qty) * flt(d.basic_rate), d.precision("basic_amount"))
-
-				if getattr(self, "pro_doc", frappe._dict()).scrap_warehouse == d.t_warehouse:
-
-					scrap_material_cost += flt(d.basic_amount)
-
-		number_of_fg_items = len([t.t_warehouse for t in self.get("items") if t.t_warehouse])
-		if (fg_basic_rate == 0.0 and number_of_fg_items == 1) or update_finished_item_rate:
-			self.set_basic_rate_for_finished_goods(raw_material_cost, scrap_material_cost)
+					outgoing_items_cost += flt(d.basic_amount)
+		return outgoing_items_cost
 
 	def get_args_for_incoming_rate(self, item):
 		return frappe._dict({
@@ -498,44 +493,44 @@
 			"allow_zero_valuation": item.allow_zero_valuation_rate,
 		})
 
-	def set_basic_rate_for_finished_goods(self, raw_material_cost=0, scrap_material_cost=0):
-		total_fg_qty = 0
-		if not raw_material_cost and self.get("items"):
-			raw_material_cost = sum([flt(row.basic_amount) for row in self.items
-				if row.s_warehouse and not row.t_warehouse])
+	def get_basic_rate_for_repacked_items(self, finished_item_qty, outgoing_items_cost):
+		finished_items = [d.item_code for d in self.get("items") if d.is_finished_item]
+		if len(finished_items) == 1:
+			return flt(outgoing_items_cost / finished_item_qty)
+		else:
+			unique_finished_items = set(finished_items)
+			if len(unique_finished_items) == 1:
+				total_fg_qty = sum([flt(d.transfer_qty) for d in self.items if d.is_finished_item])
+				return flt(outgoing_items_cost / total_fg_qty)
 
-			total_fg_qty = sum([flt(row.qty) for row in self.items
-				if row.t_warehouse and not row.s_warehouse])
+	def get_basic_rate_for_manufactured_item(self, finished_item_qty, outgoing_items_cost=0):
+		scrap_items_cost = sum([flt(d.basic_amount) for d in self.get("items") if d.is_scrap_item])
 
-		if self.purpose in ["Manufacture", "Repack"]:
-			for d in self.get("items"):
-				if (d.transfer_qty and (d.bom_no or d.t_warehouse)
-					and (getattr(self, "pro_doc", frappe._dict()).scrap_warehouse != d.t_warehouse)):
+		# Get raw materials cost from BOM if multiple material consumption entries
+		if frappe.db.get_single_value("Manufacturing Settings", "material_consumption"):
+			bom_items = self.get_bom_raw_materials(finished_item_qty)
+			outgoing_items_cost = sum([flt(row.qty)*flt(row.rate) for row in bom_items.values()])
 
-					if (self.work_order and self.purpose == "Manufacture"
-						and frappe.db.get_single_value("Manufacturing Settings", "material_consumption")):
-						bom_items = self.get_bom_raw_materials(d.transfer_qty)
-						raw_material_cost = sum([flt(row.qty)*flt(row.rate) for row in bom_items.values()])
-
-					if raw_material_cost and self.purpose == "Manufacture":
-						d.basic_rate = flt((raw_material_cost - scrap_material_cost) / flt(d.transfer_qty), d.precision("basic_rate"))
-						d.basic_amount = flt((raw_material_cost - scrap_material_cost), d.precision("basic_amount"))
-					elif self.purpose == "Repack" and total_fg_qty and not d.set_basic_rate_manually:
-						d.basic_rate = flt(raw_material_cost) / flt(total_fg_qty)
-						d.basic_amount = d.basic_rate * flt(d.qty)
+		return flt(outgoing_items_cost - scrap_items_cost)
 
 	def distribute_additional_costs(self):
-		if self.purpose == "Material Issue":
+		# If no incoming items, set additional costs blank
+		if not any([d.item_code for d in self.items if d.t_warehouse]):
 			self.additional_costs = []
 
 		self.total_additional_costs = sum([flt(t.amount) for t in self.get("additional_costs")])
-		total_basic_amount = sum([flt(t.basic_amount) for t in self.get("items") if t.t_warehouse])
 
-		for d in self.get("items"):
-			if d.t_warehouse and total_basic_amount:
-				d.additional_cost = (flt(d.basic_amount) / total_basic_amount) * self.total_additional_costs
-			else:
-				d.additional_cost = 0
+		if self.purpose in ("Repack", "Manufacture"):
+			incoming_items_cost = sum([flt(t.basic_amount) for t in self.get("items") if t.is_finished_item])
+		else:
+			incoming_items_cost = sum([flt(t.basic_amount) for t in self.get("items") if t.t_warehouse])
+
+		if incoming_items_cost:
+			for d in self.get("items"):
+				if (self.purpose in ("Repack", "Manufacture") and d.is_finished_item) or d.t_warehouse:
+					d.additional_cost = (flt(d.basic_amount) / incoming_items_cost) * self.total_additional_costs
+				else:
+					d.additional_cost = 0
 
 	def update_valuation_rate(self):
 		for d in self.get("items"):
@@ -638,71 +633,115 @@
 				item_code = d.original_item or d.item_code
 				validate_bom_no(item_code, d.bom_no)
 
+	def mark_finished_and_scrap_items(self):
+		if self.purpose in ("Repack", "Manufacture"):
+			if any([d.item_code for d in self.items if (d.is_finished_item and d.t_warehouse)]):
+				return
+
+			finished_item = self.get_finished_item()
+
+			for d in self.items:
+				if d.t_warehouse and not d.s_warehouse:
+					if self.purpose=="Repack" or d.item_code == finished_item:
+						d.is_finished_item = 1
+					else:
+						d.is_scrap_item = 1
+				else:
+					d.is_finished_item = 0
+					d.is_scrap_item = 0
+
+	def get_finished_item(self):
+		finished_item = None
+		if self.work_order:
+			finished_item = frappe.db.get_value("Work Order", self.work_order, "production_item")
+		elif self.bom_no:
+			finished_item = frappe.db.get_value("BOM", self.bom_no, "item")
+
+		return finished_item
+
 	def validate_finished_goods(self):
 		"""validation: finished good quantity should be same as manufacturing quantity"""
 		if not self.work_order: return
 
-		items_with_target_warehouse = []
-		allowance_percentage = flt(frappe.db.get_single_value("Manufacturing Settings",
-			"overproduction_percentage_for_work_order"))
-
 		production_item, wo_qty = frappe.db.get_value("Work Order",
 			self.work_order, ["production_item", "qty"])
 
+		number_of_finished_items = 0
 		for d in self.get('items'):
-			if (self.purpose != "Send to Subcontractor" and d.bom_no
-				and flt(d.transfer_qty) > flt(self.fg_completed_qty) and d.item_code == production_item):
-				frappe.throw(_("Quantity in row {0} ({1}) must be same as manufactured quantity {2}"). \
-					format(d.idx, d.transfer_qty, self.fg_completed_qty))
+			if d.is_finished_item:
+				if d.item_code != production_item:
+					frappe.throw(_("Finished Item {0} does not match with Work Order {1}")
+						.format(d.item_code, self.work_order))
+				elif flt(d.transfer_qty) > flt(self.fg_completed_qty):
+					frappe.throw(_("Quantity in row {0} ({1}) must be same as manufactured quantity {2}"). \
+						format(d.idx, d.transfer_qty, self.fg_completed_qty))
+				number_of_finished_items += 1
 
-			if self.work_order and self.purpose == "Manufacture" and d.t_warehouse:
-				items_with_target_warehouse.append(d.item_code)
+		if number_of_finished_items > 1:
+			frappe.throw(_("Multiple items cannot be marked as finished item"))
 
-		if self.work_order and self.purpose == "Manufacture":
+		if self.purpose == "Manufacture":
+			allowance_percentage = flt(frappe.db.get_single_value("Manufacturing Settings",
+				"overproduction_percentage_for_work_order"))
+
 			allowed_qty = wo_qty + (allowance_percentage/100 * wo_qty)
 			if self.fg_completed_qty > allowed_qty:
 				frappe.throw(_("For quantity {0} should not be greater than work order quantity {1}")
 					.format(flt(self.fg_completed_qty), wo_qty))
 
-			if production_item not in items_with_target_warehouse:
-				frappe.throw(_("Finished Item {0} must be entered for Manufacture type entry")
-					.format(production_item))
-
 	def update_stock_ledger(self):
 		sl_entries = []
+		finished_item_row = self.get_finished_item_row()
 
-		# make sl entries for source warehouse first, then do for target warehouse
-		for d in self.get('items'):
-			if cstr(d.s_warehouse):
-				sl_entries.append(self.get_sl_entries(d, {
-					"warehouse": cstr(d.s_warehouse),
-					"actual_qty": -flt(d.transfer_qty),
-					"incoming_rate": 0
-				}))
+		# make sl entries for source warehouse first
+		self.get_sle_for_source_warehouse(sl_entries, finished_item_row)
 
-		for d in self.get('items'):
-			if cstr(d.t_warehouse):
-				sl_entries.append(self.get_sl_entries(d, {
-					"warehouse": cstr(d.t_warehouse),
-					"actual_qty": flt(d.transfer_qty),
-					"incoming_rate": flt(d.valuation_rate)
-				}))
-
-		# On cancellation, make stock ledger entry for
-		# target warehouse first, to update serial no values properly
-
-			# if cstr(d.s_warehouse) and self.docstatus == 2:
-			# 	sl_entries.append(self.get_sl_entries(d, {
-			# 		"warehouse": cstr(d.s_warehouse),
-			# 		"actual_qty": -flt(d.transfer_qty),
-			# 		"incoming_rate": 0
-			# 	}))
-
+		# SLE for target warehouse
+		self.get_sle_for_target_warehouse(sl_entries, finished_item_row)
+		
+		# reverse sl entries if cancel
 		if self.docstatus == 2:
 			sl_entries.reverse()
 
 		self.make_sl_entries(sl_entries)
 
+	def get_finished_item_row(self):
+		finished_item_row = None
+		if self.purpose in ("Manufacture", "Repack"):
+			for d in self.get('items'):
+				if d.is_finished_item:
+					finished_item_row = d
+
+		return finished_item_row
+
+	def get_sle_for_source_warehouse(self, sl_entries, finished_item_row):
+		for d in self.get('items'):
+			if cstr(d.s_warehouse):
+				sle = self.get_sl_entries(d, {
+					"warehouse": cstr(d.s_warehouse),
+					"actual_qty": -flt(d.transfer_qty),
+					"incoming_rate": 0
+				})
+				if cstr(d.t_warehouse):
+					sle.dependant_sle_voucher_detail_no = d.name
+				elif finished_item_row and (finished_item_row.item_code != d.item_code or finished_item_row.t_warehouse != d.s_warehouse):
+					sle.dependant_sle_voucher_detail_no = finished_item_row.name
+					
+				sl_entries.append(sle)
+	
+	def get_sle_for_target_warehouse(self, sl_entries, finished_item_row):
+		for d in self.get('items'):
+			if cstr(d.t_warehouse):
+				sle = self.get_sl_entries(d, {
+					"warehouse": cstr(d.t_warehouse),
+					"actual_qty": flt(d.transfer_qty),
+					"incoming_rate": flt(d.valuation_rate)
+				})
+				if cstr(d.s_warehouse) or (finished_item_row and d.name == finished_item_row.name):
+					sle.recalculate_rate = 1
+
+				sl_entries.append(sle)
+
 	def get_gl_entries(self, warehouse_account):
 		gl_entries = super(StockEntry, self).get_gl_entries(warehouse_account)
 
@@ -747,7 +786,7 @@
 						"credit": -1 * amount # put it as negative credit instead of debit purposefully
 					}, item=d))
 
-		return gl_entries
+		return process_gl_map(gl_entries)
 
 	def update_work_order(self):
 		def _validate_work_order(pro_doc):
@@ -996,6 +1035,7 @@
 				"stock_uom": item.stock_uom,
 				"expense_account": item.get("expense_account"),
 				"cost_center": item.get("buying_cost_center"),
+				"is_finished_item": 1
 			}
 		}, bom_no = self.bom_no)
 
@@ -1034,6 +1074,7 @@
 
 		for item in itervalues(item_dict):
 			item.from_warehouse = ""
+			item.is_scrap_item = 1
 		return item_dict
 
 	def get_unconsumed_raw_materials(self):
@@ -1246,6 +1287,8 @@
 			se_child.subcontracted_item = item_dict[d].get("main_item_code")
 			se_child.cost_center = (item_dict[d].get("cost_center") or
 				get_default_cost_center(item_dict[d], company = self.company))
+			se_child.is_finished_item = item_dict[d].get("is_finished_item", 0)
+			se_child.is_scrap_item = item_dict[d].get("is_scrap_item", 0)
 
 			for field in ["idx", "po_detail", "original_item",
 				"expense_account", "description", "item_name"]:
diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py
index 9b6744c..1a64185 100644
--- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py
@@ -6,7 +6,6 @@
 import frappe.defaults
 from frappe.utils import flt, nowdate, nowtime
 from erpnext.stock.doctype.serial_no.serial_no import *
-from erpnext import set_perpetual_inventory
 from erpnext.stock.doctype.stock_ledger_entry.stock_ledger_entry import StockFreezeError
 from erpnext.stock.stock_ledger import get_previous_sle
 from frappe.permissions import add_user_permission, remove_user_permission
@@ -32,7 +31,6 @@
 class TestStockEntry(unittest.TestCase):
 	def tearDown(self):
 		frappe.set_user("Administrator")
-		set_perpetual_inventory(0)
 
 	def test_fifo(self):
 		frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 1)
@@ -213,7 +211,6 @@
 
 	def test_repack_no_change_in_valuation(self):
 		company = frappe.db.get_value('Warehouse', '_Test Warehouse - _TC', 'company')
-		set_perpetual_inventory(0, company)
 
 		make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC", qty=50, basic_rate=100)
 		make_stock_entry(item_code="_Test Item Home Desktop 100", target="_Test Warehouse - _TC",
@@ -235,8 +232,6 @@
 			order by account desc""", repack.name, as_dict=1)
 		self.assertFalse(gl_entries)
 
-		set_perpetual_inventory(0, repack.company)
-
 	def test_repack_with_additional_costs(self):
 		company = frappe.db.get_value('Warehouse', 'Stores - TCP1', 'company')
 
@@ -474,7 +469,6 @@
 
 	def test_warehouse_company_validation(self):
 		company = frappe.db.get_value('Warehouse', '_Test Warehouse 2 - _TC1', 'company')
-		set_perpetual_inventory(0, company)
 		frappe.get_doc("User", "test2@example.com")\
 			.add_roles("Sales User", "Sales Manager", "Stock User", "Stock Manager")
 		frappe.set_user("test2@example.com")
@@ -500,7 +494,7 @@
 
 		st1 = frappe.copy_doc(test_records[0])
 		st1.company = "_Test Company 1"
-		set_perpetual_inventory(0, st1.company)
+
 		frappe.set_user("test@example.com")
 		st1.get("items")[0].t_warehouse="_Test Warehouse 2 - _TC1"
 		self.assertRaises(frappe.PermissionError, st1.insert)
@@ -698,47 +692,54 @@
 		repack.insert()
 		self.assertRaises(frappe.ValidationError, repack.submit)
 
-	def test_material_consumption(self):
-		from erpnext.manufacturing.doctype.work_order.work_order \
-			import make_stock_entry as _make_stock_entry
-		bom_no = frappe.db.get_value("BOM", {"item": "_Test FG Item 2",
-			"is_default": 1, "docstatus": 1})
+	# def test_material_consumption(self):
+	# 	frappe.db.set_value("Manufacturing Settings", None, "backflush_raw_materials_based_on", "BOM")
+	# 	frappe.db.set_value("Manufacturing Settings", None, "material_consumption", "0")
 
-		work_order = frappe.new_doc("Work Order")
-		work_order.update({
-			"company": "_Test Company",
-			"fg_warehouse": "_Test Warehouse 1 - _TC",
-			"production_item": "_Test FG Item 2",
-			"bom_no": bom_no,
-			"qty": 4.0,
-			"stock_uom": "_Test UOM",
-			"wip_warehouse": "_Test Warehouse - _TC",
-			"additional_operating_cost": 1000
-		})
-		work_order.insert()
-		work_order.submit()
+	# 	from erpnext.manufacturing.doctype.work_order.work_order \
+	# 		import make_stock_entry as _make_stock_entry
+	# 	bom_no = frappe.db.get_value("BOM", {"item": "_Test FG Item 2",
+	# 		"is_default": 1, "docstatus": 1})
 
-		make_stock_entry(item_code="_Test Serialized Item With Series", target="_Test Warehouse - _TC", qty=50, basic_rate=100)
-		make_stock_entry(item_code="_Test Item 2", target="_Test Warehouse - _TC", qty=50, basic_rate=20)
+	# 	work_order = frappe.new_doc("Work Order")
+	# 	work_order.update({
+	# 		"company": "_Test Company",
+	# 		"fg_warehouse": "_Test Warehouse 1 - _TC",
+	# 		"production_item": "_Test FG Item 2",
+	# 		"bom_no": bom_no,
+	# 		"qty": 4.0,
+	# 		"stock_uom": "_Test UOM",
+	# 		"wip_warehouse": "_Test Warehouse - _TC",
+	# 		"additional_operating_cost": 1000,
+	# 		"use_multi_level_bom": 1
+	# 	})
+	# 	work_order.insert()
+	# 	work_order.submit()
 
-		item_quantity = {
-			'_Test Item': 10.0,
-			'_Test Item 2': 12.0,
-			'_Test Serialized Item With Series': 6.0
-		}
+	# 	make_stock_entry(item_code="_Test Serialized Item With Series", target="_Test Warehouse - _TC", qty=50, basic_rate=100)
+	# 	make_stock_entry(item_code="_Test Item 2", target="_Test Warehouse - _TC", qty=50, basic_rate=20)
 
-		stock_entry = frappe.get_doc(_make_stock_entry(work_order.name, "Material Consumption for Manufacture", 2))
-		for d in stock_entry.get('items'):
-			self.assertEqual(item_quantity.get(d.item_code), d.qty)
+	# 	item_quantity = {
+	# 		'_Test Item': 2.0,
+	# 		'_Test Item 2': 12.0,
+	# 		'_Test Serialized Item With Series': 6.0
+	# 	}
+
+	# 	stock_entry = frappe.get_doc(_make_stock_entry(work_order.name, "Material Consumption for Manufacture", 2))
+	# 	for d in stock_entry.get('items'):
+	# 		self.assertEqual(item_quantity.get(d.item_code), d.qty)
 
 	def test_customer_provided_parts_se(self):
 		create_item('CUST-0987', is_customer_provided_item = 1, customer = '_Test Customer', is_purchase_item = 0)
-		se = make_stock_entry(item_code='CUST-0987', purpose = 'Material Receipt', qty=4, to_warehouse = "_Test Warehouse - _TC")
+		se = make_stock_entry(item_code='CUST-0987', purpose = 'Material Receipt',
+			qty=4, to_warehouse = "_Test Warehouse - _TC")
 		self.assertEqual(se.get("items")[0].allow_zero_valuation_rate, 1)
 		self.assertEqual(se.get("items")[0].amount, 0)
 
 	def test_gle_for_opening_stock_entry(self):
-		mr = make_stock_entry(item_code="_Test Item", target="Stores - TCP1", company="_Test Company with perpetual inventory",qty=50, basic_rate=100, expense_account="Stock Adjustment - TCP1", is_opening="Yes", do_not_save=True)
+		mr = make_stock_entry(item_code="_Test Item", target="Stores - TCP1",
+			company="_Test Company with perpetual inventory", qty=50, basic_rate=100,
+			expense_account="Stock Adjustment - TCP1", is_opening="Yes", do_not_save=True)
 
 		self.assertRaises(OpeningEntryAccountError, mr.save)
 
@@ -759,8 +760,8 @@
 		"company":"_Test Company with perpetual inventory",
 		"items":[
 			{
-				"item_code":"Basil Leaves",
-				"description":"Basil Leaves",
+				"item_code":"_Test Item",
+				"description":"_Test Item",
 				"qty": 1,
 				"basic_rate": 0,
 				"uom":"Nos",
@@ -769,8 +770,8 @@
 				"cost_center": "Main - TCP1"
 			 },
 			 {
-				"item_code":"Basil Leaves",
-				"description":"Basil Leaves",
+				"item_code":"_Test Item",
+				"description":"_Test Item",
 				"qty": 2,
 				"basic_rate": 0,
 				"uom":"Nos",
diff --git a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json
index 79e8f9a..6fe6029 100644
--- a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json
+++ b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json
@@ -13,8 +13,10 @@
   "t_warehouse",
   "sec_break1",
   "item_code",
-  "col_break2",
   "item_name",
+  "col_break2",
+  "is_finished_item",
+  "is_scrap_item",
   "subcontracted_item",
   "section_break_8",
   "description",
@@ -22,35 +24,37 @@
   "item_group",
   "image",
   "image_view",
-  "quantity_and_rate",
-  "set_basic_rate_manually",
+  "quantity_section",
   "qty",
-  "basic_rate",
-  "basic_amount",
-  "additional_cost",
-  "amount",
-  "valuation_rate",
-  "col_break3",
-  "uom",
-  "conversion_factor",
-  "stock_uom",
   "transfer_qty",
   "retain_sample",
+  "column_break_20",
+  "uom",
+  "stock_uom",
+  "conversion_factor",
   "sample_quantity",
+  "rates_section",
+  "basic_rate",
+  "additional_cost",
+  "valuation_rate",
+  "allow_zero_valuation_rate",
+  "col_break3",
+  "set_basic_rate_manually",
+  "basic_amount",
+  "amount",
   "serial_no_batch",
   "serial_no",
   "col_break4",
   "batch_no",
-  "quality_inspection",
   "accounting",
   "expense_account",
-  "col_break5",
   "accounting_dimensions_section",
   "cost_center",
+  "project",
   "dimension_col_break",
   "more_info",
-  "allow_zero_valuation_rate",
   "actual_qty",
+  "transferred_qty",
   "bom_no",
   "allow_alternative_item",
   "col_break6",
@@ -62,9 +66,8 @@
   "ste_detail",
   "po_detail",
   "column_break_51",
-  "transferred_qty",
   "reference_purchase_receipt",
-  "project"
+  "quality_inspection"
  ],
  "fields": [
   {
@@ -160,11 +163,6 @@
    "print_hide": 1
   },
   {
-   "fieldname": "quantity_and_rate",
-   "fieldtype": "Section Break",
-   "label": "Quantity and Rate"
-  },
-  {
    "bold": 1,
    "fieldname": "qty",
    "fieldtype": "Float",
@@ -322,10 +320,6 @@
    "print_hide": 1
   },
   {
-   "fieldname": "col_break5",
-   "fieldtype": "Column Break"
-  },
-  {
    "default": ":Company",
    "depends_on": "eval:cint(erpnext.is_perpetual_inventory_enabled(parent.company))",
    "fieldname": "cost_center",
@@ -335,6 +329,7 @@
    "print_hide": 1
   },
   {
+   "collapsible": 1,
    "fieldname": "more_info",
    "fieldtype": "Section Break",
    "label": "More Information"
@@ -456,6 +451,7 @@
    "read_only": 1
   },
   {
+   "collapsible": 1,
    "fieldname": "accounting_dimensions_section",
    "fieldtype": "Section Break",
    "label": "Accounting Dimensions"
@@ -498,6 +494,32 @@
    "fieldname": "set_basic_rate_manually",
    "fieldtype": "Check",
    "label": "Set Basic Rate Manually"
+  },
+  {
+   "fieldname": "quantity_section",
+   "fieldtype": "Section Break",
+   "label": "Quantity"
+  },
+  {
+   "fieldname": "column_break_20",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "rates_section",
+   "fieldtype": "Section Break",
+   "label": "Rates"
+  },
+  {
+   "default": "0",
+   "fieldname": "is_scrap_item",
+   "fieldtype": "Check",
+   "label": "Is Scrap Item"
+  },
+  {
+   "default": "0",
+   "fieldname": "is_finished_item",
+   "fieldtype": "Check",
+   "label": "Is Finished Item"
   }
  ],
  "idx": 1,
diff --git a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json
index fda17e0..2463a21 100644
--- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json
+++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json
@@ -8,26 +8,33 @@
  "engine": "InnoDB",
  "field_order": [
   "item_code",
-  "serial_no",
-  "batch_no",
   "warehouse",
   "posting_date",
   "posting_time",
+  "column_break_6",
   "voucher_type",
   "voucher_no",
   "voucher_detail_no",
+  "dependant_sle_voucher_detail_no",
+  "recalculate_rate",
+  "section_break_11",
   "actual_qty",
+  "qty_after_transaction",
   "incoming_rate",
   "outgoing_rate",
-  "stock_uom",
-  "qty_after_transaction",
+  "column_break_17",
   "valuation_rate",
   "stock_value",
   "stock_value_difference",
   "stock_queue",
-  "project",
+  "section_break_21",
   "company",
+  "stock_uom",
+  "project",
+  "batch_no",
+  "column_break_26",
   "fiscal_year",
+  "serial_no",
   "is_cancelled",
   "to_rename"
  ],
@@ -50,7 +57,6 @@
   {
    "fieldname": "serial_no",
    "fieldtype": "Long Text",
-   "in_list_view": 1,
    "label": "Serial No",
    "print_width": "100px",
    "read_only": 1,
@@ -59,7 +65,6 @@
   {
    "fieldname": "batch_no",
    "fieldtype": "Data",
-   "in_list_view": 1,
    "label": "Batch No",
    "oldfieldname": "batch_no",
    "oldfieldtype": "Data",
@@ -119,6 +124,7 @@
    "fieldname": "voucher_no",
    "fieldtype": "Dynamic Link",
    "in_filter": 1,
+   "in_list_view": 1,
    "in_standard_filter": 1,
    "label": "Voucher No",
    "oldfieldname": "voucher_no",
@@ -142,6 +148,7 @@
    "fieldname": "actual_qty",
    "fieldtype": "Float",
    "in_filter": 1,
+   "in_list_view": 1,
    "label": "Actual Quantity",
    "oldfieldname": "actual_qty",
    "oldfieldtype": "Currency",
@@ -152,6 +159,7 @@
   {
    "fieldname": "incoming_rate",
    "fieldtype": "Currency",
+   "in_list_view": 1,
    "label": "Incoming Rate",
    "oldfieldname": "incoming_rate",
    "oldfieldtype": "Currency",
@@ -217,13 +225,11 @@
   {
    "fieldname": "stock_queue",
    "fieldtype": "Text",
-   "hidden": 1,
    "label": "Stock Queue (FIFO)",
    "oldfieldname": "fcfs_stack",
    "oldfieldtype": "Text",
    "print_hide": 1,
-   "read_only": 1,
-   "report_hide": 1
+   "read_only": 1
   },
   {
    "fieldname": "project",
@@ -269,14 +275,48 @@
    "hidden": 1,
    "label": "To Rename",
    "search_index": 1
+  },
+  {
+   "fieldname": "dependant_sle_voucher_detail_no",
+   "fieldtype": "Data",
+   "label": "Dependant SLE Voucher Detail No"
+  },
+  {
+   "fieldname": "column_break_6",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "section_break_11",
+   "fieldtype": "Section Break"
+  },
+  {
+   "fieldname": "column_break_17",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "section_break_21",
+   "fieldtype": "Section Break"
+  },
+  {
+   "fieldname": "column_break_26",
+   "fieldtype": "Column Break"
+  },
+  {
+   "default": "0",
+   "fieldname": "recalculate_rate",
+   "fieldtype": "Check",
+   "label": "Recalculate Incoming/Outgoing Rate",
+   "no_copy": 1,
+   "read_only": 1
   }
  ],
  "hide_toolbar": 1,
  "icon": "fa fa-list",
  "idx": 1,
  "in_create": 1,
+ "index_web_pages_for_search": 1,
  "links": [],
- "modified": "2020-04-23 05:57:03.985520",
+ "modified": "2020-09-07 11:10:35.318872",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Stock Ledger Entry",
diff --git a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
index bb356f6..a5c303c 100644
--- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
+++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
@@ -10,8 +10,10 @@
 from datetime import date
 from erpnext.controllers.item_variant import ItemTemplateCannotHaveStock
 from erpnext.accounts.utils import get_fiscal_year
+from frappe.core.doctype.role.role import get_users
 
 class StockFreezeError(frappe.ValidationError): pass
+class BackDatedStockTransaction(frappe.ValidationError): pass
 
 exclude_from_linked_with = True
 
@@ -34,7 +36,6 @@
 		self.validate_and_set_fiscal_year()
 		self.block_transactions_against_group_warehouse()
 		self.validate_with_last_transaction_posting_time()
-		self.validate_future_posting()
 
 	def on_submit(self):
 		self.check_stock_frozen_date()
@@ -48,7 +49,7 @@
 	def calculate_batch_qty(self):
 		if self.batch_no:
 			batch_qty = frappe.db.get_value("Stock Ledger Entry",
-				{"docstatus": 1, "batch_no": self.batch_no},
+				{"docstatus": 1, "batch_no": self.batch_no, "is_cancelled": 0},
 				"sum(actual_qty)") or 0
 			frappe.db.set_value("Batch", self.batch_no, "batch_qty", batch_qty)
 
@@ -88,14 +89,14 @@
 
 		# check if batch number is required
 		if self.voucher_type != 'Stock Reconciliation':
-			if item_det.has_batch_no ==1:
+			if item_det.has_batch_no == 1:
 				batch_item = self.item_code if self.item_code == item_det.item_name else self.item_code + ":" +  item_det.item_name
 				if not self.batch_no:
 					frappe.throw(_("Batch number is mandatory for Item {0}").format(batch_item))
 				elif not frappe.db.get_value("Batch",{"item": self.item_code, "name": self.batch_no}):
 					frappe.throw(_("{0} is not a valid Batch Number for Item {1}").format(self.batch_no, batch_item))
 
-			elif item_det.has_batch_no ==0 and self.batch_no:
+			elif item_det.has_batch_no == 0 and self.batch_no and self.is_cancelled == 0:
 				frappe.throw(_("The Item {0} cannot have Batch").format(self.item_code))
 
 		if item_det.has_variants:
@@ -142,28 +143,28 @@
 		is_group_warehouse(self.warehouse)
 
 	def validate_with_last_transaction_posting_time(self):
-		last_transaction_time = frappe.db.sql("""
-			select MAX(timestamp(posting_date, posting_time)) as posting_time
-			from `tabStock Ledger Entry`
-			where docstatus = 1 and item_code = %s
-			and warehouse = %s""", (self.item_code, self.warehouse))[0][0]
+		authorized_role = frappe.db.get_single_value("Stock Settings", "role_allowed_to_create_edit_back_dated_transactions")
+		if authorized_role:
+			authorized_users = get_users(authorized_role)
+			if authorized_users and frappe.session.user not in authorized_users:
+				last_transaction_time = frappe.db.sql("""
+					select MAX(timestamp(posting_date, posting_time)) as posting_time
+					from `tabStock Ledger Entry`
+					where docstatus = 1 and item_code = %s
+					and warehouse = %s""", (self.item_code, self.warehouse))[0][0]
 
-		cur_doc_posting_datetime = "%s %s" % (self.posting_date, self.get("posting_time") or "00:00:00")
+				cur_doc_posting_datetime = "%s %s" % (self.posting_date, self.get("posting_time") or "00:00:00")
 
-		if last_transaction_time and get_datetime(cur_doc_posting_datetime) < get_datetime(last_transaction_time):
-			msg = _("Last Stock Transaction for item {0} under warehouse {1} was on {2}.").format(frappe.bold(self.item_code),
-				frappe.bold(self.warehouse), frappe.bold(last_transaction_time))
+				if last_transaction_time and get_datetime(cur_doc_posting_datetime) < get_datetime(last_transaction_time):
+					msg = _("Last Stock Transaction for item {0} under warehouse {1} was on {2}.").format(frappe.bold(self.item_code),
+						frappe.bold(self.warehouse), frappe.bold(last_transaction_time))
 
-			msg += "<br><br>" + _("Stock Transactions for Item {0} under warehouse {1} cannot be posted before this time.").format(
-				frappe.bold(self.item_code), frappe.bold(self.warehouse))
+					msg += "<br><br>" + _("You are not authorized to make/edit Stock Transactions for Item {0} under warehouse {1} before this time.").format(
+						frappe.bold(self.item_code), frappe.bold(self.warehouse))
 
-			msg += "<br><br>" + _("Please remove this item and try to submit again or update the posting time.")
-			frappe.throw(msg, title=_("Backdated Stock Entry"))
-
-	def validate_future_posting(self):
-		if date_diff(self.posting_date, getdate()) > 0:
-			msg = _("Posting future stock transactions are not allowed due to Immutable Ledger")
-			frappe.throw(msg, title=_("Future Posting Not Allowed"))
+					msg += "<br><br>" + _("Please contact any of the following users to {} this transaction.")
+					msg += "<br>" + "<br>".join(authorized_users)
+					frappe.throw(msg, BackDatedStockTransaction, title=_("Backdated Stock Entry"))
 
 def on_doctype_update():
 	if not frappe.db.has_index('tabStock Ledger Entry', 'posting_sort_index'):
diff --git a/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py
index 04dae83..59f1f39 100644
--- a/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py
+++ b/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py
@@ -5,8 +5,397 @@
 
 import frappe
 import unittest
-
-# test_records = frappe.get_test_records('Stock Ledger Entry')
+from frappe.utils import today, add_days
+from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
+from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation \
+	import create_stock_reconciliation
+from erpnext.stock.doctype.item.test_item import make_item
+from erpnext.stock.stock_ledger import get_previous_sle
+from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
+from erpnext.stock.doctype.landed_cost_voucher.test_landed_cost_voucher import create_landed_cost_voucher
+from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
+from erpnext.stock.doctype.stock_ledger_entry.stock_ledger_entry import BackDatedStockTransaction
 
 class TestStockLedgerEntry(unittest.TestCase):
-	pass
+	def setUp(self):
+		items = create_items()
+
+		# delete SLE and BINs for all items
+		frappe.db.sql("delete from `tabStock Ledger Entry` where item_code in (%s)" % (', '.join(['%s']*len(items))), items)
+		frappe.db.sql("delete from `tabBin` where item_code in (%s)" % (', '.join(['%s']*len(items))), items)
+
+	def test_item_cost_reposting(self):
+		company = "_Test Company"
+
+		# _Test Item for Reposting at Stores warehouse on 10-04-2020: Qty = 50, Rate = 100
+		create_stock_reconciliation(
+			item_code="_Test Item for Reposting",
+			warehouse="Stores - _TC",
+			qty=50,
+			rate=100,
+			company=company,
+			expense_account = "Stock Adjustment - _TC",
+			posting_date='2020-04-10',
+			posting_time='14:00'
+		)
+
+		# _Test Item for Reposting at FG warehouse on 20-04-2020: Qty = 10, Rate = 200
+		create_stock_reconciliation(
+			item_code="_Test Item for Reposting",
+			warehouse="Finished Goods - _TC",
+			qty=10,
+			rate=200,
+			company=company,
+			expense_account = "Stock Adjustment - _TC",
+			posting_date='2020-04-20',
+			posting_time='14:00'
+		)
+
+		# _Test Item for Reposting transferred from Stores to FG warehouse on 30-04-2020
+		make_stock_entry(
+			item_code="_Test Item for Reposting",
+			source="Stores - _TC",
+			target="Finished Goods - _TC",
+			company=company,
+			qty=10,
+			expense_account="Stock Adjustment - _TC",
+			posting_date='2020-04-30',
+			posting_time='14:00'
+		)
+		target_wh_sle = get_previous_sle({
+			"item_code": "_Test Item for Reposting",
+			"warehouse": "Finished Goods - _TC",
+			"posting_date": '2020-04-30',
+			"posting_time": '14:00'
+		})
+
+		self.assertEqual(target_wh_sle.get("valuation_rate"), 150)
+
+		# Repack entry on 5-5-2020
+		repack = create_repack_entry(company=company, posting_date='2020-05-05', posting_time='14:00')
+
+		finished_item_sle = get_previous_sle({
+			"item_code": "_Test Finished Item for Reposting",
+			"warehouse": "Finished Goods - _TC",
+			"posting_date": '2020-05-05',
+			"posting_time": '14:00'
+		})
+		self.assertEqual(finished_item_sle.get("incoming_rate"), 540)
+		self.assertEqual(finished_item_sle.get("valuation_rate"), 540)
+
+		# Reconciliation for _Test Item for Reposting at Stores on 12-04-2020: Qty = 50, Rate = 150
+		create_stock_reconciliation(
+			item_code="_Test Item for Reposting",
+			warehouse="Stores - _TC",
+			qty=50,
+			rate=150,
+			company=company,
+			expense_account = "Stock Adjustment - _TC",
+			posting_date='2020-04-12',
+			posting_time='14:00'
+		)
+
+
+		# Check valuation rate of finished goods warehouse after back-dated entry at Stores
+		target_wh_sle = get_previous_sle({
+			"item_code": "_Test Item for Reposting",
+			"warehouse": "Finished Goods - _TC",
+			"posting_date": '2020-04-30',
+			"posting_time": '14:00'
+		})
+		self.assertEqual(target_wh_sle.get("incoming_rate"), 150)
+		self.assertEqual(target_wh_sle.get("valuation_rate"), 175)
+
+		# Check valuation rate of repacked item after back-dated entry at Stores
+		finished_item_sle = get_previous_sle({
+			"item_code": "_Test Finished Item for Reposting",
+			"warehouse": "Finished Goods - _TC",
+			"posting_date": '2020-05-05',
+			"posting_time": '14:00'
+		})
+		self.assertEqual(finished_item_sle.get("incoming_rate"), 790)
+		self.assertEqual(finished_item_sle.get("valuation_rate"), 790)
+
+		# Check updated rate in Repack entry
+		repack.reload()
+		self.assertEqual(repack.items[0].get("basic_rate"), 150)
+		self.assertEqual(repack.items[1].get("basic_rate"), 750)
+
+	def test_purchase_return_valuation_reposting(self):
+		pr = make_purchase_receipt(company="_Test Company", posting_date='2020-04-10',
+			warehouse="Stores - _TC", item_code="_Test Item for Reposting", qty=5, rate=100)
+
+		return_pr = make_purchase_receipt(company="_Test Company", posting_date='2020-04-15', 
+			warehouse="Stores - _TC", item_code="_Test Item for Reposting", is_return=1, return_against=pr.name, qty=-2)
+
+		# check sle
+		outgoing_rate, stock_value_difference = frappe.db.get_value("Stock Ledger Entry", {"voucher_type": "Purchase Receipt",
+			"voucher_no": return_pr.name}, ["outgoing_rate", "stock_value_difference"])
+
+		self.assertEqual(outgoing_rate, 100)
+		self.assertEqual(stock_value_difference, -200)
+
+		create_landed_cost_voucher("Purchase Receipt", pr.name, pr.company)
+
+		outgoing_rate, stock_value_difference = frappe.db.get_value("Stock Ledger Entry", {"voucher_type": "Purchase Receipt",
+			"voucher_no": return_pr.name}, ["outgoing_rate", "stock_value_difference"])
+
+		self.assertEqual(outgoing_rate, 110)
+		self.assertEqual(stock_value_difference, -220)
+
+	def test_sales_return_valuation_reposting(self):
+		company = "_Test Company"
+		item_code="_Test Item for Reposting"
+
+		# Purchase Return: Qty = 5, Rate = 100
+		pr = make_purchase_receipt(company=company, posting_date='2020-04-10',
+			warehouse="Stores - _TC", item_code=item_code, qty=5, rate=100)
+
+		#Delivery Note: Qty = 5, Rate = 150
+		dn = create_delivery_note(item_code=item_code, qty=5, rate=150, warehouse="Stores - _TC",
+			company=company, expense_account="Cost of Goods Sold - _TC", cost_center="Main - _TC")
+
+		# check outgoing_rate for DN
+		outgoing_rate = abs(frappe.db.get_value("Stock Ledger Entry", {"voucher_type": "Delivery Note",
+			"voucher_no": dn.name}, "stock_value_difference") / 5)
+
+		self.assertEqual(dn.items[0].incoming_rate, 100)
+		self.assertEqual(outgoing_rate, 100)
+
+		# Return Entry: Qty = -2, Rate = 150
+		return_dn = create_delivery_note(is_return=1, return_against=dn.name, item_code=item_code, qty=-2, rate=150,
+			company=company, warehouse="Stores - _TC", expense_account="Cost of Goods Sold - _TC", cost_center="Main - _TC")
+
+		# check incoming rate for Return entry
+		incoming_rate, stock_value_difference = frappe.db.get_value("Stock Ledger Entry",
+			{"voucher_type": "Delivery Note", "voucher_no": return_dn.name},
+			["incoming_rate", "stock_value_difference"])
+
+		self.assertEqual(return_dn.items[0].incoming_rate, 100)
+		self.assertEqual(incoming_rate, 100)
+		self.assertEqual(stock_value_difference, 200)
+
+		#-------------------------------
+
+		# Landed Cost Voucher to update the rate of incoming Purchase Return: Additional cost = 50
+		lcv = create_landed_cost_voucher("Purchase Receipt", pr.name, pr.company)
+
+		# check outgoing_rate for DN after reposting
+		outgoing_rate = abs(frappe.db.get_value("Stock Ledger Entry", {"voucher_type": "Delivery Note",
+			"voucher_no": dn.name}, "stock_value_difference") / 5)
+		self.assertEqual(outgoing_rate, 110)
+
+		dn.reload()
+		self.assertEqual(dn.items[0].incoming_rate, 110)
+
+		# check incoming rate for Return entry after reposting
+		incoming_rate, stock_value_difference = frappe.db.get_value("Stock Ledger Entry",
+			{"voucher_type": "Delivery Note", "voucher_no": return_dn.name},
+			["incoming_rate", "stock_value_difference"])
+
+		self.assertEqual(incoming_rate, 110)
+		self.assertEqual(stock_value_difference, 220)
+
+		return_dn.reload()
+		self.assertEqual(return_dn.items[0].incoming_rate, 110)
+
+		# Cleanup data
+		return_dn.cancel()
+		dn.cancel()
+		lcv.cancel()
+		pr.cancel()
+
+	def test_reposting_of_sales_return_for_packed_item(self):
+		company = "_Test Company"
+		packed_item_code="_Test Item for Reposting"
+		bundled_item = "_Test Bundled Item for Reposting"
+		create_product_bundle_item(bundled_item, [[packed_item_code, 4]])
+
+		# Purchase Return: Qty = 50, Rate = 100
+		pr = make_purchase_receipt(company=company, posting_date='2020-04-10',
+			warehouse="Stores - _TC", item_code=packed_item_code, qty=50, rate=100)
+
+		#Delivery Note: Qty = 5, Rate = 150
+		dn = create_delivery_note(item_code=bundled_item, qty=5, rate=150, warehouse="Stores - _TC",
+			company=company, expense_account="Cost of Goods Sold - _TC", cost_center="Main - _TC")
+
+		# check outgoing_rate for DN
+		outgoing_rate = abs(frappe.db.get_value("Stock Ledger Entry", {"voucher_type": "Delivery Note",
+			"voucher_no": dn.name}, "stock_value_difference") / 20)
+
+		self.assertEqual(dn.packed_items[0].incoming_rate, 100)
+		self.assertEqual(outgoing_rate, 100)
+
+		# Return Entry: Qty = -2, Rate = 150
+		return_dn = create_delivery_note(is_return=1, return_against=dn.name, item_code=bundled_item, qty=-2, rate=150,
+			company=company, warehouse="Stores - _TC", expense_account="Cost of Goods Sold - _TC", cost_center="Main - _TC")
+
+		# check incoming rate for Return entry
+		incoming_rate, stock_value_difference = frappe.db.get_value("Stock Ledger Entry",
+			{"voucher_type": "Delivery Note", "voucher_no": return_dn.name},
+			["incoming_rate", "stock_value_difference"])
+
+		self.assertEqual(return_dn.packed_items[0].incoming_rate, 100)
+		self.assertEqual(incoming_rate, 100)
+		self.assertEqual(stock_value_difference, 800)
+
+		#-------------------------------
+
+		# Landed Cost Voucher to update the rate of incoming Purchase Return: Additional cost = 50
+		lcv = create_landed_cost_voucher("Purchase Receipt", pr.name, pr.company)
+
+		# check outgoing_rate for DN after reposting
+		outgoing_rate = abs(frappe.db.get_value("Stock Ledger Entry", {"voucher_type": "Delivery Note",
+			"voucher_no": dn.name}, "stock_value_difference") / 20)
+		self.assertEqual(outgoing_rate, 101)
+
+		dn.reload()
+		self.assertEqual(dn.packed_items[0].incoming_rate, 101)
+
+		# check incoming rate for Return entry after reposting
+		incoming_rate, stock_value_difference = frappe.db.get_value("Stock Ledger Entry",
+			{"voucher_type": "Delivery Note", "voucher_no": return_dn.name},
+			["incoming_rate", "stock_value_difference"])
+
+		self.assertEqual(incoming_rate, 101)
+		self.assertEqual(stock_value_difference, 808)
+
+		return_dn.reload()
+		self.assertEqual(return_dn.packed_items[0].incoming_rate, 101)
+
+		# Cleanup data
+		return_dn.cancel()
+		dn.cancel()
+		lcv.cancel()
+		pr.cancel()
+
+	def test_sub_contracted_item_costing(self):
+		from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
+
+		company = "_Test Company"
+		rm_item_code="_Test Item for Reposting"
+		subcontracted_item = "_Test Subcontracted Item for Reposting"
+
+		frappe.db.set_value("Buying Settings", None, "backflush_raw_materials_of_subcontract_based_on", "BOM")
+		make_bom(item = subcontracted_item, raw_materials =[rm_item_code], currency="INR")
+		
+		# Purchase raw materials on supplier warehouse: Qty = 50, Rate = 100
+		pr = make_purchase_receipt(company=company, posting_date='2020-04-10',
+			warehouse="Stores - _TC", item_code=rm_item_code, qty=10, rate=100)
+
+		# Purchase Receipt for subcontracted item
+		pr1 = make_purchase_receipt(company=company, posting_date='2020-04-20',
+			warehouse="Finished Goods - _TC", supplier_warehouse="Stores - _TC",
+			item_code=subcontracted_item, qty=10, rate=20, is_subcontracted="Yes")
+
+		self.assertEqual(pr1.items[0].valuation_rate, 120)
+
+		# Update raw material's valuation via LCV, Additional cost = 50
+		lcv = create_landed_cost_voucher("Purchase Receipt", pr.name, pr.company)
+		
+		pr1.reload()
+		self.assertEqual(pr1.items[0].valuation_rate, 125)
+
+		# check outgoing_rate for DN after reposting
+		incoming_rate = frappe.db.get_value("Stock Ledger Entry", {"voucher_type": "Purchase Receipt",
+			"voucher_no": pr1.name, "item_code": subcontracted_item}, "incoming_rate")
+		self.assertEqual(incoming_rate, 125)
+
+		# cleanup data
+		pr1.cancel()
+		lcv.cancel()
+		pr.cancel()
+
+	def test_back_dated_entry_not_allowed(self):
+		# Back dated stock transactions are only allowed to stock managers
+		frappe.db.set_value("Stock Settings", None,
+			"role_allowed_to_create_edit_back_dated_transactions", "Stock Manager")
+		
+		# Set User with Stock User role but not Stock Manager
+		frappe.set_user("test@example.com")
+		user = frappe.get_doc("User", "test@example.com")
+		user.add_roles("Stock User")
+		user.remove_roles("Stock Manager")
+
+		stock_entry_on_today = make_stock_entry(target="_Test Warehouse - _TC", qty=10, basic_rate=100)
+		back_dated_se_1 = make_stock_entry(target="_Test Warehouse - _TC", qty=10, basic_rate=100,
+			posting_date=add_days(today(), -1), do_not_submit=True)
+
+		# Block back-dated entry
+		self.assertRaises(BackDatedStockTransaction, back_dated_se_1.submit)
+
+		user.add_roles("Stock Manager")
+
+		# Back dated entry allowed to Stock Manager
+		back_dated_se_2 = make_stock_entry(target="_Test Warehouse - _TC", qty=10, basic_rate=100,
+			posting_date=add_days(today(), -1))
+
+		back_dated_se_2.cancel()
+		stock_entry_on_today.cancel()
+
+		frappe.db.set_value("Stock Settings", None, "role_allowed_to_create_edit_back_dated_transactions", None)
+		frappe.set_user("Administrator")
+
+
+def create_repack_entry(**args):
+	args = frappe._dict(args)
+	repack = frappe.new_doc("Stock Entry")
+	repack.stock_entry_type = "Repack"
+	repack.company = args.company or "_Test Company"
+	repack.posting_date = args.posting_date
+	repack.set_posting_time = 1
+	repack.append("items", {
+		"item_code": "_Test Item for Reposting",
+		"s_warehouse": "Stores - _TC",
+		"qty": 5,
+		"conversion_factor": 1,
+		"expense_account": "Stock Adjustment - _TC",
+		"cost_center": "Main - _TC"
+	})
+
+	repack.append("items", {
+		"item_code": "_Test Finished Item for Reposting",
+		"t_warehouse": "Finished Goods - _TC",
+		"qty": 1,
+		"conversion_factor": 1,
+		"expense_account": "Stock Adjustment - _TC",
+		"cost_center": "Main - _TC"
+	})
+
+	repack.append("additional_costs", {
+		"expense_account": "Freight and Forwarding Charges - _TC",
+		"description": "transport cost",
+		"amount": 40
+	})
+
+	repack.save()
+	repack.submit()
+
+	return repack
+
+def create_product_bundle_item(new_item_code, packed_items):
+	if not frappe.db.exists("Product Bundle", new_item_code):
+		item = frappe.new_doc("Product Bundle")
+		item.new_item_code = new_item_code
+
+		for d in packed_items:
+			item.append("items", {
+				"item_code": d[0],
+				"qty": d[1]
+			})
+
+		item.save()
+
+def create_items():
+	items = ["_Test Item for Reposting", "_Test Finished Item for Reposting",
+		"_Test Subcontracted Item for Reposting", "_Test Bundled Item for Reposting"]
+	for d in items:
+		properties = {"valuation_method": "FIFO"}
+		if d == "_Test Bundled Item for Reposting":
+			properties.update({"is_stock_item": 0})
+		elif d == "_Test Subcontracted Item for Reposting":
+			properties.update({"is_sub_contracted_item": 1})
+
+		make_item(d, properties=properties)
+
+	return items
\ No newline at end of file
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
index 00b8f69..5b40292 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
@@ -37,14 +37,16 @@
 	def on_submit(self):
 		self.update_stock_ledger()
 		self.make_gl_entries()
+		self.repost_future_sle_and_gle()
 
 		from erpnext.stock.doctype.serial_no.serial_no import update_serial_nos_after_submit
 		update_serial_nos_after_submit(self, "items")
 
 	def on_cancel(self):
-		self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry')
+		self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry', 'Repost Item Valuation')
 		self.make_sle_on_cancel()
 		self.make_gl_entries_on_cancel()
+		self.repost_future_sle_and_gle()
 
 	def remove_items_with_no_change(self):
 		"""Remove items if qty or rate is not changed"""
diff --git a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
index 23d48d4..088456f 100644
--- a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
@@ -8,12 +8,11 @@
 import frappe, unittest
 from frappe.utils import flt, nowdate, nowtime
 from erpnext.accounts.utils import get_stock_and_account_balance
-from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory
 from erpnext.stock.stock_ledger import get_previous_sle, update_entries_after
 from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import EmptyStockReconciliationItemsError, get_items
 from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
 from erpnext.stock.doctype.item.test_item import create_item
-from erpnext.stock.utils import get_stock_balance, get_incoming_rate, get_available_serial_nos, get_stock_value_on
+from erpnext.stock.utils import get_incoming_rate, get_stock_value_on, get_valuation_method
 from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
 
 class TestStockReconciliation(unittest.TestCase):
@@ -29,16 +28,17 @@
 		self._test_reco_sle_gle("Moving Average")
 
 	def _test_reco_sle_gle(self, valuation_method):
-		insert_existing_sle(warehouse='Stores - TCP1')
+		se1, se2, se3 = insert_existing_sle(warehouse='Stores - TCP1')
 		company = frappe.db.get_value('Warehouse', 'Stores - TCP1', 'company')
 		# [[qty, valuation_rate, posting_date,
 		#		posting_time, expected_stock_value, bin_qty, bin_valuation]]
+		
 		input_data = [
-			[50, 1000],
-			[25, 900],
-			["", 1000],
-			[20, ""],
-			[0, ""]
+			[50, 1000, "2012-12-26", "12:00"],
+			[25, 900, "2012-12-26", "12:00"],
+			["", 1000, "2012-12-20", "12:05"],
+			[20, "", "2012-12-26", "12:05"],
+			[0, "", "2012-12-31", "12:10"]
 		]
 
 		for d in input_data:
@@ -47,13 +47,13 @@
 			last_sle = get_previous_sle({
 				"item_code": "_Test Item",
 				"warehouse": "Stores - TCP1",
-				"posting_date": nowdate(),
-				"posting_time": nowtime()
+				"posting_date": d[2],
+				"posting_time": d[3]
 			})
 
 			# submit stock reconciliation
 			stock_reco = create_stock_reconciliation(qty=d[0], rate=d[1],
-				posting_date=nowdate(), posting_time=nowtime(), warehouse="Stores - TCP1",
+				posting_date=d[2], posting_time=d[3], warehouse="Stores - TCP1",
 				company=company, expense_account = "Stock Adjustment - TCP1")
 
 			# check stock value
@@ -81,10 +81,15 @@
 
 				stock_reco.cancel()
 
+		se3.cancel()
+		se2.cancel()
+		se1.cancel()
+
 	def test_get_items(self):
-		create_warehouse("_Test Warehouse Group 1", {"is_group": 1})
+		create_warehouse("_Test Warehouse Group 1", 
+			{"is_group": 1, "company": "_Test Company", "parent_warehouse": "All Warehouses - _TC"})
 		create_warehouse("_Test Warehouse Ledger 1",
-			{"is_group": 0, "parent_warehouse": "_Test Warehouse Group 1 - _TC"})
+			{"is_group": 0, "parent_warehouse": "_Test Warehouse Group 1 - _TC", "company": "_Test Company"})
 
 		create_item("_Test Stock Reco Item", is_stock_item=1, valuation_rate=100,
 			warehouse="_Test Warehouse Ledger 1 - _TC", opening_stock=100)
@@ -95,8 +100,6 @@
 			[items[0]["item_code"], items[0]["warehouse"], items[0]["qty"]])
 
 	def test_stock_reco_for_serialized_item(self):
-		set_perpetual_inventory()
-
 		to_delete_records = []
 		to_delete_serial_nos = []
 
@@ -148,8 +151,6 @@
 			stock_doc.cancel()
 
 	def test_stock_reco_for_batch_item(self):
-		set_perpetual_inventory()
-
 		to_delete_records = []
 		to_delete_serial_nos = []
 
@@ -196,15 +197,17 @@
 def insert_existing_sle(warehouse):
 	from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
 
-	make_stock_entry(posting_date=nowdate(), posting_time=nowtime(), item_code="_Test Item",
+	se1 = make_stock_entry(posting_date="2012-12-15", posting_time="02:00", item_code="_Test Item",
 		target=warehouse, qty=10, basic_rate=700)
 
-	make_stock_entry(posting_date=nowdate(), posting_time=nowtime(), item_code="_Test Item",
+	se2 = make_stock_entry(posting_date="2012-12-25", posting_time="03:00", item_code="_Test Item",
 		source=warehouse, qty=15)
 
-	make_stock_entry(posting_date=nowdate(), posting_time=nowtime(), item_code="_Test Item",
+	se3 = make_stock_entry(posting_date="2013-01-05", posting_time="07:00", item_code="_Test Item",
 		target=warehouse, qty=15, basic_rate=1200)
 
+	return se1, se2, se3
+
 def create_batch_or_serial_no_items():
 	create_warehouse("_Test Warehouse for Stock Reco1",
 		{"is_group": 0, "parent_warehouse": "_Test Warehouse Group - _TC"})
@@ -256,6 +259,10 @@
 	return sr
 
 def set_valuation_method(item_code, valuation_method):
+	existing_valuation_method = get_valuation_method(item_code)
+	if valuation_method == existing_valuation_method:
+		return
+
 	frappe.db.set_value("Item", item_code, "valuation_method", valuation_method)
 
 	for warehouse in frappe.get_all("Warehouse", filters={"company": "_Test Company"}, fields=["name", "is_group"]):
diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.json b/erpnext/stock/doctype/stock_settings/stock_settings.json
index a166657..859aea2 100644
--- a/erpnext/stock/doctype/stock_settings/stock_settings.json
+++ b/erpnext/stock/doctype/stock_settings/stock_settings.json
@@ -28,7 +28,9 @@
   "inter_warehouse_transfer_settings_section",
   "allow_from_dn",
   "allow_from_pr",
-  "freeze_stock_entries",
+  "control_historical_stock_transactions_section",
+  "role_allowed_to_create_edit_back_dated_transactions",
+  "column_break_26",
   "stock_frozen_upto",
   "stock_frozen_upto_days",
   "stock_auth_role",
@@ -156,21 +158,20 @@
    "label": "Notify by Email on Creation of Automatic Material Request"
   },
   {
-   "fieldname": "freeze_stock_entries",
-   "fieldtype": "Section Break",
-   "label": "Freeze Stock Entries"
-  },
-  {
+   "description": "No stock transactions can be created or modified before this date.",
    "fieldname": "stock_frozen_upto",
    "fieldtype": "Date",
    "label": "Stock Frozen Upto"
   },
   {
+   "description": "Stock transactions that are older than the mentioned days cannot be modified.",
    "fieldname": "stock_frozen_upto_days",
    "fieldtype": "Int",
    "label": "Freeze Stocks Older Than (Days)"
   },
   {
+   "depends_on": "eval:(doc.stock_frozen_upto || doc.stock_frozen_upto_days)",
+   "description": "The users with this Role are allowed to create/modify a stock transaction, even though the transaction is frozen.",
    "fieldname": "stock_auth_role",
    "fieldtype": "Link",
    "label": "Role Allowed to Edit Frozen Stock",
@@ -210,6 +211,22 @@
    "fieldname": "allow_from_pr",
    "fieldtype": "Check",
    "label": "Allow Material Transfer from Purchase Receipt to Purchase Invoice"
+  },
+  {
+   "description": "If mentioned, the system will allow only the users with this Role to create or modify any stock transaction earlier than the latest stock transaction for a specific item and warehouse. If set as blank, it allows all users to create/edit back-dated transactions.",
+   "fieldname": "role_allowed_to_create_edit_back_dated_transactions",
+   "fieldtype": "Link",
+   "label": "Role Allowed to Create/Edit Back-dated Transactions",
+   "options": "User"
+  },
+  {
+   "fieldname": "column_break_26",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "control_historical_stock_transactions_section",
+   "fieldtype": "Section Break",
+   "label": "Control Historical Stock Transactions"
   }
  ],
  "icon": "icon-cog",
@@ -217,7 +234,7 @@
  "index_web_pages_for_search": 1,
  "issingle": 1,
  "links": [],
- "modified": "2020-11-23 15:26:54.225608",
+ "modified": "2020-11-23 22:26:54.225608",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Stock Settings",
diff --git a/erpnext/stock/doctype/warehouse/test_warehouse.py b/erpnext/stock/doctype/warehouse/test_warehouse.py
index 3101e8a..95478f6 100644
--- a/erpnext/stock/doctype/warehouse/test_warehouse.py
+++ b/erpnext/stock/doctype/warehouse/test_warehouse.py
@@ -10,13 +10,10 @@
 
 import erpnext
 from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
-from erpnext import set_perpetual_inventory
 from erpnext.accounts.doctype.account.test_account import get_inventory_account, create_account
 
-
 test_records = frappe.get_test_records('Warehouse')
 
-
 class TestWarehouse(unittest.TestCase):
 	def setUp(self):
 		if not frappe.get_value('Item', '_Test Item'):
@@ -37,63 +34,63 @@
 			self.assertEqual(child_warehouse.is_group, 0)
 
 	def test_warehouse_renaming(self):
-		set_perpetual_inventory(1)
-		create_warehouse("Test Warehouse for Renaming 1")
-		account = get_inventory_account("_Test Company", "Test Warehouse for Renaming 1 - _TC")
+		create_warehouse("Test Warehouse for Renaming 1", company="_Test Company with perpetual inventory")
+		account = get_inventory_account("_Test Company with perpetual inventory", "Test Warehouse for Renaming 1 - TCP1")
 		self.assertTrue(frappe.db.get_value("Warehouse", filters={"account": account}))
 
 		# Rename with abbr
-		if frappe.db.exists("Warehouse", "Test Warehouse for Renaming 2 - _TC"):
-			frappe.delete_doc("Warehouse", "Test Warehouse for Renaming 2 - _TC")
-		frappe.rename_doc("Warehouse", "Test Warehouse for Renaming 1 - _TC", "Test Warehouse for Renaming 2 - _TC")
+		if frappe.db.exists("Warehouse", "Test Warehouse for Renaming 2 - TCP1"):
+			frappe.delete_doc("Warehouse", "Test Warehouse for Renaming 2 - TCP1")
+		frappe.rename_doc("Warehouse", "Test Warehouse for Renaming 1 - TCP1", "Test Warehouse for Renaming 2 - TCP1")
 
 		self.assertTrue(frappe.db.get_value("Warehouse",
-			filters={"account": "Test Warehouse for Renaming 1 - _TC"}))
+			filters={"account": "Test Warehouse for Renaming 1 - TCP1"}))
 
 		# Rename without abbr
-		if frappe.db.exists("Warehouse", "Test Warehouse for Renaming 3 - _TC"):
-			frappe.delete_doc("Warehouse", "Test Warehouse for Renaming 3 - _TC")
+		if frappe.db.exists("Warehouse", "Test Warehouse for Renaming 3 - TCP1"):
+			frappe.delete_doc("Warehouse", "Test Warehouse for Renaming 3 - TCP1")
 
-		frappe.rename_doc("Warehouse", "Test Warehouse for Renaming 2 - _TC", "Test Warehouse for Renaming 3")
+		frappe.rename_doc("Warehouse", "Test Warehouse for Renaming 2 - TCP1", "Test Warehouse for Renaming 3")
 
 		self.assertTrue(frappe.db.get_value("Warehouse",
-			filters={"account": "Test Warehouse for Renaming 1 - _TC"}))
+			filters={"account": "Test Warehouse for Renaming 1 - TCP1"}))
 
 		# Another rename with multiple dashes
-		if frappe.db.exists("Warehouse", "Test - Warehouse - Company - _TC"):
-			frappe.delete_doc("Warehouse", "Test - Warehouse - Company - _TC")
-		frappe.rename_doc("Warehouse", "Test Warehouse for Renaming 3 - _TC", "Test - Warehouse - Company")
+		if frappe.db.exists("Warehouse", "Test - Warehouse - Company - TCP1"):
+			frappe.delete_doc("Warehouse", "Test - Warehouse - Company - TCP1")
+		frappe.rename_doc("Warehouse", "Test Warehouse for Renaming 3 - TCP1", "Test - Warehouse - Company")
 
 	def test_warehouse_merging(self):
-		set_perpetual_inventory(1)
+		company = "_Test Company with perpetual inventory"
+		create_warehouse("Test Warehouse for Merging 1", company=company,
+			properties={"parent_warehouse": "All Warehouses - TCP1"})
+		create_warehouse("Test Warehouse for Merging 2", company=company,
+			properties={"parent_warehouse": "All Warehouses - TCP1"})
 
-		create_warehouse("Test Warehouse for Merging 1")
-		create_warehouse("Test Warehouse for Merging 2")
-
-		make_stock_entry(item_code="_Test Item", target="Test Warehouse for Merging 1 - _TC",
-			qty=1, rate=100)
-		make_stock_entry(item_code="_Test Item", target="Test Warehouse for Merging 2 - _TC",
-			qty=1, rate=100)
+		make_stock_entry(item_code="_Test Item", target="Test Warehouse for Merging 1 - TCP1",
+			qty=1, rate=100, company=company)
+		make_stock_entry(item_code="_Test Item", target="Test Warehouse for Merging 2 - TCP1",
+			qty=1, rate=100, company=company)
 
 		existing_bin_qty = (
 			cint(frappe.db.get_value("Bin",
-				{"item_code": "_Test Item", "warehouse": "Test Warehouse for Merging 1 - _TC"}, "actual_qty"))
+				{"item_code": "_Test Item", "warehouse": "Test Warehouse for Merging 1 - TCP1"}, "actual_qty"))
 			+ cint(frappe.db.get_value("Bin",
-				{"item_code": "_Test Item", "warehouse": "Test Warehouse for Merging 2 - _TC"}, "actual_qty"))
+				{"item_code": "_Test Item", "warehouse": "Test Warehouse for Merging 2 - TCP1"}, "actual_qty"))
 		)
 
-		frappe.rename_doc("Warehouse", "Test Warehouse for Merging 1 - _TC",
-			"Test Warehouse for Merging 2 - _TC", merge=True)
+		frappe.rename_doc("Warehouse", "Test Warehouse for Merging 1 - TCP1",
+			"Test Warehouse for Merging 2 - TCP1", merge=True)
 
-		self.assertFalse(frappe.db.exists("Warehouse", "Test Warehouse for Merging 1 - _TC"))
+		self.assertFalse(frappe.db.exists("Warehouse", "Test Warehouse for Merging 1 - TCP1"))
 
 		bin_qty = frappe.db.get_value("Bin",
-			{"item_code": "_Test Item", "warehouse": "Test Warehouse for Merging 2 - _TC"}, "actual_qty")
+			{"item_code": "_Test Item", "warehouse": "Test Warehouse for Merging 2 - TCP1"}, "actual_qty")
 
 		self.assertEqual(bin_qty, existing_bin_qty)
 
 		self.assertTrue(frappe.db.get_value("Warehouse",
-			filters={"account": "Test Warehouse for Merging 2 - _TC"}))
+			filters={"account": "Test Warehouse for Merging 2 - TCP1"}))
 
 def create_warehouse(warehouse_name, properties=None, company=None):
 	if not company:
diff --git a/erpnext/stock/doctype/warehouse/warehouse.py b/erpnext/stock/doctype/warehouse/warehouse.py
index cd86be3..6c84f16 100644
--- a/erpnext/stock/doctype/warehouse/warehouse.py
+++ b/erpnext/stock/doctype/warehouse/warehouse.py
@@ -29,7 +29,6 @@
 				self.set_onload('account', account)
 		load_address_and_contact(self)
 
-
 	def on_update(self):
 		self.update_nsm_model()
 
diff --git a/erpnext/stock/report/stock_analytics/stock_analytics.py b/erpnext/stock/report/stock_analytics/stock_analytics.py
index 54eefdf..0cc8ca4 100644
--- a/erpnext/stock/report/stock_analytics/stock_analytics.py
+++ b/erpnext/stock/report/stock_analytics/stock_analytics.py
@@ -7,9 +7,11 @@
 from frappe.utils import getdate, flt
 from erpnext.stock.report.stock_balance.stock_balance import (get_items, get_stock_ledger_entries, get_item_details)
 from erpnext.accounts.utils import get_fiscal_year
+from erpnext.stock.utils import is_reposting_item_valuation_in_progress
 from six import iteritems
 
 def execute(filters=None):
+	is_reposting_item_valuation_in_progress()
 	filters = frappe._dict(filters or {})
 	columns = get_columns(filters)
 	data = get_data(filters)
diff --git a/erpnext/stock/report/stock_balance/stock_balance.py b/erpnext/stock/report/stock_balance/stock_balance.py
index ccd0100..e5d4d62 100644
--- a/erpnext/stock/report/stock_balance/stock_balance.py
+++ b/erpnext/stock/report/stock_balance/stock_balance.py
@@ -7,12 +7,13 @@
 from frappe.utils import flt, cint, getdate, now, date_diff
 from erpnext.stock.utils import add_additional_uom_columns
 from erpnext.stock.report.stock_ledger.stock_ledger import get_item_group_condition
-
+from erpnext.stock.utils import is_reposting_item_valuation_in_progress
 from erpnext.stock.report.stock_ageing.stock_ageing import get_fifo_queue, get_average_age
 
 from six import iteritems
 
 def execute(filters=None):
+	is_reposting_item_valuation_in_progress()
 	if not filters: filters = {}
 
 	validate_filters(filters)
diff --git a/erpnext/stock/report/stock_ledger/stock_ledger.py b/erpnext/stock/report/stock_ledger/stock_ledger.py
index 86af5e0..7b5701a 100644
--- a/erpnext/stock/report/stock_ledger/stock_ledger.py
+++ b/erpnext/stock/report/stock_ledger/stock_ledger.py
@@ -5,11 +5,12 @@
 
 import frappe
 from frappe.utils import cint, flt
-from erpnext.stock.utils import update_included_uom_in_report
+from erpnext.stock.utils import update_included_uom_in_report, is_reposting_item_valuation_in_progress
 from frappe import _
 from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
 
 def execute(filters=None):
+	is_reposting_item_valuation_in_progress()
 	include_uom = filters.get("include_uom")
 	columns = get_columns()
 	items = get_items(filters)
diff --git a/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py b/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py
index c8efb16..1183e41 100644
--- a/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py
+++ b/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py
@@ -5,9 +5,10 @@
 import frappe
 from frappe import _
 from frappe.utils import flt, today
-from erpnext.stock.utils import update_included_uom_in_report
+from erpnext.stock.utils import update_included_uom_in_report, is_reposting_item_valuation_in_progress
 
 def execute(filters=None):
+	is_reposting_item_valuation_in_progress()
 	filters = frappe._dict(filters or {})
 	include_uom = filters.get("include_uom")
 	columns = get_columns()
diff --git a/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.py b/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.py
index ebcb106..04f7d34 100644
--- a/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.py
+++ b/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.py
@@ -11,9 +11,11 @@
 from erpnext.stock.report.stock_balance.stock_balance import (get_item_details,
 	get_item_reorder_details, get_item_warehouse_map, get_items, get_stock_ledger_entries)
 from erpnext.stock.report.stock_ageing.stock_ageing import get_fifo_queue, get_average_age
+from erpnext.stock.utils import is_reposting_item_valuation_in_progress
 from six import iteritems
 
 def execute(filters=None):
+	is_reposting_item_valuation_in_progress()
 	if not filters: filters = {}
 
 	validate_filters(filters)
diff --git a/erpnext/stock/stock_balance.py b/erpnext/stock/stock_balance.py
index b5ae1b7..8ba1f1c 100644
--- a/erpnext/stock/stock_balance.py
+++ b/erpnext/stock/stock_balance.py
@@ -6,6 +6,7 @@
 from frappe.utils import flt, cstr, nowdate, nowtime
 from erpnext.stock.utils import update_bin
 from erpnext.stock.stock_ledger import update_entries_after
+from erpnext.controllers.stock_controller import create_repost_item_valuation_entry
 
 def repost(only_actual=False, allow_negative_stock=False, allow_zero_rate=False, only_bin=False):
 	"""
@@ -56,12 +57,18 @@
 		update_bin_qty(item_code, warehouse, qty_dict)
 
 def repost_actual_qty(item_code, warehouse, allow_zero_rate=False, allow_negative_stock=False):
-	update_entries_after({ "item_code": item_code, "warehouse": warehouse },
-		allow_zero_rate=allow_zero_rate, allow_negative_stock=allow_negative_stock)
+	create_repost_item_valuation_entry({
+		"item_code": item_code,
+		"warehouse": warehouse,
+		"posting_date": "1900-01-01",
+		"posting_time": "00:01",
+		"allow_negative_stock": allow_negative_stock,
+		"allow_zero_rate": allow_zero_rate
+	})
 
 def get_balance_qty_from_sle(item_code, warehouse):
 	balance_qty = frappe.db.sql("""select qty_after_transaction from `tabStock Ledger Entry`
-		where item_code=%s and warehouse=%s
+		where item_code=%s and warehouse=%s and is_cancelled=0
 		order by posting_date desc, posting_time desc, creation desc
 		limit 1""", (item_code, warehouse))
 
@@ -191,7 +198,7 @@
 			print(d[0], d[1], d[2], serial_nos[0][0])
 
 		sle = frappe.db.sql("""select valuation_rate, company from `tabStock Ledger Entry`
-			where item_code = %s and warehouse = %s
+			where item_code = %s and warehouse = %s and is_cancelled = 0
 			order by posting_date desc limit 1""", (d[0], d[1]))
 
 		sle_dict = {
@@ -223,7 +230,8 @@
 		})
 
 		update_bin(args)
-		update_entries_after({
+		
+		create_repost_item_valuation_entry({
 			"item_code": d[0],
 			"warehouse": d[1],
 			"posting_date": posting_date,
diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py
index f4490f1..5b9ada0 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -5,9 +5,10 @@
 import frappe, erpnext
 from frappe import _
 from frappe.utils import cint, flt, cstr, now, now_datetime
+from frappe.model.meta import get_field_precision
 from erpnext.stock.utils import get_valuation_method, get_incoming_outgoing_rate_for_cancel
+from erpnext.stock.utils import get_bin
 import json
-
 from six import iteritems
 
 # future reposting
@@ -25,32 +26,23 @@
 			set_as_cancel(sl_entries[0].get('voucher_type'), sl_entries[0].get('voucher_no'))
 
 		for sle in sl_entries:
-			sle_id = None
-			if via_landed_cost_voucher or cancel:
-				sle['posting_date'] = now_datetime().strftime('%Y-%m-%d')
-				sle['posting_time'] = now_datetime().strftime('%H:%M:%S.%f')
+			if cancel:
+				sle['actual_qty'] = -flt(sle.get('actual_qty'))
 
-				if cancel:
-					sle['actual_qty'] = -flt(sle.get('actual_qty'))
+				if sle['actual_qty'] < 0 and not sle.get('outgoing_rate'):
+					sle['outgoing_rate'] = get_incoming_outgoing_rate_for_cancel(sle.item_code,
+						sle.voucher_type, sle.voucher_no, sle.voucher_detail_no)
+					sle['incoming_rate'] = 0.0
 
-					if sle['actual_qty'] < 0 and not sle.get('outgoing_rate'):
-						sle['outgoing_rate'] = get_incoming_outgoing_rate_for_cancel(sle.item_code,
-							sle.voucher_type, sle.voucher_no, sle.voucher_detail_no)
-						sle['incoming_rate'] = 0.0
-
-					if sle['actual_qty'] > 0 and not sle.get('incoming_rate'):
-						sle['incoming_rate'] = get_incoming_outgoing_rate_for_cancel(sle.item_code,
-							sle.voucher_type, sle.voucher_no, sle.voucher_detail_no)
-						sle['outgoing_rate'] = 0.0
-
+				if sle['actual_qty'] > 0 and not sle.get('incoming_rate'):
+					sle['incoming_rate'] = get_incoming_outgoing_rate_for_cancel(sle.item_code,
+						sle.voucher_type, sle.voucher_no, sle.voucher_detail_no)
+					sle['outgoing_rate'] = 0.0
 
 			if sle.get("actual_qty") or sle.get("voucher_type")=="Stock Reconciliation":
-				sle_id = make_entry(sle, allow_negative_stock, via_landed_cost_voucher)
-
-			args = sle.copy()
-			args.update({
-				"sle_id": sle_id
-			})
+				sle_doc = make_entry(sle, allow_negative_stock, via_landed_cost_voucher)
+			
+			args = sle_doc.as_dict()
 			update_bin(args, allow_negative_stock, via_landed_cost_voucher)
 
 
@@ -68,8 +60,36 @@
 	sle.via_landed_cost_voucher = via_landed_cost_voucher
 	sle.insert()
 	sle.submit()
-	return sle.name
+	return sle
 
+def repost_future_sle(args=None, voucher_type=None, voucher_no=None, allow_negative_stock=False, via_landed_cost_voucher=False):
+	if not args and voucher_type and voucher_no:
+		args = get_args_for_voucher(voucher_type, voucher_no)
+	
+	distinct_item_warehouses = [(d.item_code, d.warehouse) for d in args]
+
+	i = 0
+	while i < len(args):
+		obj = update_entries_after({
+			"item_code": args[i].item_code,
+			"warehouse": args[i].warehouse,
+			"posting_date": args[i].posting_date,
+			"posting_time": args[i].posting_time
+		}, allow_negative_stock=allow_negative_stock, via_landed_cost_voucher=via_landed_cost_voucher)
+
+		for item_wh, new_sle in iteritems(obj.new_items):
+			if item_wh not in distinct_item_warehouses:
+				args.append(new_sle)
+		
+		i += 1
+
+def get_args_for_voucher(voucher_type, voucher_no):
+	return frappe.db.get_all("Stock Ledger Entry",
+		filters={"voucher_type": voucher_type, "voucher_no": voucher_no},
+		fields=["item_code", "warehouse", "posting_date", "posting_time"],
+		order_by="creation asc",
+		group_by="item_code, warehouse"
+	)
 
 class update_entries_after(object):
 	"""
@@ -86,141 +106,299 @@
 			}
 	"""
 	def __init__(self, args, allow_zero_rate=False, allow_negative_stock=None, via_landed_cost_voucher=False, verbose=1):
-		from frappe.model.meta import get_field_precision
-
-		self.exceptions = []
+		self.exceptions = {}
 		self.verbose = verbose
 		self.allow_zero_rate = allow_zero_rate
-		self.allow_negative_stock = allow_negative_stock
 		self.via_landed_cost_voucher = via_landed_cost_voucher
-		if not self.allow_negative_stock:
-			self.allow_negative_stock = cint(frappe.db.get_single_value("Stock Settings",
-				"allow_negative_stock"))
+		self.allow_negative_stock = allow_negative_stock \
+			or cint(frappe.db.get_single_value("Stock Settings", "allow_negative_stock"))
 
-		self.args = args
-		for key, value in iteritems(args):
-			setattr(self, key, value)
+		self.args = frappe._dict(args)
+		self.item_code = args.get("item_code")
+		if self.args.sle_id:
+			self.args['name'] = self.args.sle_id
 
-		self.previous_sle = self.get_sle_before_datetime()
-		self.previous_sle = self.previous_sle[0] if self.previous_sle else frappe._dict()
+		self.company = frappe.get_cached_value("Warehouse", self.args.warehouse, "company")
+		self.get_precision()
+		self.valuation_method = get_valuation_method(self.item_code)
+		self.new_items = {}
+
+		self.data = frappe._dict()
+		self.initialize_previous_data(self.args)
+
+		self.build()
+	
+	def get_precision(self):
+		company_base_currency = frappe.get_cached_value('Company',  self.company,  "default_currency")
+		self.precision = get_field_precision(frappe.get_meta("Stock Ledger Entry").get_field("stock_value"),
+			currency=company_base_currency)
+
+	def initialize_previous_data(self, args):
+		"""
+			Get previous sl entries for current item for each related warehouse
+			and assigns into self.data dict
+
+			:Data Structure:
+
+			self.data = {
+				warehouse1: {
+					'previus_sle': {},
+					'qty_after_transaction': 10,
+					'valuation_rate': 100,
+					'stock_value': 1000,
+					'prev_stock_value': 1000,
+					'stock_queue': '[[10, 100]]',
+					'stock_value_difference': 1000
+				}
+			}
+
+		"""
+		self.data.setdefault(args.warehouse, frappe._dict())
+		warehouse_dict = self.data[args.warehouse]
+		previous_sle = self.get_sle_before_datetime(args)
+		warehouse_dict.previous_sle = previous_sle
 
 		for key in ("qty_after_transaction", "valuation_rate", "stock_value"):
-			setattr(self, key, flt(self.previous_sle.get(key)))
+			setattr(warehouse_dict, key, flt(previous_sle.get(key)))
 
-		self.company = frappe.db.get_value("Warehouse", self.warehouse, "company")
-		self.precision = get_field_precision(frappe.get_meta("Stock Ledger Entry").get_field("stock_value"),
-			currency=frappe.get_cached_value('Company',  self.company,  "default_currency"))
+		warehouse_dict.update({
+			"prev_stock_value": previous_sle.stock_value or 0.0,
+			"stock_queue": json.loads(previous_sle.stock_queue or "[]"),
+			"stock_value_difference": 0.0
+		})
 
-		self.prev_stock_value = self.previous_sle.stock_value or 0.0
-		self.stock_queue = json.loads(self.previous_sle.stock_queue or "[]")
-		self.valuation_method = get_valuation_method(self.item_code)
-		self.stock_value_difference = 0.0
-		self.build(args.get('sle_id'))
-
-	def build(self, sle_id):
-		if sle_id:
-			sle = get_sle_by_id(sle_id)
-			self.process_sle(sle)
+	def build(self):
+		if self.args.get("sle_id"):
+			self.process_sle_against_current_voucher()
 		else:
-			# includes current entry!
-			entries_to_fix = self.get_sle_after_datetime()
-			for sle in entries_to_fix:
+			entries_to_fix = self.get_future_entries_to_fix()
+
+			i = 0
+			while i < len(entries_to_fix):
+				sle = entries_to_fix[i]
+				i += 1
+
 				self.process_sle(sle)
 
+				if sle.dependant_sle_voucher_detail_no:
+					self.get_dependent_entries_to_fix(entries_to_fix, sle)
+
 		if self.exceptions:
 			self.raise_exceptions()
 
 		self.update_bin()
 
-	def update_bin(self):
-		# update bin
-		bin_name = frappe.db.get_value("Bin", {
-			"item_code": self.item_code,
-			"warehouse": self.warehouse
-		})
+	def process_sle_against_current_voucher(self):
+		sl_entries = self.get_sle_against_current_voucher()
+		for sle in sl_entries:
+			self.process_sle(sle)
 
-		if not bin_name:
-			bin_doc = frappe.get_doc({
-				"doctype": "Bin",
-				"item_code": self.item_code,
-				"warehouse": self.warehouse
-			})
-			bin_doc.insert(ignore_permissions=True)
-		else:
-			bin_doc = frappe.get_doc("Bin", bin_name)
+	def get_sle_against_current_voucher(self):
+		return frappe.db.sql("""
+			select
+				*, timestamp(posting_date, posting_time) as "timestamp"
+			from
+				`tabStock Ledger Entry`
+			where
+				item_code = %(item_code)s
+				and warehouse = %(warehouse)s
+				and voucher_type = %(voucher_type)s
+				and voucher_no = %(voucher_no)s
+			order by
+				creation ASC
+			for update
+		""", self.args, as_dict=1)
 
-		bin_doc.update({
-			"valuation_rate": self.valuation_rate,
-			"actual_qty": self.qty_after_transaction,
-			"stock_value": self.stock_value
-		})
-		bin_doc.flags.via_stock_ledger_entry = True
+	def get_future_entries_to_fix(self):
+		# includes current entry!
+		args = self.data[self.args.warehouse].previous_sle \
+			or frappe._dict({"item_code": self.item_code, "warehouse": self.args.warehouse})
+		
+		return list(self.get_sle_after_datetime(args))
 
-		bin_doc.save(ignore_permissions=True)
+	def get_dependent_entries_to_fix(self, entries_to_fix, sle):
+		dependant_sle = get_sle_by_voucher_detail_no(sle.dependant_sle_voucher_detail_no,
+			excluded_sle=sle.name)
+		
+		if not dependant_sle:
+			return
+		elif dependant_sle.item_code == self.item_code and dependant_sle.warehouse == self.args.warehouse:
+			return
+		elif dependant_sle.item_code != self.item_code \
+				and (dependant_sle.item_code, dependant_sle.warehouse) not in self.new_items:
+			self.new_items[(dependant_sle.item_code, dependant_sle.warehouse)] = dependant_sle
+			return
+
+		self.initialize_previous_data(dependant_sle)
+
+		args = self.data[dependant_sle.warehouse].previous_sle \
+			or frappe._dict({"item_code": self.item_code, "warehouse": dependant_sle.warehouse})
+		future_sle_for_dependant = list(self.get_sle_after_datetime(args))
+
+		entries_to_fix.extend(future_sle_for_dependant)
+		entries_to_fix = sorted(entries_to_fix, key=lambda k: k['timestamp'])
 
 	def process_sle(self, sle):
+		# previous sle data for this warehouse
+		self.wh_data = self.data[sle.warehouse]
+
 		if (sle.serial_no and not self.via_landed_cost_voucher) or not cint(self.allow_negative_stock):
 			# validate negative stock for serialized items, fifo valuation
 			# or when negative stock is not allowed for moving average
 			if not self.validate_negative_stock(sle):
-				self.qty_after_transaction += flt(sle.actual_qty)
+				self.wh_data.qty_after_transaction += flt(sle.actual_qty)
 				return
 
+		# Get dynamic incoming/outgoing rate
+		self.get_dynamic_incoming_outgoing_rate(sle)
+		
 		if sle.serial_no:
 			self.get_serialized_values(sle)
-			self.qty_after_transaction += flt(sle.actual_qty)
+			self.wh_data.qty_after_transaction += flt(sle.actual_qty)
 			if sle.voucher_type == "Stock Reconciliation":
-				self.qty_after_transaction = sle.qty_after_transaction
+				self.wh_data.qty_after_transaction = sle.qty_after_transaction
 
-			self.stock_value = flt(self.qty_after_transaction) * flt(self.valuation_rate)
+			self.wh_data.stock_value = flt(self.wh_data.qty_after_transaction) * flt(self.wh_data.valuation_rate)
 		else:
 			if sle.voucher_type=="Stock Reconciliation" and not sle.batch_no:
 				# assert
-				self.valuation_rate = sle.valuation_rate
-				self.qty_after_transaction = sle.qty_after_transaction
-				self.stock_queue = [[self.qty_after_transaction, self.valuation_rate]]
-				self.stock_value = flt(self.qty_after_transaction) * flt(self.valuation_rate)
+				self.wh_data.valuation_rate = sle.valuation_rate
+				self.wh_data.qty_after_transaction = sle.qty_after_transaction
+				self.wh_data.stock_queue = [[self.wh_data.qty_after_transaction, self.wh_data.valuation_rate]]
+				self.wh_data.stock_value = flt(self.wh_data.qty_after_transaction) * flt(self.wh_data.valuation_rate)
 			else:
 				if self.valuation_method == "Moving Average":
 					self.get_moving_average_values(sle)
-					self.qty_after_transaction += flt(sle.actual_qty)
-					self.stock_value = flt(self.qty_after_transaction) * flt(self.valuation_rate)
+					self.wh_data.qty_after_transaction += flt(sle.actual_qty)
+					self.wh_data.stock_value = flt(self.wh_data.qty_after_transaction) * flt(self.wh_data.valuation_rate)
 				else:
 					self.get_fifo_values(sle)
-					self.qty_after_transaction += flt(sle.actual_qty)
-					self.stock_value = sum((flt(batch[0]) * flt(batch[1]) for batch in self.stock_queue))
+					self.wh_data.qty_after_transaction += flt(sle.actual_qty)
+					self.wh_data.stock_value = sum((flt(batch[0]) * flt(batch[1]) for batch in self.wh_data.stock_queue))
 
 		# rounding as per precision
-		self.stock_value = flt(self.stock_value, self.precision)
-
-		stock_value_difference = self.stock_value - self.prev_stock_value
-
-		self.prev_stock_value = self.stock_value
+		self.wh_data.stock_value = flt(self.wh_data.stock_value, self.precision)
+		stock_value_difference = self.wh_data.stock_value - self.wh_data.prev_stock_value
+		self.wh_data.prev_stock_value = self.wh_data.stock_value
 
 		# update current sle
-		sle.qty_after_transaction = self.qty_after_transaction
-		sle.valuation_rate = self.valuation_rate
-		sle.stock_value = self.stock_value
-		sle.stock_queue = json.dumps(self.stock_queue)
+		sle.qty_after_transaction = self.wh_data.qty_after_transaction
+		sle.valuation_rate = self.wh_data.valuation_rate
+		sle.stock_value = self.wh_data.stock_value
+		sle.stock_queue = json.dumps(self.wh_data.stock_queue)
 		sle.stock_value_difference = stock_value_difference
 		sle.doctype="Stock Ledger Entry"
 		frappe.get_doc(sle).db_update()
 
+		self.update_outgoing_rate_on_transaction(sle)
+
 	def validate_negative_stock(self, sle):
 		"""
 			validate negative stock for entries current datetime onwards
 			will not consider cancelled entries
 		"""
-		diff = self.qty_after_transaction + flt(sle.actual_qty)
+		diff = self.wh_data.qty_after_transaction + flt(sle.actual_qty)
 
 		if diff < 0 and abs(diff) > 0.0001:
 			# negative stock!
 			exc = sle.copy().update({"diff": diff})
-			self.exceptions.append(exc)
+			self.exceptions.setdefault(sle.warehouse, []).append(exc)
 			return False
 		else:
 			return True
 
+	def get_dynamic_incoming_outgoing_rate(self, sle):
+		# Get updated incoming/outgoing rate from transaction
+		if sle.recalculate_rate:
+			rate = self.get_incoming_outgoing_rate_from_transaction(sle)
+
+			if flt(sle.actual_qty) >= 0:
+				sle.incoming_rate = rate
+			else:
+				sle.outgoing_rate = rate
+
+	def get_incoming_outgoing_rate_from_transaction(self, sle):
+		rate = 0
+		# Material Transfer, Repack, Manufacturing
+		if sle.voucher_type == "Stock Entry":
+			rate = frappe.db.get_value("Stock Entry Detail", sle.voucher_detail_no, "valuation_rate")
+		# Sales and Purchase Return
+		elif sle.voucher_type in ("Purchase Receipt", "Purchase Invoice", "Delivery Note", "Sales Invoice"):
+			if frappe.get_cached_value(sle.voucher_type, sle.voucher_no, "is_return"):
+				from erpnext.controllers.sales_and_purchase_return import get_rate_for_return # don't move this import to top
+				rate = get_rate_for_return(sle.voucher_type, sle.voucher_no, sle.item_code, voucher_detail_no=sle.voucher_detail_no)
+			else:
+				if sle.voucher_type in ("Purchase Receipt", "Purchase Invoice"):
+					rate_field = "valuation_rate" 
+				else:
+					rate_field = "incoming_rate"
+
+				# check in item table
+				item_code, incoming_rate = frappe.db.get_value(sle.voucher_type + " Item",
+					sle.voucher_detail_no, ["item_code", rate_field])
+
+				if item_code == sle.item_code:
+					rate = incoming_rate
+				else:
+					if sle.voucher_type in ("Delivery Note", "Sales Invoice"):
+						ref_doctype = "Packed Item"
+					else:
+						ref_doctype = "Purchase Receipt Item Supplied"
+	
+					rate = frappe.db.get_value(ref_doctype, {"parent_detail_docname": sle.voucher_detail_no,
+						"item_code": sle.item_code}, rate_field)
+
+		return rate
+
+	def update_outgoing_rate_on_transaction(self, sle):
+		"""
+			Update outgoing rate in Stock Entry, Delivery Note, Sales Invoice and Sales Return
+			In case of Stock Entry, also calculate FG Item rate and total incoming/outgoing amount
+		"""
+		if sle.actual_qty and sle.voucher_detail_no:
+			outgoing_rate = abs(flt(sle.stock_value_difference)) / abs(sle.actual_qty)
+
+			if flt(sle.actual_qty) < 0 and sle.voucher_type == "Stock Entry":
+				self.update_rate_on_stock_entry(sle, outgoing_rate)
+			elif sle.voucher_type in ("Delivery Note", "Sales Invoice"):
+				self.update_rate_on_delivery_and_sales_return(sle, outgoing_rate)
+			elif flt(sle.actual_qty) < 0 and sle.voucher_type in ("Purchase Receipt", "Purchase Invoice"):
+				self.update_rate_on_purchase_receipt(sle, outgoing_rate)
+
+	def update_rate_on_stock_entry(self, sle, outgoing_rate):
+		frappe.db.set_value("Stock Entry Detail", sle.voucher_detail_no, "basic_rate", outgoing_rate)
+
+		# Update outgoing item's rate, recalculate FG Item's rate and total incoming/outgoing amount
+		stock_entry = frappe.get_doc("Stock Entry", sle.voucher_no)
+		stock_entry.calculate_rate_and_amount(reset_outgoing_rate=False, raise_error_if_no_rate=False)
+		stock_entry.db_update()
+		for d in stock_entry.items:
+			d.db_update()
+	
+	def update_rate_on_delivery_and_sales_return(self, sle, outgoing_rate):
+		# Update item's incoming rate on transaction
+		item_code = frappe.db.get_value(sle.voucher_type + " Item", sle.voucher_detail_no, "item_code")
+		if item_code == sle.item_code:
+			frappe.db.set_value(sle.voucher_type + " Item", sle.voucher_detail_no, "incoming_rate", outgoing_rate)
+		else:
+			# packed item
+			frappe.db.set_value("Packed Item",
+				{"parent_detail_docname": sle.voucher_detail_no, "item_code": sle.item_code},
+				"incoming_rate", outgoing_rate)
+
+	def update_rate_on_purchase_receipt(self, sle, outgoing_rate):
+		if frappe.db.exists(sle.voucher_type + " Item", sle.voucher_detail_no):
+			frappe.db.set_value(sle.voucher_type + " Item", sle.voucher_detail_no, "base_net_rate", outgoing_rate)
+		else:
+			frappe.db.set_value("Purchase Receipt Item Supplied", sle.voucher_detail_no, "rate", outgoing_rate)
+
+		# Recalculate subcontracted item's rate in case of subcontracted purchase receipt/invoice
+		if frappe.db.get_value(sle.voucher_type, sle.voucher_no, "is_subcontracted"):
+			doc = frappe.get_cached_doc(sle.voucher_type, sle.voucher_no)
+			doc.update_valuation_rate(reset_outgoing_rate=False)
+			for d in (doc.items + doc.supplied_items):
+				d.db_update()
+
 	def get_serialized_values(self, sle):
 		incoming_rate = flt(sle.incoming_rate)
 		actual_qty = flt(sle.actual_qty)
@@ -228,7 +406,7 @@
 
 		if incoming_rate < 0:
 			# wrong incoming rate
-			incoming_rate = self.valuation_rate
+			incoming_rate = self.wh_data.valuation_rate
 
 		stock_value_change = 0
 		if incoming_rate:
@@ -236,22 +414,25 @@
 		elif actual_qty < 0:
 			# In case of delivery/stock issue, get average purchase rate
 			# of serial nos of current entry
-			outgoing_value = self.get_incoming_value_for_serial_nos(sle, serial_nos)
-			stock_value_change = -1 * outgoing_value
+			if not sle.is_cancelled:
+				outgoing_value = self.get_incoming_value_for_serial_nos(sle, serial_nos)
+				stock_value_change = -1 * outgoing_value
+			else:
+				stock_value_change = actual_qty * sle.outgoing_rate
 
-		new_stock_qty = self.qty_after_transaction + actual_qty
+		new_stock_qty = self.wh_data.qty_after_transaction + actual_qty
 
 		if new_stock_qty > 0:
-			new_stock_value = (self.qty_after_transaction * self.valuation_rate) + stock_value_change
+			new_stock_value = (self.wh_data.qty_after_transaction * self.wh_data.valuation_rate) + stock_value_change
 			if new_stock_value >= 0:
 				# calculate new valuation rate only if stock value is positive
 				# else it remains the same as that of previous entry
-				self.valuation_rate = new_stock_value / new_stock_qty
+				self.wh_data.valuation_rate = new_stock_value / new_stock_qty
 
-		if not self.valuation_rate and sle.voucher_detail_no:
+		if not self.wh_data.valuation_rate and sle.voucher_detail_no:
 			allow_zero_rate = self.check_if_allow_zero_valuation_rate(sle.voucher_type, sle.voucher_detail_no)
 			if not allow_zero_rate:
-				self.valuation_rate = get_valuation_rate(sle.item_code, sle.warehouse,
+				self.wh_data.valuation_rate = get_valuation_rate(sle.item_code, sle.warehouse,
 					sle.voucher_type, sle.voucher_no, self.allow_zero_rate,
 					currency=erpnext.get_company_currency(sle.company))
 
@@ -287,39 +468,39 @@
 
 	def get_moving_average_values(self, sle):
 		actual_qty = flt(sle.actual_qty)
-		new_stock_qty = flt(self.qty_after_transaction) + actual_qty
+		new_stock_qty = flt(self.wh_data.qty_after_transaction) + actual_qty
 		if new_stock_qty >= 0:
 			if actual_qty > 0:
-				if flt(self.qty_after_transaction) <= 0:
-					self.valuation_rate = sle.incoming_rate
+				if flt(self.wh_data.qty_after_transaction) <= 0:
+					self.wh_data.valuation_rate = sle.incoming_rate
 				else:
-					new_stock_value = (self.qty_after_transaction * self.valuation_rate) + \
+					new_stock_value = (self.wh_data.qty_after_transaction * self.wh_data.valuation_rate) + \
 						(actual_qty * sle.incoming_rate)
 
-					self.valuation_rate = new_stock_value / new_stock_qty
+					self.wh_data.valuation_rate = new_stock_value / new_stock_qty
 
 			elif sle.outgoing_rate:
 				if new_stock_qty:
-					new_stock_value = (self.qty_after_transaction * self.valuation_rate) + \
+					new_stock_value = (self.wh_data.qty_after_transaction * self.wh_data.valuation_rate) + \
 						(actual_qty * sle.outgoing_rate)
 
-					self.valuation_rate = new_stock_value / new_stock_qty
+					self.wh_data.valuation_rate = new_stock_value / new_stock_qty
 				else:
-					self.valuation_rate = sle.outgoing_rate
+					self.wh_data.valuation_rate = sle.outgoing_rate
 
 		else:
-			if flt(self.qty_after_transaction) >= 0 and sle.outgoing_rate:
-				self.valuation_rate = sle.outgoing_rate
+			if flt(self.wh_data.qty_after_transaction) >= 0 and sle.outgoing_rate:
+				self.wh_data.valuation_rate = sle.outgoing_rate
 
-			if not self.valuation_rate and actual_qty > 0:
-				self.valuation_rate = sle.incoming_rate
+			if not self.wh_data.valuation_rate and actual_qty > 0:
+				self.wh_data.valuation_rate = sle.incoming_rate
 
 			# Get valuation rate from previous SLE or Item master, if item does not have the
 			# allow zero valuration rate flag set
-			if not self.valuation_rate and sle.voucher_detail_no:
+			if not self.wh_data.valuation_rate and sle.voucher_detail_no:
 				allow_zero_valuation_rate = self.check_if_allow_zero_valuation_rate(sle.voucher_type, sle.voucher_detail_no)
 				if not allow_zero_valuation_rate:
-					self.valuation_rate = get_valuation_rate(sle.item_code, sle.warehouse,
+					self.wh_data.valuation_rate = get_valuation_rate(sle.item_code, sle.warehouse,
 						sle.voucher_type, sle.voucher_no, self.allow_zero_rate,
 						currency=erpnext.get_company_currency(sle.company))
 
@@ -329,22 +510,22 @@
 		outgoing_rate = flt(sle.outgoing_rate)
 
 		if actual_qty > 0:
-			if not self.stock_queue:
-				self.stock_queue.append([0, 0])
+			if not self.wh_data.stock_queue:
+				self.wh_data.stock_queue.append([0, 0])
 
 			# last row has the same rate, just updated the qty
-			if self.stock_queue[-1][1]==incoming_rate:
-				self.stock_queue[-1][0] += actual_qty
+			if self.wh_data.stock_queue[-1][1]==incoming_rate:
+				self.wh_data.stock_queue[-1][0] += actual_qty
 			else:
-				if self.stock_queue[-1][0] > 0:
-					self.stock_queue.append([actual_qty, incoming_rate])
+				if self.wh_data.stock_queue[-1][0] > 0:
+					self.wh_data.stock_queue.append([actual_qty, incoming_rate])
 				else:
-					qty = self.stock_queue[-1][0] + actual_qty
-					self.stock_queue[-1] = [qty, incoming_rate]
+					qty = self.wh_data.stock_queue[-1][0] + actual_qty
+					self.wh_data.stock_queue[-1] = [qty, incoming_rate]
 		else:
 			qty_to_pop = abs(actual_qty)
 			while qty_to_pop:
-				if not self.stock_queue:
+				if not self.wh_data.stock_queue:
 					# Get valuation rate from last sle if exists or from valuation rate field in item master
 					allow_zero_valuation_rate = self.check_if_allow_zero_valuation_rate(sle.voucher_type, sle.voucher_detail_no)
 					if not allow_zero_valuation_rate:
@@ -354,35 +535,35 @@
 					else:
 						_rate = 0
 
-					self.stock_queue.append([0, _rate])
+					self.wh_data.stock_queue.append([0, _rate])
 
 				index = None
 				if outgoing_rate > 0:
 					# Find the entry where rate matched with outgoing rate
-					for i, v in enumerate(self.stock_queue):
+					for i, v in enumerate(self.wh_data.stock_queue):
 						if v[1] == outgoing_rate:
 							index = i
 							break
 
 					# If no entry found with outgoing rate, collapse stack
 					if index == None:
-						new_stock_value = sum((d[0]*d[1] for d in self.stock_queue)) - qty_to_pop*outgoing_rate
-						new_stock_qty = sum((d[0] for d in self.stock_queue)) - qty_to_pop
-						self.stock_queue = [[new_stock_qty, new_stock_value/new_stock_qty if new_stock_qty > 0 else outgoing_rate]]
+						new_stock_value = sum((d[0]*d[1] for d in self.wh_data.stock_queue)) - qty_to_pop*outgoing_rate
+						new_stock_qty = sum((d[0] for d in self.wh_data.stock_queue)) - qty_to_pop
+						self.wh_data.stock_queue = [[new_stock_qty, new_stock_value/new_stock_qty if new_stock_qty > 0 else outgoing_rate]]
 						break
 				else:
 					index = 0
 
 				# select first batch or the batch with same rate
-				batch = self.stock_queue[index]
+				batch = self.wh_data.stock_queue[index]
 				if qty_to_pop >= batch[0]:
 					# consume current batch
 					qty_to_pop = qty_to_pop - batch[0]
-					self.stock_queue.pop(index)
-					if not self.stock_queue and qty_to_pop:
+					self.wh_data.stock_queue.pop(index)
+					if not self.wh_data.stock_queue and qty_to_pop:
 						# stock finished, qty still remains to be withdrawn
 						# negative stock, keep in as a negative batch
-						self.stock_queue.append([-qty_to_pop, outgoing_rate or batch[1]])
+						self.wh_data.stock_queue.append([-qty_to_pop, outgoing_rate or batch[1]])
 						break
 
 				else:
@@ -391,14 +572,14 @@
 					batch[0] = batch[0] - qty_to_pop
 					qty_to_pop = 0
 
-		stock_value = sum((flt(batch[0]) * flt(batch[1]) for batch in self.stock_queue))
-		stock_qty = sum((flt(batch[0]) for batch in self.stock_queue))
+		stock_value = sum((flt(batch[0]) * flt(batch[1]) for batch in self.wh_data.stock_queue))
+		stock_qty = sum((flt(batch[0]) for batch in self.wh_data.stock_queue))
 
 		if stock_qty:
-			self.valuation_rate = stock_value / flt(stock_qty)
+			self.wh_data.valuation_rate = stock_value / flt(stock_qty)
 
-		if not self.stock_queue:
-			self.stock_queue.append([0, sle.incoming_rate or sle.outgoing_rate or self.valuation_rate])
+		if not self.wh_data.stock_queue:
+			self.wh_data.stock_queue.append([0, sle.incoming_rate or sle.outgoing_rate or self.wh_data.valuation_rate])
 
 	def check_if_allow_zero_valuation_rate(self, voucher_type, voucher_detail_no):
 		ref_item_dt = ""
@@ -413,39 +594,56 @@
 		else:
 			return 0
 
-	def get_sle_before_datetime(self):
+	def get_sle_before_datetime(self, args):
 		"""get previous stock ledger entry before current time-bucket"""
-		if self.args.get('sle_id'):
-			self.args['name'] = self.args.get('sle_id')
+		sle = get_stock_ledger_entries(args, "<", "desc", "limit 1", for_update=False)
+		sle = sle[0] if sle else frappe._dict()
+		return sle
 
-		return get_stock_ledger_entries(self.args, "<=", "desc", "limit 1", for_update=False)
-
-	def get_sle_after_datetime(self):
+	def get_sle_after_datetime(self, args):
 		"""get Stock Ledger Entries after a particular datetime, for reposting"""
-		return get_stock_ledger_entries(self.previous_sle or frappe._dict({
-				"item_code": self.args.get("item_code"), "warehouse": self.args.get("warehouse") }),
-			">", "asc", for_update=True, check_serial_no=False)
+		return get_stock_ledger_entries(args, ">", "asc", for_update=True, check_serial_no=False)
 
 	def raise_exceptions(self):
-		deficiency = min(e["diff"] for e in self.exceptions)
+		msg_list = []
+		for warehouse, exceptions in iteritems(self.exceptions):
+			deficiency = min(e["diff"] for e in exceptions)
 
-		if ((self.exceptions[0]["voucher_type"], self.exceptions[0]["voucher_no"]) in
-			frappe.local.flags.currently_saving):
+			if ((exceptions[0]["voucher_type"], exceptions[0]["voucher_no"]) in
+				frappe.local.flags.currently_saving):
 
-			msg = _("{0} units of {1} needed in {2} to complete this transaction.").format(
-				abs(deficiency), frappe.get_desk_link('Item', self.item_code),
-				frappe.get_desk_link('Warehouse', self.warehouse))
-		else:
-			msg = _("{0} units of {1} needed in {2} on {3} {4} for {5} to complete this transaction.").format(
-				abs(deficiency), frappe.get_desk_link('Item', self.item_code),
-				frappe.get_desk_link('Warehouse', self.warehouse),
-				self.exceptions[0]["posting_date"], self.exceptions[0]["posting_time"],
-				frappe.get_desk_link(self.exceptions[0]["voucher_type"], self.exceptions[0]["voucher_no"]))
+				msg = _("{0} units of {1} needed in {2} to complete this transaction.").format(
+					abs(deficiency), frappe.get_desk_link('Item', self.item_code),
+					frappe.get_desk_link('Warehouse', warehouse))
+			else:
+				msg = _("{0} units of {1} needed in {2} on {3} {4} for {5} to complete this transaction.").format(
+					abs(deficiency), frappe.get_desk_link('Item', self.item_code),
+					frappe.get_desk_link('Warehouse', warehouse),
+					exceptions[0]["posting_date"], exceptions[0]["posting_time"],
+					frappe.get_desk_link(exceptions[0]["voucher_type"], exceptions[0]["voucher_no"]))
 
-		if self.verbose:
-			frappe.throw(msg, NegativeStockError, title='Insufficient Stock')
-		else:
-			raise NegativeStockError(msg)
+			if msg:
+				msg_list.append(msg)
+
+		if msg_list:
+			message = "\n\n".join(msg_list)
+			if self.verbose:
+				frappe.throw(message, NegativeStockError, title='Insufficient Stock')
+			else:
+				raise NegativeStockError(message)
+	
+	def update_bin(self):
+		# update bin for each warehouse
+		for warehouse, data in iteritems(self.data):
+			bin_doc = get_bin(self.item_code, warehouse)
+
+			bin_doc.update({
+				"valuation_rate": data.valuation_rate,
+				"actual_qty": data.qty_after_transaction,
+				"stock_value": data.stock_value
+			})
+			bin_doc.flags.via_stock_ledger_entry = True
+			bin_doc.save(ignore_permissions=True)
 
 def get_previous_sle(args, for_update=False):
 	"""
@@ -489,6 +687,7 @@
 		select *, timestamp(posting_date, posting_time) as "timestamp"
 		from `tabStock Ledger Entry`
 		where item_code = %%(item_code)s
+		and is_cancelled = 0
 		%(conditions)s
 		order by timestamp(posting_date, posting_time) %(order)s, creation %(order)s
 		%(limit)s %(for_update)s""" % {
@@ -498,10 +697,11 @@
 			"order": order
 		}, previous_sle, as_dict=1, debug=debug)
 
-def get_sle_by_id(sle_id):
-	return frappe.db.get_all('Stock Ledger Entry',
-		fields=['*', 'timestamp(posting_date, posting_time) as timestamp'],
-		filters={'name': sle_id})[0]
+def get_sle_by_voucher_detail_no(voucher_detail_no, excluded_sle=None):
+	return frappe.db.get_value('Stock Ledger Entry',
+		{'voucher_detail_no': voucher_detail_no, 'name': ['!=', excluded_sle]},
+		['item_code', 'warehouse', 'posting_date', 'posting_time', 'timestamp(posting_date, posting_time) as timestamp'],
+		as_dict=1)
 
 def get_valuation_rate(item_code, warehouse, voucher_type, voucher_no,
 	allow_zero_rate=False, currency=None, company=None, raise_error_if_no_rate=True):
@@ -529,7 +729,7 @@
 			order by posting_date desc, posting_time desc, name desc limit 1""", (item_code, voucher_no, voucher_type))
 
 	if last_valuation_rate:
-		return flt(last_valuation_rate[0][0]) # as there is previous records, it might come with zero rate
+		return flt(last_valuation_rate[0][0])
 
 	# If negative stock allowed, and item delivered without any incoming entry,
 	# system does not found any SLE, then take valuation rate from Item
@@ -561,3 +761,54 @@
 		frappe.throw(msg=msg, title=_("Valuation Rate Missing"))
 
 	return valuation_rate
+
+def update_qty_in_future_sle(args, allow_negative_stock=None):
+	frappe.db.sql("""
+		update `tabStock Ledger Entry`
+		set qty_after_transaction = qty_after_transaction + {qty}
+		where 
+			item_code = %(item_code)s
+			and warehouse = %(warehouse)s
+			and voucher_no != %(voucher_no)s
+			and is_cancelled = 0
+			and (timestamp(posting_date, posting_time) > timestamp(%(posting_date)s, %(posting_time)s)
+				or (
+					timestamp(posting_date, posting_time) = timestamp(%(posting_date)s, %(posting_time)s)
+					and creation > %(creation)s
+				)
+			)
+	""".format(qty=args.actual_qty), args)
+
+	validate_negative_qty_in_future_sle(args, allow_negative_stock)
+
+def validate_negative_qty_in_future_sle(args, allow_negative_stock=None):
+	allow_negative_stock = allow_negative_stock \
+		or cint(frappe.db.get_single_value("Stock Settings", "allow_negative_stock"))
+
+	if args.actual_qty < 0 and not allow_negative_stock:
+		sle = get_future_sle_with_negative_qty(args)
+		if sle:
+			message = _("{0} units of {1} needed in {2} on {3} {4} for {5} to complete this transaction.").format(
+				abs(sle[0]["qty_after_transaction"]),
+				frappe.get_desk_link('Item', args.item_code),
+				frappe.get_desk_link('Warehouse', args.warehouse),
+				sle[0]["posting_date"], sle[0]["posting_time"],
+				frappe.get_desk_link(sle[0]["voucher_type"], sle[0]["voucher_no"]))
+						
+			frappe.throw(message, NegativeStockError, title='Insufficient Stock')
+
+def get_future_sle_with_negative_qty(args):
+	return frappe.db.sql("""
+		select
+			qty_after_transaction, posting_date, posting_time,
+			voucher_type, voucher_no
+		from `tabStock Ledger Entry`
+		where 
+			item_code = %(item_code)s
+			and warehouse = %(warehouse)s
+			and voucher_no != %(voucher_no)s
+			and timestamp(posting_date, posting_time) >= timestamp(%(posting_date)s, %(posting_time)s)
+			and is_cancelled = 0
+			and qty_after_transaction < 0
+		limit 1
+	""", args, as_dict=1)
\ No newline at end of file
diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py
index f9ac254..4ea7e4f 100644
--- a/erpnext/stock/utils.py
+++ b/erpnext/stock/utils.py
@@ -63,6 +63,7 @@
 		SELECT item_code, stock_value, name, warehouse
 		FROM `tabStock Ledger Entry` sle
 		WHERE posting_date <= %s {0}
+			and is_cancelled = 0
 		ORDER BY timestamp(posting_date, posting_time) DESC, creation DESC
 	""".format(condition), values, as_dict=1)
 
@@ -211,7 +212,7 @@
 			currency=erpnext.get_company_currency(args.get('company')), company=args.get('company'),
 			raise_error_if_no_rate=raise_error_if_no_rate)
 
-	return in_rate
+	return flt(in_rate)
 
 def get_avg_purchase_rate(serial_nos):
 	"""get average value of serial numbers"""
@@ -375,4 +376,10 @@
 
 	outgoing_rate = outgoing_rate[0][0] if outgoing_rate else 0.0
 
-	return outgoing_rate
\ No newline at end of file
+	return outgoing_rate
+
+def is_reposting_item_valuation_in_progress():
+	reposting_in_progress = frappe.db.exists("Repost Item Valuation",
+		{'docstatus': 1, 'status': ['in', ['Queued','In Progress']]})
+	if reposting_in_progress:
+		frappe.msgprint(_("Item valuation reposting in progress. Report might show incorrect item valuation."), alert=1)
\ No newline at end of file