feat: Filter rows to be mapped on server side mapping function
- Pass dialog selections to `make_sales_order`
- Map either original item or its alternative depending on mapping
- Only qty check for simple rows (without alternatives and not an alternative itself)
diff --git a/erpnext/selling/doctype/quotation/quotation.js b/erpnext/selling/doctype/quotation/quotation.js
index 6f75673..0ea424f 100644
--- a/erpnext/selling/doctype/quotation/quotation.js
+++ b/erpnext/selling/doctype/quotation/quotation.js
@@ -233,7 +233,9 @@
show_alternative_item_dialog() {
// Create a `{original item: [alternate items]}` map
- const item_alt_map = {};
+ var me = this;
+ let item_alt_map = {};
+
this.frm.doc.items.filter(
(item) => item.is_alternative
).forEach((item) =>
@@ -286,7 +288,14 @@
},
],
primary_action: function() {
- this.hide();
+ frappe.model.open_mapped_doc({
+ method: "erpnext.selling.doctype.quotation.quotation.make_sales_order",
+ frm: me.frm,
+ args: {
+ mapping: dialog.get_value("alternative_items")
+ }
+ });
+ dialog.hide();
},
primary_action_label: __('Continue')
});
@@ -297,13 +306,6 @@
cur_frm.script_manager.make(erpnext.selling.QuotationController);
-cur_frm.cscript['Make Sales Order'] = function() {
- frappe.model.open_mapped_doc({
- method: "erpnext.selling.doctype.quotation.quotation.make_sales_order",
- frm: cur_frm
- })
-}
-
frappe.ui.form.on("Quotation Item", "items_on_form_rendered", "packed_items_on_form_rendered", function(frm, cdt, cdn) {
// enable tax_amount field if Actual
})
diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py
index 6836d56..d4ae66e 100644
--- a/erpnext/selling/doctype/quotation/quotation.py
+++ b/erpnext/selling/doctype/quotation/quotation.py
@@ -210,6 +210,10 @@
)
)
+ alternative_map = {
+ x.get("original_item") : x.get("alternative_item") for x in frappe.flags.get("args", {}).get("mapping", [])
+ }
+
def set_missing_values(source, target):
if customer:
target.customer = customer.name
@@ -233,6 +237,29 @@
target.blanket_order = obj.blanket_order
target.blanket_order_rate = obj.blanket_order_rate
+ def can_map_row(item) -> bool:
+ """
+ Row mapping from Quotation to Sales order:
+ 1. Simple row: Map if adequate qty
+ 2. Has Alternative Item: Map if no alternative was selected against original item and #1
+ 3. Is Alternative Item: Map if alternative was selected against original item and #1
+ """
+ has_qty = item.qty > 0
+
+ has_alternative = item.item_code in alternative_map
+ is_alternative = item.is_alternative
+
+ if not alternative_map or not (is_alternative or has_alternative):
+ # No alternative items in doc or current row is a simple item (without alternatives)
+ return has_qty
+
+ if is_alternative:
+ is_selected = alternative_map.get(item.alternative_to) == item.item_code
+ else:
+ is_selected = alternative_map.get(item.item_code) is None
+ return is_selected and has_qty
+
+
doclist = get_mapped_doc(
"Quotation",
source_name,
@@ -242,7 +269,7 @@
"doctype": "Sales Order Item",
"field_map": {"parent": "prevdoc_docname", "name": "quotation_item"},
"postprocess": update_item,
- "condition": lambda doc: doc.qty > 0,
+ "condition": can_map_row,
},
"Sales Taxes and Charges": {"doctype": "Sales Taxes and Charges", "add_if_empty": True},
"Sales Team": {"doctype": "Sales Team", "add_if_empty": True},