Merge pull request #38090 from blaggacao/fix/dn-mapper

fix(dn): regression from bulk transaction fix
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py
index fc22f53..448224b 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py
@@ -1054,9 +1054,9 @@
 				item=self,
 			)
 
-			dr_or_cr = "credit" if self.payment_type == "Receive" else "debit"
-
 			for d in self.get("references"):
+				# re-defining dr_or_cr for every reference in order to avoid the last value affecting calculation of reverse
+				dr_or_cr = "credit" if self.payment_type == "Receive" else "debit"
 				cost_center = self.cost_center
 				if d.reference_doctype == "Sales Invoice" and not cost_center:
 					cost_center = frappe.db.get_value(d.reference_doctype, d.reference_name, "cost_center")
@@ -1072,11 +1072,9 @@
 					against_voucher_type = d.reference_doctype
 					against_voucher = d.reference_name
 
-				reverse_dr_or_cr, standalone_note = 0, 0
+				reverse_dr_or_cr = 0
 				if d.reference_doctype in ["Sales Invoice", "Purchase Invoice"]:
-					is_return, return_against = frappe.db.get_value(
-						d.reference_doctype, d.reference_name, ["is_return", "return_against"]
-					)
+					is_return = frappe.db.get_value(d.reference_doctype, d.reference_name, "is_return")
 					payable_party_types = get_party_types_from_account_type("Payable")
 					receivable_party_types = get_party_types_from_account_type("Receivable")
 					if is_return and self.party_type in receivable_party_types and (self.payment_type == "Pay"):
@@ -1086,7 +1084,7 @@
 					):
 						reverse_dr_or_cr = 1
 
-					if is_return and not return_against and not reverse_dr_or_cr:
+					if is_return and not reverse_dr_or_cr:
 						dr_or_cr = "debit" if dr_or_cr == "credit" else "credit"
 
 				gle.update(
@@ -1100,6 +1098,7 @@
 				)
 				gl_entries.append(gle)
 
+			dr_or_cr = "credit" if self.payment_type == "Receive" else "debit"
 			if self.unallocated_amount:
 				exchange_rate = self.get_exchange_rate()
 				base_unallocated_amount = self.unallocated_amount * exchange_rate
diff --git a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py
index 603f24a..f4b0c55 100644
--- a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py
@@ -1290,6 +1290,9 @@
 		self.assertEqual(references[2].payment_term, "Tax Receivable")
 
 	def test_receive_payment_from_payable_party_type(self):
+		"""
+		Checks GL entries generated while receiving payments from a Payable Party Type.
+		"""
 		pe = create_payment_entry(
 			party_type="Supplier",
 			party="_Test Supplier",
@@ -1301,8 +1304,57 @@
 		)
 		self.voucher_no = pe.name
 		self.expected_gle = [
-			{"account": "_Test Cash - _TC", "debit": 1000.0, "credit": 0.0},
 			{"account": "Creditors - _TC", "debit": 0.0, "credit": 1000.0},
+			{"account": "_Test Cash - _TC", "debit": 1000.0, "credit": 0.0},
+		]
+		self.check_gl_entries()
+
+	def test_payment_against_partial_return_invoice(self):
+		"""
+		Checks GL entries generated for partial return invoice payments.
+		"""
+		si = create_sales_invoice(qty=10, rate=10, customer="_Test Customer")
+		credit_note = create_sales_invoice(
+			qty=-4, rate=10, customer="_Test Customer", is_return=1, return_against=si.name
+		)
+		pe = create_payment_entry(
+			party_type="Customer",
+			party="_Test Customer",
+			payment_type="Receive",
+			paid_from="Debtors - _TC",
+			paid_to="_Test Cash - _TC",
+		)
+		pe.set(
+			"references",
+			[
+				{
+					"reference_doctype": "Sales Invoice",
+					"reference_name": si.name,
+					"due_date": si.get("due_date"),
+					"total_amount": si.grand_total,
+					"outstanding_amount": si.outstanding_amount,
+					"allocated_amount": si.outstanding_amount,
+				},
+				{
+					"reference_doctype": "Sales Invoice",
+					"reference_name": credit_note.name,
+					"due_date": credit_note.get("due_date"),
+					"total_amount": credit_note.grand_total,
+					"outstanding_amount": credit_note.outstanding_amount,
+					"allocated_amount": credit_note.outstanding_amount,
+				},
+			],
+		)
+		pe.save()
+		pe.submit()
+		self.assertEqual(pe.total_allocated_amount, 60)
+		self.assertEqual(pe.unallocated_amount, 940)
+		self.voucher_no = pe.name
+		self.expected_gle = [
+			{"account": "Debtors - _TC", "debit": 40.0, "credit": 0.0},
+			{"account": "Debtors - _TC", "debit": 0.0, "credit": 940.0},
+			{"account": "Debtors - _TC", "debit": 0.0, "credit": 100.0},
+			{"account": "_Test Cash - _TC", "debit": 1000.0, "credit": 0.0},
 		]
 		self.check_gl_entries()
 
@@ -1316,7 +1368,7 @@
 				gle.credit,
 			)
 			.where((gle.voucher_no == self.voucher_no) & (gle.is_cancelled == 0))
-			.orderby(gle.account)
+			.orderby(gle.account, gle.debit, gle.credit, order=frappe.qb.desc)
 		).run(as_dict=True)
 		for row in range(len(self.expected_gle)):
 			for field in ["account", "debit", "credit"]:
diff --git a/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.json b/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.json
index cb0ed3d..5a281aa 100644
--- a/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.json
+++ b/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.json
@@ -186,6 +186,7 @@
    "label": "Image"
   },
   {
+   "fetch_from": "item_code.image",
    "fieldname": "image",
    "fieldtype": "Attach",
    "hidden": 1,
@@ -833,7 +834,7 @@
  ],
  "istable": 1,
  "links": [],
- "modified": "2023-03-12 13:36:40.160468",
+ "modified": "2023-11-14 18:33:22.585715",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "POS Invoice Item",
diff --git a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
index 424e942..bcedb7c 100644
--- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
+++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
@@ -158,6 +158,7 @@
    "width": "300px"
   },
   {
+   "fetch_from": "item_code.image",
    "fieldname": "image",
    "fieldtype": "Attach",
    "hidden": 1,
@@ -915,7 +916,7 @@
  "idx": 1,
  "istable": 1,
  "links": [],
- "modified": "2023-10-03 21:01:01.824892",
+ "modified": "2023-11-14 18:33:48.547297",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Purchase Invoice Item",
diff --git a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
index 5d2764b..a403b14 100644
--- a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
+++ b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
@@ -167,6 +167,7 @@
    "print_hide": 1
   },
   {
+   "fetch_from": "item_code.image",
    "fieldname": "image",
    "fieldtype": "Attach",
    "hidden": 1,
@@ -901,7 +902,7 @@
  "idx": 1,
  "istable": 1,
  "links": [],
- "modified": "2023-07-26 12:53:22.404057",
+ "modified": "2023-11-14 18:34:10.479329",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Sales Invoice Item",
@@ -911,4 +912,4 @@
  "sort_field": "modified",
  "sort_order": "DESC",
  "states": []
-}
+}
\ No newline at end of file
diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py
index 31bc6fd..7d91309 100644
--- a/erpnext/accounts/utils.py
+++ b/erpnext/accounts/utils.py
@@ -53,6 +53,9 @@
 def get_fiscal_year(
 	date=None, fiscal_year=None, label="Date", verbose=1, company=None, as_dict=False, boolean=False
 ):
