fix: multiple pos issues (#23725)

* fix: point of sale search

* fix: pos fetches expired serial nos

* fix: pos doesn't refresh on route change

* fix: opening balances not set in opening entry

* fix: remove debug statement

* fix: invalid query if no serial no is reserved by pos invoice

* chore: make new order btn primary

* chore: filter warehouse by company

* chore: add shortcuts for new order and email

* fix: cannot fetch serial no if no batch control

* chore: add shortcuts for menu items

* feat: standard keyboard shortcuts for pos page

* feat: display only items that are in stock

* fix: empty point of sale page if opening entry dialog is closed

* feat: conversion factor in pos item details

* fix: show all invalid mode of payments

* chore: show all items if allow negative stock is checked

* fix: -ve amount set when changing mode of payment

* fix: pos closing validations

* fix: test

* fix: non expired serial no fetching query

* fix: cannot dismiss pos opening creation dialog

* fix: transalation strings

* fix: msgprint to throw

* chore: use as_list in frappe.throw

* chore: clean up pos invoice.js & .py

* fix: codacy

* fix: transalation syntax

* fix: codacy

* fix: set_missing_values called twice from pos page

* fix: mode selector with double spaces

* fix: do not allow tables in pos additional fields

* fix: pos is not defined

* feat: set mode of payments for returns

* fix: remove naming series from pos profile

* fix: error message

* fix: minor bugs

* chore: re-arrange pos closing entry detail fields

* fix: sider & frappe linter

* fix: more translation strings

* fix: travis

* fix: more translation strings

* fix: sider

* fix: travis

* fix: unexpected end of string

* fix: travis
diff --git a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js
index 9336fc3..57baac7 100644
--- a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js
+++ b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js
@@ -51,6 +51,7 @@
 			args: {
 				start: frappe.datetime.get_datetime_as_string(frm.doc.period_start_date),
 				end: frappe.datetime.get_datetime_as_string(frm.doc.period_end_date),
+				pos_profile: frm.doc.pos_profile,
 				user: frm.doc.user
 			},
 			callback: (r) => {
diff --git a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py
index 9899219..2b91c74 100644
--- a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py
+++ b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py
@@ -14,19 +14,51 @@
 
 class POSClosingEntry(Document):
 	def validate(self):
-		user = frappe.get_all('POS Closing Entry',
-			filters = { 'user': self.user, 'docstatus': 1 },
+		if frappe.db.get_value("POS Opening Entry", self.pos_opening_entry, "status") != "Open":
+			frappe.throw(_("Selected POS Opening Entry should be open."), title=_("Invalid Opening Entry"))
+
+		self.validate_pos_closing()
+		self.validate_pos_invoices()
+	
+	def validate_pos_closing(self):
+		user = frappe.get_all("POS Closing Entry", 
+			filters = { "user": self.user, "docstatus": 1, "pos_profile": self.pos_profile },
 			or_filters = {
-					'period_start_date': ('between', [self.period_start_date, self.period_end_date]),
-					'period_end_date': ('between', [self.period_start_date, self.period_end_date])
+				"period_start_date": ("between", [self.period_start_date, self.period_end_date]),
+				"period_end_date": ("between", [self.period_start_date, self.period_end_date])
 			})
 
 		if user:
-			frappe.throw(_("POS Closing Entry {} against {} between selected period"
-				.format(frappe.bold("already exists"), frappe.bold(self.user))), title=_("Invalid Period"))
+			bold_already_exists = frappe.bold(_("already exists"))
+			bold_user = frappe.bold(self.user)
+			frappe.throw(_("POS Closing Entry {} against {} between selected period")
+				.format(bold_already_exists, bold_user), title=_("Invalid Period"))
+	
+	def validate_pos_invoices(self):
+		invalid_rows = []
+		for d in self.pos_transactions:
+			invalid_row = {'idx': d.idx}
+			pos_invoice = frappe.db.get_values("POS Invoice", d.pos_invoice, 
+				["consolidated_invoice", "pos_profile", "docstatus", "owner"], as_dict=1)[0]
+			if pos_invoice.consolidated_invoice:
+				invalid_row.setdefault('msg', []).append(_('POS Invoice is {}').format(frappe.bold("already consolidated")))
+				invalid_rows.append(invalid_row)
+				continue
+			if pos_invoice.pos_profile != self.pos_profile:
+				invalid_row.setdefault('msg', []).append(_("POS Profile doesn't matches {}").format(frappe.bold(self.pos_profile)))
+			if pos_invoice.docstatus != 1:
+				invalid_row.setdefault('msg', []).append(_('POS Invoice is not {}').format(frappe.bold("submitted")))
+			if pos_invoice.owner != self.user:
+				invalid_row.setdefault('msg', []).append(_("POS Invoice isn't created by user {}").format(frappe.bold(self.owner)))
 
-		if frappe.db.get_value("POS Opening Entry", self.pos_opening_entry, "status") != "Open":
-			frappe.throw(_("Selected POS Opening Entry should be open."), title=_("Invalid Opening Entry"))
+			if invalid_row.get('msg'):
+				invalid_rows.append(invalid_row)
+
+		if not invalid_rows:
+			return
+
+		error_list = [_("Row #{}: {}").format(row.get('idx'), row.get('msg')) for row in invalid_rows]
+		frappe.throw(error_list, title=_("Invalid POS Invoices"), as_list=True)
 
 	def on_submit(self):
 		merge_pos_invoices(self.pos_transactions)
@@ -47,16 +79,15 @@
 	return [c['user'] for c in cashiers_list]
 
 @frappe.whitelist()
-def get_pos_invoices(start, end, user):
+def get_pos_invoices(start, end, pos_profile, user):
 	data = frappe.db.sql("""
 	select
 		name, timestamp(posting_date, posting_time) as "timestamp"
 	from
 		`tabPOS Invoice`
 	where
-		owner = %s and docstatus = 1 and
-		(consolidated_invoice is NULL or consolidated_invoice = '')
-	""", (user), as_dict=1)
+		owner = %s and docstatus = 1 and pos_profile = %s and ifnull(consolidated_invoice,'') = ''
+	""", (user, pos_profile), as_dict=1)
 
 	data = list(filter(lambda d: get_datetime(start) <= get_datetime(d.timestamp) <= get_datetime(end), data))
 	# need to get taxes and payments so can't avoid get_doc
@@ -76,7 +107,8 @@
 	closing_entry.net_total = 0
 	closing_entry.total_quantity = 0
 
-	invoices = get_pos_invoices(closing_entry.period_start_date, closing_entry.period_end_date, closing_entry.user)
+	invoices = get_pos_invoices(closing_entry.period_start_date, closing_entry.period_end_date,
+		closing_entry.pos_profile, closing_entry.user)
 
 	pos_transactions = []
 	taxes = []
diff --git a/erpnext/accounts/doctype/pos_closing_entry_detail/pos_closing_entry_detail.json b/erpnext/accounts/doctype/pos_closing_entry_detail/pos_closing_entry_detail.json
index 798637a..6e7768d 100644
--- a/erpnext/accounts/doctype/pos_closing_entry_detail/pos_closing_entry_detail.json
+++ b/erpnext/accounts/doctype/pos_closing_entry_detail/pos_closing_entry_detail.json
@@ -7,8 +7,8 @@
  "field_order": [
   "mode_of_payment",
   "opening_amount",
-  "closing_amount",
   "expected_amount",
+  "closing_amount",
   "difference"
  ],
  "fields": [
@@ -26,8 +26,7 @@
    "in_list_view": 1,
    "label": "Expected Amount",
    "options": "company:company_currency",
-   "read_only": 1,
-   "reqd": 1
+   "read_only": 1
   },
   {
    "fieldname": "difference",
@@ -55,9 +54,10 @@
    "reqd": 1
   }
  ],
+ "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2020-05-29 15:03:34.533607",
+ "modified": "2020-10-23 16:45:43.662034",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "POS Closing Entry Detail",
diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.js b/erpnext/accounts/doctype/pos_invoice/pos_invoice.js
index c43cb79..86062d1 100644
--- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.js
+++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.js
@@ -9,80 +9,63 @@
 		this._super(doc);
 	},
 
-	onload() {
+	onload(doc) {
 		this._super();
-		if(this.frm.doc.__islocal && this.frm.doc.is_pos) {
-			//Load pos profile data on the invoice if the default value of Is POS is 1
-
-			me.frm.script_manager.trigger("is_pos");
-			me.frm.refresh_fields();
+		if(doc.__islocal && doc.is_pos && frappe.get_route_str() !== 'point-of-sale') {
+			this.frm.script_manager.trigger("is_pos");
+			this.frm.refresh_fields();
 		}
 	},
 
 	refresh(doc) {
 		this._super();
 		if (doc.docstatus == 1 && !doc.is_return) {
-			if(doc.outstanding_amount >= 0 || Math.abs(flt(doc.outstanding_amount)) < flt(doc.grand_total)) {
-				cur_frm.add_custom_button(__('Return'),
-					this.make_sales_return, __('Create'));
-				cur_frm.page.set_inner_btn_group_as_primary(__('Create'));
-			}
+			this.frm.add_custom_button(__('Return'), this.make_sales_return, __('Create'));
+			this.frm.page.set_inner_btn_group_as_primary(__('Create'));
 		}
 
-		if (this.frm.doc.is_return) {
+		if (doc.is_return && doc.__islocal) {
 			this.frm.return_print_format = "Sales Invoice Return";
-			cur_frm.set_value('consolidated_invoice', '');
+			this.frm.set_value('consolidated_invoice', '');
 		}
 	},
 
-	is_pos: function(frm){
+	is_pos: function() {
 		this.set_pos_data();
 	},
 
-	set_pos_data: function() {
+	set_pos_data: async function() {
 		if(this.frm.doc.is_pos) {
 			this.frm.set_value("allocate_advances_automatically", 0);
 			if(!this.frm.doc.company) {
 				this.frm.set_value("is_pos", 0);
 				frappe.msgprint(__("Please specify Company to proceed"));
 			} else {
-				var me = this;
-				return this.frm.call({
-					doc: me.frm.doc,
+				const r = await this.frm.call({
+					doc: this.frm.doc,
 					method: "set_missing_values",
-					callback: function(r) {
-						if(!r.exc) {
-							if(r.message) {
-								me.frm.pos_print_format = r.message.print_format || "";
-								me.frm.meta.default_print_format = r.message.print_format || "";
-								me.frm.allow_edit_rate = r.message.allow_edit_rate;
-								me.frm.allow_edit_discount = r.message.allow_edit_discount;
-								me.frm.doc.campaign = r.message.campaign;
-								me.frm.allow_print_before_pay = r.message.allow_print_before_pay;
-							}
-							me.frm.script_manager.trigger("update_stock");
-							me.calculate_taxes_and_totals();
-							if(me.frm.doc.taxes_and_charges) {
-								me.frm.script_manager.trigger("taxes_and_charges");
-							}
-							frappe.model.set_default_values(me.frm.doc);
-							me.set_dynamic_labels();
-							
-						}
-					}
+					freeze: true
 				});
+				if(!r.exc) {
+					if(r.message) {
+						this.frm.pos_print_format = r.message.print_format || "";
+						this.frm.meta.default_print_format = r.message.print_format || "";
+						this.frm.doc.campaign = r.message.campaign;
+						this.frm.allow_print_before_pay = r.message.allow_print_before_pay;
+					}
+					this.frm.script_manager.trigger("update_stock");
+					this.calculate_taxes_and_totals();
+					this.frm.doc.taxes_and_charges && this.frm.script_manager.trigger("taxes_and_charges");
+					frappe.model.set_default_values(this.frm.doc);
+					this.set_dynamic_labels();
+				}
 			}
 		}
-		else this.frm.trigger("refresh");
 	},
 
 	customer() {
 		if (!this.frm.doc.customer) return
-
-		if (this.frm.doc.is_pos){
-			var pos_profile = this.frm.doc.pos_profile;
-		}
-		var me = this;
+		const pos_profile = this.frm.doc.pos_profile;
 		if(this.frm.updating_party_details) return;
 		erpnext.utils.get_party_details(this.frm,
 			"erpnext.accounts.party.get_party_details", {
@@ -92,8 +75,8 @@
 				account: this.frm.doc.debit_to,
 				price_list: this.frm.doc.selling_price_list,
 				pos_profile: pos_profile
-			}, function() {
-				me.apply_pricing_rule();
+			}, () => {
+				this.apply_pricing_rule();
 			});
 	},
 
diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
index 5b0822e..61263ac 100644
--- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
+++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
@@ -10,12 +10,10 @@
 from frappe.utils import cint, flt, add_months, today, date_diff, getdate, add_days, cstr, nowdate
 from erpnext.accounts.utils import get_account_currency
 from erpnext.accounts.party import get_party_account, get_due_date
-from erpnext.accounts.doctype.loyalty_program.loyalty_program import \
-	get_loyalty_program_details_with_points, validate_loyalty_points
-
-from erpnext.accounts.doctype.sales_invoice.sales_invoice import SalesInvoice, get_bank_cash_account, update_multi_mode_option
-from erpnext.stock.doctype.serial_no.serial_no import get_pos_reserved_serial_nos
 from erpnext.accounts.doctype.payment_request.payment_request import make_payment_request
+from erpnext.accounts.doctype.loyalty_program.loyalty_program import validate_loyalty_points
+from erpnext.stock.doctype.serial_no.serial_no import get_pos_reserved_serial_nos, get_serial_nos
+from erpnext.accounts.doctype.sales_invoice.sales_invoice import SalesInvoice, get_bank_cash_account, update_multi_mode_option, get_mode_of_payment_info
 
 from six import iteritems
 
@@ -30,8 +28,7 @@
 		# run on validate method of selling controller
 		super(SalesInvoice, self).validate()
 		self.validate_auto_set_posting_time()
-		self.validate_pos_paid_amount()
-		self.validate_pos_return()
+		self.validate_mode_of_payment()
 		self.validate_uom_is_integer("stock_uom", "stock_qty")
 		self.validate_uom_is_integer("uom", "qty")
 		self.validate_debit_to_acc()
@@ -41,11 +38,11 @@
 		self.validate_item_cost_centers()
 		self.validate_serialised_or_batched_item()
 		self.validate_stock_availablility()
-		self.validate_return_items()
+		self.validate_return_items_qty()
 		self.set_status()
 		self.set_account_for_mode_of_payment()
 		self.validate_pos()
-		self.verify_payment_amount()
+		self.validate_payment_amount()
 		self.validate_loyalty_transaction()
 
 	def on_submit(self):
@@ -84,70 +81,97 @@
 					return frappe.throw(_("Payment related to {0} is not completed").format(pay.mode_of_payment))
 
 	def validate_stock_availablility(self):
-		allow_negative_stock = frappe.db.get_value('Stock Settings', None, 'allow_negative_stock')
+		if self.is_return:
+			return
 
+		allow_negative_stock = frappe.db.get_value('Stock Settings', None, 'allow_negative_stock')
+		error_msg = []
 		for d in self.get('items'):
+			msg = ""
 			if d.serial_no:
-				filters = {
-					"item_code": d.item_code,
-					"warehouse": d.warehouse,
-					"delivery_document_no": "",
-					"sales_invoice": ""
-				}
+				filters = { "item_code": d.item_code, "warehouse": d.warehouse }
 				if d.batch_no:
 					filters["batch_no"] = d.batch_no
-				reserved_serial_nos, unreserved_serial_nos = get_pos_reserved_serial_nos(filters)
-				serial_nos = d.serial_no.split("\n")
-				serial_nos = ' '.join(serial_nos).split() # remove whitespaces
-				invalid_serial_nos = []
-				for s in serial_nos:
-					if s in reserved_serial_nos:
-						invalid_serial_nos.append(s)
+				reserved_serial_nos = get_pos_reserved_serial_nos(filters)
+				serial_nos = get_serial_nos(d.serial_no)
+				invalid_serial_nos = [s for s in serial_nos if s in reserved_serial_nos]
 
-				if len(invalid_serial_nos):
-					multiple_nos = 's' if len(invalid_serial_nos) > 1 else ''
-					frappe.throw(_("Row #{}: Serial No{}. {} has already been transacted into another POS Invoice. Please select valid serial no.").format(
-						d.idx, multiple_nos, frappe.bold(', '.join(invalid_serial_nos))), title=_("Not Available"))
+				bold_invalid_serial_nos = frappe.bold(', '.join(invalid_serial_nos))
+				if len(invalid_serial_nos) == 1:
+					msg = (_("Row #{}: Serial No. {} has already been transacted into another POS Invoice. Please select valid serial no.")
+								.format(d.idx, bold_invalid_serial_nos))
+				elif invalid_serial_nos:
+					msg = (_("Row #{}: Serial Nos. {} has already been transacted into another POS Invoice. Please select valid serial no.")
+								.format(d.idx, bold_invalid_serial_nos))
+
 			else:
 				if allow_negative_stock:
 					return
 
 				available_stock = get_stock_availability(d.item_code, d.warehouse)
-				if not (flt(available_stock) > 0):
-					frappe.throw(_('Row #{}: Item Code: {} is not available under warehouse {}.').format(
-						d.idx, frappe.bold(d.item_code), frappe.bold(d.warehouse)), title=_("Not Available"))
+				item_code, warehouse, qty = frappe.bold(d.item_code), frappe.bold(d.warehouse), frappe.bold(d.qty)
+				if flt(available_stock) <= 0:
+					msg = (_('Row #{}: Item Code: {} is not available under warehouse {}.').format(d.idx, item_code, warehouse))
 				elif flt(available_stock) < flt(d.qty):
-					frappe.msgprint(_('Row #{}: Stock quantity not enough for Item Code: {} under warehouse {}. Available quantity {}.').format(
-						d.idx, frappe.bold(d.item_code), frappe.bold(d.warehouse), frappe.bold(d.qty)), title=_("Not Available"))
+					msg = (_('Row #{}: Stock quantity not enough for Item Code: {} under warehouse {}. Available quantity {}.')
+								.format(d.idx, item_code, warehouse, qty))
+			if msg:
+				error_msg.append(msg)
+
+		if error_msg:
+			frappe.throw(error_msg, title=_("Item Unavailable"), as_list=True)
 
 	def validate_serialised_or_batched_item(self):
+		error_msg = []
 		for d in self.get("items"):
 			serialized = d.get("has_serial_no")
 			batched = d.get("has_batch_no")
 			no_serial_selected = not d.get("serial_no")
 			no_batch_selected = not d.get("batch_no")
 
-
+			msg = ""
+			item_code = frappe.bold(d.item_code)
 			if serialized and batched and (no_batch_selected or no_serial_selected):
-				frappe.throw(_('Row #{}: Please select a serial no and batch against item: {} or remove it to complete transaction.').format(
-					d.idx, frappe.bold(d.item_code)), title=_("Invalid Item"))
+				msg = (_('Row #{}: Please select a serial no and batch against item: {} or remove it to complete transaction.')
+							.format(d.idx, item_code))
 			if serialized and no_serial_selected:
-				frappe.throw(_('Row #{}: No serial number selected against item: {}. Please select one or remove it to complete transaction.').format(
-					d.idx, frappe.bold(d.item_code)), title=_("Invalid Item"))
+				msg = (_('Row #{}: No serial number selected against item: {}. Please select one or remove it to complete transaction.')
+							.format(d.idx, item_code))
 			if batched and no_batch_selected:
-				frappe.throw(_('Row #{}: No batch selected against item: {}. Please select a batch or remove it to complete transaction.').format(
-					d.idx, frappe.bold(d.item_code)), title=_("Invalid Item"))
+				msg = (_('Row #{}: No batch selected against item: {}. Please select a batch or remove it to complete transaction.')
+							.format(d.idx, item_code))
+			if msg:
+				error_msg.append(msg)
 
-	def validate_return_items(self):
+		if error_msg:
+			frappe.throw(error_msg, title=_("Invalid Item"), as_list=True)
+
+	def validate_return_items_qty(self):
 		if not self.get("is_return"): return
 
 		for d in self.get("items"):
 			if d.get("qty") > 0:
-				frappe.throw(_("Row #{}: You cannot add postive quantities in a return invoice. Please remove item {} to complete the return.")
-					.format(d.idx, frappe.bold(d.item_code)), title=_("Invalid Item"))
+				frappe.throw(
+					_("Row #{}: You cannot add postive quantities in a return invoice. Please remove item {} to complete the return.")
+					.format(d.idx, frappe.bold(d.item_code)), title=_("Invalid Item")
+				)
+			if d.get("serial_no"):
+				serial_nos = get_serial_nos(d.serial_no)
+				for sr in serial_nos:
+					serial_no_exists = frappe.db.exists("POS Invoice Item", {
+						"parent": self.return_against, 
+						"serial_no": ["like", d.get("serial_no")]
+					})
+					if not serial_no_exists:
+						bold_return_against = frappe.bold(self.return_against)
+						bold_serial_no = frappe.bold(sr)
+						frappe.throw(
+							_("Row #{}: Serial No {} cannot be returned since it was not transacted in original invoice {}")
+							.format(d.idx, bold_serial_no, bold_return_against)
+						)
 
-	def validate_pos_paid_amount(self):
-		if len(self.payments) == 0 and self.is_pos:
+	def validate_mode_of_payment(self):
+		if len(self.payments) == 0:
 			frappe.throw(_("At least one mode of payment is required for POS invoice."))
 
 	def validate_change_account(self):
@@ -165,20 +189,18 @@
 		if flt(self.change_amount) and not self.account_for_change_amount:
 			frappe.msgprint(_("Please enter Account for Change Amount"), raise_exception=1)
 
-	def verify_payment_amount(self):
+	def validate_payment_amount(self):
+		total_amount_in_payments = 0
 		for entry in self.payments:
+			total_amount_in_payments += entry.amount
 			if not self.is_return and entry.amount < 0:
 				frappe.throw(_("Row #{0} (Payment Table): Amount must be positive").format(entry.idx))
 			if self.is_return and entry.amount > 0:
 				frappe.throw(_("Row #{0} (Payment Table): Amount must be negative").format(entry.idx))
 
-	def validate_pos_return(self):
-		if self.is_pos and self.is_return:
-			total_amount_in_payments = 0
-			for payment in self.payments:
-				total_amount_in_payments += payment.amount
+		if self.is_return:
 			invoice_total = self.rounded_total or self.grand_total
-			if total_amount_in_payments < invoice_total:
+			if total_amount_in_payments and total_amount_in_payments < invoice_total:
 				frappe.throw(_("Total payments amount can't be greater than {}").format(-invoice_total))
 
 	def validate_loyalty_transaction(self):
@@ -233,55 +255,45 @@
 			pos_profile = get_pos_profile(self.company) or {}
 			self.pos_profile = pos_profile.get('name')
 
-		pos = {}
+		profile = {}
 		if self.pos_profile:
-			pos = frappe.get_doc('POS Profile', self.pos_profile)
+			profile = frappe.get_doc('POS Profile', self.pos_profile)
 
 		if not self.get('payments') and not for_validate:
-			update_multi_mode_option(self, pos)
+			update_multi_mode_option(self, profile)
+		
+		if self.is_return and not for_validate:
+			add_return_modes(self, profile)
 
-		if not self.account_for_change_amount:
-			self.account_for_change_amount = frappe.get_cached_value('Company',  self.company,  'default_cash_account')
-
-		if pos:
-			if not for_validate:
-				self.tax_category = pos.get("tax_category")
-
+		if profile:
 			if not for_validate and not self.customer:
-				self.customer = pos.customer
+				self.customer = profile.customer
 
-			self.ignore_pricing_rule = pos.ignore_pricing_rule
-			if pos.get('account_for_change_amount'):
-				self.account_for_change_amount = pos.get('account_for_change_amount')
-			if pos.get('warehouse'):
-				self.set_warehouse = pos.get('warehouse')
+			self.ignore_pricing_rule = profile.ignore_pricing_rule
+			self.account_for_change_amount = profile.get('account_for_change_amount') or self.account_for_change_amount
+			self.set_warehouse = profile.get('warehouse') or self.set_warehouse
 
-			for fieldname in ('naming_series', 'currency', 'letter_head', 'tc_name',
+			for fieldname in ('currency', 'letter_head', 'tc_name',
 				'company', 'select_print_heading', 'write_off_account', 'taxes_and_charges',
-				'write_off_cost_center', 'apply_discount_on', 'cost_center'):
-					if (not for_validate) or (for_validate and not self.get(fieldname)):
-						self.set(fieldname, pos.get(fieldname))
-
-			if pos.get("company_address"):
-				self.company_address = pos.get("company_address")
+				'write_off_cost_center', 'apply_discount_on', 'cost_center', 'tax_category',
+				'ignore_pricing_rule', 'company_address', 'update_stock'):
+					if not for_validate:
+						self.set(fieldname, profile.get(fieldname))
 
 			if self.customer:
 				customer_price_list, customer_group = frappe.db.get_value("Customer", self.customer, ['default_price_list', 'customer_group'])
 				customer_group_price_list = frappe.db.get_value("Customer Group", customer_group, 'default_price_list')
-				selling_price_list = customer_price_list or customer_group_price_list or pos.get('selling_price_list')
+				selling_price_list = customer_price_list or customer_group_price_list or profile.get('selling_price_list')
 			else:
-				selling_price_list = pos.get('selling_price_list')
+				selling_price_list = profile.get('selling_price_list')
 
 			if selling_price_list:
 				self.set('selling_price_list', selling_price_list)
 
-			if not for_validate:
-				self.update_stock = cint(pos.get("update_stock"))
-
 			# set pos values in items
 			for item in self.get("items"):
 				if item.get('item_code'):
-					profile_details = get_pos_profile_item_details(pos, frappe._dict(item.as_dict()), pos)
+					profile_details = get_pos_profile_item_details(profile.get("company"), frappe._dict(item.as_dict()), profile)
 					for fname, val in iteritems(profile_details):
 						if (not for_validate) or (for_validate and not item.get(fname)):
 							item.set(fname, val)
@@ -294,10 +306,13 @@
 			if self.taxes_and_charges and not len(self.get("taxes")):
 				self.set_taxes()
 
-		return pos
+		if not self.account_for_change_amount:
+			self.account_for_change_amount = frappe.get_cached_value('Company',  self.company,  'default_cash_account')
+
+		return profile
 
 	def set_missing_values(self, for_validate=False):
-		pos = self.set_pos_fields(for_validate)
+		profile = self.set_pos_fields(for_validate)
 
 		if not self.debit_to:
 			self.debit_to = get_party_account("Customer", self.customer, self.company)
@@ -307,17 +322,15 @@
 
 		super(SalesInvoice, self).set_missing_values(for_validate)
 
-		print_format = pos.get("print_format") if pos else None
+		print_format = profile.get("print_format") if profile else None
 		if not print_format and not cint(frappe.db.get_value('Print Format', 'POS Invoice', 'disabled')):
 			print_format = 'POS Invoice'
 
-		if pos:
+		if profile:
 			return {
 				"print_format": print_format,
-				"allow_edit_rate": pos.get("allow_user_to_edit_rate"),
-				"allow_edit_discount": pos.get("allow_user_to_edit_discount"),
-				"campaign": pos.get("campaign"),
-				"allow_print_before_pay": pos.get("allow_print_before_pay")
+				"campaign": profile.get("campaign"),
+				"allow_print_before_pay": profile.get("allow_print_before_pay")
 			}
 
 	def set_account_for_mode_of_payment(self):
@@ -352,6 +365,21 @@
 
 				return make_payment_request(**record)
 
+def add_return_modes(doc, pos_profile):
+	def append_payment(payment_mode):
+		payment = doc.append('payments', {})
+		payment.default = payment_mode.default
+		payment.mode_of_payment = payment_mode.parent
+		payment.account = payment_mode.default_account
+		payment.type = payment_mode.type
+
+	for pos_payment_method in pos_profile.get('payments'):
+		pos_payment_method = pos_payment_method.as_dict()
+		mode_of_payment = pos_payment_method.mode_of_payment
+		if pos_payment_method.allow_in_returns and not [d for d in doc.get('payments') if d.mode_of_payment == mode_of_payment]:
+			payment_mode = get_mode_of_payment_info(mode_of_payment, doc.company)
+			append_payment(payment_mode[0])
+
 @frappe.whitelist()
 def get_stock_availability(item_code, warehouse):
 	latest_sle = frappe.db.sql("""select qty_after_transaction
@@ -373,11 +401,9 @@
 	sle_qty = latest_sle[0].qty_after_transaction or 0 if latest_sle else 0
 	pos_sales_qty = pos_sales_qty[0].qty or 0 if pos_sales_qty else 0
 
-	if sle_qty and pos_sales_qty and sle_qty > pos_sales_qty:
+	if sle_qty and pos_sales_qty:
 		return sle_qty - pos_sales_qty
 	else:
-		# when sle_qty is 0
-		# when sle_qty > 0 and pos_sales_qty is 0
 		return sle_qty
 
 @frappe.whitelist()
diff --git a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py
index 3a229b1..add27e9 100644
--- a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py
+++ b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py
@@ -26,18 +26,25 @@
 		for d in self.pos_invoices:
 			status, docstatus, is_return, return_against = frappe.db.get_value(
 				'POS Invoice', d.pos_invoice, ['status', 'docstatus', 'is_return', 'return_against'])
-
+			
+			bold_pos_invoice = frappe.bold(d.pos_invoice)
+			bold_status = frappe.bold(status)
 			if docstatus != 1:
-				frappe.throw(_("Row #{}: POS Invoice {} is not submitted yet").format(d.idx, d.pos_invoice))
+				frappe.throw(_("Row #{}: POS Invoice {} is not submitted yet").format(d.idx, bold_pos_invoice))
 			if status == "Consolidated":
-				frappe.throw(_("Row #{}: POS Invoice {} has been {}").format(d.idx, d.pos_invoice, status))
-			if is_return and return_against not in [d.pos_invoice for d in self.pos_invoices] and status != "Consolidated":
-				# if return entry is not getting merged in the current pos closing and if it is not consolidated
-				frappe.throw(
-					_("Row #{}: Return Invoice {} cannot be made against unconsolidated invoice. \
-					You can add original invoice {} manually to proceed.")
-					.format(d.idx, frappe.bold(d.pos_invoice), frappe.bold(return_against))
-				)
+				frappe.throw(_("Row #{}: POS Invoice {} has been {}").format(d.idx, bold_pos_invoice, bold_status))
+			if is_return and return_against and return_against not in [d.pos_invoice for d in self.pos_invoices]:
+				bold_return_against = frappe.bold(return_against)
+				return_against_status = frappe.db.get_value('POS Invoice', return_against, "status")
+				if return_against_status != "Consolidated":
+					# if return entry is not getting merged in the current pos closing and if it is not consolidated
+					bold_unconsolidated = frappe.bold("not Consolidated")
+					msg = (_("Row #{}: Original Invoice {} of return invoice {} is {}. ")
+								.format(d.idx, bold_return_against, bold_pos_invoice, bold_unconsolidated))
+					msg += _("Original invoice should be consolidated before or along with the return invoice.")
+					msg += "<br><br>"
+					msg += _("You can add original invoice {} manually to proceed.").format(bold_return_against)
+					frappe.throw(msg)
 
 	def on_submit(self):
 		pos_invoice_docs = [frappe.get_doc("POS Invoice", d.pos_invoice) for d in self.pos_invoices]
diff --git a/erpnext/accounts/doctype/pos_opening_entry/pos_opening_entry.py b/erpnext/accounts/doctype/pos_opening_entry/pos_opening_entry.py
index b9e07b8..acac1c4 100644
--- a/erpnext/accounts/doctype/pos_opening_entry/pos_opening_entry.py
+++ b/erpnext/accounts/doctype/pos_opening_entry/pos_opening_entry.py
@@ -17,18 +17,25 @@
 
 	def validate_pos_profile_and_cashier(self):
 		if self.company != frappe.db.get_value("POS Profile", self.pos_profile, "company"):
-			frappe.throw(_("POS Profile {} does not belongs to company {}".format(self.pos_profile, self.company)))
+			frappe.throw(_("POS Profile {} does not belongs to company {}").format(self.pos_profile, self.company))
 
 		if not cint(frappe.db.get_value("User", self.user, "enabled")):
-			frappe.throw(_("User {} has been disabled. Please select valid user/cashier".format(self.user)))
+			frappe.throw(_("User {} is disabled. Please select valid user/cashier").format(self.user))
 	
 	def validate_payment_method_account(self):
+		invalid_modes = []
 		for d in self.balance_details:
 			account = frappe.db.get_value("Mode of Payment Account", 
 				{"parent": d.mode_of_payment, "company": self.company}, "default_account")
 			if not account:
-				frappe.throw(_("Please set default Cash or Bank account in Mode of Payment {0}")
-					.format(get_link_to_form("Mode of Payment", mode_of_payment)), title=_("Missing Account"))
+				invalid_modes.append(get_link_to_form("Mode of Payment", d.mode_of_payment))
+		
+		if invalid_modes:
+			if invalid_modes == 1:
+				msg = _("Please set default Cash or Bank account in Mode of Payment {}")
+			else:
+				msg = _("Please set default Cash or Bank account in Mode of Payments {}")
+			frappe.throw(msg.format(", ".join(invalid_modes)), title=_("Missing Account"))
 
 	def on_submit(self):
 		self.set_status(update=True)
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/pos_payment_method/pos_payment_method.json b/erpnext/accounts/doctype/pos_payment_method/pos_payment_method.json
index 4d5e1eb..30ebd30 100644
--- a/erpnext/accounts/doctype/pos_payment_method/pos_payment_method.json
+++ b/erpnext/accounts/doctype/pos_payment_method/pos_payment_method.json
@@ -6,6 +6,7 @@
  "engine": "InnoDB",
  "field_order": [
   "default",
+  "allow_in_returns",
   "mode_of_payment"
  ],
  "fields": [
@@ -24,11 +25,19 @@
    "label": "Mode of Payment",
    "options": "Mode of Payment",
    "reqd": 1
+  },
+  {
+   "default": "0",
+   "fieldname": "allow_in_returns",
+   "fieldtype": "Check",
+   "in_list_view": 1,
+   "label": "Allow In Returns"
   }
  ],
+ "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2020-05-29 15:08:41.704844",
+ "modified": "2020-10-20 12:58:46.114456",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "POS Payment Method",
diff --git a/erpnext/accounts/doctype/pos_profile/pos_profile.json b/erpnext/accounts/doctype/pos_profile/pos_profile.json
index 999da75..4e22218 100644
--- a/erpnext/accounts/doctype/pos_profile/pos_profile.json
+++ b/erpnext/accounts/doctype/pos_profile/pos_profile.json
@@ -290,28 +290,30 @@
    "fieldname": "warehouse",
    "fieldtype": "Link",
    "label": "Warehouse",
-   "mandatory_depends_on": "update_stock",
    "oldfieldname": "warehouse",
    "oldfieldtype": "Link",
-   "options": "Warehouse"
-  },
-  {
-   "default": "0",
-   "fieldname": "update_stock",
-   "fieldtype": "Check",
-   "label": "Update Stock"
+   "options": "Warehouse",
+   "reqd": 1
   },
   {
    "default": "0",
    "fieldname": "ignore_pricing_rule",
    "fieldtype": "Check",
    "label": "Ignore Pricing Rule"
+  },
+  {
+   "default": "1",
+   "fieldname": "update_stock",
+   "fieldtype": "Check",
+   "label": "Update Stock",
+   "read_only": 1
   }
  ],
  "icon": "icon-cog",
  "idx": 1,
