diff --git a/erpnext/public/js/bulk_transaction_processing.js b/erpnext/public/js/bulk_transaction_processing.js index 0e42b477ea..3ae5a5d9c1 100644 --- a/erpnext/public/js/bulk_transaction_processing.js +++ b/erpnext/public/js/bulk_transaction_processing.js @@ -1,7 +1,7 @@ frappe.provide("erpnext.bulk_transaction_processing"); $.extend(erpnext.bulk_transaction_processing, { - create: function(listview, from_doctype, to_doctype) { + create: function(listview, from_doctype, to_doctype, args) { let checked_items = listview.get_checked_items(); const doc_name = []; checked_items.forEach((Item)=> { @@ -15,7 +15,7 @@ $.extend(erpnext.bulk_transaction_processing, { if (doc_name.length == 0) { frappe.call({ method: "erpnext.utilities.bulk_transaction.transaction_processing", - args: {data: checked_items, from_doctype: from_doctype, to_doctype: to_doctype} + args: {data: checked_items, from_doctype: from_doctype, to_doctype: to_doctype, args: args} }).then(()=> { }); diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js index 2bb093dbaf..1713a7b2c5 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.js +++ b/erpnext/selling/doctype/sales_order/sales_order.js @@ -855,6 +855,8 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex var delivery_dates = this.frm.doc.items.map(i => i.delivery_date); delivery_dates = [ ...new Set(delivery_dates) ]; + var today = new Date(); + var item_grid = this.frm.fields_dict["items"].grid; if(!item_grid.get_selected().length && delivery_dates.length > 1) { var dialog = new frappe.ui.Dialog({ @@ -873,7 +875,11 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex
diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index cad145324c..4956f29eff 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -937,6 +937,9 @@ def make_delivery_note(source_name, target_doc=None, kwargs=None): if frappe.flags.args and frappe.flags.args.delivery_dates: if cstr(doc.delivery_date) not in frappe.flags.args.delivery_dates: return False + if frappe.flags.args and frappe.flags.args.until_delivery_date: + if cstr(doc.delivery_date) > frappe.flags.args.until_delivery_date: + return False return abs(doc.delivered_qty) < abs(doc.qty) and doc.delivered_by_supplier != 1 @@ -1012,6 +1015,11 @@ def make_delivery_note(source_name, target_doc=None, kwargs=None): for idx, item in enumerate(target_doc.items): item.idx = idx + 1 + if not kwargs.skip_item_mapping and frappe.flags.bulk_transaction and not target_doc.items: + # the (date) condition filter resulted in an unintendedly created empty DN; remove it + del target_doc + return + # Should be called after mapping items. set_missing_values(so, target_doc) diff --git a/erpnext/selling/doctype/sales_order/sales_order_list.js b/erpnext/selling/doctype/sales_order/sales_order_list.js index 37686a85c3..53de329b07 100644 --- a/erpnext/selling/doctype/sales_order/sales_order_list.js +++ b/erpnext/selling/doctype/sales_order/sales_order_list.js @@ -55,7 +55,24 @@ frappe.listview_settings['Sales Order'] = { }); listview.page.add_action_item(__("Delivery Note"), ()=>{ - erpnext.bulk_transaction_processing.create(listview, "Sales Order", "Delivery Note"); + frappe.db.get_single_value("Selling Settings", "enable_cutoff_date_on_bulk_delivery_note_creation").then((value) => { + if (value) { + var dialog = new frappe.ui.Dialog({ + title: __("Select Items up to Delivery Date"), + fields: [{fieldtype: "Date", fieldname: "delivery_date", default: frappe.datetime.add_days(frappe.datetime.nowdate(), 1)}] + }); + dialog.set_primary_action(__("Select"), function(values) { + var until_delivery_date = values.delivery_date; + erpnext.bulk_transaction_processing.create(listview, "Sales Order", "Delivery Note", { + until_delivery_date + }); + dialog.hide(); + }); + dialog.show(); + } else { + erpnext.bulk_transaction_processing.create(listview, "Sales Order", "Delivery Note"); + } + }) }); listview.page.add_action_item(__("Advance Payment"), ()=>{ diff --git a/erpnext/selling/doctype/selling_settings/selling_settings.json b/erpnext/selling/doctype/selling_settings/selling_settings.json index d6829ce24b..ee3d2bf31f 100644 --- a/erpnext/selling/doctype/selling_settings/selling_settings.json +++ b/erpnext/selling/doctype/selling_settings/selling_settings.json @@ -32,7 +32,8 @@ "allow_sales_order_creation_for_expired_quotation", "dont_reserve_sales_order_qty_on_sales_return", "hide_tax_id", - "enable_discount_accounting" + "enable_discount_accounting", + "enable_cutoff_date_on_bulk_delivery_note_creation" ], "fields": [ { @@ -200,6 +201,12 @@ "fieldname": "blanket_order_allowance", "fieldtype": "Float", "label": "Blanket Order Allowance (%)" + }, + { + "default": "0", + "fieldname": "enable_cutoff_date_on_bulk_delivery_note_creation", + "fieldtype": "Check", + "label": "Enable Cut-Off Date on Bulk Delivery Note Creation" } ], "icon": "fa fa-cog", @@ -207,7 +214,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2023-10-25 14:03:03.966701", + "modified": "2024-03-01 12:07:39.994520", "modified_by": "Administrator", "module": "Selling", "name": "Selling Settings", diff --git a/erpnext/selling/doctype/selling_settings/selling_settings.py b/erpnext/selling/doctype/selling_settings/selling_settings.py index 24fe909a7a..a488177157 100644 --- a/erpnext/selling/doctype/selling_settings/selling_settings.py +++ b/erpnext/selling/doctype/selling_settings/selling_settings.py @@ -30,6 +30,7 @@ class SellingSettings(Document): dont_reserve_sales_order_qty_on_sales_return: DF.Check editable_bundle_item_rates: DF.Check editable_price_list_rate: DF.Check + enable_cutoff_date_on_bulk_delivery_note_creation: DF.Check enable_discount_accounting: DF.Check hide_tax_id: DF.Check maintain_same_rate_action: DF.Literal["Stop", "Warn"] diff --git a/erpnext/utilities/bulk_transaction.py b/erpnext/utilities/bulk_transaction.py index 9678488a26..3538c24aeb 100644 --- a/erpnext/utilities/bulk_transaction.py +++ b/erpnext/utilities/bulk_transaction.py @@ -7,12 +7,15 @@ from frappe.utils import get_link_to_form, today @frappe.whitelist() -def transaction_processing(data, from_doctype, to_doctype): +def transaction_processing(data, from_doctype, to_doctype, args=None): if isinstance(data, str): deserialized_data = json.loads(data) else: deserialized_data = data + if isinstance(args, str): + args = frappe._dict(json.loads(args)) + length_of_data = len(deserialized_data) frappe.msgprint( @@ -23,6 +26,7 @@ def transaction_processing(data, from_doctype, to_doctype): deserialized_data=deserialized_data, from_doctype=from_doctype, to_doctype=to_doctype, + args=args, ) @@ -71,8 +75,13 @@ def update_log(log_name, status, retried, err=None): frappe.db.set_value("Bulk Transaction Log Detail", log_name, "error_description", err) -def job(deserialized_data, from_doctype, to_doctype): +def job(deserialized_data, from_doctype, to_doctype, args): fail_count = 0 + + if args: + # currently: flag-based transport to `task` + frappe.flags.args = args + for d in deserialized_data: try: doc_name = d.get("name") @@ -147,9 +156,12 @@ def task(doc_name, from_doctype, to_doctype): else: obj = mapper[from_doctype][to_doctype](doc_name) - obj.flags.ignore_validate = True - obj.set_title_field() - obj.insert(ignore_mandatory=True) + if obj: + obj.flags.ignore_validate = True + obj.set_title_field() + obj.insert(ignore_mandatory=True) + + del obj del frappe.flags.bulk_transaction