+	if isinstance(boolean, str):
+		boolean = frappe.json.loads(boolean)
+
 	fiscal_years = get_fiscal_years(
 		date, fiscal_year, label, verbose, company, as_dict=as_dict, boolean=boolean
 	)
diff --git a/erpnext/assets/doctype/asset/asset.json b/erpnext/assets/doctype/asset/asset.json
index 40f51ab..d6b9c46 100644
--- a/erpnext/assets/doctype/asset/asset.json
+++ b/erpnext/assets/doctype/asset/asset.json
@@ -481,11 +481,10 @@
    "read_only": 1
   },
   {
-   "depends_on": "eval.doc.asset_quantity",
    "fieldname": "asset_quantity",
    "fieldtype": "Int",
    "label": "Asset Quantity",
-   "read_only": 1
+   "read_only_depends_on": "eval:!doc.is_existing_asset && !doc.is_composite_asset"
   },
   {
    "fieldname": "depr_entry_posting_status",
@@ -572,7 +571,7 @@
    "link_fieldname": "target_asset"
   }
  ],
- "modified": "2023-10-27 17:03:46.629617",
+ "modified": "2023-11-15 17:40:17.315203",
  "modified_by": "Administrator",
  "module": "Assets",
  "name": "Asset",
diff --git a/erpnext/bulk_transaction/doctype/bulk_transaction_log/bulk_transaction_log.js b/erpnext/bulk_transaction/doctype/bulk_transaction_log/bulk_transaction_log.js
index 0073170..dc54d60 100644
--- a/erpnext/bulk_transaction/doctype/bulk_transaction_log/bulk_transaction_log.js
+++ b/erpnext/bulk_transaction/doctype/bulk_transaction_log/bulk_transaction_log.js
@@ -1,30 +1,21 @@
-// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+// Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
 
