Merge branch 'develop' into bom-update-log-cleanup-perf
diff --git a/erpnext/manufacturing/doctype/bom_update_log/bom_update_log.json b/erpnext/manufacturing/doctype/bom_update_log/bom_update_log.json
index c32e383..a926e69 100644
--- a/erpnext/manufacturing/doctype/bom_update_log/bom_update_log.json
+++ b/erpnext/manufacturing/doctype/bom_update_log/bom_update_log.json
@@ -7,11 +7,11 @@
  "editable_grid": 1,
  "engine": "InnoDB",
  "field_order": [
-  "current_bom",
-  "new_bom",
-  "column_break_3",
   "update_type",
   "status",
+  "column_break_3",
+  "current_bom",
+  "new_bom",
   "error_log",
   "progress_section",
   "current_level",
@@ -37,6 +37,7 @@
    "options": "BOM"
   },
   {
+   "depends_on": "eval:doc.update_type === \"Replace BOM\"",
    "fieldname": "column_break_3",
    "fieldtype": "Column Break"
   },
@@ -87,6 +88,7 @@
    "options": "BOM Update Batch"
   },
   {
+   "depends_on": "eval:doc.status !== \"Completed\"",
    "fieldname": "current_level",
    "fieldtype": "Int",
    "label": "Current Level"
@@ -96,7 +98,7 @@
  "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2022-06-06 15:15:23.883251",
+ "modified": "2022-06-20 15:43:55.696388",
  "modified_by": "Administrator",
  "module": "Manufacturing",
  "name": "BOM Update Log",
diff --git a/erpnext/manufacturing/doctype/bom_update_log/bom_update_log.py b/erpnext/manufacturing/doctype/bom_update_log/bom_update_log.py
index 9c9c240..c3f52d4 100644
--- a/erpnext/manufacturing/doctype/bom_update_log/bom_update_log.py
+++ b/erpnext/manufacturing/doctype/bom_update_log/bom_update_log.py
@@ -6,6 +6,8 @@
 import frappe
 from frappe import _
 from frappe.model.document import Document
+from frappe.query_builder import DocType, Interval
+from frappe.query_builder.functions import Now
 from frappe.utils import cint, cstr
 
 from erpnext.manufacturing.doctype.bom_update_log.bom_updation_utils import (
@@ -22,6 +24,17 @@
 
 
 class BOMUpdateLog(Document):
+	@staticmethod
+	def clear_old_logs(days=None):
+		days = days or 90
+		table = DocType("BOM Update Log")
+		frappe.db.delete(
+			table,
+			filters=(
+				(table.modified < (Now() - Interval(days=days))) & (table.update_type == "Update Cost")
+			),
+		)
+
 	def validate(self):
 		if self.update_type == "Replace BOM":
 			self.validate_boms_are_specified()
@@ -77,7 +90,11 @@
 				now=frappe.flags.in_test,
 			)
 		else:
-			process_boms_cost_level_wise(self)
+			frappe.enqueue(
+				method="erpnext.manufacturing.doctype.bom_update_log.bom_update_log.process_boms_cost_level_wise",
+				update_doc=self,
+				now=frappe.flags.in_test,
+			)
 
 
 def run_replace_bom_job(
@@ -112,28 +129,31 @@
 	current_boms = {}
 	values = {}
 
-	if update_doc.status == "Queued":
-		# First level yet to process. On Submit.
-		current_level = 0
-		current_boms = get_leaf_boms()
-		values = {
-			"processed_boms": json.dumps({}),
-			"status": "In Progress",
-			"current_level": current_level,
-		}
-	else:
-		# Resume next level. via Cron Job.
-		if not parent_boms:
-			return
+	try:
+		if update_doc.status == "Queued":
+			# First level yet to process. On Submit.
+			current_level = 0
+			current_boms = get_leaf_boms()
+			values = {
+				"processed_boms": json.dumps({}),
+				"status": "In Progress",
+				"current_level": current_level,
+			}
+		else:
+			# Resume next level. via Cron Job.
+			if not parent_boms:
+				return
 
-		current_level = cint(update_doc.current_level) + 1
+			current_level = cint(update_doc.current_level) + 1
 
-		# Process the next level BOMs. Stage parents as current BOMs.
-		current_boms = parent_boms.copy()
-		values = {"current_level": current_level}
+			# Process the next level BOMs. Stage parents as current BOMs.
+			current_boms = parent_boms.copy()
+			values = {"current_level": current_level}
 
-	set_values_in_log(update_doc.name, values, commit=True)
-	queue_bom_cost_jobs(current_boms, update_doc, current_level)
+		set_values_in_log(update_doc.name, values, commit=True)
+		queue_bom_cost_jobs(current_boms, update_doc, current_level)
+	except Exception:
+		handle_exception(update_doc)
 
 
 def queue_bom_cost_jobs(
@@ -199,16 +219,22 @@
 		current_boms, processed_boms = get_processed_current_boms(log, bom_batches)
 		parent_boms = get_next_higher_level_boms(child_boms=current_boms, processed_boms=processed_boms)
 
-		# Unset processed BOMs if log is complete, it is used for next level BOMs
+		# Unset processed BOMs (it is used for next level BOMs) & change status if log is complete
+		status = "Completed" if not parent_boms else "In Progress"
+		processed_boms = json.dumps([] if not parent_boms else processed_boms)
 		set_values_in_log(
 			log.name,
 			values={
-				"processed_boms": json.dumps([] if not parent_boms else processed_boms),
-				"status": "Completed" if not parent_boms else "In Progress",
+				"processed_boms": processed_boms,
+				"status": status,
 			},
 			commit=True,
 		)
 
+		# clear progress section
+		if status == "Completed":
+			frappe.db.delete("BOM Update Batch", {"parent": log.name})
+
 		if parent_boms:  # there is a next level to process
 			process_boms_cost_level_wise(
 				update_doc=frappe.get_doc("BOM Update Log", log.name), parent_boms=parent_boms
diff --git a/erpnext/manufacturing/doctype/bom_update_log/bom_update_log_list.js b/erpnext/manufacturing/doctype/bom_update_log/bom_update_log_list.js
index e39b563..bc709d8 100644
--- a/erpnext/manufacturing/doctype/bom_update_log/bom_update_log_list.js
+++ b/erpnext/manufacturing/doctype/bom_update_log/bom_update_log_list.js
@@ -1,6 +1,6 @@
 frappe.listview_settings['BOM Update Log'] = {
 	add_fields: ["status"],
-	get_indicator: function(doc) {
+	get_indicator: (doc) => {
 		let status_map = {
 			"Queued": "orange",
 			"In Progress": "blue",
@@ -9,5 +9,22 @@
 		};
 
 		return [__(doc.status), status_map[doc.status], "status,=," + doc.status];
-	}
+	},
+	onload: () => {
+		if (!frappe.model.can_write("Log Settings")) {
+			return;
+		}
+
+		let sidebar_entry = $(
+			'<ul class="list-unstyled sidebar-menu log-retention-note"></ul>'
+		).appendTo(cur_list.page.sidebar);
+		let message = __("Note: Automatic log deletion only applies to logs of type <i>Update Cost</i>");
+		$(`<hr><div class='text-muted'>${message}</div>`).appendTo(sidebar_entry);
+
+		frappe.require("logtypes.bundle.js", () => {
+			frappe.utils.logtypes.show_log_retention_message(cur_list.doctype);
+		});
+
+
+	},
 };
\ No newline at end of file