Merge branch 'develop' into fix-get-bin-details
diff --git a/erpnext/accounts/test/test_utils.py b/erpnext/accounts/test/test_utils.py
index 882cd69..3aca60e 100644
--- a/erpnext/accounts/test/test_utils.py
+++ b/erpnext/accounts/test/test_utils.py
@@ -3,11 +3,14 @@
 import frappe
 from frappe.test_runner import make_test_objects
 
+from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
+from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
 from erpnext.accounts.party import get_party_shipping_address
 from erpnext.accounts.utils import (
 	get_future_stock_vouchers,
 	get_voucherwise_gl_entries,
 	sort_stock_vouchers_by_posting_date,
+	update_reference_in_payment_entry,
 )
 from erpnext.stock.doctype.item.test_item import make_item
 from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
@@ -73,6 +76,47 @@
 		sorted_vouchers = sort_stock_vouchers_by_posting_date(list(reversed(vouchers)))
 		self.assertEqual(sorted_vouchers, vouchers)
 
+	def test_update_reference_in_payment_entry(self):
+		item = make_item().name
+
+		purchase_invoice = make_purchase_invoice(
+			item=item, supplier="_Test Supplier USD", currency="USD", conversion_rate=82.32
+		)
+		purchase_invoice.submit()
+
+		payment_entry = get_payment_entry(purchase_invoice.doctype, purchase_invoice.name)
+		payment_entry.target_exchange_rate = 62.9
+		payment_entry.paid_amount = 15725
+		payment_entry.deductions = []
+		payment_entry.insert()
+
+		self.assertEqual(payment_entry.difference_amount, -4855.00)
+		payment_entry.references = []
+		payment_entry.submit()
+
+		payment_reconciliation = frappe.new_doc("Payment Reconciliation")
+		payment_reconciliation.company = payment_entry.company
+		payment_reconciliation.party_type = "Supplier"
+		payment_reconciliation.party = purchase_invoice.supplier
+		payment_reconciliation.receivable_payable_account = payment_entry.paid_to
+		payment_reconciliation.get_unreconciled_entries()
+		payment_reconciliation.allocate_entries(
+			{
+				"payments": [d.__dict__ for d in payment_reconciliation.payments],
+				"invoices": [d.__dict__ for d in payment_reconciliation.invoices],
+			}
+		)
+		for d in payment_reconciliation.invoices:
+			# Reset invoice outstanding_amount because allocate_entries will zero this value out.
+			d.outstanding_amount = d.amount
+		for d in payment_reconciliation.allocation:
+			d.difference_account = "Exchange Gain/Loss - _TC"
+		payment_reconciliation.reconcile()
+
+		payment_entry.load_from_db()
+		self.assertEqual(len(payment_entry.references), 1)
+		self.assertEqual(payment_entry.difference_amount, 0)
+
 
 ADDRESS_RECORDS = [
 	{
diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py
index 1e573b0..445dcc5 100644
--- a/erpnext/accounts/utils.py
+++ b/erpnext/accounts/utils.py
@@ -611,11 +611,6 @@
 		new_row.docstatus = 1
 		new_row.update(reference_details)
 
-	payment_entry.flags.ignore_validate_update_after_submit = True
-	payment_entry.setup_party_account_field()
-	payment_entry.set_missing_values()
-	payment_entry.set_amounts()
-
 	if d.difference_amount and d.difference_account:
 		account_details = {
 			"account": d.difference_account,
@@ -627,6 +622,11 @@
 
 		payment_entry.set_gain_or_loss(account_details=account_details)
 
+	payment_entry.flags.ignore_validate_update_after_submit = True
+	payment_entry.setup_party_account_field()
+	payment_entry.set_missing_values()
+	payment_entry.set_amounts()
+
 	if not do_not_save:
 		payment_entry.save(ignore_permissions=True)
 
diff --git a/erpnext/projects/doctype/project/project.js b/erpnext/projects/doctype/project/project.js
index c48ed91..f366f77 100644
--- a/erpnext/projects/doctype/project/project.js
+++ b/erpnext/projects/doctype/project/project.js
@@ -20,7 +20,7 @@
 	onload: function (frm) {
 		const so = frm.get_docfield("sales_order");
 		so.get_route_options_for_new_doc = () => {
-			if (frm.is_new()) return;
+			if (frm.is_new()) return {};
 			return {
 				"customer": frm.doc.customer,
 				"project_name": frm.doc.name
diff --git a/erpnext/projects/doctype/timesheet/timesheet.py b/erpnext/projects/doctype/timesheet/timesheet.py
index b9bb37a..1179364 100644
--- a/erpnext/projects/doctype/timesheet/timesheet.py
+++ b/erpnext/projects/doctype/timesheet/timesheet.py
@@ -25,12 +25,18 @@
 	def validate(self):
 		self.set_status()
 		self.validate_dates()
+		self.calculate_hours()
 		self.validate_time_logs()
 		self.update_cost()
 		self.calculate_total_amounts()
 		self.calculate_percentage_billed()
 		self.set_dates()
 
+	def calculate_hours(self):
+		for row in self.time_logs:
+			if row.to_time and row.from_time:
+				row.hours = time_diff_in_hours(row.to_time, row.from_time)
+
 	def calculate_total_amounts(self):
 		self.total_hours = 0.0
 		self.total_billable_hours = 0.0
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index aa57bc2..f2f1ce1 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -272,7 +272,7 @@
 
 		let quality_inspection_field = this.frm.get_docfield("items", "quality_inspection");
 		quality_inspection_field.get_route_options_for_new_doc = function(row) {
-			if(me.frm.is_new()) return;
+			if(me.frm.is_new()) return {};
 			return {
 				"inspection_type": inspection_type,
 				"reference_type": me.frm.doc.doctype,
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js
index d4b4efa..897fca3 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.js
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.js
@@ -133,7 +133,7 @@
 
 		let quality_inspection_field = frm.get_docfield("items", "quality_inspection");
 		quality_inspection_field.get_route_options_for_new_doc = function(row) {
-			if (frm.is_new()) return;
+			if (frm.is_new()) return {};
 			return {
 				"inspection_type": "Incoming",
 				"reference_type": frm.doc.doctype,
diff --git a/erpnext/templates/includes/cart/cart_address.html b/erpnext/templates/includes/cart/cart_address.html
index cf60017..a8188ec 100644
--- a/erpnext/templates/includes/cart/cart_address.html
+++ b/erpnext/templates/includes/cart/cart_address.html
@@ -55,6 +55,7 @@
 {% endif %}
 
 <script>
+frappe.boot = {{ boot }}
 frappe.ready(() => {
 	$(document).on('click', '.address-card', (e) => {
 		const $target = $(e.currentTarget);
diff --git a/erpnext/www/book-appointment/__init__.py b/erpnext/www/book-appointment/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/www/book-appointment/__init__.py
diff --git a/erpnext/www/book-appointment/verify/__init__.py b/erpnext/www/book-appointment/verify/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/www/book-appointment/verify/__init__.py