-frappe.ui.form.on('Bulk Transaction Log', {
-
-	refresh: function(frm) {
-		frm.disable_save();
-		frm.add_custom_button(__('Retry Failed Transactions'), ()=>{
-			frappe.confirm(__("Retry Failing Transactions ?"), ()=>{
-				query(frm, 1);
-			}
-			);
-		});
-	}
+frappe.ui.form.on("Bulk Transaction Log", {
+	refresh(frm) {
+		frm.add_custom_button(__('Succeeded Entries'), function() {
+			frappe.set_route('List', 'Bulk Transaction Log Detail', {'date': frm.doc.date, 'transaction_status': "Success"});
+		}, __("View"));
+		frm.add_custom_button(__('Failed Entries'), function() {
+			frappe.set_route('List', 'Bulk Transaction Log Detail', {'date': frm.doc.date, 'transaction_status': "Failed"});
+		}, __("View"));
+		if (frm.doc.failed) {
+			frm.add_custom_button(__('Retry Failed Transactions'), function() {
+				frappe.call({
+					method: "erpnext.utilities.bulk_transaction.retry",
+					args: {date: frm.doc.date}
+				});
+			});
+		}
+	},
 });
-
-function query(frm) {
-	frappe.call({
-		method: "erpnext.bulk_transaction.doctype.bulk_transaction_log.bulk_transaction_log.retry_failing_transaction",
-		args: {
-			log_date: frm.doc.log_date
-		}
-	}).then((r) => {
-		if (r.message === "No Failed Records") {
-			frappe.show_alert(__(r.message), 5);
-		} else {
-			frappe.show_alert(__("Retrying Failed Transactions"), 5);
-		}
-	});
-}
\ No newline at end of file
diff --git a/erpnext/bulk_transaction/doctype/bulk_transaction_log/bulk_transaction_log.json b/erpnext/bulk_transaction/doctype/bulk_transaction_log/bulk_transaction_log.json
index da42cf1..75cb358 100644
--- a/erpnext/bulk_transaction/doctype/bulk_transaction_log/bulk_transaction_log.json
+++ b/erpnext/bulk_transaction/doctype/bulk_transaction_log/bulk_transaction_log.json
@@ -1,31 +1,64 @@
 {
  "actions": [],
- "allow_rename": 1,
- "creation": "2021-11-30 13:41:16.343827",
+ "allow_copy": 1,
+ "creation": "2023-11-09 20:14:45.139593",
+ "default_view": "List",
  "doctype": "DocType",
- "editable_grid": 1,
  "engine": "InnoDB",
  "field_order": [
-  "log_date",
-  "logger_data"
+  "date",
+  "column_break_bsan",
+  "log_entries",
+  "section_break_mdmv",
+  "succeeded",
+  "column_break_qryp",
+  "failed"
  ],
  "fields": [
   {
-   "fieldname": "log_date",
+   "fieldname": "date",
    "fieldtype": "Date",
-   "label": "Log Date",
+   "in_list_view": 1,
+   "in_standard_filter": 1,
+   "label": "Date",
    "read_only": 1
   },
   {
-   "fieldname": "logger_data",
-   "fieldtype": "Table",
-   "label": "Logger Data",
-   "options": "Bulk Transaction Log Detail"
+   "fieldname": "log_entries",
+   "fieldtype": "Int",
+   "in_list_view": 1,
+   "label": "Log Entries",
+   "read_only": 1
+  },
+  {
+   "fieldname": "column_break_bsan",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "section_break_mdmv",
+   "fieldtype": "Section Break"
+  },
+  {
+   "fieldname": "succeeded",
+   "fieldtype": "Int",
+   "label": "Succeeded",
+   "read_only": 1
+  },
+  {
+   "fieldname": "column_break_qryp",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "failed",
+   "fieldtype": "Int",
+   "label": "Failed",
+   "read_only": 1
   }
  ],
- "index_web_pages_for_search": 1,
+ "in_create": 1,
+ "is_virtual": 1,
  "links": [],
- "modified": "2022-02-03 17:23:02.935325",
+ "modified": "2023-11-11 04:52:49.347376",
  "modified_by": "Administrator",
  "module": "Bulk Transaction",
  "name": "Bulk Transaction Log",
@@ -47,5 +80,5 @@
  "sort_field": "modified",
  "sort_order": "DESC",
  "states": [],
- "track_changes": 1
+ "title_field": "date"
 }
\ No newline at end of file
diff --git a/erpnext/bulk_transaction/doctype/bulk_transaction_log/bulk_transaction_log.py b/erpnext/bulk_transaction/doctype/bulk_transaction_log/bulk_transaction_log.py
index 0596be4..712caf1 100644
--- a/erpnext/bulk_transaction/doctype/bulk_transaction_log/bulk_transaction_log.py
+++ b/erpnext/bulk_transaction/doctype/bulk_transaction_log/bulk_transaction_log.py
@@ -1,67 +1,112 @@
-# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
 # For license information, please see license.txt
 
-from datetime import date
-
 import frappe
+from frappe import qb
 from frappe.model.document import Document
-
-from erpnext.utilities.bulk_transaction import task, update_logger
+from frappe.query_builder.functions import Count
+from frappe.utils import cint
+from pypika import Order
 
 
 class BulkTransactionLog(Document):
-	pass
+	def db_insert(self, *args, **kwargs):
+		pass
 
+	def load_from_db(self):
+		log_detail = qb.DocType("Bulk Transaction Log Detail")
 
-@frappe.whitelist()
-def retry_failing_transaction(log_date=None):
-	if not log_date:
-		log_date = str(date.today())
-	btp = frappe.qb.DocType("Bulk Transaction Log Detail")
-	data = (
-		frappe.qb.from_(btp)
-		.select(btp.transaction_name, btp.from_doctype, btp.to_doctype)
-		.distinct()
-		.where(btp.retried != 1)
-		.where(btp.transaction_status == "Failed")
-		.where(btp.date == log_date)
-	).run(as_dict=True)
+		has_records = frappe.db.sql(
+			f"select exists (select * from `tabBulk Transaction Log Detail` where date = '{self.name}');"
+		)[0][0]
+		if not has_records:
+			raise frappe.DoesNotExistError
 
-	if data:
-		if len(data) > 10:
-			frappe.enqueue(job, queue="long", job_name="bulk_retry", data=data, log_date=log_date)
-		else:
-			job(data, log_date)
-	else:
-		return "No Failed Records"
+		succeeded_logs = (
+			qb.from_(log_detail)
+			.select(Count(log_detail.date).as_("count"))
+			.where((log_detail.date == self.name) & (log_detail.transaction_status == "Success"))
+			.run()
+		)[0][0] or 0
+		failed_logs = (
+			qb.from_(log_detail)
+			.select(Count(log_detail.date).as_("count"))
+			.where((log_detail.date == self.name) & (log_detail.transaction_status == "Failed"))
+			.run()
+		)[0][0] or 0
+		total_logs = succeeded_logs + failed_logs
+		transaction_log = frappe._dict(
+			{
+				"date": self.name,
+				"count": total_logs,
+				"succeeded": succeeded_logs,
+				"failed": failed_logs,
+			}
+		)
+		super(Document, self).__init__(serialize_transaction_log(transaction_log))
 
+	@staticmethod
+	def get_list(args):
+		filter_date = parse_list_filters(args)
+		limit = cint(args.get("page_length")) or 20
+		log_detail = qb.DocType("Bulk Transaction Log Detail")
 
-def job(data, log_date):
-	for d in data:
-		failed = []
-		try:
-			frappe.db.savepoint("before_creation_of_record")
-			task(d.transaction_name, d.from_doctype, d.to_doctype)
-		except Exception as e:
-			frappe.db.rollback(save_point="before_creation_of_record")
-			failed.append(e)
-			update_logger(
-				d.transaction_name,
-				e,
-				d.from_doctype,
-				d.to_doctype,
-				status="Failed",
-				log_date=log_date,
-				restarted=1,
+		dates_query = (
+			qb.from_(log_detail)
+			.select(log_detail.date)
+			.distinct()
+			.orderby(log_detail.date, order=Order.desc)
+			.limit(limit)
+		)
+		if filter_date:
+			dates_query = dates_query.where(log_detail.date == filter_date)
+		dates = dates_query.run()
+
+		transaction_logs = []
+		if dates:
+			transaction_logs_query = (
+				qb.from_(log_detail)
+				.select(log_detail.date.as_("date"), Count(log_detail.date).as_("count"))
+				.where(log_detail.date.isin(dates))
+				.orderby(log_detail.date, order=Order.desc)
+				.groupby(log_detail.date)
+				.limit(limit)
 			)
+			transaction_logs = transaction_logs_query.run(as_dict=True)
 
-		if not failed:
-			update_logger(
-				d.transaction_name,
-				None,
-				d.from_doctype,
-				d.to_doctype,
-				status="Success",
-				log_date=log_date,
-				restarted=1,
-			)
+		return [serialize_transaction_log(x) for x in transaction_logs]
+
+	@staticmethod
+	def get_count(args):
+		pass
+
+	@staticmethod
+	def get_stats(args):
+		pass
+
+	def db_update(self, *args, **kwargs):
+		pass
+
+	def delete(self):
+		pass
+
+
+def serialize_transaction_log(data):
+	return frappe._dict(
+		name=data.date,
+		date=data.date,
+		log_entries=data.count,
+		succeeded=data.succeeded,
+		failed=data.failed,
+	)
+
+
+def parse_list_filters(args):
+	# parse date filter
+	filter_date = None
+	for fil in args.get("filters"):
+		if isinstance(fil, list):
+			for elem in fil:
+				if elem == "date":
+					filter_date = fil[3]
+	return filter_date
diff --git a/erpnext/bulk_transaction/doctype/bulk_transaction_log/test_bulk_transaction_log.py b/erpnext/bulk_transaction/doctype/bulk_transaction_log/test_bulk_transaction_log.py
index c673be8..01bb615 100644
--- a/erpnext/bulk_transaction/doctype/bulk_transaction_log/test_bulk_transaction_log.py
+++ b/erpnext/bulk_transaction/doctype/bulk_transaction_log/test_bulk_transaction_log.py
@@ -1,79 +1,9 @@
-# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
+# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors
 # See license.txt
 
-import unittest
-from datetime import date
-
-import frappe
-
-from erpnext.utilities.bulk_transaction import transaction_processing
+# import frappe
+from frappe.tests.utils import FrappeTestCase
 
 
-class TestBulkTransactionLog(unittest.TestCase):
-	def setUp(self):
-		create_company()
-		create_customer()
-		create_item()
-
-	def test_entry_in_log(self):
-		so_name = create_so()
-		transaction_processing([{"name": so_name}], "Sales Order", "Sales Invoice")
-		doc = frappe.get_doc("Bulk Transaction Log", str(date.today()))
-		for d in doc.get("logger_data"):
-			if d.transaction_name == so_name:
-				self.assertEqual(d.transaction_name, so_name)
-				self.assertEqual(d.transaction_status, "Success")
-				self.assertEqual(d.from_doctype, "Sales Order")
-				self.assertEqual(d.to_doctype, "Sales Invoice")
-				self.assertEqual(d.retried, 0)
-
-
-def create_company():
-	if not frappe.db.exists("Company", "_Test Company"):
-		frappe.get_doc(
-			{
-				"doctype": "Company",
-				"company_name": "_Test Company",
-				"country": "India",
-				"default_currency": "INR",
-			}
-		).insert()
-
-
-def create_customer():
-	if not frappe.db.exists("Customer", "Bulk Customer"):
-		frappe.get_doc({"doctype": "Customer", "customer_name": "Bulk Customer"}).insert()
-
-
-def create_item():
-	if not frappe.db.exists("Item", "MK"):
-		frappe.get_doc(
-			{
-				"doctype": "Item",
-				"item_code": "MK",
-				"item_name": "Milk",
-				"description": "Milk",
-				"item_group": "Products",
-			}
-		).insert()
-
-
-def create_so(intent=None):
-	so = frappe.new_doc("Sales Order")
-	so.customer = "Bulk Customer"
-	so.company = "_Test Company"
-	so.transaction_date = date.today()
-
-	so.set_warehouse = "Finished Goods - _TC"
-	so.append(
-		"items",
-		{
-			"item_code": "MK",
-			"delivery_date": date.today(),
-			"qty": 10,
-			"rate": 80,
-		},
-	)
-	so.insert()
-	so.submit()
-	return so.name
+class TestBulkTransactionLog(FrappeTestCase):
+	pass
diff --git a/erpnext/bulk_transaction/doctype/bulk_transaction_log_detail/bulk_transaction_log_detail.js b/erpnext/bulk_transaction/doctype/bulk_transaction_log_detail/bulk_transaction_log_detail.js
new file mode 100644
index 0000000..5669601
--- /dev/null
+++ b/erpnext/bulk_transaction/doctype/bulk_transaction_log_detail/bulk_transaction_log_detail.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+// frappe.ui.form.on("Bulk Transaction Log Detail", {
+// 	refresh(frm) {
+
+// 	},
+// });
diff --git a/erpnext/bulk_transaction/doctype/bulk_transaction_log_detail/bulk_transaction_log_detail.json b/erpnext/bulk_transaction/doctype/bulk_transaction_log_detail/bulk_transaction_log_detail.json
index 8262caa..9590325 100644
--- a/erpnext/bulk_transaction/doctype/bulk_transaction_log_detail/bulk_transaction_log_detail.json
+++ b/erpnext/bulk_transaction/doctype/bulk_transaction_log_detail/bulk_transaction_log_detail.json
@@ -6,12 +6,12 @@
  "editable_grid": 1,
  "engine": "InnoDB",
  "field_order": [
+  "from_doctype",
   "transaction_name",
   "date",
   "time",
   "transaction_status",
   "error_description",
-  "from_doctype",
   "to_doctype",
   "retried"
  ],
@@ -20,8 +20,11 @@
    "fieldname": "transaction_name",
    "fieldtype": "Dynamic Link",
    "in_list_view": 1,
+   "in_standard_filter": 1,
    "label": "Name",
-   "options": "from_doctype"
+   "options": "from_doctype",
+   "read_only": 1,
+   "search_index": 1
   },
   {
    "fieldname": "transaction_status",
@@ -39,9 +42,11 @@
   {
    "fieldname": "from_doctype",
    "fieldtype": "Link",
+   "in_standard_filter": 1,
    "label": "From Doctype",
    "options": "DocType",
-   "read_only": 1
+   "read_only": 1,
+   "search_index": 1
   },
   {
    "fieldname": "to_doctype",
@@ -54,8 +59,10 @@
    "fieldname": "date",
    "fieldtype": "Date",
    "in_list_view": 1,
+   "in_standard_filter": 1,
    "label": "Date ",
-   "read_only": 1
+   "read_only": 1,
+   "search_index": 1
   },
   {
    "fieldname": "time",
@@ -66,19 +73,33 @@
   {
    "fieldname": "retried",
    "fieldtype": "Int",
+   "in_list_view": 1,
    "label": "Retried",
    "read_only": 1
   }
  ],
+ "in_create": 1,
  "index_web_pages_for_search": 1,
- "istable": 1,
  "links": [],
- "modified": "2022-02-03 19:57:31.650359",
+ "modified": "2023-11-10 11:44:10.758342",
  "modified_by": "Administrator",
  "module": "Bulk Transaction",
  "name": "Bulk Transaction Log Detail",
  "owner": "Administrator",
- "permissions": [],
+ "permissions": [
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "System Manager",
+   "share": 1,
+   "write": 1
+  }
+ ],
  "sort_field": "modified",
  "sort_order": "DESC",
  "states": [],
diff --git a/erpnext/bulk_transaction/doctype/bulk_transaction_log_detail/test_bulk_transaction_log_detail.py b/erpnext/bulk_transaction/doctype/bulk_transaction_log_detail/test_bulk_transaction_log_detail.py
new file mode 100644
index 0000000..5217b60
--- /dev/null
+++ b/erpnext/bulk_transaction/doctype/bulk_transaction_log_detail/test_bulk_transaction_log_detail.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+
+# import frappe
+from frappe.tests.utils import FrappeTestCase
+
+
+class TestBulkTransactionLogDetail(FrappeTestCase):
+	pass
diff --git a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json
index 2b6ffb7..2d706f4 100644
--- a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json
+++ b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json
@@ -189,6 +189,7 @@
    "fieldtype": "Column Break"
   },
   {
+   "fetch_from": "item_code.image",
    "fieldname": "image",
    "fieldtype": "Attach",
    "hidden": 1,
@@ -916,7 +917,7 @@
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2023-11-06 11:00:53.596417",
+ "modified": "2023-11-14 18:34:27.267382",
  "modified_by": "Administrator",
  "module": "Buying",
  "name": "Purchase Order Item",
diff --git a/erpnext/buying/doctype/request_for_quotation_item/request_for_quotation_item.json b/erpnext/buying/doctype/request_for_quotation_item/request_for_quotation_item.json
index 82fcfa2..6cdd2ba 100644
--- a/erpnext/buying/doctype/request_for_quotation_item/request_for_quotation_item.json
+++ b/erpnext/buying/doctype/request_for_quotation_item/request_for_quotation_item.json
@@ -87,6 +87,7 @@
    "width": "300px"
   },
   {
+   "fetch_from": "item_code.image",
    "fieldname": "image",
    "fieldtype": "Attach",
    "hidden": 1,
@@ -260,13 +261,15 @@
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2020-09-24 17:26:46.276934",
+ "modified": "2023-11-14 18:34:48.327224",
  "modified_by": "Administrator",
  "module": "Buying",
  "name": "Request for Quotation Item",
+ "naming_rule": "Random",
  "owner": "Administrator",
  "permissions": [],
  "sort_field": "modified",
  "sort_order": "DESC",
+ "states": [],
  "track_changes": 1
-}
+}
\ No newline at end of file
diff --git a/erpnext/buying/doctype/supplier_quotation_item/supplier_quotation_item.json b/erpnext/buying/doctype/supplier_quotation_item/supplier_quotation_item.json
index 8d491fb..4bbcacf 100644
--- a/erpnext/buying/doctype/supplier_quotation_item/supplier_quotation_item.json
+++ b/erpnext/buying/doctype/supplier_quotation_item/supplier_quotation_item.json
@@ -133,6 +133,7 @@
    "fieldtype": "Column Break"
   },
   {
+   "fetch_from": "item_code.image",
    "fieldname": "image",
    "fieldtype": "Attach",
    "hidden": 1,
@@ -559,13 +560,15 @@
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2020-10-19 12:36:26.913211",
+ "modified": "2023-11-14 18:35:03.435817",
  "modified_by": "Administrator",
  "module": "Buying",
  "name": "Supplier Quotation Item",
+ "naming_rule": "Random",
  "owner": "Administrator",
  "permissions": [],
  "sort_field": "modified",
  "sort_order": "DESC",
+ "states": [],
  "track_changes": 1
-}
+}
\ No newline at end of file
diff --git a/erpnext/controllers/subcontracting_controller.py b/erpnext/controllers/subcontracting_controller.py
index 5fa66b1..3d55a08 100644
--- a/erpnext/controllers/subcontracting_controller.py
+++ b/erpnext/controllers/subcontracting_controller.py
@@ -626,6 +626,18 @@
 						(row.item_code, row.get(self.subcontract_data.order_field))
 					] -= row.qty
 