+ "index_web_pages_for_search": 1,
  "links": [],
- "modified": "2020-10-01 17:29:27.759088",
+ "modified": "2020-10-20 13:16:50.665081",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "POS Profile",
diff --git a/erpnext/accounts/doctype/pos_profile/pos_profile.py b/erpnext/accounts/doctype/pos_profile/pos_profile.py
index 1d160a5..ee76bba 100644
--- a/erpnext/accounts/doctype/pos_profile/pos_profile.py
+++ b/erpnext/accounts/doctype/pos_profile/pos_profile.py
@@ -56,19 +56,29 @@
 		if not self.payments:
 			frappe.throw(_("Payment methods are mandatory. Please add at least one payment method."))
 
-		default_mode_of_payment = [d.default for d in self.payments if d.default]
-		if not default_mode_of_payment:
+		default_mode = [d.default for d in self.payments if d.default]
+		if not default_mode:
 			frappe.throw(_("Please select a default mode of payment"))
 
-		if len(default_mode_of_payment) > 1:
+		if len(default_mode) > 1:
 			frappe.throw(_("You can only select one mode of payment as default"))
 		
+		invalid_modes = []
 		for d in self.payments:
-			account = frappe.db.get_value("Mode of Payment Account", 
-				{"parent": d.mode_of_payment, "company": self.company}, "default_account")
+			account = frappe.db.get_value(
+				"Mode of Payment Account", 
+				{"parent": d.mode_of_payment, "company": self.company},
+				"default_account"
+			)
 			if not account:
-				frappe.throw(_("Please set default Cash or Bank account in Mode of Payment {0}")
-					.format(get_link_to_form("Mode of Payment", mode_of_payment)), title=_("Missing Account"))
+				invalid_modes.append(get_link_to_form("Mode of Payment", d.mode_of_payment))
+
+		if invalid_modes:
+			if invalid_modes == 1:
+				msg = _("Please set default Cash or Bank account in Mode of Payment {}")
+			else:
+				msg = _("Please set default Cash or Bank account in Mode of Payments {}")
+			frappe.throw(msg.format(", ".join(invalid_modes)), title=_("Missing Account"))
 
 	def on_update(self):
 		self.set_defaults()
