feat: Dialog to select alternative item before creating Sales order

- Users can leave the row blank in the dialog if original item is to be used
- Else users can select an alternative item against an original item
- In the document, users must check `Is Alternative Item` if needed and also specify which item it is an altenrative to since there are no documented mappings
diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py
index 5815cfa..1edd7bf 100644
--- a/erpnext/controllers/taxes_and_totals.py
+++ b/erpnext/controllers/taxes_and_totals.py
@@ -32,7 +32,7 @@
 
 	def filter_rows(self):
 		"""Exclude rows, that do not fulfill the filter criteria, from totals computation."""
-		items = list(filter(lambda item: not item.get("is_alternative_item"), self.doc.get("items")))
+		items = list(filter(lambda item: not item.get("is_alternative"), self.doc.get("items")))
 		return items
 
 	def calculate(self):
diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js
index 607b928..029d6c0 100644
--- a/erpnext/public/js/controllers/taxes_and_totals.js
+++ b/erpnext/public/js/controllers/taxes_and_totals.js
@@ -889,6 +889,6 @@
 	}
 
 	filtered_items() {
-		return this.frm.doc.items.filter(item => !item["is_alternative_item"]);
+		return this.frm.doc.items.filter(item => !item["is_alternative"]);
 	}
 };
diff --git a/erpnext/selling/doctype/quotation/quotation.js b/erpnext/selling/doctype/quotation/quotation.js
index 6b42e4d..6f75673 100644
--- a/erpnext/selling/doctype/quotation/quotation.js
+++ b/erpnext/selling/doctype/quotation/quotation.js
@@ -87,7 +87,7 @@
 		if (doc.docstatus == 1 && !["Lost", "Ordered"].includes(doc.status)) {
 			this.frm.add_custom_button(
 				__("Sales Order"),
-				this.frm.cscript["Make Sales Order"],
+				() => this.make_sales_order(),
 				__("Create")
 			);
 
@@ -141,6 +141,20 @@
 
 	}
 
+	make_sales_order() {
+		var me = this;
+
+		let has_alternative_item = this.frm.doc.items.some((item) => item.is_alternative);
+		if (has_alternative_item) {
+			this.show_alternative_item_dialog();
+		} else {
+			frappe.model.open_mapped_doc({
+				method: "erpnext.selling.doctype.quotation.quotation.make_sales_order",
+				frm: me.frm
+			});
+		}
+	}
+
 	set_dynamic_field_label(){
 		if (this.frm.doc.quotation_to == "Customer")
 		{
@@ -216,6 +230,69 @@
 			}
 		})
 	}
+
+	show_alternative_item_dialog() {
+		// Create a `{original item: [alternate items]}` map
+		const item_alt_map = {};
+		this.frm.doc.items.filter(
+			(item) => item.is_alternative
+		).forEach((item) =>
+			(item_alt_map[item.alternative_to] ??= []).push(item.item_code)
+		)
+
+		const fields = [{
+			fieldtype:"Link",
+			fieldname:"original_item",
+			options: "Item",
+			label: __("Original Item"),
+			read_only: 1,
+			in_list_view: 1,
+		},
+		{
+			fieldtype:"Link",
+			fieldname:"alternative_item",
+			options: "Item",
+			label: __("Alternative Item"),
+			in_list_view: 1,
+			get_query: (row, cdt, cdn) => {
+				return {
+					filters: {
+						"item_code": ["in", item_alt_map[row.original_item]]
+					}
+				}
+			},
+		}];
+
+		this.data = Object.keys(item_alt_map).map((item) => {
+			return {"original_item": item}
+		});
+
+		const dialog = new frappe.ui.Dialog({
+			title: __("Select Alternatives for Sales Order"),
+			fields: [
+				{
+					fieldname: "alternative_items",
+					fieldtype: "Table",
+					label: "Items with Alternatives",
+					cannot_add_rows: true,
+					in_place_edit: true,
+					reqd: 1,
+					data: this.data,
+					description: __("Select an alternative to be used in the Sales Order or leave it blank to use the original item."),
+					get_data: () => {
+						return this.data;
+					},
+					fields: fields
+				},
+			],
+			primary_action: function() {
+				this.hide();
+			},
+			primary_action_label: __('Continue')
+		});
+
+		dialog.show();
+	}
 };
 
 cur_frm.script_manager.make(erpnext.selling.QuotationController);
diff --git a/erpnext/selling/doctype/quotation_item/quotation_item.json b/erpnext/selling/doctype/quotation_item/quotation_item.json
index eaa4d1d..f62a099 100644
--- a/erpnext/selling/doctype/quotation_item/quotation_item.json
+++ b/erpnext/selling/doctype/quotation_item/quotation_item.json
@@ -49,7 +49,8 @@
   "pricing_rules",
   "stock_uom_rate",
   "is_free_item",
-  "is_alternative_item",
+  "is_alternative",
+  "alternative_to",
   "section_break_43",
   "valuation_rate",
   "column_break_45",
@@ -647,16 +648,25 @@
   },
   {
    "default": "0",
-   "fieldname": "is_alternative_item",
+   "fieldname": "is_alternative",
    "fieldtype": "Check",
-   "label": "Is Alternative Item",
+   "label": "Is Alternative",
+   "print_hide": 1
+  },
+  {
+   "depends_on": "is_alternative",
+   "fieldname": "alternative_to",
+   "fieldtype": "Link",
+   "label": "Alternative To",
+   "mandatory_depends_on": "is_alternative",
+   "options": "Item",
    "print_hide": 1
   }
  ],
  "idx": 1,
  "istable": 1,
  "links": [],
- "modified": "2023-01-24 08:48:06.290335",
+ "modified": "2023-01-26 07:32:02.768197",
  "modified_by": "Administrator",
  "module": "Selling",
  "name": "Quotation Item",