+	def __set_rate_for_serial_and_batch_bundle(self):
+		if self.doctype != "Subcontracting Receipt":
+			return
+
+		for row in self.get(self.raw_material_table):
+			if not row.get("serial_and_batch_bundle"):
+				continue
+
+			row.rate = frappe.get_cached_value(
+				"Serial and Batch Bundle", row.serial_and_batch_bundle, "avg_rate"
+			)
+
 	def __modify_serial_and_batch_bundle(self):
 		if self.is_new():
 			return
@@ -681,6 +693,7 @@
 		self.__remove_changed_rows()
 		self.__set_supplied_items()
 		self.__modify_serial_and_batch_bundle()
+		self.__set_rate_for_serial_and_batch_bundle()
 
 	def __validate_batch_no(self, row, key):
 		if row.get("batch_no") and row.get("batch_no") not in self.__transferred_items.get(key).get(
diff --git a/erpnext/crm/doctype/opportunity_item/opportunity_item.json b/erpnext/crm/doctype/opportunity_item/opportunity_item.json
index 1b4973c..732f80d 100644
--- a/erpnext/crm/doctype/opportunity_item/opportunity_item.json
+++ b/erpnext/crm/doctype/opportunity_item/opportunity_item.json
@@ -103,6 +103,7 @@
    "fieldtype": "Column Break"
   },
   {
+   "fetch_from": "item_code.image",
    "fieldname": "image",
    "fieldtype": "Attach",
    "hidden": 1,
@@ -165,7 +166,7 @@
  "idx": 1,
  "istable": 1,
  "links": [],
- "modified": "2021-07-30 16:39:09.775720",
+ "modified": "2023-11-14 18:35:30.887278",
  "modified_by": "Administrator",
  "module": "CRM",
  "name": "Opportunity Item",
@@ -173,5 +174,6 @@
  "permissions": [],
  "sort_field": "modified",
  "sort_order": "DESC",
+ "states": [],
  "track_changes": 1
 }