diff --git a/erpnext/accounts/doctype/pos_settings/pos_settings.js b/erpnext/accounts/doctype/pos_settings/pos_settings.js
index 05cb7f0..8890d59 100644
--- a/erpnext/accounts/doctype/pos_settings/pos_settings.js
+++ b/erpnext/accounts/doctype/pos_settings/pos_settings.js
@@ -9,8 +9,7 @@
 	get_invoice_fields: function(frm) {
 		frappe.model.with_doctype("POS Invoice", () => {
 			var fields = $.map(frappe.get_doc("DocType", "POS Invoice").fields, function(d) {
-				if (frappe.model.no_value_type.indexOf(d.fieldtype) === -1 ||
-					['Table', 'Button'].includes(d.fieldtype)) {
+				if (frappe.model.no_value_type.indexOf(d.fieldtype) === -1 || ['Button'].includes(d.fieldtype)) {
 					return { label: d.label + ' (' + d.fieldtype + ')', value: d.fieldname };
 				} else {
 					return null;
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
index c5260a1..91c4dfb 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -151,14 +151,16 @@
 			["account_type", "report_type", "account_currency"], as_dict=True)
 
 		if account.report_type != "Balance Sheet":
-			frappe.throw(_("Please ensure {} account is a Balance Sheet account. \
-					You can change the parent account to a Balance Sheet account or select a different account.")
-				.format(frappe.bold("Credit To")), title=_("Invalid Account"))
+			frappe.throw(
+				_("Please ensure {} account is a Balance Sheet account. You can change the parent account to a Balance Sheet account or select a different account.")
+				.format(frappe.bold("Credit To")), title=_("Invalid Account")
+			)
 
 		if self.supplier and account.account_type != "Payable":
-			frappe.throw(_("Please ensure {} account is a Payable account. \
-					Change the account type to Payable or select a different account.")
-				.format(frappe.bold("Credit To")), title=_("Invalid Account"))
+			frappe.throw(
+				_("Please ensure {} account is a Payable account. Change the account type to Payable or select a different account.")
+				.format(frappe.bold("Credit To")), title=_("Invalid Account")
+			)
 
 		self.party_account_currency = account.account_currency
 
@@ -244,10 +246,10 @@
 
 				if self.update_stock and (not item.from_warehouse):
 					if for_validate and item.expense_account and item.expense_account != warehouse_account[item.warehouse]["account"]:
-						frappe.msgprint(_('''Row {0}: Expense Head changed to {1} because account {2}
-							is not linked to warehouse {3} or it is not the default inventory account'''.format(
-								item.idx, frappe.bold(warehouse_account[item.warehouse]["account"]),
-								frappe.bold(item.expense_account), frappe.bold(item.warehouse))))
+						msg = _("Row {}: Expense Head changed to {} ").format(item.idx, frappe.bold(warehouse_account[item.warehouse]["account"]))
+						msg += _("because account {} is not linked to warehouse {} ").format(frappe.bold(item.expense_account), frappe.bold(item.warehouse))
+						msg += _("or it is not the default inventory account")
+						frappe.msgprint(msg, title=_("Expense Head Changed"))
 
 					item.expense_account = warehouse_account[item.warehouse]["account"]
 				else:
@@ -259,19 +261,19 @@
 
 						if negative_expense_booked_in_pr:
 							if for_validate and item.expense_account and item.expense_account != stock_not_billed_account:
-								frappe.msgprint(_('''Row {0}: Expense Head changed to {1} because
-								expense is booked against this account in Purchase Receipt {2}'''.format(
-								item.idx, frappe.bold(stock_not_billed_account), frappe.bold(item.purchase_receipt))))
+								msg = _("Row {}: Expense Head changed to {} ").format(item.idx, frappe.bold(stock_not_billed_account))
+								msg += _("because expense is booked against this account in Purchase Receipt {}").format(frappe.bold(item.purchase_receipt))
+								frappe.msgprint(msg, title=_("Expense Head Changed"))
 
 							item.expense_account = stock_not_billed_account
 					else:
 						# If no purchase receipt present then book expense in 'Stock Received But Not Billed'
 						# This is done in cases when Purchase Invoice is created before Purchase Receipt
 						if for_validate and item.expense_account and item.expense_account != stock_not_billed_account:
-							frappe.msgprint(_('''Row {0}: Expense Head changed to {1} as no Purchase
-								Receipt is created against Item {2}. This is done to handle accounting for cases
-								when Purchase Receipt is created after Purchase Invoice'''.format(
-								item.idx, frappe.bold(stock_not_billed_account), frappe.bold(item.item_code))))
+							msg = _("Row {}: Expense Head changed to {} ").format(item.idx, frappe.bold(stock_not_billed_account))
+							msg += _("as no Purchase Receipt is created against Item {}. ").format(frappe.bold(item.item_code))
+							msg += _("This is done to handle accounting for cases when Purchase Receipt is created after Purchase Invoice")
+							frappe.msgprint(msg, title=_("Expense Head Changed"))
 
 						item.expense_account = stock_not_billed_account
 
@@ -299,10 +301,11 @@
 
 			for d in self.get('items'):
 				if not d.purchase_order:
-					throw(_("""Purchase Order Required for item {0}
-						To submit the invoice without purchase order please set
-						{1} as {2} in {3}""").format(frappe.bold(d.item_code), frappe.bold(_('Purchase Order Required')),
-						frappe.bold('No'), get_link_to_form('Buying Settings', 'Buying Settings', 'Buying Settings')))
+					msg = _("Purchase Order Required for item {}").format(frappe.bold(d.item_code))
+					msg += "<br><br>"
+					msg += _("To submit the invoice without purchase order please set {} ").format(frappe.bold(_('Purchase Order Required')))
+					msg += _("as {} in {}").format(frappe.bold('No'), get_link_to_form('Buying Settings', 'Buying Settings', 'Buying Settings'))
+					throw(msg, title=_("Mandatory Purchase Order"))
 
 	def pr_required(self):
 		stock_items = self.get_stock_items()
@@ -313,10 +316,11 @@
 
 			for d in self.get('items'):
 				if not d.purchase_receipt and d.item_code in stock_items:
-					throw(_("""Purchase Receipt Required for item {0}
-						To submit the invoice without purchase receipt please set
-						{1} as {2} in {3}""").format(frappe.bold(d.item_code), frappe.bold(_('Purchase Receipt Required')),
-						frappe.bold('No'), get_link_to_form('Buying Settings', 'Buying Settings', 'Buying Settings')))
+					msg = _("Purchase Receipt Required for item {}").format(frappe.bold(d.item_code))
+					msg += "<br><br>"
+					msg += _("To submit the invoice without purchase receipt please set {} ").format(frappe.bold(_('Purchase Receipt Required')))
+					msg += _("as {} in {}").format(frappe.bold('No'), get_link_to_form('Buying Settings', 'Buying Settings', 'Buying Settings'))
+					throw(msg, title=_("Mandatory Purchase Receipt"))
 
 	def validate_write_off_account(self):
 		if self.write_off_amount and not self.write_off_account:
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index 801e688..4b59887 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -479,14 +479,14 @@
 			frappe.throw(_("Debit To is required"), title=_("Account Missing"))
 
 		if account.report_type != "Balance Sheet":
-			frappe.throw(_("Please ensure {} account is a Balance Sheet account. \
-					You can change the parent account to a Balance Sheet account or select a different account.")
-				.format(frappe.bold("Debit To")), title=_("Invalid Account"))
+			msg = _("Please ensure {} account is a Balance Sheet account. ").format(frappe.bold("Debit To"))
+			msg += _("You can change the parent account to a Balance Sheet account or select a different account.")
+			frappe.throw(msg, title=_("Invalid Account"))
 
 		if self.customer and account.account_type != "Receivable":
-			frappe.throw(_("Please ensure {} account is a Receivable account. \
-					Change the account type to Receivable or select a different account.")
-				.format(frappe.bold("Debit To")), title=_("Invalid Account"))
+			msg = _("Please ensure {} account is a Receivable account. ").format(frappe.bold("Debit To"))
+			msg += _("Change the account type to Receivable or select a different account.")
+			frappe.throw(msg, title=_("Invalid Account"))
 
 		self.party_account_currency = account.account_currency
 
@@ -1141,8 +1141,10 @@
 			where redeem_against=%s''', (lp_entry[0].name), as_dict=1)
 		if against_lp_entry:
 			invoice_list = ", ".join([d.invoice for d in against_lp_entry])
-			frappe.throw(_('''{} can't be cancelled since the Loyalty Points earned has been redeemed.
-				First cancel the {} No {}''').format(self.doctype, self.doctype, invoice_list))
+			frappe.throw(
+				_('''{} can't be cancelled since the Loyalty Points earned has been redeemed. First cancel the {} No {}''')
+				.format(self.doctype, self.doctype, invoice_list)
+			)
 		else:
 			frappe.db.sql('''delete from `tabLoyalty Point Entry` where invoice=%s''', (self.name))
 			# Set loyalty program
@@ -1613,17 +1615,25 @@
 		payment.type = payment_mode.type
 
 	doc.set('payments', [])
+	invalid_modes = []
 	for pos_payment_method in pos_profile.get('payments'):
 		pos_payment_method = pos_payment_method.as_dict()
 
 		payment_mode = get_mode_of_payment_info(pos_payment_method.mode_of_payment, doc.company)
 		if not payment_mode:
-			frappe.throw(_("Please set default Cash or Bank account in Mode of Payment {0}")
-				.format(get_link_to_form("Mode of Payment", pos_payment_method.mode_of_payment)), title=_("Missing Account"))
+			invalid_modes.append(get_link_to_form("Mode of Payment", pos_payment_method.mode_of_payment))
+			continue
 
 		payment_mode[0].default = pos_payment_method.default
 		append_payment(payment_mode[0])
 
+	if invalid_modes:
+		if invalid_modes == 1:
+			msg = _("Please set default Cash or Bank account in Mode of Payment {}")
+		else:
+			msg = _("Please set default Cash or Bank account in Mode of Payments {}")
+		frappe.throw(msg.format(", ".join(invalid_modes)), title=_("Missing Account"))
+
 def get_all_mode_of_payments(doc):
 	return frappe.db.sql("""
 		select mpa.default_account, mpa.parent, mp.type as type
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index fc32977..983cfa8 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -946,8 +946,10 @@
 	company_currency = frappe.get_cached_value('Company',  company,  "default_currency")
 
 	if not conversion_rate:
-		throw(_("{0} is mandatory. Maybe Currency Exchange record is not created for {1} to {2}.").format(
-			conversion_rate_label, currency, company_currency))
+		throw(
+			_("{0} is mandatory. Maybe Currency Exchange record is not created for {1} to {2}.")
+			.format(conversion_rate_label, currency, company_currency)
+		)
 
 
 def validate_taxes_and_charges(tax):
diff --git a/erpnext/public/css/pos.css b/erpnext/public/css/pos.css
index e80e3ed..47f5771 100644
--- a/erpnext/public/css/pos.css
+++ b/erpnext/public/css/pos.css
@@ -210,6 +210,7 @@
 [data-route="point-of-sale"] .item-summary-wrapper:last-child { border-bottom: none; }
 [data-route="point-of-sale"] .total-summary-wrapper:last-child { border-bottom: none; }
 [data-route="point-of-sale"] .invoices-container .invoice-wrapper:last-child { border-bottom: none; }
+[data-route="point-of-sale"] .new-btn { background-color: #5e64ff; color: white; border: none;}
 [data-route="point-of-sale"] .summary-btns:last-child { margin-right: 0px; }
 [data-route="point-of-sale"] ::-webkit-scrollbar { width: 1px }
 
diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js
index be30086..99f3995 100644
--- a/erpnext/public/js/controllers/taxes_and_totals.js
+++ b/erpnext/public/js/controllers/taxes_and_totals.js
@@ -6,6 +6,7 @@
 
 	apply_pricing_rule_on_item: function(item){
 		let effective_item_rate = item.price_list_rate;
+		let item_rate = item.rate;
 		if (in_list(["Sales Order", "Quotation"], item.parenttype) && item.blanket_order_rate) {
 			effective_item_rate = item.blanket_order_rate;
 		}
@@ -17,15 +18,17 @@
 		}
 		item.base_rate_with_margin = flt(item.rate_with_margin) * flt(this.frm.doc.conversion_rate);
 
-		item.rate = flt(item.rate_with_margin , precision("rate", item));
+		item_rate = flt(item.rate_with_margin , precision("rate", item));
 
 		if(item.discount_percentage){
 			item.discount_amount = flt(item.rate_with_margin) * flt(item.discount_percentage) / 100;
 		}
 
 		if (item.discount_amount) {
-			item.rate = flt((item.rate_with_margin) - (item.discount_amount), precision('rate', item));
+			item_rate = flt((item.rate_with_margin) - (item.discount_amount), precision('rate', item));
 		}
+
+		frappe.model.set_value(item.doctype, item.name, "rate", item_rate);
 	},
 
 	calculate_taxes_and_totals: function(update_paid_amount) {
@@ -88,11 +91,8 @@
 			if(this.frm.doc.currency == company_currency) {
 				this.frm.set_value("conversion_rate", 1);
 			} else {
-				const err_message = __('{0} is mandatory. Maybe Currency Exchange record is not created for {1} to {2}', [
-					conversion_rate_label,
-					this.frm.doc.currency,
-					company_currency
-				]);
+				const subs =  [conversion_rate_label, this.frm.doc.currency, company_currency];
+				const err_message = __('{0} is mandatory. Maybe Currency Exchange record is not created for {1} to {2}', subs);
 				frappe.throw(err_message);
 			}
 		}
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index 3391179..23705a8 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -1049,14 +1049,13 @@
 		if(item.item_code && item.uom) {
 			return this.frm.call({
 				method: "erpnext.stock.get_item_details.get_conversion_factor",
-				child: item,
 				args: {
 					item_code: item.item_code,
 					uom: item.uom
 				},
 				callback: function(r) {
 					if(!r.exc) {
-						me.conversion_factor(me.frm.doc, cdt, cdn);
+						frappe.model.set_value(cdt, cdn, 'conversion_factor', r.message.conversion_factor);
 					}
 				}
 			});
diff --git a/erpnext/public/js/utils/serial_no_batch_selector.js b/erpnext/public/js/utils/serial_no_batch_selector.js
index d9f6e1d..2623c3c 100644
--- a/erpnext/public/js/utils/serial_no_batch_selector.js
+++ b/erpnext/public/js/utils/serial_no_batch_selector.js
@@ -75,7 +75,7 @@
 				fieldtype:'Float',
 				read_only: me.has_batch && !me.has_serial_no,
 				label: __(me.has_batch && !me.has_serial_no ? 'Total Qty' : 'Qty'),
-				default: 0
+				default: flt(me.item.stock_qty),
 			},
 			{
 				fieldname: 'auto_fetch_button',
@@ -91,7 +91,8 @@
 							qty: qty,
 							item_code: me.item_code,
 							warehouse: typeof me.warehouse_details.name == "string" ? me.warehouse_details.name : '',
-							batch_no: me.item.batch_no || null
+							batch_no: me.item.batch_no || null,
+							posting_date: me.frm.doc.posting_date || me.frm.doc.transaction_date
 						}
 					});
 
@@ -100,11 +101,12 @@
 						let records_length = auto_fetched_serial_numbers.length;
 						if (!records_length) {
 							const warehouse = me.dialog.fields_dict.warehouse.get_value().bold();
-							frappe.msgprint(__(`Serial numbers unavailable for Item ${me.item.item_code.bold()} 
-								under warehouse ${warehouse}. Please try changing warehouse.`));
+							frappe.msgprint(
+								__('Serial numbers unavailable for Item {0} under warehouse {1}. Please try changing warehouse.', [me.item.item_code.bold(), warehouse])
+							);
 						}
 						if (records_length < qty) {
-							frappe.msgprint(__(`Fetched only ${records_length} available serial numbers.`));
+							frappe.msgprint(__('Fetched only {0} available serial numbers.', [records_length]));
 						}
 						let serial_no_list_field = this.dialog.fields_dict.serial_no;
 						numbers = auto_fetched_serial_numbers.join('\n');
@@ -189,15 +191,12 @@
 		}
 		if(this.has_batch && !this.has_serial_no) {
 			if(values.batches.length === 0 || !values.batches) {
-				frappe.throw(__("Please select batches for batched item "
-					+ values.item_code));
-				return false;
+				frappe.throw(__("Please select batches for batched item {0}", [values.item_code]));
 			}
 			values.batches.map((batch, i) => {
 				if(!batch.selected_qty || batch.selected_qty === 0 ) {
 					if (!this.show_dialog) {
-						frappe.throw(__("Please select quantity on row " + (i+1)));
-						return false;
+						frappe.throw(__("Please select quantity on row {0}", [i+1]));
 					}
 				}
 			});
@@ -206,9 +205,7 @@
 		} else {
 			let serial_nos = values.serial_no || '';
 			if (!serial_nos || !serial_nos.replace(/\s/g, '').length) {
-				frappe.throw(__("Please enter serial numbers for serialized item "
-					+ values.item_code));
-				return false;
+				frappe.throw(__("Please enter serial numbers for serialized item {0}", [values.item_code]));
 			}
 			return true;
 		}
@@ -355,8 +352,7 @@
 							});
 							if (selected_batches.includes(val)) {
 								this.set_value("");
-								frappe.throw(__(`Batch ${val} already selected.`));
-								return;
+								frappe.throw(__('Batch {0} already selected.', [val]));
 							}
 
 							if (me.warehouse_details.name) {
@@ -375,8 +371,7 @@
 
 							} else {
 								this.set_value("");
-								frappe.throw(__(`Please select a warehouse to get available
-									quantities`));
+								frappe.throw(__('Please select a warehouse to get available quantities'));
 							}
 							// e.stopImmediatePropagation();
 						}
@@ -411,8 +406,7 @@
 								parseFloat(available_qty) < parseFloat(selected_qty)) {
 
 								this.set_value('0');
-								frappe.throw(__(`For transfer from source, selected quantity cannot be
-									greater than available quantity`));
+								frappe.throw(__('For transfer from source, selected quantity cannot be greater than available quantity'));
 							} else {
 								this.grid.refresh();
 							}
@@ -451,20 +445,12 @@
 			frappe.call({
 				method: "erpnext.stock.doctype.serial_no.serial_no.get_pos_reserved_serial_nos",
 				args: {
-					item_code: me.item_code,
-					warehouse: typeof me.warehouse_details.name == "string" ? me.warehouse_details.name : ''
+					filters: {
+						item_code: me.item_code,
+						warehouse: typeof me.warehouse_details.name == "string" ? me.warehouse_details.name : '',
+					}
 				}
 			}).then((data) => {
-				if (!data.message[1].length) {
-					this.showing_reserved_serial_nos_error = true;
-					const warehouse = me.dialog.fields_dict.warehouse.get_value().bold();
-					const d = frappe.msgprint(__(`Serial numbers unavailable for Item ${me.item.item_code.bold()} 
-						under warehouse ${warehouse}. Please try changing warehouse.`));
-					d.get_close_btn().on('click', () => {
-						this.showing_reserved_serial_nos_error = false;
-						d.hide();
-					});
-				}
 				serial_no_filters['name'] = ["not in", data.message[0]]
 			})
 		}
diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.js b/erpnext/selling/page/point_of_sale/point_of_sale.js
index 8d4ac78..9d44a9f 100644
--- a/erpnext/selling/page/point_of_sale/point_of_sale.js
+++ b/erpnext/selling/page/point_of_sale/point_of_sale.js
@@ -12,4 +12,12 @@
 
 	wrapper.pos = new erpnext.PointOfSale.Controller(wrapper);
 	window.cur_pos = wrapper.pos;
-};
\ No newline at end of file
+};
+
+frappe.pages['point-of-sale'].refresh = function(wrapper) {
+	if (document.scannerDetectionData) {
+		onScan.detachFrom(document);
+		wrapper.pos.wrapper.html("");
+		wrapper.pos.check_opening_entry();
+	}
+}
\ No newline at end of file
diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.py b/erpnext/selling/page/point_of_sale/point_of_sale.py
index 83bd71d..e5b50d7 100644
--- a/erpnext/selling/page/point_of_sale/point_of_sale.py
+++ b/erpnext/selling/page/point_of_sale/point_of_sale.py
@@ -11,54 +11,67 @@
 from six import string_types
 
 @frappe.whitelist()
-def get_items(start, page_length, price_list, item_group, search_value="", pos_profile=None):
+def get_items(start, page_length, price_list, item_group, pos_profile, search_value=""):
 	data = dict()
-	warehouse = ""
+	result = []
+	warehouse, show_only_available_items = "", False
 
-	if pos_profile:
-		warehouse = frappe.db.get_value('POS Profile', pos_profile, ['warehouse'])
+	allow_negative_stock = frappe.db.get_single_value('Stock Settings', 'allow_negative_stock')
+	if not allow_negative_stock:
+		warehouse, show_only_available_items = frappe.db.get_value('POS Profile', pos_profile, ['warehouse', 'show_only_available_items'])
 
 	if not frappe.db.exists('Item Group', item_group):
 		item_group = get_root_of('Item Group')
 
 	if search_value:
 		data = search_serial_or_batch_or_barcode_number(search_value)
-
+	
 	item_code = data.get("item_code") if data.get("item_code") else search_value
 	serial_no = data.get("serial_no") if data.get("serial_no") else ""
 	batch_no = data.get("batch_no") if data.get("batch_no") else ""
 	barcode = data.get("barcode") if data.get("barcode") else ""
 
-	condition = get_conditions(item_code, serial_no, batch_no, barcode)
+	if data:
+		item_info = frappe.db.get_value(
+			"Item", data.get("item_code"), 
+			["name as item_code", "item_name", "description", "stock_uom", "image as item_image", "is_stock_item"]
+		, as_dict=1)
+		item_info.setdefault('serial_no', serial_no)
+		item_info.setdefault('batch_no', batch_no)
+		item_info.setdefault('barcode', barcode)
 
-	if pos_profile:
-		condition += get_item_group_condition(pos_profile)
+		return { 'items': [item_info] }
+
+	condition = get_conditions(item_code, serial_no, batch_no, barcode)
+	condition += get_item_group_condition(pos_profile)
 
 	lft, rgt = frappe.db.get_value('Item Group', item_group, ['lft', 'rgt'])
-	# locate function is used to sort by closest match from the beginning of the value
 
-	result = []
+	bin_join_selection, bin_join_condition = "", ""
+	if show_only_available_items:
+		bin_join_selection = ", `tabBin` bin"
+		bin_join_condition = "AND bin.warehouse = %(warehouse)s AND bin.item_code = item.name AND bin.actual_qty > 0"
 
 	items_data = frappe.db.sql("""
 		SELECT
-			name AS item_code,
-			item_name,
-			description,
-			stock_uom,
-			image AS item_image,
-			idx AS idx,
-			is_stock_item
+			item.name AS item_code,
+			item.item_name,
+			item.description,
+			item.stock_uom,
+			item.image AS item_image,
+			item.is_stock_item
 		FROM
-			`tabItem`
+			`tabItem` item {bin_join_selection}
 		WHERE
-			disabled = 0
-				AND has_variants = 0
-				AND is_sales_item = 1
-				AND is_fixed_asset = 0
-				AND item_group in (SELECT name FROM `tabItem Group` WHERE lft >= {lft} AND rgt <= {rgt})
-				AND {condition}
+			item.disabled = 0
+			AND item.has_variants = 0
+			AND item.is_sales_item = 1
+			AND item.is_fixed_asset = 0
+			AND item.item_group in (SELECT name FROM `tabItem Group` WHERE lft >= {lft} AND rgt <= {rgt})
+			AND {condition}
+			{bin_join_condition}
 		ORDER BY
-			name asc
+			item.name asc
 		LIMIT
 			{start}, {page_length}"""
 		.format(
@@ -66,8 +79,10 @@
 			page_length=page_length,
 			lft=lft,
 			rgt=rgt,
-			condition=condition
-		), as_dict=1)
+			condition=condition,
+			bin_join_selection=bin_join_selection,
+			bin_join_condition=bin_join_condition
+		), {'warehouse': warehouse}, as_dict=1)
 
 	if items_data:
 		items = [d.item_code for d in items_data]
@@ -82,46 +97,24 @@
 		for item in items_data:
 			item_code = item.item_code
 			item_price = item_prices.get(item_code) or {}
-			item_stock_qty = get_stock_availability(item_code, warehouse)
-
-			if not item_stock_qty:
-				pass
+			if not allow_negative_stock:
+				item_stock_qty = frappe.db.sql("""select ifnull(sum(actual_qty), 0) from `tabBin` where item_code = %s""", item_code)[0][0]
 			else:
-				row = {}
-				row.update(item)
-				row.update({
-					'price_list_rate': item_price.get('price_list_rate'),
-					'currency': item_price.get('currency'),
-					'actual_qty': item_stock_qty,
-				})
-				result.append(row)
+				item_stock_qty = get_stock_availability(item_code, warehouse)
+
+			row = {}
+			row.update(item)
+			row.update({
+				'price_list_rate': item_price.get('price_list_rate'),
+				'currency': item_price.get('currency'),
+				'actual_qty': item_stock_qty,
+			})
+			result.append(row)
 
 	res = {
 		'items': result
 	}
 
-	if len(res['items']) == 1:
-		res['items'][0].setdefault('serial_no', serial_no)
-		res['items'][0].setdefault('batch_no', batch_no)
-		res['items'][0].setdefault('barcode', barcode)
-
-		return res
-
-	if serial_no:
-		res.update({
-			'serial_no': serial_no
-		})
-
-	if batch_no:
-		res.update({
-			'batch_no': batch_no
-		})
-
-	if barcode:
-		res.update({
-			'barcode': barcode
-		})
-
 	return res
 
 @frappe.whitelist()
@@ -145,16 +138,16 @@
 
 def get_conditions(item_code, serial_no, batch_no, barcode):
 	if serial_no or batch_no or barcode:
-		return "name = {0}".format(frappe.db.escape(item_code))
+		return "item.name = {0}".format(frappe.db.escape(item_code))
 
-	return """(name like {item_code}
-		or item_name like {item_code})""".format(item_code = frappe.db.escape('%' + item_code + '%'))
+	return """(item.name like {item_code}
+		or item.item_name like {item_code})""".format(item_code = frappe.db.escape('%' + item_code + '%'))
 
 def get_item_group_condition(pos_profile):
 	cond = "and 1=1"
 	item_groups = get_item_groups(pos_profile)
 	if item_groups:
-		cond = "and item_group in (%s)"%(', '.join(['%s']*len(item_groups)))
+		cond = "and item.item_group in (%s)"%(', '.join(['%s']*len(item_groups)))
 
 	return cond % tuple(item_groups)
 
diff --git a/erpnext/selling/page/point_of_sale/pos_controller.js b/erpnext/selling/page/point_of_sale/pos_controller.js
index 5018254..3d00546 100644
--- a/erpnext/selling/page/point_of_sale/pos_controller.js
+++ b/erpnext/selling/page/point_of_sale/pos_controller.js
@@ -20,27 +20,58 @@
 		frappe.require(['assets/erpnext/css/pos.css'], this.check_opening_entry.bind(this));
 	}
 
+	fetch_opening_entry() {
+		return frappe.call("erpnext.selling.page.point_of_sale.point_of_sale.check_opening_entry", { "user": frappe.session.user });
+	}
+
 	check_opening_entry() {
-		return frappe.call("erpnext.selling.page.point_of_sale.point_of_sale.check_opening_entry", { "user": frappe.session.user })
-			.then((r) => {
-				if (r.message.length) {
-					// assuming only one opening voucher is available for the current user
-					this.prepare_app_defaults(r.message[0]);
-				} else {
-					this.create_opening_voucher();
-				}
-			});
+		this.fetch_opening_entry().then((r) => {
+			if (r.message.length) {
+				// assuming only one opening voucher is available for the current user
+				this.prepare_app_defaults(r.message[0]);
+			} else {
+				this.create_opening_voucher();
+			}
+		});
 	}
 
 	create_opening_voucher() {
 		const table_fields = [
-			{ fieldname: "mode_of_payment", fieldtype: "Link", in_list_view: 1, label: "Mode of Payment", options: "Mode of Payment", reqd: 1 },
-			{ fieldname: "opening_amount", fieldtype: "Currency", default: 0, in_list_view: 1, label: "Opening Amount", 
-				options: "company:company_currency" }
+			{
+				fieldname: "mode_of_payment", fieldtype: "Link",
+				in_list_view: 1, label: "Mode of Payment",
+				options: "Mode of Payment", reqd: 1
+			},
+			{
+				fieldname: "opening_amount", fieldtype: "Currency",
+				in_list_view: 1, label: "Opening Amount",
+				options: "company:company_currency", 
+				change: function () {
+					dialog.fields_dict.balance_details.df.data.some(d => {
+						if (d.idx == this.doc.idx) {
+							d.opening_amount = this.value;
+							dialog.fields_dict.balance_details.grid.refresh();
+							return true;
+						}
+					});
+				}
+			}
 		];
-
+		const fetch_pos_payment_methods = () => {
+			const pos_profile = dialog.fields_dict.pos_profile.get_value();
+			if (!pos_profile) return;
+			frappe.db.get_doc("POS Profile", pos_profile).then(({ payments }) => {
+				dialog.fields_dict.balance_details.df.data = [];
+				payments.forEach(pay => {
+					const { mode_of_payment } = pay;
+					dialog.fields_dict.balance_details.df.data.push({ mode_of_payment, opening_amount: '0' });
+				});
+				dialog.fields_dict.balance_details.grid.refresh();
+			});
+		}
 		const dialog = new frappe.ui.Dialog({
 			title: __('Create POS Opening Entry'),
+			static: true,
 			fields: [
 				{
 					fieldtype: 'Link', label: __('Company'), default: frappe.defaults.get_default('company'),
@@ -49,20 +80,7 @@
 				{
 					fieldtype: 'Link', label: __('POS Profile'),
 					options: 'POS Profile', fieldname: 'pos_profile', reqd: 1,
-					onchange: () => {
-						const pos_profile = dialog.fields_dict.pos_profile.get_value();
-
-						if (!pos_profile) return;
-
-						frappe.db.get_doc("POS Profile", pos_profile).then(doc => {
-							dialog.fields_dict.balance_details.df.data = [];
-							doc.payments.forEach(pay => {
-								const { mode_of_payment } = pay;
-								dialog.fields_dict.balance_details.df.data.push({ mode_of_payment });
-							});
-							dialog.fields_dict.balance_details.grid.refresh();
-						});
-					}
+					onchange: () => fetch_pos_payment_methods()
 				},
 				{
 					fieldname: "balance_details",
@@ -75,25 +93,18 @@
 					fields: table_fields
 				}
 			],
-			primary_action: ({ company, pos_profile, balance_details }) => {
+			primary_action: async ({ company, pos_profile, balance_details }) => {
 				if (!balance_details.length) {
 					frappe.show_alert({
 						message: __("Please add Mode of payments and opening balance details."),
 						indicator: 'red'
 					})
-					frappe.utils.play_sound("error");
-					return;
+					return frappe.utils.play_sound("error");
 				}
-				frappe.dom.freeze();
-				return frappe.call("erpnext.selling.page.point_of_sale.point_of_sale.create_opening_voucher", 
-					{ pos_profile, company, balance_details })
-					.then((r) => {
-						frappe.dom.unfreeze();
-						dialog.hide();
-						if (r.message) {
-							this.prepare_app_defaults(r.message);
-						}
-					})
+				const method = "erpnext.selling.page.point_of_sale.point_of_sale.create_opening_voucher";
+				const res = await frappe.call({ method, args: { pos_profile, company, balance_details }, freeze:true });
+				!res.exc && this.prepare_app_defaults(res.message);
+				dialog.hide();
 			},
 			primary_action_label: __('Submit')
 		});
@@ -145,8 +156,8 @@
 	}
 
 	prepare_dom() {
-		this.wrapper.append(`
-			<div class="app grid grid-cols-10 pt-8 gap-6"></div>`
+		this.wrapper.append(
+			`<div class="app grid grid-cols-10 pt-8 gap-6"></div>`
 		);
 
 		this.$components_wrapper = this.wrapper.find('.app');
@@ -162,26 +173,25 @@
 	}
 
 	prepare_menu() {
-		var me = this;
 		this.page.clear_menu();
 
-		this.page.add_menu_item(__("Form View"), function () {
-			frappe.model.sync(me.frm.doc);
-			frappe.set_route("Form", me.frm.doc.doctype, me.frm.doc.name);
-		});
+		this.page.add_menu_item(__("Open Form View"), this.open_form_view.bind(this), false, 'Ctrl+F');
 
-		this.page.add_menu_item(__("Toggle Recent Orders"), () => {
-			const show = this.recent_order_list.$component.hasClass('d-none');
-			this.toggle_recent_order_list(show);
-		});
+		this.page.add_menu_item(__("Toggle Recent Orders"), this.toggle_recent_order.bind(this), false, 'Ctrl+O');
 
-		this.page.add_menu_item(__("Save as Draft"), this.save_draft_invoice.bind(this));
+		this.page.add_menu_item(__("Save as Draft"), this.save_draft_invoice.bind(this), false, 'Ctrl+S');
 
-		frappe.ui.keys.on("ctrl+s", this.save_draft_invoice.bind(this));
+		this.page.add_menu_item(__('Close the POS'), this.close_pos.bind(this), false, 'Shift+Ctrl+C');
+	}
 
-		this.page.add_menu_item(__('Close the POS'), this.close_pos.bind(this));
+	open_form_view() {
+		frappe.model.sync(this.frm.doc);
+		frappe.set_route("Form", this.frm.doc.doctype, this.frm.doc.name);
+	}
 
-		frappe.ui.keys.on("shift+ctrl+s", this.close_pos.bind(this));
+	toggle_recent_order() {
+		const show = this.recent_order_list.$component.hasClass('d-none');
+		this.toggle_recent_order_list(show);
 	}
 
 	save_draft_invoice() {
@@ -356,13 +366,12 @@
 				submit_invoice: () => {
 					this.frm.savesubmit()
 						.then((r) => {
-							// this.set_invoice_status();
 							this.toggle_components(false);
 							this.order_summary.toggle_component(true);
 							this.order_summary.load_summary_of(this.frm.doc, true);
 							frappe.show_alert({
 								indicator: 'green',
-								message: __(`POS invoice ${r.doc.name} created succesfully`)
+								message: __('POS invoice {0} created succesfully', [r.doc.name])
 							});
 						});
 				}
@@ -495,31 +504,7 @@
 		if (this.pos_profile && !this.frm.doc.pos_profile) this.frm.doc.pos_profile = this.pos_profile;
 		if (!this.frm.doc.company) return;
 
-		return new Promise(resolve => {
-			return this.frm.call({
-				doc: this.frm.doc,
-				method: "set_missing_values",
-			}).then((r) => {
-				if(!r.exc) {
-					if (!this.frm.doc.pos_profile) {
-						frappe.dom.unfreeze();
-						this.raise_exception_for_pos_profile();
-					}
-					this.frm.trigger("update_stock");
-					this.frm.trigger('calculate_taxes_and_totals');
-					if(this.frm.doc.taxes_and_charges) this.frm.script_manager.trigger("taxes_and_charges");
-					frappe.model.set_default_values(this.frm.doc);
-					if (r.message) {
-						this.frm.pos_print_format = r.message.print_format || "";
-						this.frm.meta.default_print_format = r.message.print_format || "";
-						this.frm.allow_edit_rate = r.message.allow_edit_rate;
-						this.frm.allow_edit_discount = r.message.allow_edit_discount;
-						this.frm.doc.campaign = r.message.campaign;
-					}
-				}
-				resolve();
-			});
-		});
+		return this.frm.trigger("set_pos_data");
 	}
 
 	raise_exception_for_pos_profile() {
@@ -529,11 +514,11 @@
 
 	set_invoice_status() {
 		const [status, indicator] = frappe.listview_settings["POS Invoice"].get_indicator(this.frm.doc);
-		this.page.set_indicator(__(`${status}`), indicator);
+		this.page.set_indicator(status, indicator);
 	}
 
 	set_pos_profile_status() {
-		this.page.set_indicator(__(`${this.pos_profile}`), "blue");
+		this.page.set_indicator(this.pos_profile, "blue");
 	}
 
 	async on_cart_update(args) {
@@ -550,8 +535,10 @@
 
 				field === 'qty' && (value = flt(value));
 
-				if (field === 'qty' && value > 0 && !this.allow_negative_stock)
-					await this.check_stock_availability(item_row, value, this.frm.doc.set_warehouse);
+				if (['qty', 'conversion_factor'].includes(field) && value > 0 && !this.allow_negative_stock) {
+					const qty_needed = field === 'qty' ? value * item_row.conversion_factor : item_row.qty * value;
+					await this.check_stock_availability(item_row, qty_needed, this.frm.doc.set_warehouse);
+				}
 				
 				if (this.is_current_item_being_edited(item_row) || item_selected_from_selector) {
 					await frappe.model.set_value(item_row.doctype, item_row.name, field, value);
@@ -572,7 +559,10 @@
 
 				const args = { item_code, batch_no, [field]: value };
 
-				if (serial_no) args['serial_no'] = serial_no;
+				if (serial_no) {
+					await this.check_serial_no_availablilty(item_code, this.frm.doc.set_warehouse, serial_no);
+					args['serial_no'] = serial_no;
+				}
 
 				if (field === 'serial_no') args['qty'] = value.split(`\n`).length || 0;
 
@@ -633,29 +623,47 @@
 	}
 
 	async trigger_new_item_events(item_row) {
-		await this.frm.script_manager.trigger('item_code', item_row.doctype, item_row.name)
-		await this.frm.script_manager.trigger('qty', item_row.doctype, item_row.name)
+		await this.frm.script_manager.trigger('item_code', item_row.doctype, item_row.name);
+		await this.frm.script_manager.trigger('qty', item_row.doctype, item_row.name);
 	}
 
 	async check_stock_availability(item_row, qty_needed, warehouse) {
 		const available_qty = (await this.get_available_stock(item_row.item_code, warehouse)).message;
 
 		frappe.dom.unfreeze();
+		const bold_item_code = item_row.item_code.bold();
+		const bold_warehouse = warehouse.bold();
+		const bold_available_qty = available_qty.toString().bold()
 		if (!(available_qty > 0)) {
 			frappe.model.clear_doc(item_row.doctype, item_row.name);
-			frappe.throw(__(`Item Code: ${item_row.item_code.bold()} is not available under warehouse ${warehouse.bold()}.`))
+			frappe.throw({
+				title: _("Not Available"),
+				message: __('Item Code: {0} is not available under warehouse {1}.', [bold_item_code, bold_warehouse])
+			})
 		} else if (available_qty < qty_needed) {
 			frappe.show_alert({
-				message: __(`Stock quantity not enough for Item Code: ${item_row.item_code.bold()} under warehouse ${warehouse.bold()}. 
-					Available quantity ${available_qty.toString().bold()}.`),
+				message: __('Stock quantity not enough for Item Code: {0} under warehouse {1}. Available quantity {2}.',
+					[bold_item_code, bold_warehouse, bold_available_qty]),
 				indicator: 'orange'
 			});
 			frappe.utils.play_sound("error");
-			this.item_details.qty_control.set_value(flt(available_qty));
 		}
 		frappe.dom.freeze();
 	}
 
+	async check_serial_no_availablilty(item_code, warehouse, serial_no) {
+		const method = "erpnext.stock.doctype.serial_no.serial_no.get_pos_reserved_serial_nos";
+		const args = {filters: { item_code, warehouse }}
+		const res = await frappe.call({ method, args });
+
+		if (res.message.includes(serial_no)) {
+			frappe.throw({
+				title: __("Not Available"),
+				message: __('Serial No: {0} has already been transacted into another POS Invoice.', [serial_no.bold()])
+			});
+		}
+	}
+
 	get_available_stock(item_code, warehouse) {
 		const me = this;
 		return frappe.call({
diff --git a/erpnext/selling/page/point_of_sale/pos_item_cart.js b/erpnext/selling/page/point_of_sale/pos_item_cart.js
index 724b60b..7799dac 100644
--- a/erpnext/selling/page/point_of_sale/pos_item_cart.js
+++ b/erpnext/selling/page/point_of_sale/pos_item_cart.js
@@ -223,6 +223,8 @@
 	attach_shortcuts() {
 		for (let row of this.number_pad.keys) {
 			for (let btn of row) {
+				if (typeof btn !== 'string') continue; // do not make shortcuts for numbers
+
 				let shortcut_key = `ctrl+${frappe.scrub(String(btn))[0]}`;
 				if (btn === 'Delete') shortcut_key = 'ctrl+backspace';
 				if (btn === 'Remove') shortcut_key = 'shift+ctrl+backspace'
@@ -232,6 +234,10 @@
 				const fieldname = this.number_pad.fieldnames[btn] ? this.number_pad.fieldnames[btn] : 
 					typeof btn === 'string' ? frappe.scrub(btn) : btn;
 
+				let shortcut_label = shortcut_key.split('+').map(frappe.utils.to_title_case).join('+');
+				shortcut_label = frappe.utils.is_mac() ? shortcut_label.replace('Ctrl', '⌘') : shortcut_label;
+				this.$numpad_section.find(`.numpad-btn[data-button-value="${fieldname}"]`).attr("title", shortcut_label);
+
 				frappe.ui.keys.on(`${shortcut_key}`, () => {
 					const cart_is_visible = this.$component.is(":visible");
 					if (cart_is_visible && this.item_is_selected && this.$numpad_section.is(":visible")) {
@@ -240,12 +246,36 @@
 				})
 			}
 		}
-
-		frappe.ui.keys.on("ctrl+enter", () => {
-			const cart_is_visible = this.$component.is(":visible");
-			const payment_section_hidden = this.$totals_section.find('.edit-cart-btn').hasClass('d-none');
-			if (cart_is_visible && payment_section_hidden) {
-				this.$component.find(".checkout-btn").click();
+		const ctrl_label = frappe.utils.is_mac() ? '⌘' : 'Ctrl';
+		this.$component.find(".checkout-btn").attr("title", `${ctrl_label}+Enter`);
+		frappe.ui.keys.add_shortcut({
+			shortcut: "ctrl+enter",
+			action: () => this.$component.find(".checkout-btn").click(),
+			condition: () => this.$component.is(":visible") && this.$totals_section.find('.edit-cart-btn').hasClass('d-none'),
+			description: __("Checkout Order / Submit Order / New Order"),
+			ignore_inputs: true,
+			page: cur_page.page.page
+		});
+		this.$component.find(".edit-cart-btn").attr("title", `${ctrl_label}+E`);
+		frappe.ui.keys.on("ctrl+e", () => {
+			const item_cart_visible = this.$component.is(":visible");
+			if (item_cart_visible && this.$totals_section.find('.checkout-btn').hasClass('d-none')) {
+				this.$component.find(".edit-cart-btn").click()
+			}
+		});
+		this.$component.find(".add-discount").attr("title", `${ctrl_label}+D`);
+		frappe.ui.keys.add_shortcut({
+			shortcut: "ctrl+d",
+			action: () => this.$component.find(".add-discount").click(),
+			condition: () => this.$add_discount_elem.is(":visible"),
+			description: __("Add Order Discount"),
+			ignore_inputs: true,
+			page: cur_page.page.page
+		});
+		frappe.ui.keys.on("escape", () => {
+			const item_cart_visible = this.$component.is(":visible");
+			if (item_cart_visible && this.discount_field && this.discount_field.parent.is(":visible")) {
+				this.discount_field.set_value(0);
 			}
 		});
 	}
@@ -343,8 +373,7 @@
 	show_discount_control() {
 		this.$add_discount_elem.removeClass("pr-4 pl-4");
 		this.$add_discount_elem.html(
-			`<div class="add-dicount-field flex flex-1 items-center"></div>
-			<div class="submit-field flex items-center"></div>`
+			`<div class="add-discount-field flex flex-1 items-center"></div>`
 		);
 		const me = this;
 
@@ -354,14 +383,18 @@
 				fieldtype: 'Data',
 				placeholder: __('Enter discount percentage.'),
 				onchange: function() {
-					if (this.value || this.value == 0) {
-						const frm = me.events.get_frm();
+					const frm = me.events.get_frm();
+					if (this.value.length || this.value === 0) {
 						frappe.model.set_value(frm.doc.doctype, frm.doc.name, 'additional_discount_percentage', flt(this.value));
 						me.hide_discount_control(this.value);
+					} else {
+						frappe.model.set_value(frm.doc.doctype, frm.doc.name, 'additional_discount_percentage', 0);
+						me.$add_discount_elem.html(`+ Add Discount`);
+						me.discount_field = undefined;
 					}
 				},
 			},
-			parent: this.$add_discount_elem.find('.add-dicount-field'),
+			parent: this.$add_discount_elem.find('.add-discount-field'),
 			render_input: true,
 		});
 		this.discount_field.toggle_label(false);
@@ -369,17 +402,24 @@
 	}
 
 	hide_discount_control(discount) {
-		this.$add_discount_elem.addClass('pr-4 pl-4');
-		this.$add_discount_elem.html(
-			`<svg class="mr-2" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1" 
-				stroke-linecap="round" stroke-linejoin="round">
-				<path d="M12 20h9"/><path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z"/>
-			</svg> 
-			<div class="edit-discount p-1 pr-3 pl-3 text-dark-grey rounded w-fit bg-green-200 mb-2">
-				${String(discount).bold()}% off
-			</div>
-			`
-		);
+		if (!discount) {
+			this.$add_discount_elem.removeClass("pr-4 pl-4");
+			this.$add_discount_elem.html(
+				`<div class="add-discount-field flex flex-1 items-center"></div>`
+			);
+		} else {
+			this.$add_discount_elem.addClass('pr-4 pl-4');
+			this.$add_discount_elem.html(
+				`<svg class="mr-2" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1" 
+					stroke-linecap="round" stroke-linejoin="round">
+					<path d="M12 20h9"/><path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z"/>
+				</svg> 
+				<div class="edit-discount p-1 pr-3 pl-3 text-dark-grey rounded w-fit bg-green-200 mb-2">
+					${String(discount).bold()}% off
+				</div>
+				`
+			);
+		}
 	}
 	
 	update_customer_section() {
@@ -475,19 +515,20 @@
 			const currency = this.events.get_frm().doc.currency;
 			this.$totals_section.find('.taxes').html(
 				`<div class="flex items-center justify-between h-16 pr-8 pl-8 border-b-grey">
-					<div class="flex">
+					<div class="flex overflow-hidden whitespace-nowrap">
 						<div class="text-md text-dark-grey text-bold w-fit">Tax Charges</div>
-						<div class="flex ml-6 text-dark-grey">
+						<div class="flex ml-4 text-dark-grey">
 						${	
 							taxes.map((t, i) => {
 								let margin_left = '';
 								if (i !== 0) margin_left = 'ml-2';
-								return `<span class="border-grey p-1 pl-2 pr-2 rounded ${margin_left}">${t.description}</span>`
+								const description = /[0-9]+/.test(t.description) ? t.description : `${t.description} @ ${t.rate}%`;
+								return `<span class="border-grey p-1 pl-2 pr-2 rounded ${margin_left}">${description}</span>`
 							}).join('')
 						}
 						</div>
 					</div>
-					<div class="flex flex-col text-right">
+					<div class="flex flex-col text-right f-shrink-0 ml-4">
 						<div class="text-md text-dark-grey text-bold">${format_currency(value, currency)}</div>
 					</div>
 				</div>`
diff --git a/erpnext/selling/page/point_of_sale/pos_item_details.js b/erpnext/selling/page/point_of_sale/pos_item_details.js
index 3a5f89b..9874d1b 100644
--- a/erpnext/selling/page/point_of_sale/pos_item_details.js
+++ b/erpnext/selling/page/point_of_sale/pos_item_details.js
@@ -177,7 +177,7 @@
 	}
 
 	get_form_fields(item) {
-		const fields = ['qty', 'uom', 'rate', 'price_list_rate', 'discount_percentage', 'warehouse', 'actual_qty'];
+		const fields = ['qty', 'uom', 'rate', 'conversion_factor', 'discount_percentage', 'warehouse', 'actual_qty', 'price_list_rate'];
 		if (item.has_serial_no) fields.push('serial_no');
 		if (item.has_batch_no) fields.push('batch_no');
 		return fields;
@@ -208,7 +208,7 @@
 		const me = this;
 		if (this.rate_control) {
 			this.rate_control.df.onchange = function() {
-				if (this.value) {
+				if (this.value || flt(this.value) === 0) {
 					me.events.form_updated(me.doctype, me.name, 'rate', this.value).then(() => {
 						const item_row = frappe.get_doc(me.doctype, me.name);
 						const doc = me.events.get_frm().doc;
@@ -234,24 +234,22 @@
 							})
 						} else if (available_qty === 0) {
 							me.warehouse_control.set_value('');
-							frappe.throw(__(`Item Code: ${me.item_row.item_code.bold()} is not available under warehouse ${this.value.bold()}.`));
+							const bold_item_code = me.item_row.item_code.bold();
+							const bold_warehouse = this.value.bold();
+							frappe.throw(
+								__('Item Code: {0} is not available under warehouse {1}.', [bold_item_code, bold_warehouse])
+							);
 						}
 						me.actual_qty_control.set_value(available_qty);
 					});
 				}
 			}
-			this.warehouse_control.refresh();
-		}
-
-		if (this.discount_percentage_control) {
-			this.discount_percentage_control.df.onchange = function() {
-				if (this.value) {
-					me.events.form_updated(me.doctype, me.name, 'discount_percentage', this.value).then(() => {
-						const item_row = frappe.get_doc(me.doctype, me.name);
-						me.rate_control.set_value(item_row.rate);
-					});
+			this.warehouse_control.df.get_query = () => {
+				return {
+					filters: { company: this.events.get_frm().doc.company }
 				}
-			}
+			};
+			this.warehouse_control.refresh();
 		}
 
 		if (this.serial_no_control) {
@@ -270,7 +268,8 @@
 					query: 'erpnext.controllers.queries.get_batch_no',
 					filters: {
 						item_code: me.item_row.item_code,
-						warehouse: me.item_row.warehouse
+						warehouse: me.item_row.warehouse,
+						posting_date: me.events.get_frm().doc.posting_date
 					}
 				}
 			};
@@ -287,8 +286,20 @@
 				me.events.set_value_in_current_cart_item('uom', this.value);
 				me.events.form_updated(me.doctype, me.name, 'uom', this.value);
 				me.current_item.uom = this.value;
+				
+				const item_row = frappe.get_doc(me.doctype, me.name);
+				me.conversion_factor_control.df.read_only = (item_row.stock_uom == this.value);
+				me.conversion_factor_control.refresh();
 			}
 		}
+
+		frappe.model.on("POS Invoice Item", "*", (fieldname, value, item_row) => {
+			const field_control = me[`${fieldname}_control`];
+			if (field_control) {
+				field_control.set_value(value);
+				cur_pos.update_cart_html(item_row);
+			}
+		});
 	}
 	
 	async auto_update_batch_no() {
@@ -337,6 +348,7 @@
 	}
 
 	attach_shortcuts() {
+		this.wrapper.find('.close-btn').attr("title", "Esc");
 		frappe.ui.keys.on("escape", () => {
 			const item_details_visible = this.$component.is(":visible");
 			if (item_details_visible) {
@@ -358,8 +370,10 @@
 	
 	bind_auto_serial_fetch_event() {
 		this.$form_container.on('click', '.auto-fetch-btn', () => {
-			this.batch_no_control.set_value('');
+			this.batch_no_control && this.batch_no_control.set_value('');
 			let qty = this.qty_control.get_value();
+			let expiry_date = this.item_row.has_batch_no ? this.events.get_frm().doc.posting_date : "";
+
 			let numbers = frappe.call({
 				method: "erpnext.stock.doctype.serial_no.serial_no.auto_fetch_serial_number",
 				args: {
@@ -367,6 +381,7 @@
 					item_code: this.current_item.item_code,
 					warehouse: this.warehouse_control.get_value() || '',
 					batch_nos: this.current_item.batch_no || '',
+					posting_date: expiry_date,
 					for_doctype: 'POS Invoice'
 				}
 			});
@@ -376,10 +391,14 @@
 				let records_length = auto_fetched_serial_numbers.length;
 				if (!records_length) {
 					const warehouse = this.warehouse_control.get_value().bold();
-					frappe.msgprint(__(`Serial numbers unavailable for Item ${this.current_item.item_code.bold()} 
-						under warehouse ${warehouse}. Please try changing warehouse.`));
+					const item_code = this.current_item.item_code.bold();
+					frappe.msgprint(
+						__('Serial numbers unavailable for Item {0} under warehouse {1}. Please try changing warehouse.', [item_code, warehouse])
+					);
 				} else if (records_length < qty) {
-					frappe.msgprint(`Fetched only ${records_length} available serial numbers.`);
+					frappe.msgprint(
+						__('Fetched only {0} available serial numbers.', [records_length])
+					);
 					this.qty_control.set_value(records_length);
 				}
 				numbers = auto_fetched_serial_numbers.join(`\n`);
diff --git a/erpnext/selling/page/point_of_sale/pos_item_selector.js b/erpnext/selling/page/point_of_sale/pos_item_selector.js
index c87b845..4139e29 100644
--- a/erpnext/selling/page/point_of_sale/pos_item_selector.js
+++ b/erpnext/selling/page/point_of_sale/pos_item_selector.js
@@ -76,7 +76,7 @@
 
 	get_item_html(item) {
 		const { item_image, serial_no, batch_no, barcode, actual_qty, stock_uom } = item;
-		const indicator_color = actual_qty > 10 ? "green" : actual_qty !== 0 ? "orange" : "red";
+		const indicator_color = actual_qty > 10 ? "green" : actual_qty <= 0 ? "red" : "orange";
 
 		function get_item_image_html() {
 			if (item_image) {
@@ -184,15 +184,24 @@
 	}
 
 	attach_shortcuts() {
-		frappe.ui.keys.on("ctrl+i", () => {
-			const selector_is_visible = this.$component.is(':visible');
-			if (!selector_is_visible) return;
-			this.search_field.set_focus();
+		const ctrl_label = frappe.utils.is_mac() ? '⌘' : 'Ctrl';
+		this.search_field.parent.attr("title", `${ctrl_label}+I`);
+		frappe.ui.keys.add_shortcut({
+			shortcut: "ctrl+i",
+			action: () => this.search_field.set_focus(),
+			condition: () => this.$component.is(':visible'),
+			description: __("Focus on search input"),
+			ignore_inputs: true,
+			page: cur_page.page.page
 		});
-		frappe.ui.keys.on("ctrl+g", () => {
-			const selector_is_visible = this.$component.is(':visible');
-			if (!selector_is_visible) return;
-			this.item_group_field.set_focus();
+		this.item_group_field.parent.attr("title", `${ctrl_label}+G`);
+		frappe.ui.keys.add_shortcut({
+			shortcut: "ctrl+g",
+			action: () => this.item_group_field.set_focus(),
+			condition: () => this.$component.is(':visible'),
+			description: __("Focus on Item Group filter"),
+			ignore_inputs: true,
+			page: cur_page.page.page
 		});
 		// for selecting the last filtered item on search
 		frappe.ui.keys.on("enter", () => {
diff --git a/erpnext/selling/page/point_of_sale/pos_past_order_list.js b/erpnext/selling/page/point_of_sale/pos_past_order_list.js
index 9181ee8..b256247 100644
--- a/erpnext/selling/page/point_of_sale/pos_past_order_list.js
+++ b/erpnext/selling/page/point_of_sale/pos_past_order_list.js
@@ -1,55 +1,55 @@
 erpnext.PointOfSale.PastOrderList = class {
-    constructor({ wrapper, events }) {
-        this.wrapper = wrapper;
-        this.events = events;
+	constructor({ wrapper, events }) {
+		this.wrapper = wrapper;
+		this.events = events;
 
-        this.init_component();
-    }
+		this.init_component();
+	}
 
-    init_component() {
-        this.prepare_dom();
-        this.make_filter_section();
-        this.bind_events();
-    }
+	init_component() {
+		this.prepare_dom();
+		this.make_filter_section();
+		this.bind_events();
+	}
 
-    prepare_dom() {
-        this.wrapper.append(
-            `<section class="col-span-4 flex flex-col shadow rounded past-order-list bg-white mx-h-70 h-100 d-none">
-                <div class="flex flex-col rounded w-full scroll-y">
-                    <div class="filter-section flex flex-col p-8 pb-2 bg-white sticky z-100">
-                        <div class="search-field flex items-center text-grey"></div>
-                        <div class="status-field flex items-center text-grey text-bold"></div>
-                    </div>
-                    <div class="flex flex-1 flex-col p-8 pt-2">
-                        <div class="text-grey mb-6">RECENT ORDERS</div>
-                        <div class="invoices-container rounded border grid grid-cols-1"></div>					
-                    </div>
-                </div>
-            </section>`
-        )
+	prepare_dom() {
+		this.wrapper.append(
+			`<section class="col-span-4 flex flex-col shadow rounded past-order-list bg-white mx-h-70 h-100 d-none">
+				<div class="flex flex-col rounded w-full scroll-y">
+					<div class="filter-section flex flex-col p-8 pb-2 bg-white sticky z-100">
+						<div class="search-field flex items-center text-grey"></div>
+						<div class="status-field flex items-center text-grey text-bold"></div>
+					</div>
+					<div class="flex flex-1 flex-col p-8 pt-2">
+						<div class="text-grey mb-6">RECENT ORDERS</div>
+						<div class="invoices-container rounded border grid grid-cols-1"></div>					
+					</div>
+				</div>
+			</section>`
+		);
 
-        this.$component = this.wrapper.find('.past-order-list');
-        this.$invoices_container = this.$component.find('.invoices-container');
-    }
+		this.$component = this.wrapper.find('.past-order-list');
+		this.$invoices_container = this.$component.find('.invoices-container');
+	}
 
-    bind_events() {
-        this.search_field.$input.on('input', (e) => {
+	bind_events() {
+		this.search_field.$input.on('input', (e) => {
 			clearTimeout(this.last_search);
 			this.last_search = setTimeout(() => {
-                const search_term = e.target.value;
-                this.refresh_list(search_term, this.status_field.get_value());
+				const search_term = e.target.value;
+				this.refresh_list(search_term, this.status_field.get_value());
 			}, 300);
-        });
-        const me = this;
-        this.$invoices_container.on('click', '.invoice-wrapper', function() {
-            const invoice_name = unescape($(this).attr('data-invoice-name'));
+		});
+		const me = this;
+		this.$invoices_container.on('click', '.invoice-wrapper', function() {
+			const invoice_name = unescape($(this).attr('data-invoice-name'));
 
-            me.events.open_invoice_data(invoice_name);
-        })
-    }
+			me.events.open_invoice_data(invoice_name);
+		});
+	}
 
-    make_filter_section() {
-        const me = this;
+	make_filter_section() {
+		const me = this;
 		this.search_field = frappe.ui.form.make_control({
 			df: {
 				label: __('Search'),
@@ -58,73 +58,71 @@
 			},
 			parent: this.$component.find('.search-field'),
 			render_input: true,
-        });
+		});
 		this.status_field = frappe.ui.form.make_control({
 			df: {
 				label: __('Invoice Status'),
-                fieldtype: 'Select',
+				fieldtype: 'Select',
 				options: `Draft\nPaid\nConsolidated\nReturn`,
-                placeholder: __('Filter by invoice status'),
-                onchange: function() {
-                    me.refresh_list(me.search_field.get_value(), this.value);
-                }
+				placeholder: __('Filter by invoice status'),
+				onchange: function() {
+					me.refresh_list(me.search_field.get_value(), this.value);
+				}
 			},
-            parent: this.$component.find('.status-field'),
+			parent: this.$component.find('.status-field'),
 			render_input: true,
-        });
-        this.search_field.toggle_label(false);
-        this.status_field.toggle_label(false);
-        this.status_field.set_value('Paid');
-    }
-    
-    toggle_component(show) {
-        show ? 
-        this.$component.removeClass('d-none') && this.refresh_list() :
-        this.$component.addClass('d-none');
-    }
+		});
+		this.search_field.toggle_label(false);
+		this.status_field.toggle_label(false);
+		this.status_field.set_value('Draft');
+	}
 
-    refresh_list() {
-        frappe.dom.freeze();
-        this.events.reset_summary();
-        const search_term = this.search_field.get_value();
-        const status = this.status_field.get_value();
+	toggle_component(show) {
+		show ? this.$component.removeClass('d-none') && this.refresh_list() : this.$component.addClass('d-none');
+	}
 
-        this.$invoices_container.html('');
+	refresh_list() {
+		frappe.dom.freeze();
+		this.events.reset_summary();
+		const search_term = this.search_field.get_value();
+		const status = this.status_field.get_value();
 
-        return frappe.call({
+		this.$invoices_container.html('');
+
+		return frappe.call({
 			method: "erpnext.selling.page.point_of_sale.point_of_sale.get_past_order_list",
 			freeze: true,
-            args: { search_term, status },
-            callback: (response) => {
-                frappe.dom.unfreeze();
-                response.message.forEach(invoice => {
-                    const invoice_html = this.get_invoice_html(invoice);
-                    this.$invoices_container.append(invoice_html);
-                });
-            }
-       });
-    }
+			args: { search_term, status },
+			callback: (response) => {
+				frappe.dom.unfreeze();
+				response.message.forEach(invoice => {
+					const invoice_html = this.get_invoice_html(invoice);
+					this.$invoices_container.append(invoice_html);
+				});
+			}
+		});
+	}
 
-    get_invoice_html(invoice) {
-        const posting_datetime = moment(invoice.posting_date+" "+invoice.posting_time).format("Do MMMM, h:mma");
-        return (
-            `<div class="invoice-wrapper flex p-4 justify-between border-b-grey pointer no-select" data-invoice-name="${escape(invoice.name)}">
-                <div class="flex flex-col justify-end">
-                    <div class="text-dark-grey text-bold overflow-hidden whitespace-nowrap mb-2">${invoice.name}</div>
-                    <div class="flex items-center">
-                        <div class="flex items-center f-shrink-1 text-dark-grey overflow-hidden whitespace-nowrap">
-                            <svg class="mr-2" width="12" height="12" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1" stroke-linecap="round" stroke-linejoin="round">
-                                <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/>
-                            </svg>
-                            ${invoice.customer}
-                        </div>
-                    </div>
-                </div>
-                <div class="flex flex-col text-right">
-                    <div class="f-shrink-0 text-lg text-dark-grey text-bold ml-4">${format_currency(invoice.grand_total, invoice.currency, 0) || 0}</div>
-                    <div class="f-shrink-0 text-grey ml-4">${posting_datetime}</div>
-                </div>
-            </div>`
-        )
-    }
-}
\ No newline at end of file
+	get_invoice_html(invoice) {
+		const posting_datetime = moment(invoice.posting_date+" "+invoice.posting_time).format("Do MMMM, h:mma");
+		return (
+			`<div class="invoice-wrapper flex p-4 justify-between border-b-grey pointer no-select" data-invoice-name="${escape(invoice.name)}">
+				<div class="flex flex-col justify-end">
+					<div class="text-dark-grey text-bold overflow-hidden whitespace-nowrap mb-2">${invoice.name}</div>
+					<div class="flex items-center">
+						<div class="flex items-center f-shrink-1 text-dark-grey overflow-hidden whitespace-nowrap">
+							<svg class="mr-2" width="12" height="12" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1" stroke-linecap="round" stroke-linejoin="round">
+								<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/>
+							</svg>
+							${invoice.customer}
+						</div>
+					</div>
+				</div>
+				<div class="flex flex-col text-right">
+					<div class="f-shrink-0 text-lg text-dark-grey text-bold ml-4">${format_currency(invoice.grand_total, invoice.currency, 0) || 0}</div>
+					<div class="f-shrink-0 text-grey ml-4">${posting_datetime}</div>
+				</div>
+			</div>`
+		);
+	}
+};
\ No newline at end of file
diff --git a/erpnext/selling/page/point_of_sale/pos_past_order_summary.js b/erpnext/selling/page/point_of_sale/pos_past_order_summary.js
index 30e0918..6fd4c26 100644
--- a/erpnext/selling/page/point_of_sale/pos_past_order_summary.js
+++ b/erpnext/selling/page/point_of_sale/pos_past_order_summary.js
@@ -1,456 +1,476 @@
 erpnext.PointOfSale.PastOrderSummary = class {
-    constructor({ wrapper, events }) {
-        this.wrapper = wrapper;
-        this.events = events;
+	constructor({ wrapper, events }) {
+		this.wrapper = wrapper;
+		this.events = events;
 
-        this.init_component();
-    }
+		this.init_component();
+	}
 
-    init_component() {
-        this.prepare_dom();
-        this.init_child_components();
-        this.bind_events();
-        this.attach_shortcuts();
-    }
+	init_component() {
+		this.prepare_dom();
+		this.init_child_components();
+		this.bind_events();
+		this.attach_shortcuts();
+	}
 
-    prepare_dom() {
-        this.wrapper.append(
-            `<section class="col-span-6 flex flex-col items-center shadow rounded past-order-summary bg-white mx-h-70 h-100 d-none">
-                <div class="no-summary-placeholder flex flex-1 items-center justify-center p-16">
-                    <div class="no-item-wrapper flex items-center h-18 pr-4 pl-4">
-                        <div class="flex-1 text-center text-grey">Select an invoice to load summary data</div>
-                    </div>
-                </div>
-                <div class="summary-wrapper d-none flex-1 w-66 text-dark-grey relative">
-                    <div class="summary-container absolute flex flex-col pt-16 pb-16 pr-8 pl-8 w-full h-full"></div>
-                </div>
-            </section>`
-        )
+	prepare_dom() {
+		this.wrapper.append(
+			`<section class="col-span-6 flex flex-col items-center shadow rounded past-order-summary bg-white mx-h-70 h-100 d-none">
+				<div class="no-summary-placeholder flex flex-1 items-center justify-center p-16">
+					<div class="no-item-wrapper flex items-center h-18 pr-4 pl-4">
+						<div class="flex-1 text-center text-grey">Select an invoice to load summary data</div>
+					</div>
+				</div>
+				<div class="summary-wrapper d-none flex-1 w-66 text-dark-grey relative">
+					<div class="summary-container absolute flex flex-col pt-16 pb-16 pr-8 pl-8 w-full h-full"></div>
+				</div>
+			</section>`
+		);
 
-        this.$component = this.wrapper.find('.past-order-summary');
-        this.$summary_wrapper = this.$component.find('.summary-wrapper');
-        this.$summary_container = this.$component.find('.summary-container');
-    }
+		this.$component = this.wrapper.find('.past-order-summary');
+		this.$summary_wrapper = this.$component.find('.summary-wrapper');
+		this.$summary_container = this.$component.find('.summary-container');
+	}
 
-    init_child_components() {
-        this.init_upper_section();
-        this.init_items_summary();
-        this.init_totals_summary();
-        this.init_payments_summary();
-        this.init_summary_buttons();
-        this.init_email_print_dialog();
-    }
+	init_child_components() {
+		this.init_upper_section();
+		this.init_items_summary();
+		this.init_totals_summary();
+		this.init_payments_summary();
+		this.init_summary_buttons();
+		this.init_email_print_dialog();
+	}
 
-    init_upper_section() {
-        this.$summary_container.append(
-            `<div class="flex upper-section justify-between w-full h-24"></div>`
-        );
+	init_upper_section() {
+		this.$summary_container.append(
+			`<div class="flex upper-section justify-between w-full h-24"></div>`
+		);
 
-        this.$upper_section = this.$summary_container.find('.upper-section');
-    }
+		this.$upper_section = this.$summary_container.find('.upper-section');
+	}
 
-    init_items_summary() {
-        this.$summary_container.append(
-            `<div class="flex flex-col flex-1 mt-6 w-full scroll-y">
-                <div class="text-grey mb-4 sticky bg-white">ITEMS</div>
-                <div class="items-summary-container border rounded flex flex-col w-full"></div>
-            </div>`
-        )
+	init_items_summary() {
+		this.$summary_container.append(
+			`<div class="flex flex-col flex-1 mt-6 w-full scroll-y">
+				<div class="text-grey mb-4 sticky bg-white">ITEMS</div>
+				<div class="items-summary-container border rounded flex flex-col w-full"></div>
+			</div>`
+		);
 
-        this.$items_summary_container = this.$summary_container.find('.items-summary-container');
-    }
+		this.$items_summary_container = this.$summary_container.find('.items-summary-container');
+	}
 
-    init_totals_summary() {
-        this.$summary_container.append(
-            `<div class="flex flex-col mt-6 w-full f-shrink-0">
-                <div class="text-grey mb-4">TOTALS</div>
-                <div class="summary-totals-container border rounded flex flex-col w-full"></div>
-            </div>`
-        )
+	init_totals_summary() {
+		this.$summary_container.append(
+			`<div class="flex flex-col mt-6 w-full f-shrink-0">
+				<div class="text-grey mb-4">TOTALS</div>
+				<div class="summary-totals-container border rounded flex flex-col w-full"></div>
+			</div>`
+		);
 
-        this.$totals_summary_container = this.$summary_container.find('.summary-totals-container');
-    }
+		this.$totals_summary_container = this.$summary_container.find('.summary-totals-container');
+	}
 
-    init_payments_summary() {
-        this.$summary_container.append(
-            `<div class="flex flex-col mt-6 w-full f-shrink-0">
-                <div class="text-grey mb-4">PAYMENTS</div>
-                <div class="payments-summary-container border rounded flex flex-col w-full mb-4"></div>
-            </div>`
-        )
+	init_payments_summary() {
+		this.$summary_container.append(
+			`<div class="flex flex-col mt-6 w-full f-shrink-0">
+				<div class="text-grey mb-4">PAYMENTS</div>
+				<div class="payments-summary-container border rounded flex flex-col w-full mb-4"></div>
+			</div>`
+		);
 
-        this.$payment_summary_container = this.$summary_container.find('.payments-summary-container');
-    }
+		this.$payment_summary_container = this.$summary_container.find('.payments-summary-container');
+	}
 
-    init_summary_buttons() {
-        this.$summary_container.append(
-            `<div class="summary-btns flex summary-btns justify-between w-full f-shrink-0"></div>`
-        )
+	init_summary_buttons() {
+		this.$summary_container.append(
+			`<div class="summary-btns flex summary-btns justify-between w-full f-shrink-0"></div>`
+		);
 
-        this.$summary_btns = this.$summary_container.find('.summary-btns');
-    }
+		this.$summary_btns = this.$summary_container.find('.summary-btns');
+	}
 
-    init_email_print_dialog() {
-        const email_dialog = new frappe.ui.Dialog({
-            title: 'Email Receipt',
-            fields: [
-                {fieldname:'email_id', fieldtype:'Data', options: 'Email', label:'Email ID'},
-                // {fieldname:'remarks', fieldtype:'Text', label:'Remarks (if any)'}
-            ],
-            primary_action: () => {
-                this.send_email();
-            },
-            primary_action_label: __('Send'),
-        });
-        this.email_dialog = email_dialog;
+	init_email_print_dialog() {
+		const email_dialog = new frappe.ui.Dialog({
+			title: 'Email Receipt',
+			fields: [
+				{fieldname:'email_id', fieldtype:'Data', options: 'Email', label:'Email ID'},
+				// {fieldname:'remarks', fieldtype:'Text', label:'Remarks (if any)'}
+			],
+			primary_action: () => {
+				this.send_email();
+			},
+			primary_action_label: __('Send'),
+		});
+		this.email_dialog = email_dialog;
 
-        const print_dialog = new frappe.ui.Dialog({
-            title: 'Print Receipt',
-            fields: [
-                {fieldname:'print', fieldtype:'Data', label:'Print Preview'}
-            ],
-            primary_action: () => {
-                const frm = this.events.get_frm();
-                frm.doc = this.doc;
-                frm.print_preview.lang_code = frm.doc.language;
-                frm.print_preview.printit(true);
-            },
-            primary_action_label: __('Print'),
-        });
-        this.print_dialog = print_dialog;
-    }
+		const print_dialog = new frappe.ui.Dialog({
+			title: 'Print Receipt',
+			fields: [
+				{fieldname:'print', fieldtype:'Data', label:'Print Preview'}
+			],
+			primary_action: () => {
+				const frm = this.events.get_frm();
+				frm.doc = this.doc;
+				frm.print_preview.lang_code = frm.doc.language;
+				frm.print_preview.printit(true);
+			},
+			primary_action_label: __('Print'),
+		});
+		this.print_dialog = print_dialog;
+	}
 
-    get_upper_section_html(doc) {
-        const { status } = doc; let indicator_color = '';
+	get_upper_section_html(doc) {
+		const { status } = doc; let indicator_color = '';
 
-        in_list(['Paid', 'Consolidated'], status) && (indicator_color = 'green');
-        status === 'Draft' && (indicator_color = 'red');
-        status === 'Return' && (indicator_color = 'grey');
+		in_list(['Paid', 'Consolidated'], status) && (indicator_color = 'green');
+		status === 'Draft' && (indicator_color = 'red');
+		status === 'Return' && (indicator_color = 'grey');
 
-        return `<div class="flex flex-col items-start justify-end pr-4">
-                    <div class="text-lg text-bold pt-2">${doc.customer}</div>
-                    <div class="text-grey">${this.customer_email}</div>
-                    <div class="text-grey mt-auto">Sold by: ${doc.owner}</div>
-                </div>
-                <div class="flex flex-col flex-1 items-end justify-between">
-                    <div class="text-2-5xl text-bold">${format_currency(doc.paid_amount, doc.currency)}</div>
-                    <div class="flex justify-between">
-                        <div class="text-grey mr-4">${doc.name}</div>
-                        <div class="text-grey text-bold indicator ${indicator_color}">${doc.status}</div>
-                    </div>
-                </div>`
-    }
+		return `<div class="flex flex-col items-start justify-end pr-4">
+					<div class="text-lg text-bold pt-2">${doc.customer}</div>
+					<div class="text-grey">${this.customer_email}</div>
+					<div class="text-grey mt-auto">Sold by: ${doc.owner}</div>
+				</div>
+				<div class="flex flex-col flex-1 items-end justify-between">
+					<div class="text-2-5xl text-bold">${format_currency(doc.paid_amount, doc.currency)}</div>
+					<div class="flex justify-between">
+						<div class="text-grey mr-4">${doc.name}</div>
+						<div class="text-grey text-bold indicator ${indicator_color}">${doc.status}</div>
+					</div>
+				</div>`;
+	}
 
-    get_discount_html(doc) {
-        if (doc.discount_amount) {
-            return `<div class="total-summary-wrapper flex items-center h-12 pr-4 pl-4 pointer border-b-grey no-select">
-                    <div class="flex f-shrink-1 items-center">
-                        <div class="text-md-0 text-dark-grey text-bold overflow-hidden whitespace-nowrap  mr-2">
-                            Discount
-                        </div>
-                        <span class="text-grey">(${doc.additional_discount_percentage} %)</span>
-                    </div>
-                    <div class="flex flex-col f-shrink-0 ml-auto text-right">
-                        <div class="text-md-0 text-dark-grey text-bold">${format_currency(doc.discount_amount, doc.currency)}</div>
-                    </div>
-                </div>`;
-        } else {
-            return ``;
-        }
-    }
+	get_discount_html(doc) {
+		if (doc.discount_amount) {
+			return `<div class="total-summary-wrapper flex items-center h-12 pr-4 pl-4 pointer border-b-grey no-select">
+					<div class="flex f-shrink-1 items-center">
+						<div class="text-md-0 text-dark-grey text-bold overflow-hidden whitespace-nowrap  mr-2">
+							Discount
+						</div>
+						<span class="text-grey">(${doc.additional_discount_percentage} %)</span>
+					</div>
+					<div class="flex flex-col f-shrink-0 ml-auto text-right">
+						<div class="text-md-0 text-dark-grey text-bold">${format_currency(doc.discount_amount, doc.currency)}</div>
+					</div>
+				</div>`;
+		} else {
+			return ``;
+		}
+	}
 
-    get_net_total_html(doc) {
-        return `<div class="total-summary-wrapper flex items-center h-12 pr-4 pl-4 pointer border-b-grey no-select">
-                    <div class="flex f-shrink-1 items-center">
-                        <div class="text-md-0 text-dark-grey text-bold overflow-hidden whitespace-nowrap">
-                            Net Total
-                        </div>
-                    </div>
-                    <div class="flex flex-col f-shrink-0 ml-auto text-right">
-                        <div class="text-md-0 text-dark-grey text-bold">${format_currency(doc.net_total, doc.currency)}</div>
-                    </div>
-                </div>`
-    }
+	get_net_total_html(doc) {
+		return `<div class="total-summary-wrapper flex items-center h-12 pr-4 pl-4 pointer border-b-grey no-select">
+					<div class="flex f-shrink-1 items-center">
+						<div class="text-md-0 text-dark-grey text-bold overflow-hidden whitespace-nowrap">
+							Net Total
+						</div>
+					</div>
+					<div class="flex flex-col f-shrink-0 ml-auto text-right">
+						<div class="text-md-0 text-dark-grey text-bold">${format_currency(doc.net_total, doc.currency)}</div>
+					</div>
+				</div>`;
+	}
 
-    get_taxes_html(doc) {
-        return `<div class="total-summary-wrapper flex items-center justify-between h-12 pr-4 pl-4 border-b-grey">
-                    <div class="flex">
-                        <div class="text-md-0 text-dark-grey text-bold w-fit">Tax Charges</div>
-                        <div class="flex ml-6 text-dark-grey">
-                        ${
-                            doc.taxes.map((t, i) => {
-                                let margin_left = '';
-                                if (i !== 0) margin_left = 'ml-2';
-                                return `<span class="pl-2 pr-2 ${margin_left}">${t.description} @${t.rate}%</span>`
-                            }).join('')
-                        }
-                        </div>
-                    </div>
-                    <div class="flex flex-col text-right">
-                        <div class="text-md-0 text-dark-grey text-bold">${format_currency(doc.base_total_taxes_and_charges, doc.currency)}</div>
-                    </div>
-                </div>`
-    }
+	get_taxes_html(doc) {
+		const taxes = doc.taxes.map((t, i) => {
+			let margin_left = '';
+			if (i !== 0) margin_left = 'ml-2';
+			return `<span class="pl-2 pr-2 ${margin_left}">${t.description} @${t.rate}%</span>`;
+		}).join('');
 
-    get_grand_total_html(doc) {
-        return `<div class="total-summary-wrapper flex items-center h-12 pr-4 pl-4 pointer border-b-grey no-select">
-                    <div class="flex f-shrink-1 items-center">
-                        <div class="text-md-0 text-dark-grey text-bold overflow-hidden whitespace-nowrap">
-                            Grand Total
-                        </div>
-                    </div>
-                    <div class="flex flex-col f-shrink-0 ml-auto text-right">
-                        <div class="text-md-0 text-dark-grey text-bold">${format_currency(doc.grand_total, doc.currency)}</div>
-                    </div>
-                </div>`
-    }
+		return `
+			<div class="total-summary-wrapper flex items-center justify-between h-12 pr-4 pl-4 border-b-grey">
+				<div class="flex">
+					<div class="text-md-0 text-dark-grey text-bold w-fit">Tax Charges</div>
+					<div class="flex ml-6 text-dark-grey">${taxes}</div>
+				</div>
+				<div class="flex flex-col text-right">
+					<div class="text-md-0 text-dark-grey text-bold">
+						${format_currency(doc.base_total_taxes_and_charges, doc.currency)}
+					</div>
+				</div>
+			</div>`;
+	}
 
-    get_item_html(doc, item_data) {
-        return `<div class="item-summary-wrapper flex items-center h-12 pr-4 pl-4 border-b-grey pointer no-select">
-                    <div class="flex w-6 h-6 rounded bg-light-grey mr-4 items-center justify-center font-bold f-shrink-0">
-                        <span>${item_data.qty || 0}</span>
-                    </div>
-                    <div class="flex flex-col f-shrink-1">
-                        <div class="text-md text-dark-grey text-bold overflow-hidden whitespace-nowrap">
-                            ${item_data.item_name}
-                        </div>
-                    </div>
-                    <div class="flex f-shrink-0 ml-auto text-right">
-                        ${get_rate_discount_html()}
-                    </div>
-                </div>`
+	get_grand_total_html(doc) {
+		return `<div class="total-summary-wrapper flex items-center h-12 pr-4 pl-4 pointer border-b-grey no-select">
+					<div class="flex f-shrink-1 items-center">
+						<div class="text-md-0 text-dark-grey text-bold overflow-hidden whitespace-nowrap">
+							Grand Total
+						</div>
+					</div>
+					<div class="flex flex-col f-shrink-0 ml-auto text-right">
+						<div class="text-md-0 text-dark-grey text-bold">${format_currency(doc.grand_total, doc.currency)}</div>
+					</div>
+				</div>`;
+	}
 
-        function get_rate_discount_html() {
-            if (item_data.rate && item_data.price_list_rate && item_data.rate !== item_data.price_list_rate) {
-                return `<span class="text-grey mr-2">(${item_data.discount_percentage}% off)</span>
-                        <div class="text-md-0 text-dark-grey text-bold">${format_currency(item_data.rate, doc.currency)}</div>`
-            } else {
-                return `<div class="text-md-0 text-dark-grey text-bold">${format_currency(item_data.price_list_rate || item_data.rate, doc.currency)}</div>`
-            }
-        }
-    }
+	get_item_html(doc, item_data) {
+		return `<div class="item-summary-wrapper flex items-center h-12 pr-4 pl-4 border-b-grey pointer no-select">
+					<div class="flex w-6 h-6 rounded bg-light-grey mr-4 items-center justify-center font-bold f-shrink-0">
+						<span>${item_data.qty || 0}</span>
+					</div>
+					<div class="flex flex-col f-shrink-1">
+						<div class="text-md text-dark-grey text-bold overflow-hidden whitespace-nowrap">
+							${item_data.item_name}
+						</div>
+					</div>
+					<div class="flex f-shrink-0 ml-auto text-right">
+						${get_rate_discount_html()}
+					</div>
+				</div>`;
 
-    get_payment_html(doc, payment) {
-        return `<div class="payment-summary-wrapper flex items-center h-12 pr-4 pl-4 pointer border-b-grey no-select">
-                    <div class="flex f-shrink-1 items-center">
-                        <div class="text-md-0 text-dark-grey text-bold overflow-hidden whitespace-nowrap">
-                            ${payment.mode_of_payment}
-                        </div>
-                    </div>
-                    <div class="flex flex-col f-shrink-0 ml-auto text-right">
-                        <div class="text-md-0 text-dark-grey text-bold">${format_currency(payment.amount, doc.currency)}</div>
-                    </div>
-                </div>`
-    }
+		function get_rate_discount_html() {
+			if (item_data.rate && item_data.price_list_rate && item_data.rate !== item_data.price_list_rate) {
+				return `<span class="text-grey mr-2">
+							(${item_data.discount_percentage}% off)
+						</span>
+						<div class="text-md-0 text-dark-grey text-bold">
+							${format_currency(item_data.rate, doc.currency)}
+						</div>`;
+			} else {
+				return `<div class="text-md-0 text-dark-grey text-bold">
+							${format_currency(item_data.price_list_rate || item_data.rate, doc.currency)}
+						</div>`;
+			}
+		}
+	}
 
-    bind_events() {
-        this.$summary_container.on('click', '.return-btn', () => {
-            this.events.process_return(this.doc.name);
-            this.toggle_component(false);
-            this.$component.find('.no-summary-placeholder').removeClass('d-none');
-            this.$summary_wrapper.addClass('d-none');
-        });
+	get_payment_html(doc, payment) {
+		return `<div class="payment-summary-wrapper flex items-center h-12 pr-4 pl-4 pointer border-b-grey no-select">
+					<div class="flex f-shrink-1 items-center">
+						<div class="text-md-0 text-dark-grey text-bold overflow-hidden whitespace-nowrap">
+							${payment.mode_of_payment}
+						</div>
+					</div>
+					<div class="flex flex-col f-shrink-0 ml-auto text-right">
+						<div class="text-md-0 text-dark-grey text-bold">${format_currency(payment.amount, doc.currency)}</div>
+					</div>
+				</div>`;
+	}
 
-        this.$summary_container.on('click', '.edit-btn', () => {
-            this.events.edit_order(this.doc.name);
-            this.toggle_component(false);
-            this.$component.find('.no-summary-placeholder').removeClass('d-none');
-            this.$summary_wrapper.addClass('d-none');
-        });
+	bind_events() {
+		this.$summary_container.on('click', '.return-btn', () => {
+			this.events.process_return(this.doc.name);
+			this.toggle_component(false);
+			this.$component.find('.no-summary-placeholder').removeClass('d-none');
+			this.$summary_wrapper.addClass('d-none');
+		});
 
-        this.$summary_container.on('click', '.new-btn', () => {
-            this.events.new_order();
-            this.toggle_component(false);
-            this.$component.find('.no-summary-placeholder').removeClass('d-none');
-            this.$summary_wrapper.addClass('d-none');
-        });
+		this.$summary_container.on('click', '.edit-btn', () => {
+			this.events.edit_order(this.doc.name);
+			this.toggle_component(false);
+			this.$component.find('.no-summary-placeholder').removeClass('d-none');
+			this.$summary_wrapper.addClass('d-none');
+		});
 
-        this.$summary_container.on('click', '.email-btn', () => {
-            this.email_dialog.fields_dict.email_id.set_value(this.customer_email);
-            this.email_dialog.show();
-        });
+		this.$summary_container.on('click', '.new-btn', () => {
+			this.events.new_order();
+			this.toggle_component(false);
+			this.$component.find('.no-summary-placeholder').removeClass('d-none');
+			this.$summary_wrapper.addClass('d-none');
+		});
 
-        this.$summary_container.on('click', '.print-btn', () => {
-            // this.print_dialog.show();
-            const frm = this.events.get_frm();
-            frm.doc = this.doc;
-            frm.print_preview.lang_code = frm.doc.language;
-            frm.print_preview.printit(true);
-        });
-    }
+		this.$summary_container.on('click', '.email-btn', () => {
+			this.email_dialog.fields_dict.email_id.set_value(this.customer_email);
+			this.email_dialog.show();
+		});
 
-    attach_shortcuts() {
-        frappe.ui.keys.on("ctrl+p", () => {
-            const print_btn_visible = this.$summary_container.find('.print-btn').is(":visible");
-            const summary_visible = this.$component.is(":visible");
-            if (!summary_visible || !print_btn_visible) return;
+		this.$summary_container.on('click', '.print-btn', () => {
+			const frm = this.events.get_frm();
+			frm.doc = this.doc;
+			frm.print_preview.lang_code = frm.doc.language;
+			frm.print_preview.printit(true);
+		});
+	}
 
-            this.$summary_container.find('.print-btn').click();
-        });
-    }
+	attach_shortcuts() {
+		const ctrl_label = frappe.utils.is_mac() ? '⌘' : 'Ctrl';
+		this.$summary_container.find('.print-btn').attr("title", `${ctrl_label}+P`);
+		frappe.ui.keys.add_shortcut({
+			shortcut: "ctrl+p",
+			action: () => this.$summary_container.find('.print-btn').click(),
+			condition: () => this.$component.is(':visible') && this.$summary_container.find('.print-btn').is(":visible"),
+			description: __("Print Receipt"),
+			page: cur_page.page.page
+		});
+		this.$summary_container.find('.new-btn').attr("title", `${ctrl_label}+Enter`);
+		frappe.ui.keys.on("ctrl+enter", () => {
+			const summary_is_visible = this.$component.is(":visible");
+			if (summary_is_visible && this.$summary_container.find('.new-btn').is(":visible")) {
+				this.$summary_container.find('.new-btn').click();
+			}
+		});
+		this.$summary_container.find('.edit-btn').attr("title", `${ctrl_label}+E`);
+		frappe.ui.keys.add_shortcut({
+			shortcut: "ctrl+e",
+			action: () => this.$summary_container.find('.edit-btn').click(),
+			condition: () => this.$component.is(':visible') && this.$summary_container.find('.edit-btn').is(":visible"),
+			description: __("Edit Receipt"),
+			page: cur_page.page.page
+		});
+	}
 
-    toggle_component(show) {
-        show ?
-        this.$component.removeClass('d-none') :
-        this.$component.addClass('d-none');
-    }
+	toggle_component(show) {
+		show ? this.$component.removeClass('d-none') : this.$component.addClass('d-none');
+	}
 
-    send_email() {
-        const frm = this.events.get_frm();
-        const recipients = this.email_dialog.get_values().recipients;
-        const doc = this.doc || frm.doc;
-        const print_format = frm.pos_print_format;
+	send_email() {
+		const frm = this.events.get_frm();
+		const recipients = this.email_dialog.get_values().recipients;
+		const doc = this.doc || frm.doc;
+		const print_format = frm.pos_print_format;
 
-        frappe.call({
-            method:"frappe.core.doctype.communication.email.make",
-            args: {
-                recipients: recipients,
-                subject: __(frm.meta.name) + ': ' + doc.name,
-                doctype: doc.doctype,
-                name: doc.name,
-                send_email: 1,
-                print_format,
-                sender_full_name: frappe.user.full_name(),
-                _lang : doc.language
-            },
-            callback: r => {
-                if(!r.exc) {
-                    frappe.utils.play_sound("email");
-                    if(r.message["emails_not_sent_to"]) {
-                        frappe.msgprint(__("Email not sent to {0} (unsubscribed / disabled)",
-                            [ frappe.utils.escape_html(r.message["emails_not_sent_to"]) ]) );
-                    } else {
-                        frappe.show_alert({
-                            message: __('Email sent successfully.'),
-                            indicator: 'green'
-                        });
-                    }
-                    this.email_dialog.hide();
-                } else {
-                    frappe.msgprint(__("There were errors while sending email. Please try again."));
-                }
-            }
-        });
-    }
+		frappe.call({
+			method:"frappe.core.doctype.communication.email.make",
+			args: {
+				recipients: recipients,
+				subject: __(frm.meta.name) + ': ' + doc.name,
+				doctype: doc.doctype,
+				name: doc.name,
+				send_email: 1,
+				print_format,
+				sender_full_name: frappe.user.full_name(),
+				_lang : doc.language
+			},
+			callback: r => {
+				if(!r.exc) {
+					frappe.utils.play_sound("email");
+					if(r.message["emails_not_sent_to"]) {
+						frappe.msgprint(__("Email not sent to {0} (unsubscribed / disabled)",
+							[ frappe.utils.escape_html(r.message["emails_not_sent_to"]) ]) );
+					} else {
+						frappe.show_alert({
+							message: __('Email sent successfully.'),
+							indicator: 'green'
+						});
+					}
+					this.email_dialog.hide();
+				} else {
+					frappe.msgprint(__("There were errors while sending email. Please try again."));
+				}
+			}
+		});
+	}
 
-    add_summary_btns(map) {
-        this.$summary_btns.html('');
-        map.forEach(m => {
-            if (m.condition) {
-                m.visible_btns.forEach(b => {
-                    const class_name = b.split(' ')[0].toLowerCase();
-                    this.$summary_btns.append(
-                        `<div class="${class_name}-btn border rounded h-14 flex flex-1 items-center mr-4 justify-center text-md text-bold no-select pointer">
-                            ${b}
-                        </div>`
-                    )
-                });
-            }
-        });
-        this.$summary_btns.children().last().removeClass('mr-4');
-    }
+	add_summary_btns(map) {
+		this.$summary_btns.html('');
+		map.forEach(m => {
+			if (m.condition) {
+				m.visible_btns.forEach(b => {
+					const class_name = b.split(' ')[0].toLowerCase();
+					this.$summary_btns.append(
+						`<div class="${class_name}-btn border rounded h-14 flex flex-1 items-center mr-4 justify-center text-md text-bold no-select pointer">
+							${b}
+						</div>`
+					);
+				});
+			}
+		});
+		this.$summary_btns.children().last().removeClass('mr-4');
+	}
 
-    show_summary_placeholder() {
-        this.$summary_wrapper.addClass("d-none");
-        this.$component.find('.no-summary-placeholder').removeClass('d-none');
-    }
+	show_summary_placeholder() {
+		this.$summary_wrapper.addClass("d-none");
+		this.$component.find('.no-summary-placeholder').removeClass('d-none');
+	}
 
-    switch_to_post_submit_summary() {
-        // switch to full width view
-        this.$component.removeClass('col-span-6').addClass('col-span-10');
-        this.$summary_wrapper.removeClass('w-66').addClass('w-40');
+	switch_to_post_submit_summary() {
+		// switch to full width view
+		this.$component.removeClass('col-span-6').addClass('col-span-10');
+		this.$summary_wrapper.removeClass('w-66').addClass('w-40');
 
-        // switch place holder with summary container
-        this.$component.find('.no-summary-placeholder').addClass('d-none');
-        this.$summary_wrapper.removeClass('d-none');
-    }
+		// switch place holder with summary container
+		this.$component.find('.no-summary-placeholder').addClass('d-none');
+		this.$summary_wrapper.removeClass('d-none');
+	}
 
-    switch_to_recent_invoice_summary() {
-        // switch full width view with 60% view
-        this.$component.removeClass('col-span-10').addClass('col-span-6');
-        this.$summary_wrapper.removeClass('w-40').addClass('w-66');
+	switch_to_recent_invoice_summary() {
+		// switch full width view with 60% view
+		this.$component.removeClass('col-span-10').addClass('col-span-6');
+		this.$summary_wrapper.removeClass('w-40').addClass('w-66');
 
-        // switch place holder with summary container
-        this.$component.find('.no-summary-placeholder').addClass('d-none');
-        this.$summary_wrapper.removeClass('d-none');
-    }
+		// switch place holder with summary container
+		this.$component.find('.no-summary-placeholder').addClass('d-none');
+		this.$summary_wrapper.removeClass('d-none');
+	}
 
-    get_condition_btn_map(after_submission) {
-        if (after_submission)
-            return [{ condition: true, visible_btns: ['Print Receipt', 'Email Receipt', 'New Order'] }];
+	get_condition_btn_map(after_submission) {
+		if (after_submission)
+			return [{ condition: true, visible_btns: ['Print Receipt', 'Email Receipt', 'New Order'] }];
 
-        return [
-            { condition: this.doc.docstatus === 0, visible_btns: ['Edit Order'] },
-            { condition: !this.doc.is_return && this.doc.docstatus === 1, visible_btns: ['Print Receipt', 'Email Receipt', 'Return']},
-            { condition: this.doc.is_return && this.doc.docstatus === 1, visible_btns: ['Print Receipt', 'Email Receipt']}
-        ];
-    }
+		return [
+			{ condition: this.doc.docstatus === 0, visible_btns: ['Edit Order'] },
+			{ condition: !this.doc.is_return && this.doc.docstatus === 1, visible_btns: ['Print Receipt', 'Email Receipt', 'Return']},
+			{ condition: this.doc.is_return && this.doc.docstatus === 1, visible_btns: ['Print Receipt', 'Email Receipt']}
+		];
+	}
 
-    load_summary_of(doc, after_submission=false) {
-        this.$summary_wrapper.removeClass("d-none");
+	load_summary_of(doc, after_submission=false) {
+		this.$summary_wrapper.removeClass("d-none");
 
-        after_submission ?
-            this.switch_to_post_submit_summary() : this.switch_to_recent_invoice_summary();
+		after_submission ?
+			this.switch_to_post_submit_summary() : this.switch_to_recent_invoice_summary();
 
-        this.doc = doc;
+		this.doc = doc;
 
-        this.attach_basic_info(doc);
+		this.attach_basic_info(doc);
 
-        this.attach_items_info(doc);
+		this.attach_items_info(doc);
 
-        this.attach_totals_info(doc);
+		this.attach_totals_info(doc);
 
-        this.attach_payments_info(doc);
+		this.attach_payments_info(doc);
 
-        const condition_btns_map = this.get_condition_btn_map(after_submission);
+		const condition_btns_map = this.get_condition_btn_map(after_submission);
 
-        this.add_summary_btns(condition_btns_map);
-    }
+		this.add_summary_btns(condition_btns_map);
+	}
 
-    attach_basic_info(doc) {
-        frappe.db.get_value('Customer', this.doc.customer, 'email_id').then(({ message }) => {
-            this.customer_email = message.email_id || '';
-            const upper_section_dom = this.get_upper_section_html(doc);
-            this.$upper_section.html(upper_section_dom);
-        });
-    }
+	attach_basic_info(doc) {
+		frappe.db.get_value('Customer', this.doc.customer, 'email_id').then(({ message }) => {
+			this.customer_email = message.email_id || '';
+			const upper_section_dom = this.get_upper_section_html(doc);
+			this.$upper_section.html(upper_section_dom);
+		});
+	}
 
-    attach_items_info(doc) {
-        this.$items_summary_container.html('');
-        doc.items.forEach(item => {
-            const item_dom = this.get_item_html(doc, item);
-            this.$items_summary_container.append(item_dom);
-        });
-    }
+	attach_items_info(doc) {
+		this.$items_summary_container.html('');
+		doc.items.forEach(item => {
+			const item_dom = this.get_item_html(doc, item);
+			this.$items_summary_container.append(item_dom);
+		});
+	}
 
-    attach_payments_info(doc) {
-        this.$payment_summary_container.html('');
-        doc.payments.forEach(p => {
-            if (p.amount) {
-                const payment_dom = this.get_payment_html(doc, p);
-                this.$payment_summary_container.append(payment_dom);
-            }
-        });
-        if (doc.redeem_loyalty_points && doc.loyalty_amount) {
-            const payment_dom = this.get_payment_html(doc, {
-                mode_of_payment: 'Loyalty Points',
-                amount: doc.loyalty_amount,
-            });
-            this.$payment_summary_container.append(payment_dom);
-        }
-    }
+	attach_payments_info(doc) {
+		this.$payment_summary_container.html('');
+		doc.payments.forEach(p => {
+			if (p.amount) {
+				const payment_dom = this.get_payment_html(doc, p);
+				this.$payment_summary_container.append(payment_dom);
+			}
+		});
+		if (doc.redeem_loyalty_points && doc.loyalty_amount) {
+			const payment_dom = this.get_payment_html(doc, {
+				mode_of_payment: 'Loyalty Points',
+				amount: doc.loyalty_amount,
+			});
+			this.$payment_summary_container.append(payment_dom);
+		}
+	}
 
-    attach_totals_info(doc) {
-        this.$totals_summary_container.html('');
+	attach_totals_info(doc) {
+		this.$totals_summary_container.html('');
 
-        const discount_dom = this.get_discount_html(doc);
-        const net_total_dom = this.get_net_total_html(doc);
-        const taxes_dom = this.get_taxes_html(doc);
-        const grand_total_dom = this.get_grand_total_html(doc);
-        this.$totals_summary_container.append(discount_dom);
-        this.$totals_summary_container.append(net_total_dom);
-        this.$totals_summary_container.append(taxes_dom);
-        this.$totals_summary_container.append(grand_total_dom);
-    }
-
-}
\ No newline at end of file
+		const discount_dom = this.get_discount_html(doc);
+		const net_total_dom = this.get_net_total_html(doc);
+		const taxes_dom = this.get_taxes_html(doc);
+		const grand_total_dom = this.get_grand_total_html(doc);
+		this.$totals_summary_container.append(discount_dom);
+		this.$totals_summary_container.append(net_total_dom);
+		this.$totals_summary_container.append(taxes_dom);
+		this.$totals_summary_container.append(grand_total_dom);
+	}
+};
\ No newline at end of file
diff --git a/erpnext/selling/page/point_of_sale/pos_payment.js b/erpnext/selling/page/point_of_sale/pos_payment.js
index ec886d7..e89cf01 100644
--- a/erpnext/selling/page/point_of_sale/pos_payment.js
+++ b/erpnext/selling/page/point_of_sale/pos_payment.js
@@ -26,7 +26,7 @@
 					<div class="payment-modes flex flex-wrap"></div>
 					<div class="invoice-details-section"></div>
 					<div class="flex mt-auto justify-center w-full">
-							<div class="flex flex-col justify-center flex-1 ml-4">
+						<div class="flex flex-col justify-center flex-1 ml-4">
 							<div class="flex w-full">
 								<div class="totals-remarks items-end justify-end flex flex-1">
 									<div class="remarks text-md-0 text-grey mr-auto"></div>
@@ -170,7 +170,8 @@
 				me.selected_mode = me[`${mode}_control`];
 				const doc = me.events.get_frm().doc;
 				me.selected_mode?.$input?.get(0).focus();
-				!me.selected_mode?.get_value() ? me.selected_mode?.set_value(doc.grand_total - doc.paid_amount) : '';
+				const current_value = me.selected_mode?.get_value()
+				!current_value && doc.grand_total > doc.paid_amount ? me.selected_mode?.set_value(doc.grand_total - doc.paid_amount) : '';
 			}
 		})
 
@@ -197,10 +198,6 @@
 			me.selected_mode.set_value(value);
 		})
 
-		// this.$totals_remarks.on('click', '.remarks', () => {
-		// 	this.toggle_remarks_control();
-		// })
-
 		this.$component.on('click', '.submit-order', () => {
 			const doc = this.events.get_frm().doc;
 			const paid_amount = doc.paid_amount;
@@ -233,7 +230,7 @@
 		frappe.ui.form.on("Sales Invoice Payment", "amount", (frm, cdt, cdn) => {
 			// for setting correct amount after loyalty points are redeemed
 			const default_mop = locals[cdt][cdn];
-			const mode = default_mop.mode_of_payment.replace(' ', '_').toLowerCase();
+			const mode = default_mop.mode_of_payment.replace(/ +/g, "_").toLowerCase();
 			if (this[`${mode}_control`] && this[`${mode}_control`].get_value() != default_mop.amount) {
 				this[`${mode}_control`].set_value(default_mop.amount);
 			}
@@ -254,6 +251,8 @@
 	}
 
 	attach_shortcuts() {
+		const ctrl_label = frappe.utils.is_mac() ? '⌘' : 'Ctrl';
+		this.$component.find('.submit-order').attr("title", `${ctrl_label}+Enter`);
 		frappe.ui.keys.on("ctrl+enter", () => {
 			const payment_is_visible = this.$component.is(":visible");
 			const active_mode = this.$payment_modes.find(".border-primary");
@@ -262,21 +261,28 @@
 			}
 		});
 
-		frappe.ui.keys.on("tab", () => {
-			const payment_is_visible = this.$component.is(":visible");
-			const mode_of_payments = Array.from(this.$payment_modes.find(".mode-of-payment")).map(m => $(m).attr("data-mode"));
-			let active_mode = this.$payment_modes.find(".border-primary");
-			active_mode = active_mode.length ? active_mode.attr("data-mode") : undefined;
-
-			if (!active_mode) return;
-
-			const mode_index = mode_of_payments.indexOf(active_mode);
-			const next_mode_index = (mode_index + 1) % mode_of_payments.length;
-			const next_mode_to_be_clicked = this.$payment_modes.find(`.mode-of-payment[data-mode="${mode_of_payments[next_mode_index]}"]`);
-
-			if (payment_is_visible && mode_index != next_mode_index) {
-				next_mode_to_be_clicked.click();
-			}
+		frappe.ui.keys.add_shortcut({
+			shortcut: "tab",
+			action: () => {
+				const payment_is_visible = this.$component.is(":visible");
+				let active_mode = this.$payment_modes.find(".border-primary");
+				active_mode = active_mode.length ? active_mode.attr("data-mode") : undefined;
+	
+				if (!active_mode) return;
+	
+				const mode_of_payments = Array.from(this.$payment_modes.find(".mode-of-payment")).map(m => $(m).attr("data-mode"));
+				const mode_index = mode_of_payments.indexOf(active_mode);
+				const next_mode_index = (mode_index + 1) % mode_of_payments.length;
+				const next_mode_to_be_clicked = this.$payment_modes.find(`.mode-of-payment[data-mode="${mode_of_payments[next_mode_index]}"]`);
+	
+				if (payment_is_visible && mode_index != next_mode_index) {
+					next_mode_to_be_clicked.click();
+				}
+			},
+			condition: () => this.$component.is(':visible') && this.$payment_modes.find(".border-primary").length,
+			description: __("Switch Between Payment Modes"),
+			ignore_inputs: true,
+			page: cur_page.page.page
 		});
 	}
 
@@ -336,7 +342,7 @@
 		this.$payment_modes.html(
 		   `${
 			   payments.map((p, i) => {
-				const mode = p.mode_of_payment.replace(' ', '_').toLowerCase();
+				const mode = p.mode_of_payment.replace(/ +/g, "_").toLowerCase();
 				const payment_type = p.type;
 				const margin = i % 2 === 0 ? 'pr-2' : 'pl-2';
 				const amount = p.amount > 0 ? format_currency(p.amount, currency) : '';
@@ -356,13 +362,13 @@
 		)
 
 		payments.forEach(p => {
-			const mode = p.mode_of_payment.replace(' ', '_').toLowerCase();
+			const mode = p.mode_of_payment.replace(/ +/g, "_").toLowerCase();
 			const me = this;
 			this[`${mode}_control`] = frappe.ui.form.make_control({
 				df: {
-					label: __(`${p.mode_of_payment}`),
+					label: p.mode_of_payment,
 					fieldtype: 'Currency',
-					placeholder: __(`Enter ${p.mode_of_payment} amount.`),
+					placeholder: __('Enter {0} amount.', [p.mode_of_payment]),
 					onchange: function() {
 						if (this.value || this.value == 0) {
 							frappe.model.set_value(p.doctype, p.name, 'amount', flt(this.value))
@@ -440,11 +446,11 @@
 
 		let description, read_only, max_redeemable_amount;
 		if (!loyalty_points) {
-			description = __(`You don't have enough points to redeem.`);
+			description = __("You don't have enough points to redeem.");
 			read_only = true;
 		} else {
 			max_redeemable_amount = flt(flt(loyalty_points) * flt(conversion_factor), precision("loyalty_amount", doc))
-			description = __(`You can redeem upto ${format_currency(max_redeemable_amount)}.`);
+			description = __("You can redeem upto {0}.", [format_currency(max_redeemable_amount)]);
 			read_only = false;
 		}
 
@@ -464,9 +470,9 @@
 
 		this['loyalty-amount_control'] = frappe.ui.form.make_control({
 			df: {
-				label: __('Redeem Loyalty Points'),
+				label: __("Redeem Loyalty Points"),
 				fieldtype: 'Currency',
-				placeholder: __(`Enter amount to be redeemed.`),
+				placeholder: __("Enter amount to be redeemed."),
 				options: 'company:currency',
 				read_only,
 				onchange: async function() {
@@ -474,7 +480,7 @@
 
 					if (this.value > max_redeemable_amount) {
 						frappe.show_alert({
-							message: __(`You cannot redeem more than ${format_currency(max_redeemable_amount)}.`),
+							message: __("You cannot redeem more than {0}.", [format_currency(max_redeemable_amount)]),
 							indicator: "red"
 						});
 						frappe.utils.play_sound("submit");
diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py
index f7ff916..dec4fe2 100644
--- a/erpnext/stock/doctype/serial_no/serial_no.py
+++ b/erpnext/stock/doctype/serial_no/serial_no.py
@@ -12,6 +12,7 @@
 from frappe import _, ValidationError
 
 from erpnext.controllers.stock_controller import StockController
+from six import string_types
 from six.moves import map
 class SerialNoCannotCreateDirectError(ValidationError): pass
 class SerialNoCannotCannotChangeError(ValidationError): pass
@@ -285,8 +286,10 @@
 								if sle.voucher_type == "Sales Invoice":
 									if not frappe.db.exists("Sales Invoice Item", {"parent": sle.voucher_no,
 										"item_code": sle.item_code, "sales_order": sr.sales_order}):
-										frappe.throw(_("Cannot deliver Serial No {0} of item {1} as it is reserved \
-											to fullfill Sales Order {2}").format(sr.name, sle.item_code, sr.sales_order))
+										frappe.throw(
+											_("Cannot deliver Serial No {0} of item {1} as it is reserved to fullfill Sales Order {2}")
+											.format(sr.name, sle.item_code, sr.sales_order)
+										)
 								elif sle.voucher_type == "Delivery Note":
 									if not frappe.db.exists("Delivery Note Item", {"parent": sle.voucher_no,
 										"item_code": sle.item_code, "against_sales_order": sr.sales_order}):
@@ -295,8 +298,10 @@
 										if not invoice or frappe.db.exists("Sales Invoice Item",
 											{"parent": invoice, "item_code": sle.item_code,
 											"sales_order": sr.sales_order}):
-											frappe.throw(_("Cannot deliver Serial No {0} of item {1} as it is reserved to \
-												fullfill Sales Order {2}").format(sr.name, sle.item_code, sr.sales_order))
+											frappe.throw(
+												_("Cannot deliver Serial No {0} of item {1} as it is reserved to fullfill Sales Order {2}")
+												.format(sr.name, sle.item_code, sr.sales_order)
+											)
 							# if Sales Order reference in Delivery Note or Invoice validate SO reservations for item
 							if sle.voucher_type == "Sales Invoice":
 								sales_order = frappe.db.get_value("Sales Invoice Item", {"parent": sle.voucher_no,
@@ -336,11 +341,12 @@
 		else:
 			sle_doc.skip_serial_no_validaiton = True
 
-def validate_so_serial_no(sr, sales_order,):
+def validate_so_serial_no(sr, sales_order):
 	if not sr.sales_order or sr.sales_order!= sales_order:
-		frappe.throw(_("""Sales Order {0} has reservation for item {1}, you can
-		only deliver reserved {1} against {0}. Serial No {2} cannot
-		be delivered""").format(sales_order, sr.item_code, sr.name))
+		msg = _("Sales Order {0} has reservation for item {1}")
+		msg += _(", you can only deliver reserved {1} against {0}.")
+		msg += _(" Serial No {2} cannot be delivered")
+		frappe.throw(msg.format(sales_order, sr.item_code, sr.name))
 
 def has_duplicate_serial_no(sn, sle):
 	if (sn.warehouse and not sle.skip_serial_no_validaiton
@@ -538,54 +544,81 @@
 	return serial_nos
 
 @frappe.whitelist()
-def auto_fetch_serial_number(qty, item_code, warehouse, batch_nos=None, for_doctype=None):
-	filters = {
-		"item_code": item_code,
-		"warehouse": warehouse,
-		"delivery_document_no": "",
-		"sales_invoice": ""
-	}
+def auto_fetch_serial_number(qty, item_code, warehouse, posting_date=None, batch_nos=None, for_doctype=None):
+	filters = { "item_code": item_code, "warehouse": warehouse }
 
 	if batch_nos:
 		try:
-			filters["batch_no"] = ["in", json.loads(batch_nos)]
+			filters["batch_no"] = json.loads(batch_nos)
 		except:
-			filters["batch_no"] = ["in", [batch_nos]]
+			filters["batch_no"] = [batch_nos]
 
+	if posting_date:
+		filters["expiry_date"] = posting_date
+
+	serial_numbers = []
 	if for_doctype == 'POS Invoice':
-		reserved_serial_nos, unreserved_serial_nos = get_pos_reserved_serial_nos(filters, qty)
-		return unreserved_serial_nos
+		reserved_sr_nos = get_pos_reserved_serial_nos(filters)
+		serial_numbers = fetch_serial_numbers(filters, qty, do_not_include=reserved_sr_nos)
+	else:
+		serial_numbers = fetch_serial_numbers(filters, qty)
 
-	serial_numbers = frappe.get_list("Serial No", filters=filters, limit=qty, order_by="creation")
-	return [item['name'] for item in serial_numbers]
+	return [d.get('name') for d in serial_numbers]
 
 @frappe.whitelist()
-def get_pos_reserved_serial_nos(filters, qty=None):
-	batch_no_cond = ""
-	if filters.get("batch_no"):
-		batch_no_cond = "and item.batch_no = {}".format(frappe.db.escape(filters.get('batch_no')))
+def get_pos_reserved_serial_nos(filters):
+	if isinstance(filters, string_types):
+		filters = json.loads(filters)
 
-	reserved_serial_nos_str = [d.serial_no for d in frappe.db.sql("""select item.serial_no as serial_no
+	pos_transacted_sr_nos = frappe.db.sql("""select item.serial_no as serial_no
 		from `tabPOS Invoice` p, `tabPOS Invoice Item` item
 		where p.name = item.parent 
 		and p.consolidated_invoice is NULL 
 		and p.docstatus = 1
 		and item.docstatus = 1
-		and item.item_code = %s
-		and item.warehouse = %s
-		{}
-		""".format(batch_no_cond), [filters.get('item_code'), filters.get('warehouse')], as_dict=1)]
+		and item.item_code = %(item_code)s
+		and item.warehouse = %(warehouse)s
+		and item.serial_no is NOT NULL and item.serial_no != ''
+		""", filters, as_dict=1)
 
-	reserved_serial_nos = []
-	for s in reserved_serial_nos_str:
-		if not s: continue
+	reserved_sr_nos = []
+	for d in pos_transacted_sr_nos:
+		reserved_sr_nos += get_serial_nos(d.serial_no)
 
-		serial_nos = s.split("\n")
-		serial_nos = ' '.join(serial_nos).split() # remove whitespaces
-		if len(serial_nos): reserved_serial_nos += serial_nos
+	return reserved_sr_nos
 
-	filters["name"] = ["not in", reserved_serial_nos]
-	serial_numbers = frappe.get_list("Serial No", filters=filters, limit=qty, order_by="creation")
-	unreserved_serial_nos = [item['name'] for item in serial_numbers]
+def fetch_serial_numbers(filters, qty, do_not_include=[]):
+	batch_join_selection = ""
+	batch_no_condition = ""
+	batch_nos = filters.get("batch_no")
+	expiry_date = filters.get("expiry_date")
+	if batch_nos:
+		batch_no_condition = """and sr.batch_no in ({}) """.format(', '.join(["'%s'" % d for d in batch_nos]))
 
-	return reserved_serial_nos, unreserved_serial_nos
\ No newline at end of file
+	if expiry_date:
+		batch_join_selection = "LEFT JOIN `tabBatch` batch on sr.batch_no = batch.name "
+		expiry_date_cond = "AND ifnull(batch.expiry_date, '2500-12-31') >= %(expiry_date)s "
+		batch_no_condition += expiry_date_cond
+
+	excluded_sr_nos = ", ".join(["" + frappe.db.escape(sr) + "" for sr in do_not_include]) or "''"
+	serial_numbers = frappe.db.sql("""
+		SELECT sr.name FROM `tabSerial No` sr {batch_join_selection}
+		WHERE
+			sr.name not in ({excluded_sr_nos}) AND
+			sr.item_code = %(item_code)s AND 
+			sr.warehouse = %(warehouse)s AND
+			ifnull(sr.sales_invoice,'') = '' AND
+			ifnull(sr.delivery_document_no, '') = ''
+			{batch_no_condition}
+		ORDER BY
+			sr.creation
+		LIMIT
+			{qty}
+		""".format(
+				excluded_sr_nos=excluded_sr_nos,
+				qty=qty or 1,
+				batch_join_selection=batch_join_selection,
+				batch_no_condition=batch_no_condition
+			), filters, as_dict=1)
+		
+	return serial_numbers
\ No newline at end of file