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",