\ No newline at end of file
diff --git a/erpnext/manufacturing/doctype/bom_explosion_item/bom_explosion_item.json b/erpnext/manufacturing/doctype/bom_explosion_item/bom_explosion_item.json
index 9b1db63..c75ac32 100644
--- a/erpnext/manufacturing/doctype/bom_explosion_item/bom_explosion_item.json
+++ b/erpnext/manufacturing/doctype/bom_explosion_item/bom_explosion_item.json
@@ -85,6 +85,7 @@
    "fieldtype": "Column Break"
   },
   {
+   "fetch_from": "item_code.image",
    "fieldname": "image",
    "fieldtype": "Attach",
    "hidden": 1,
@@ -169,7 +170,7 @@
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2022-05-27 13:42:23.305455",
+ "modified": "2023-11-14 18:35:40.856895",
  "modified_by": "Administrator",
  "module": "Manufacturing",
  "name": "BOM Explosion Item",
diff --git a/erpnext/manufacturing/doctype/bom_item/bom_item.json b/erpnext/manufacturing/doctype/bom_item/bom_item.json
index c526611..cb58af1 100644
--- a/erpnext/manufacturing/doctype/bom_item/bom_item.json
+++ b/erpnext/manufacturing/doctype/bom_item/bom_item.json
@@ -111,6 +111,7 @@
    "fieldtype": "Column Break"
   },
   {
+   "fetch_from": "item_code.image",
    "fieldname": "image",
    "fieldtype": "Attach",
    "hidden": 1,
@@ -289,7 +290,7 @@
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2022-07-28 10:20:51.559010",
+ "modified": "2023-11-14 18:35:51.378513",
  "modified_by": "Administrator",
  "module": "Manufacturing",
  "name": "BOM Item",
diff --git a/erpnext/public/js/financial_statements.js b/erpnext/public/js/financial_statements.js
index 907a775..1b10d8a 100644
--- a/erpnext/public/js/financial_statements.js
+++ b/erpnext/public/js/financial_statements.js
@@ -139,7 +139,6 @@
 			"label": __("Start Year"),
 			"fieldtype": "Link",
 			"options": "Fiscal Year",
-			"default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
 			"reqd": 1,
 			"depends_on": "eval:doc.filter_based_on == 'Fiscal Year'"
 		},
@@ -148,7 +147,6 @@
 			"label": __("End Year"),
 			"fieldtype": "Link",
 			"options": "Fiscal Year",
-			"default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
 			"reqd": 1,
 			"depends_on": "eval:doc.filter_based_on == 'Fiscal Year'"
 		},
@@ -197,5 +195,13 @@
 		}
 	]
 
