feat: select child item when creating one document from another

This commit is contained in:
Mihir Kandoi
2025-08-12 20:27:41 +05:30
parent 81c8972a66
commit a9936ae133
12 changed files with 168 additions and 27 deletions

View File

@@ -2,6 +2,8 @@
# License: GNU General Public License v3. See license.txt
import json
import frappe
from frappe import _
from frappe.model.mapper import get_mapped_doc
@@ -348,7 +350,7 @@ def get_list_context(context=None):
@frappe.whitelist()
def make_sales_order(source_name: str, target_doc=None):
def make_sales_order(source_name: str, target_doc=None, args=None):
if not frappe.db.get_singles_value(
"Selling Settings", "allow_sales_order_creation_for_expired_quotation"
):
@@ -360,10 +362,15 @@ def make_sales_order(source_name: str, target_doc=None):
):
frappe.throw(_("Validity period of this quotation has ended."))
return _make_sales_order(source_name, target_doc)
return _make_sales_order(source_name, target_doc, args=args)
def _make_sales_order(source_name, target_doc=None, ignore_permissions=False):
def _make_sales_order(source_name, target_doc=None, ignore_permissions=False, args=None):
if args is None:
args = {}
if isinstance(args, str):
args = json.loads(args)
customer = _make_customer(source_name, ignore_permissions)
ordered_items = get_ordered_items(source_name)
@@ -431,6 +438,11 @@ def _make_sales_order(source_name, target_doc=None, ignore_permissions=False):
# Simple row
return True
def select_item(d):
filtered_items = args.get("filtered_children", [])
child_filter = d.name in filtered_items if filtered_items else True
return child_filter
doclist = get_mapped_doc(
"Quotation",
source_name,
@@ -440,7 +452,7 @@ def _make_sales_order(source_name, target_doc=None, ignore_permissions=False):
"doctype": "Sales Order Item",
"field_map": {"parent": "prevdoc_docname", "name": "quotation_item"},
"postprocess": update_item,
"condition": can_map_row,
"condition": lambda d: can_map_row(d) and select_item(d),
},
"Sales Taxes and Charges": {"doctype": "Sales Taxes and Charges", "reset_value": True},
"Sales Team": {"doctype": "Sales Team", "add_if_empty": True},
@@ -477,11 +489,16 @@ def set_expired_status():
@frappe.whitelist()
def make_sales_invoice(source_name, target_doc=None):
return _make_sales_invoice(source_name, target_doc)
def make_sales_invoice(source_name, target_doc=None, args=None):
return _make_sales_invoice(source_name, target_doc, args=args)
def _make_sales_invoice(source_name, target_doc=None, ignore_permissions=False):
def _make_sales_invoice(source_name, target_doc=None, ignore_permissions=False, args=None):
if args is None:
args = {}
if isinstance(args, str):
args = json.loads(args)
customer = _make_customer(source_name, ignore_permissions)
def set_missing_values(source, target):
@@ -497,6 +514,11 @@ def _make_sales_invoice(source_name, target_doc=None, ignore_permissions=False):
target.cost_center = None
target.stock_qty = flt(obj.qty) * flt(obj.conversion_factor)
def select_item(d):
filtered_items = args.get("filtered_children", [])
child_filter = d.name in filtered_items if filtered_items else True
return child_filter
doclist = get_mapped_doc(
"Quotation",
source_name,
@@ -505,7 +527,7 @@ def _make_sales_invoice(source_name, target_doc=None, ignore_permissions=False):
"Quotation Item": {
"doctype": "Sales Invoice Item",
"postprocess": update_item,
"condition": lambda row: not row.is_alternative,
"condition": lambda row: not row.is_alternative and select_item(row),
},
"Sales Taxes and Charges": {"doctype": "Sales Taxes and Charges", "reset_value": True},
"Sales Team": {"doctype": "Sales Team", "add_if_empty": True},

View File

@@ -793,6 +793,9 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex
docstatus: 1,
status: ["!=", "Lost"],
},
allow_child_item_selection: true,
child_fieldname: "items",
child_columns: ["item_code", "item_name", "qty", "rate", "amount"],
});
},
__("Get Items From")

View File

@@ -993,6 +993,11 @@ def make_delivery_note(source_name, target_doc=None, kwargs=None):
def is_unit_price_row(source):
return has_unit_price_items and source.qty == 0
def select_item(d):
filtered_items = kwargs.get("filtered_children", [])
child_filter = d.name in filtered_items if filtered_items else True
return child_filter
def set_missing_values(source, target):
if kwargs.get("ignore_pricing_rule"):
# Skip pricing rule when the dn is creating from the pick list
@@ -1061,7 +1066,7 @@ def make_delivery_note(source_name, target_doc=None, kwargs=None):
"name": "so_detail",
"parent": "against_sales_order",
},
"condition": condition,
"condition": lambda d: condition(d) and select_item(d),
"postprocess": update_item,
}
@@ -1129,7 +1134,12 @@ def make_delivery_note(source_name, target_doc=None, kwargs=None):
@frappe.whitelist()
def make_sales_invoice(source_name, target_doc=None, ignore_permissions=False):
def make_sales_invoice(source_name, target_doc=None, ignore_permissions=False, args=None):
if args is None:
args = {}
if isinstance(args, str):
args = json.loads(args)
# 0 qty is accepted, as the qty is uncertain for some items
has_unit_price_items = frappe.db.get_value("Sales Order", source_name, "has_unit_price_items")
@@ -1190,6 +1200,11 @@ def make_sales_invoice(source_name, target_doc=None, ignore_permissions=False):
if cost_center:
target.cost_center = cost_center
def select_item(d):
filtered_items = args.get("filtered_children", [])
child_filter = d.name in filtered_items if filtered_items else True
return child_filter
doclist = get_mapped_doc(
"Sales Order",
source_name,
@@ -1214,7 +1229,8 @@ def make_sales_invoice(source_name, target_doc=None, ignore_permissions=False):
True
if is_unit_price_row(doc)
else (doc.qty and (doc.base_amount == 0 or abs(doc.billed_amt) < abs(doc.amount)))
),
)
and select_item(doc),
},
"Sales Taxes and Charges": {
"doctype": "Sales Taxes and Charges",