fix(pos): allow validating stock on save
diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
index 91c07ad..9e6b7ce 100644
--- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
+++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
@@ -172,10 +172,14 @@
 			frappe.throw(error_msg, title=_("Invalid Item"), as_list=True)
 
 	def validate_stock_availablility(self):
+		if self.is_return:
+			return
+
+		if self.docstatus.is_draft() and not frappe.db.get_value('POS Profile', self.pos_profile, 'validate_stock_on_save'):
+			return
+
 		from erpnext.stock.stock_ledger import is_negative_stock_allowed
 
-		if self.is_return or self.docstatus != 1:
-			return
 		for d in self.get('items'):
 			is_service_item = not (frappe.db.get_value('Item', d.get('item_code'), 'is_stock_item'))
 			if is_service_item:
diff --git a/erpnext/accounts/doctype/pos_profile/pos_profile.json b/erpnext/accounts/doctype/pos_profile/pos_profile.json
index 9c9f37b..11646a6 100644
--- a/erpnext/accounts/doctype/pos_profile/pos_profile.json
+++ b/erpnext/accounts/doctype/pos_profile/pos_profile.json
@@ -22,6 +22,7 @@
   "hide_images",
   "hide_unavailable_items",
   "auto_add_item_to_cart",
+  "validate_stock_on_save",
   "column_break_16",
   "update_stock",
   "ignore_pricing_rule",
@@ -351,6 +352,12 @@
   {
    "fieldname": "column_break_25",
    "fieldtype": "Column Break"
+  },
+  {
+   "default": "0",
+   "fieldname": "validate_stock_on_save",
+   "fieldtype": "Check",
+   "label": "Validate Stock on Save"
   }
  ],
  "icon": "icon-cog",
@@ -378,10 +385,11 @@
    "link_fieldname": "pos_profile"
   }
  ],
- "modified": "2021-10-14 14:17:00.469298",
+ "modified": "2022-03-21 13:29:28.480533",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "POS Profile",
+ "naming_rule": "Set by user",
  "owner": "Administrator",
  "permissions": [
   {
@@ -404,5 +412,6 @@
   }
  ],
  "sort_field": "modified",
- "sort_order": "DESC"
+ "sort_order": "DESC",
+ "states": []
 }
\ No newline at end of file
diff --git a/erpnext/selling/page/point_of_sale/pos_controller.js b/erpnext/selling/page/point_of_sale/pos_controller.js
index 49e85ec..6974bed 100644
--- a/erpnext/selling/page/point_of_sale/pos_controller.js
+++ b/erpnext/selling/page/point_of_sale/pos_controller.js
@@ -720,7 +720,14 @@
 	}
 
 	async save_and_checkout() {
-		this.frm.is_dirty() && await this.frm.save();
-		this.payment.checkout();
+		if (this.frm.is_dirty()) {
+			// only move to payment section if save is successful
+			frappe.route_hooks.after_save = () => this.payment.checkout();
+			return this.frm.save(
+				null, null, null, () => this.cart.toggle_checkout_btn(true) // show checkout button on error
+			);
+		} else {
+			this.payment.checkout();
+		}
 	}
 };
diff --git a/erpnext/selling/page/point_of_sale/pos_payment.js b/erpnext/selling/page/point_of_sale/pos_payment.js
index 326ee59..b4ece46 100644
--- a/erpnext/selling/page/point_of_sale/pos_payment.js
+++ b/erpnext/selling/page/point_of_sale/pos_payment.js
@@ -172,7 +172,7 @@
 		frappe.ui.form.on('POS Invoice', 'coupon_code', (frm) => {
 			if (frm.doc.coupon_code && !frm.applying_pos_coupon_code) {
 				if (!frm.doc.ignore_pricing_rule) {
-					frm.applying_pos_coupon_code = true
+					frm.applying_pos_coupon_code = true;
 					frappe.run_serially([
 						() => frm.doc.ignore_pricing_rule=1,
 						() => frm.trigger('ignore_pricing_rule'),