+	// Dynamically set 'default' values for fiscal year filters
+	let fy_filters = filters.filter(x=>{return ["from_fiscal_year", "to_fiscal_year"].includes(x.fieldname);})
+	let fiscal_year = erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), false, true);
+	if (fiscal_year) {
+		let fy = erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), false, false);
+		fy_filters.forEach(x=>{x.default = fy;})
+	}
+
 	return filters;
 }
diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js
index d435711..25fc754 100755
--- a/erpnext/public/js/utils.js
+++ b/erpnext/public/js/utils.js
@@ -404,7 +404,7 @@
 		});
 	},
 
-	get_fiscal_year: function(date, with_dates=false) {
+	get_fiscal_year: function(date, with_dates=false, boolean=false) {
 		if(!date) {
 			date = frappe.datetime.get_today();
 		}
@@ -413,7 +413,8 @@
 		frappe.call({
 			method: "erpnext.accounts.utils.get_fiscal_year",
 			args: {
-				date: date
+				date: date,
+				boolean: boolean
 			},
 			async: false,
 			callback: function(r) {
diff --git a/erpnext/selling/doctype/quotation_item/quotation_item.json b/erpnext/selling/doctype/quotation_item/quotation_item.json
index 5016f1f..0e25313 100644
--- a/erpnext/selling/doctype/quotation_item/quotation_item.json
+++ b/erpnext/selling/doctype/quotation_item/quotation_item.json
@@ -135,6 +135,7 @@
    "width": "300px"
   },
   {
+   "fetch_from": "item_code.image",
    "fieldname": "image",
    "fieldtype": "Attach",
    "hidden": 1,
@@ -666,7 +667,7 @@
  "idx": 1,
  "istable": 1,
  "links": [],
- "modified": "2023-09-26 13:42:11.410294",
+ "modified": "2023-11-14 18:24:24.619832",
  "modified_by": "Administrator",
  "module": "Selling",
  "name": "Quotation Item",
@@ -676,4 +677,4 @@
  "sort_order": "DESC",
  "states": [],
  "track_changes": 1
-}
+}
\ No newline at end of file
diff --git a/erpnext/selling/doctype/sales_order_item/sales_order_item.json b/erpnext/selling/doctype/sales_order_item/sales_order_item.json
index f82047f..b4f7300 100644
--- a/erpnext/selling/doctype/sales_order_item/sales_order_item.json
+++ b/erpnext/selling/doctype/sales_order_item/sales_order_item.json
@@ -68,7 +68,6 @@
   "total_weight",
   "column_break_21",
   "weight_uom",
-  "accounting_dimensions_section",
   "warehouse_and_reference",
   "warehouse",
   "target_warehouse",
@@ -177,6 +176,7 @@
    "print_hide": 1
   },
   {
+   "fetch_from": "item_code.image",
    "fieldname": "image",
    "fieldtype": "Attach",
    "hidden": 1,
@@ -890,18 +890,12 @@
    "label": "Production Plan Qty",
    "no_copy": 1,
    "read_only": 1
-  },
-  {
-   "collapsible": 1,
-   "fieldname": "accounting_dimensions_section",
-   "fieldtype": "Section Break",
-   "label": "Accounting Dimensions"
   }
  ],
  "idx": 1,
  "istable": 1,
  "links": [],
- "modified": "2023-10-17 18:18:26.475259",
+ "modified": "2023-11-14 18:37:12.787893",
  "modified_by": "Administrator",
  "module": "Selling",
  "name": "Sales Order Item",
diff --git a/erpnext/stock/doctype/closing_stock_balance/closing_stock_balance.json b/erpnext/stock/doctype/closing_stock_balance/closing_stock_balance.json
index 225da6d..0c4757f 100644
--- a/erpnext/stock/doctype/closing_stock_balance/closing_stock_balance.json
+++ b/erpnext/stock/doctype/closing_stock_balance/closing_stock_balance.json
@@ -104,15 +104,6 @@
    "read_only": 1
   },
   {
-   "fieldname": "amended_from",
-   "fieldtype": "Link",
-   "label": "Amended From",
-   "no_copy": 1,
-   "options": "Closing Stock Balance",
-   "print_hide": 1,
-   "read_only": 1
-  },
-  {
    "fieldname": "include_uom",
    "fieldtype": "Link",
    "label": "Include UOM",
@@ -145,4 +136,4 @@
  "sort_field": "modified",
  "sort_order": "DESC",
  "states": []
-}
\ No newline at end of file
+}
diff --git a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json
index 6148950..a44b9ac 100644
--- a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json
+++ b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json
@@ -168,6 +168,7 @@
    "width": "300px"
   },
   {
+   "fetch_from": "item_code.image",
    "fieldname": "image",
    "fieldtype": "Attach",
    "hidden": 1,
@@ -893,7 +894,7 @@
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2023-10-16 16:18:18.013379",
+ "modified": "2023-11-14 18:37:38.638144",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Delivery Note Item",
diff --git a/erpnext/stock/doctype/material_request_item/material_request_item.json b/erpnext/stock/doctype/material_request_item/material_request_item.json
index 9912be1..5dc07c9 100644
--- a/erpnext/stock/doctype/material_request_item/material_request_item.json
+++ b/erpnext/stock/doctype/material_request_item/material_request_item.json
@@ -110,6 +110,7 @@
    "width": "250px"
   },
   {
+   "fetch_from": "item_code.image",
    "fieldname": "image",
    "fieldtype": "Attach Image",
    "label": "Image",
@@ -478,7 +479,7 @@
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2023-10-27 15:53:41.444236",
+ "modified": "2023-11-14 18:37:59.599115",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Material Request Item",
diff --git a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
index 718f007..ce2e5d7 100644
--- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
+++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
@@ -192,6 +192,7 @@
    "width": "300px"
   },
   {
+   "fetch_from": "item_code.image",
    "fieldname": "image",
    "fieldtype": "Attach",
    "hidden": 1,
@@ -1090,7 +1091,7 @@
  "idx": 1,
  "istable": 1,
  "links": [],
- "modified": "2023-10-30 17:32:24.560337",
+ "modified": "2023-11-14 18:38:15.251994",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Purchase Receipt Item",
diff --git a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json
index dcbd9b2..be37994 100644
--- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json
+++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json
@@ -12,6 +12,7 @@
   "posting_date",
   "posting_time",
   "is_adjustment_entry",
+  "auto_created_serial_and_batch_bundle",
   "column_break_6",
   "voucher_type",
   "voucher_no",
@@ -340,6 +341,13 @@
    "fieldname": "is_adjustment_entry",
    "fieldtype": "Check",
    "label": "Is Adjustment Entry"
+  },
+  {
+   "default": "0",
+   "depends_on": "serial_and_batch_bundle",
+   "fieldname": "auto_created_serial_and_batch_bundle",
+   "fieldtype": "Check",
+   "label": "Auto Created Serial and Batch Bundle"
   }
  ],
  "hide_toolbar": 1,
@@ -348,7 +356,7 @@
  "in_create": 1,
  "index_web_pages_for_search": 1,
  "links": [],
- "modified": "2023-10-23 18:07:42.063615",
+ "modified": "2023-11-14 16:47:39.791967",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Stock Ledger Entry",
diff --git a/erpnext/stock/serial_batch_bundle.py b/erpnext/stock/serial_batch_bundle.py
index 5998274..da98455 100644
--- a/erpnext/stock/serial_batch_bundle.py
+++ b/erpnext/stock/serial_batch_bundle.py
@@ -129,7 +129,9 @@
 			frappe.throw(_(error_msg))
 
 	def set_serial_and_batch_bundle(self, sn_doc):
-		self.sle.db_set("serial_and_batch_bundle", sn_doc.name)
+		self.sle.db_set(
+			{"serial_and_batch_bundle": sn_doc.name, "auto_created_serial_and_batch_bundle": 1}
+		)
 
 		if sn_doc.is_rejected:
 			frappe.db.set_value(
@@ -143,6 +145,12 @@
 	@property
 	def child_doctype(self):
 		child_doctype = self.sle.voucher_type + " Item"
+
+		if (
+			self.sle.voucher_type == "Subcontracting Receipt" and self.sle.dependant_sle_voucher_detail_no
+		):
+			child_doctype = "Subcontracting Receipt Supplied Item"
+
 		if self.sle.voucher_type == "Stock Entry":
 			child_doctype = "Stock Entry Detail"
 
diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py
index 6390894..9142a27 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -766,7 +766,9 @@
 		sle.doctype = "Stock Ledger Entry"
 		frappe.get_doc(sle).db_update()
 
-		if not self.args.get("sle_id"):
+		if not self.args.get("sle_id") or (
+			sle.serial_and_batch_bundle and sle.auto_created_serial_and_batch_bundle
+		):
 			self.update_outgoing_rate_on_transaction(sle)
 
 	def reset_actual_qty_for_stock_reco(self, sle):
diff --git a/erpnext/subcontracting/doctype/subcontracting_order_item/subcontracting_order_item.json b/erpnext/subcontracting/doctype/subcontracting_order_item/subcontracting_order_item.json
index d77e774..46c229b 100644
--- a/erpnext/subcontracting/doctype/subcontracting_order_item/subcontracting_order_item.json
+++ b/erpnext/subcontracting/doctype/subcontracting_order_item/subcontracting_order_item.json
@@ -112,6 +112,7 @@
    "fieldtype": "Column Break"
   },
   {
+   "fetch_from": "item_code.image",
    "fieldname": "image",
    "fieldtype": "Attach",
    "hidden": 1,
@@ -337,7 +338,7 @@
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2023-01-20 23:25:45.363281",
+ "modified": "2023-11-14 18:38:37.640677",
  "modified_by": "Administrator",
  "module": "Subcontracting",
  "name": "Subcontracting Order Item",
diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js
index 19a1c93..36001eb 100644
--- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js
+++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js
@@ -13,6 +13,16 @@
 		frm.trigger('set_queries');
 	},
 
+	on_submit(frm) {
+		frm.events.refresh_serial_batch_bundle_field(frm);
+	},
+
+	refresh_serial_batch_bundle_field(frm) {
+		frappe.route_hooks.after_submit = (frm_obj) => {
+			frm_obj.reload_doc();
+		}
+	},
+
 	refresh: (frm) => {
 		if (frm.doc.docstatus > 0) {
 			frm.add_custom_button(__('Stock Ledger'), () => {
diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py
index 96babf2..6191a8c 100644
--- a/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py
+++ b/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py
@@ -576,6 +576,97 @@
 		self.assertEqual(scr.items[0].rm_cost_per_qty, 300)
 		self.assertEqual(scr.items[0].service_cost_per_qty, 100)
 
+	def test_subcontracting_receipt_valuation_with_auto_created_serial_batch_bundle(self):
+		set_backflush_based_on("BOM")
+
+		fg_item = make_item(properties={"is_stock_item": 1, "is_sub_contracted_item": 1}).name
+		rm_item1 = make_item(
+			properties={
+				"is_stock_item": 1,
+				"has_batch_no": 1,
+				"create_new_batch": 1,
+				"batch_number_series": "BNGS-.####",
+			}
+		).name
+
+		rm_item2 = make_item(
+			properties={
+				"is_stock_item": 1,
+				"has_batch_no": 1,
+				"has_serial_no": 1,
+				"create_new_batch": 1,
+				"batch_number_series": "BNGS-.####",
+				"serial_no_series": "BNSS-.####",
+			}
+		).name
+
+		rm_item3 = make_item(
+			properties={
+				"is_stock_item": 1,
+				"has_serial_no": 1,
+				"serial_no_series": "BSSSS-.####",
+			}
+		).name
+
+		bom = make_bom(item=fg_item, raw_materials=[rm_item1, rm_item2, rm_item3])
+
+		rm_batch_no = None
+		for row in bom.items:
+			make_stock_entry(
+				item_code=row.item_code,
+				qty=1,
+				target="_Test Warehouse 1 - _TC",
+				rate=300,
+			)
+
+			make_stock_entry(
+				item_code=row.item_code,
+				qty=1,
+				target="_Test Warehouse 1 - _TC",
+				rate=400,
+			)
+
+		service_items = [
+			{
+				"warehouse": "_Test Warehouse - _TC",
+				"item_code": "Subcontracted Service Item 1",
+				"qty": 1,
+				"rate": 100,
+				"fg_item": fg_item,
+				"fg_item_qty": 1,
+			},
+		]
+		sco = get_subcontracting_order(service_items=service_items)
+
+		frappe.db.set_single_value(
+			"Stock Settings", "auto_create_serial_and_batch_bundle_for_outward", 1
+		)
+		scr = make_subcontracting_receipt(sco.name)
+		scr.save()
+		for row in scr.supplied_items:
+			self.assertNotEqual(row.rate, 300.00)
+			self.assertFalse(row.serial_and_batch_bundle)
+
+		scr.submit()
+		scr.reload()
+
+		for row in scr.supplied_items:
+			self.assertEqual(row.rate, 300.00)
+			self.assertTrue(row.serial_and_batch_bundle)
+			auto_created_serial_batch = frappe.db.get_value(
+				"Stock Ledger Entry",
+				{"voucher_no": scr.name, "voucher_detail_no": row.name},
+				"auto_created_serial_and_batch_bundle",
+			)
+
+			self.assertTrue(auto_created_serial_batch)
+
+		self.assertEqual(scr.items[0].rm_cost_per_qty, 900)
+		self.assertEqual(scr.items[0].service_cost_per_qty, 100)
+		frappe.db.set_single_value(
+			"Stock Settings", "auto_create_serial_and_batch_bundle_for_outward", 0
+		)
+
 	def test_subcontracting_receipt_raw_material_rate(self):
 		# Step - 1: Set Backflush Based On as "BOM"
 		set_backflush_based_on("BOM")
diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json b/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json
index 38432be..26a29dd 100644
--- a/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json
+++ b/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json
@@ -109,6 +109,7 @@
    "width": "300px"
   },
   {
+   "fetch_from": "item_code.image",
    "fieldname": "image",
    "fieldtype": "Attach",
    "hidden": 1,
@@ -521,7 +522,7 @@
  "idx": 1,
  "istable": 1,
  "links": [],
- "modified": "2023-09-03 17:04:21.214316",
+ "modified": "2023-11-14 18:38:26.459669",
  "modified_by": "Administrator",
  "module": "Subcontracting",
  "name": "Subcontracting Receipt Item",
diff --git a/erpnext/utilities/bulk_transaction.py b/erpnext/utilities/bulk_transaction.py
index 56f3b41..57c2f9d 100644
--- a/erpnext/utilities/bulk_transaction.py
+++ b/erpnext/utilities/bulk_transaction.py
@@ -3,6 +3,7 @@
 
 import frappe
 from frappe import _
+from frappe.utils import get_link_to_form, today
 
 
 @frappe.whitelist()
@@ -28,6 +29,48 @@
 		job(deserialized_data, from_doctype, to_doctype)
 
 
+@frappe.whitelist()
+def retry(date: str | None):
+	if date:
+		failed_docs = frappe.db.get_all(
+			"Bulk Transaction Log Detail",
+			filters={"date": date, "transaction_status": "Failed", "retried": 0},
+			fields=["name", "transaction_name", "from_doctype", "to_doctype"],
+		)
+		if not failed_docs:
+			frappe.msgprint(_("There are no Failed transactions"))
+		else:
+			job = frappe.enqueue(
+				retry_failed_transactions,
+				failed_docs=failed_docs,
+			)
+			frappe.msgprint(
+				_("Job: {0} has been triggered for processing failed transactions").format(
+					get_link_to_form("RQ Job", job.id)
+				)
+			)
+
+
+def retry_failed_transactions(failed_docs: list | None):
+	if failed_docs:
+		for log in failed_docs:
+			try:
+				frappe.db.savepoint("before_creation_state")
+				task(log.transaction_name, log.from_doctype, log.to_doctype)
+			except Exception as e:
+				frappe.db.rollback(save_point="before_creation_state")
+				update_log(log.name, "Failed", 1, str(frappe.get_traceback()))
+			else:
+				update_log(log.name, "Success", 1)
+
+
+def update_log(log_name, status, retried, err=None):
+	frappe.db.set_value("Bulk Transaction Log Detail", log_name, "transaction_status", status)
+	frappe.db.set_value("Bulk Transaction Log Detail", log_name, "retried", retried)
+	if err:
+		frappe.db.set_value("Bulk Transaction Log Detail", log_name, "error_description", err)
+
+
 def job(deserialized_data, from_doctype, to_doctype):
 	fail_count = 0
 	for d in deserialized_data:
@@ -38,7 +81,7 @@
 		except Exception as e:
 			frappe.db.rollback(save_point="before_creation_state")
 			fail_count += 1
-			update_logger(
+			create_log(
 				doc_name,
 				str(frappe.get_traceback()),
 				from_doctype,
@@ -47,7 +90,7 @@
 				log_date=str(date.today()),
 			)
 		else:
-			update_logger(
+			create_log(
 				doc_name, None, from_doctype, to_doctype, status="Success", log_date=str(date.today())
 			)
 
@@ -110,45 +153,18 @@
 	del frappe.flags.bulk_transaction
 
 
-def check_logger_doc_exists(log_date):
-	return frappe.db.exists("Bulk Transaction Log", log_date)
-
-
-def get_logger_doc(log_date):
-	return frappe.get_doc("Bulk Transaction Log", log_date)
-
-
-def create_logger_doc():
-	log_doc = frappe.new_doc("Bulk Transaction Log")
-	log_doc.set_new_name(set_name=str(date.today()))
-	log_doc.log_date = date.today()
-
-	return log_doc
-
-
-def append_data_to_logger(log_doc, doc_name, error, from_doctype, to_doctype, status, restarted):
-	row = log_doc.append("logger_data", {})
-	row.transaction_name = doc_name
-	row.date = date.today()
+def create_log(doc_name, e, from_doctype, to_doctype, status, log_date=None, restarted=0):
+	transaction_log = frappe.new_doc("Bulk Transaction Log Detail")
+	transaction_log.transaction_name = doc_name
+	transaction_log.date = today()
 	now = datetime.now()
-	row.time = now.strftime("%H:%M:%S")
-	row.transaction_status = status
-	row.error_description = str(error)
-	row.from_doctype = from_doctype
-	row.to_doctype = to_doctype
-	row.retried = restarted
-
-
-def update_logger(doc_name, e, from_doctype, to_doctype, status, log_date=None, restarted=0):
-	if not check_logger_doc_exists(log_date):
-		log_doc = create_logger_doc()
-		append_data_to_logger(log_doc, doc_name, e, from_doctype, to_doctype, status, restarted)
-		log_doc.insert()
-	else:
-		log_doc = get_logger_doc(log_date)
-		if record_exists(log_doc, doc_name, status):
-			append_data_to_logger(log_doc, doc_name, e, from_doctype, to_doctype, status, restarted)
-			log_doc.save()
+	transaction_log.time = now.strftime("%H:%M:%S")
+	transaction_log.transaction_status = status
+	transaction_log.error_description = str(e)
+	transaction_log.from_doctype = from_doctype
+	transaction_log.to_doctype = to_doctype
+	transaction_log.retried = restarted
+	transaction_log.save()
 
 
 def show_job_status(fail_count, deserialized_data_count, to_doctype):
@@ -178,23 +194,3 @@
 			title="Failed",
 			indicator="red",
 		)
-
-
-def record_exists(log_doc, doc_name, status):
-	record = mark_retrired_transaction(log_doc, doc_name)
-	if record and status == "Failed":
-		return False
-	elif record and status == "Success":
-		return True
-	else:
-		return True
-
-
-def mark_retrired_transaction(log_doc, doc_name):
-	record = 0
-	for d in log_doc.get("logger_data"):
-		if d.transaction_name == doc_name and d.transaction_status == "Failed":
-			frappe.db.set_value("Bulk Transaction Log Detail", d.name, "retried", 1)
-			record = record + 1
-
-	return record