Merge pull request #35380 from CodeVenturers/OpenContrib

fix: TypeError while saving Job card
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js
index 07761c7..2843824 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.js
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js
@@ -904,7 +904,7 @@
 			function(d) { return flt(d.amount) }));
 
 		frm.set_value("difference_amount", difference_amount - total_deductions +
-			frm.doc.base_total_taxes_and_charges);
+			flt(frm.doc.base_total_taxes_and_charges));
 
 		frm.events.hide_unhide_fields(frm);
 	},
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
index 5c9168b..ab7884d 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
@@ -303,7 +303,7 @@
 
 	apply_tds(frm) {
 		var me = this;
-
+		me.frm.set_value("tax_withheld_vouchers", []);
 		if (!me.frm.doc.apply_tds) {
 			me.frm.set_value("tax_withholding_category", '');
 			me.frm.set_df_property("tax_withholding_category", "hidden", 1);
diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
index ad3477e..1f2d980 100644
--- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
+++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
@@ -302,7 +302,7 @@
 		"docstatus": 1,
 	}
 
-	if not tax_details.get("consider_party_ledger_amount") and doctype != "Sales Invoice":
+	if doctype != "Sales Invoice":
 		filters.update(
 			{"apply_tds": 1, "tax_withholding_category": tax_details.get("tax_withholding_category")}
 		)
diff --git a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py
index 1e86cf5..bc4f670 100644
--- a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py
+++ b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py
@@ -110,9 +110,9 @@
 		invoices.append(pi1)
 
 		# Cumulative threshold is 30000
-		# Threshold calculation should be on both the invoices
-		# TDS should be applied only on 1000
-		self.assertEqual(pi1.taxes[0].tax_amount, 1000)
+		# Threshold calculation should be only on the Second invoice
+		# Second didn't breach, no TDS should be applied
+		self.assertEqual(pi1.taxes, [])
 
 		for d in reversed(invoices):
 			d.cancel()
diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py
index 203612f..f3a9ba0 100644
--- a/erpnext/assets/doctype/asset/test_asset.py
+++ b/erpnext/assets/doctype/asset/test_asset.py
@@ -691,7 +691,7 @@
 		)
 
 		self.assertEqual(asset.status, "Draft")
-		expected_schedules = [["2032-12-31", 30000.0, 77095.89], ["2033-06-06", 12904.11, 90000.0]]
+		expected_schedules = [["2032-12-31", 42904.11, 90000.0]]
 		schedules = [
 			[cstr(d.schedule_date), flt(d.depreciation_amount, 2), d.accumulated_depreciation_amount]
 			for d in get_depr_schedule(asset.name, "Draft")
diff --git a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py
index 116593a..ad5ec3d 100644
--- a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py
+++ b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py
@@ -337,7 +337,7 @@
 				depreciation_amount += value_after_depreciation - row.expected_value_after_useful_life
 				skip_row = True
 
-			if depreciation_amount > 0:
+			if flt(depreciation_amount, asset_doc.precision("gross_purchase_amount")) > 0:
 				self.add_depr_schedule_row(
 					schedule_date,
 					depreciation_amount,
@@ -521,9 +521,11 @@
 		)
 	# if the Depreciation Schedule is being prepared for the first time
 	else:
-		return (flt(asset.gross_purchase_amount) - flt(row.expected_value_after_useful_life)) / flt(
-			row.total_number_of_depreciations
-		)
+		return (
+			flt(asset.gross_purchase_amount)
+			- flt(asset.opening_accumulated_depreciation)
+			- flt(row.expected_value_after_useful_life)
+		) / flt(row.total_number_of_depreciations - asset.number_of_depreciations_booked)
 
 
 def get_wdv_or_dd_depr_amount(
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index 3d930d6..78bb056 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -1682,6 +1682,9 @@
 					d.base_payment_amount = flt(
 						d.payment_amount * self.get("conversion_rate"), d.precision("base_payment_amount")
 					)
+		else:
+			self.fetch_payment_terms_from_order(po_or_so, doctype)
+			self.ignore_default_payment_terms_template = 1
 
 	def get_order_details(self):
 		if self.doctype == "Sales Invoice":
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index 09089be..befde71 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -770,6 +770,9 @@
 			}
 		)
 
+		if self.docstatus == 2:
+			force = True
+
 		if force or future_sle_exists(args) or repost_required_for_queue(self):
 			item_based_reposting = cint(
 				frappe.db.get_single_value("Stock Reposting Settings", "item_based_reposting")
diff --git a/erpnext/controllers/subcontracting_controller.py b/erpnext/controllers/subcontracting_controller.py
index c3fa894..1e9c4dc 100644
--- a/erpnext/controllers/subcontracting_controller.py
+++ b/erpnext/controllers/subcontracting_controller.py
@@ -689,7 +689,6 @@
 								"actual_qty": flt(item.rejected_qty) * flt(item.conversion_factor),
 								"serial_no": cstr(item.rejected_serial_no).strip(),
 								"incoming_rate": 0.0,
-								"recalculate_rate": 1,
 							},
 						)
 					)
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.json b/erpnext/manufacturing/doctype/production_plan/production_plan.json
index fdaa4a2..232f1cb 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.json
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.json
@@ -35,8 +35,12 @@
   "section_break_25",
   "prod_plan_references",
   "section_break_24",
-  "get_sub_assembly_items",
   "combine_sub_items",
+  "section_break_ucc4",
+  "skip_available_sub_assembly_item",
+  "column_break_igxl",
+  "get_sub_assembly_items",
+  "section_break_g4ip",
   "sub_assembly_items",
   "download_materials_request_plan_section_section",
   "download_materials_required",
@@ -351,12 +355,12 @@
   {
    "fieldname": "section_break_24",
    "fieldtype": "Section Break",
-   "hide_border": 1
+   "hide_border": 1,
+   "label": "Sub Assembly Items"
   },
   {
    "fieldname": "sub_assembly_items",
    "fieldtype": "Table",
-   "label": "Sub Assembly Items",
    "no_copy": 1,
    "options": "Production Plan Sub Assembly Item"
   },
@@ -392,13 +396,33 @@
    "fieldname": "download_materials_request_plan_section_section",
    "fieldtype": "Section Break",
    "label": "Download Materials Request Plan Section"
+  },
+  {
+   "default": "0",
+   "description": "System consider the projected quantity to check available or will be available sub-assembly items ",
+   "fieldname": "skip_available_sub_assembly_item",
+   "fieldtype": "Check",
+   "label": "Skip Available Sub Assembly Items"
+  },
+  {
+   "fieldname": "section_break_ucc4",
+   "fieldtype": "Column Break",
+   "hide_border": 1
+  },
+  {
+   "fieldname": "section_break_g4ip",
+   "fieldtype": "Section Break"
+  },
+  {
+   "fieldname": "column_break_igxl",
+   "fieldtype": "Column Break"
   }
  ],
  "icon": "fa fa-calendar",
  "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2023-03-31 10:30:48.118932",
+ "modified": "2023-05-22 23:36:31.770517",
  "modified_by": "Administrator",
  "module": "Manufacturing",
  "name": "Production Plan",
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py
index f9e68b9..e40539a 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py
@@ -718,7 +718,9 @@
 				frappe.throw(_("Row #{0}: Please select Item Code in Assembly Items").format(row.idx))
 
 			bom_data = []
-			get_sub_assembly_items(row.bom_no, bom_data, row.planned_qty)
+
+			warehouse = row.warehouse if self.skip_available_sub_assembly_item else None
+			get_sub_assembly_items(row.bom_no, bom_data, row.planned_qty, self.company, warehouse=warehouse)
 			self.set_sub_assembly_items_based_on_level(row, bom_data, manufacturing_type)
 			sub_assembly_items_store.extend(bom_data)
 
@@ -894,7 +896,9 @@
 	build_csv_response(item_list, doc.name)
 
 
-def get_exploded_items(item_details, company, bom_no, include_non_stock_items, planned_qty=1):
+def get_exploded_items(
+	item_details, company, bom_no, include_non_stock_items, planned_qty=1, doc=None
+):
 	bei = frappe.qb.DocType("BOM Explosion Item")
 	bom = frappe.qb.DocType("BOM")
 	item = frappe.qb.DocType("Item")
@@ -1271,6 +1275,12 @@
 	include_safety_stock = doc.get("include_safety_stock")
 
 	so_item_details = frappe._dict()
+
+	sub_assembly_items = {}
+	if doc.get("skip_available_sub_assembly_item"):
+		for d in doc.get("sub_assembly_items"):
+			sub_assembly_items.setdefault((d.get("production_item"), d.get("bom_no")), d.get("qty"))
+
 	for data in po_items:
 		if not data.get("include_exploded_items") and doc.get("sub_assembly_items"):
 			data["include_exploded_items"] = 1
@@ -1296,10 +1306,24 @@
 				frappe.throw(_("For row {0}: Enter Planned Qty").format(data.get("idx")))
 
 			if bom_no:
-				if data.get("include_exploded_items") and include_subcontracted_items:
+				if (
+					data.get("include_exploded_items")
+					and doc.get("sub_assembly_items")
+					and doc.get("skip_available_sub_assembly_item")
+				):
+					item_details = get_raw_materials_of_sub_assembly_items(
+						item_details,
+						company,
+						bom_no,
+						include_non_stock_items,
+						sub_assembly_items,
+						planned_qty=planned_qty,
+					)
+
+				elif data.get("include_exploded_items") and include_subcontracted_items:
 					# fetch exploded items from BOM
 					item_details = get_exploded_items(
-						item_details, company, bom_no, include_non_stock_items, planned_qty=planned_qty
+						item_details, company, bom_no, include_non_stock_items, planned_qty=planned_qty, doc=doc
 					)
 				else:
 					item_details = get_subitems(
@@ -1456,12 +1480,22 @@
 	}
 
 
-def get_sub_assembly_items(bom_no, bom_data, to_produce_qty, indent=0):
+def get_sub_assembly_items(bom_no, bom_data, to_produce_qty, company, warehouse=None, indent=0):
 	data = get_bom_children(parent=bom_no)
 	for d in data:
 		if d.expandable:
 			parent_item_code = frappe.get_cached_value("BOM", bom_no, "item")
 			stock_qty = (d.stock_qty / d.parent_bom_qty) * flt(to_produce_qty)
+
+			if warehouse:
+				bin_dict = get_bin_details(d, company, for_warehouse=warehouse)
+
+				if bin_dict and bin_dict[0].projected_qty > 0:
+					if bin_dict[0].projected_qty > stock_qty:
+						continue
+					else:
+						stock_qty = stock_qty - bin_dict[0].projected_qty
+
 			bom_data.append(
 				frappe._dict(
 					{
@@ -1481,7 +1515,7 @@
 			)
 
 			if d.value:
-				get_sub_assembly_items(d.value, bom_data, stock_qty, indent=indent + 1)
+				get_sub_assembly_items(d.value, bom_data, stock_qty, company, warehouse, indent=indent + 1)
 
 
 def set_default_warehouses(row, default_warehouses):
@@ -1519,3 +1553,68 @@
 	)
 
 	return reserved_qty_for_production_plan - reserved_qty_for_production
+
+
+def get_raw_materials_of_sub_assembly_items(
+	item_details, company, bom_no, include_non_stock_items, sub_assembly_items, planned_qty=1
+):
+
+	bei = frappe.qb.DocType("BOM Item")
+	bom = frappe.qb.DocType("BOM")
+	item = frappe.qb.DocType("Item")
+	item_default = frappe.qb.DocType("Item Default")
+	item_uom = frappe.qb.DocType("UOM Conversion Detail")
+
+	items = (
+		frappe.qb.from_(bei)
+		.join(bom)
+		.on(bom.name == bei.parent)
+		.join(item)
+		.on(item.name == bei.item_code)
+		.left_join(item_default)
+		.on((item_default.parent == item.name) & (item_default.company == company))
+		.left_join(item_uom)
+		.on((item.name == item_uom.parent) & (item_uom.uom == item.purchase_uom))
+		.select(
+			(IfNull(Sum(bei.stock_qty / IfNull(bom.quantity, 1)), 0) * planned_qty).as_("qty"),
+			item.item_name,
+			item.name.as_("item_code"),
+			bei.description,
+			bei.stock_uom,
+			bei.bom_no,
+			item.min_order_qty,
+			bei.source_warehouse,
+			item.default_material_request_type,
+			item.min_order_qty,
+			item_default.default_warehouse,
+			item.purchase_uom,
+			item_uom.conversion_factor,
+			item.safety_stock,
+		)
+		.where(
+			(bei.docstatus == 1)
+			& (bom.name == bom_no)
+			& (item.is_stock_item.isin([0, 1]) if include_non_stock_items else item.is_stock_item == 1)
+		)
+		.groupby(bei.item_code, bei.stock_uom)
+	).run(as_dict=True)
+
+	for item in items:
+		key = (item.item_code, item.bom_no)
+		if item.bom_no and key in sub_assembly_items:
+			planned_qty = flt(sub_assembly_items[key])
+			get_raw_materials_of_sub_assembly_items(
+				item_details,
+				company,
+				item.bom_no,
+				include_non_stock_items,
+				sub_assembly_items,
+				planned_qty=planned_qty,
+			)
+		else:
+			if not item.conversion_factor and item.purchase_uom:
+				item.conversion_factor = get_uom_conversion_factor(item.item_code, item.purchase_uom)
+
+			item_details.setdefault(item.get("item_code"), item)
+
+	return item_details
diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
index 4648d89..75b43ec 100644
--- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
@@ -926,6 +926,50 @@
 
 		self.assertEqual(after_qty, before_qty)
 
+	def test_skip_available_qty_for_sub_assembly_items(self):
+		from erpnext.manufacturing.doctype.bom.test_bom import create_nested_bom
+
+		bom_tree = {
+			"Fininshed Goods1 For SUB Test": {
+				"SubAssembly1 For SUB Test": {"ChildPart1 For SUB Test": {}},
+				"SubAssembly2 For SUB Test": {},
+			}
+		}
+
+		parent_bom = create_nested_bom(bom_tree, prefix="")
+		plan = create_production_plan(
+			item_code=parent_bom.item,
+			planned_qty=10,
+			ignore_existing_ordered_qty=1,
+			do_not_submit=1,
+			skip_available_sub_assembly_item=1,
+			warehouse="_Test Warehouse - _TC",
+		)
+
+		make_stock_entry(
+			item_code="SubAssembly1 For SUB Test",
+			qty=5,
+			rate=100,
+			target="_Test Warehouse - _TC",
+		)
+
+		self.assertTrue(plan.skip_available_sub_assembly_item)
+
+		plan.get_sub_assembly_items()
+
+		for row in plan.sub_assembly_items:
+			if row.production_item == "SubAssembly1 For SUB Test":
+				self.assertEqual(row.qty, 5)
+
+		mr_items = get_items_for_material_requests(plan.as_dict())
+		for row in mr_items:
+			row = frappe._dict(row)
+			if row.item_code == "ChildPart1 For SUB Test":
+				self.assertEqual(row.quantity, 5)
+
+			if row.item_code == "SubAssembly2 For SUB Test":
+				self.assertEqual(row.quantity, 10)
+
 
 def create_production_plan(**args):
 	"""
@@ -945,6 +989,7 @@
 			"include_subcontracted_items": args.include_subcontracted_items or 0,
 			"ignore_existing_ordered_qty": args.ignore_existing_ordered_qty or 0,
 			"get_items_from": "Sales Order",
+			"skip_available_sub_assembly_item": args.skip_available_sub_assembly_item or 0,
 		}
 	)
 
@@ -958,6 +1003,7 @@
 				"planned_qty": args.planned_qty or 1,
 				"planned_start_date": args.planned_start_date or now_datetime(),
 				"stock_uom": args.stock_uom or "Nos",
+				"warehouse": args.warehouse,
 			},
 		)
 
diff --git a/erpnext/manufacturing/doctype/production_plan_sub_assembly_item/production_plan_sub_assembly_item.json b/erpnext/manufacturing/doctype/production_plan_sub_assembly_item/production_plan_sub_assembly_item.json
index 4eb6bf6..fde0404 100644
--- a/erpnext/manufacturing/doctype/production_plan_sub_assembly_item/production_plan_sub_assembly_item.json
+++ b/erpnext/manufacturing/doctype/production_plan_sub_assembly_item/production_plan_sub_assembly_item.json
@@ -28,7 +28,11 @@
   "uom",
   "stock_uom",
   "column_break_22",
-  "description"
+  "description",
+  "section_break_4rxf",
+  "actual_qty",
+  "column_break_xfhm",
+  "projected_qty"
  ],
  "fields": [
   {
@@ -183,12 +187,34 @@
    "fieldtype": "Datetime",
    "in_list_view": 1,
    "label": "Schedule Date"
+  },
+  {
+   "fieldname": "section_break_4rxf",
+   "fieldtype": "Section Break"
+  },
+  {
+   "fieldname": "actual_qty",
+   "fieldtype": "Float",
+   "label": "Actual Qty",
+   "no_copy": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "column_break_xfhm",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "projected_qty",
+   "fieldtype": "Float",
+   "label": "Projected Qty",
+   "no_copy": 1,
+   "read_only": 1
   }
  ],
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2022-11-28 13:50:15.116082",
+ "modified": "2023-05-22 17:52:34.708879",
  "modified_by": "Administrator",
  "module": "Manufacturing",
  "name": "Production Plan Sub Assembly Item",
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index e158df6..7e68ec1 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -326,7 +326,7 @@
 erpnext.patches.v15_0.update_asset_value_for_manual_depr_entries
 erpnext.patches.v15_0.update_gpa_and_ndb_for_assdeprsch
 erpnext.patches.v14_0.create_accounting_dimensions_for_closing_balance
-erpnext.patches.v14_0.update_closing_balances #10-05-2023
+erpnext.patches.v14_0.update_closing_balances #17-05-2023
 execute:frappe.db.set_single_value("Accounts Settings", "merge_similar_account_heads", 0)
 # below migration patches should always run last
 erpnext.patches.v14_0.migrate_gl_to_payment_ledger
diff --git a/erpnext/patches/v13_0/update_amt_in_work_order_required_items.py b/erpnext/patches/v13_0/update_amt_in_work_order_required_items.py
index e37f291..64170ed 100644
--- a/erpnext/patches/v13_0/update_amt_in_work_order_required_items.py
+++ b/erpnext/patches/v13_0/update_amt_in_work_order_required_items.py
@@ -7,4 +7,6 @@
 	frappe.reload_doc("manufacturing", "doctype", "work_order")
 	frappe.reload_doc("manufacturing", "doctype", "work_order_item")
 
-	frappe.db.sql("""UPDATE `tabWork Order Item` SET amount = rate * required_qty""")
+	frappe.db.sql(
+		"""UPDATE `tabWork Order Item` SET amount = ifnull(rate, 0.0) * ifnull(required_qty, 0.0)"""
+	)
diff --git a/erpnext/patches/v14_0/update_closing_balances.py b/erpnext/patches/v14_0/update_closing_balances.py
index bb108ab..d664677 100644
--- a/erpnext/patches/v14_0/update_closing_balances.py
+++ b/erpnext/patches/v14_0/update_closing_balances.py
@@ -13,8 +13,8 @@
 def execute():
 	frappe.db.truncate("Account Closing Balance")
 
+	i = 0
 	company_wise_order = {}
-	get_opening_entries = True
 	for pcv in frappe.db.get_all(
 		"Period Closing Voucher",
 		fields=["company", "posting_date", "name"],
@@ -29,6 +29,7 @@
 				pcv.posting_date, pcv.fiscal_year, company=pcv.company
 			)[1]
 
+			# get gl entries against pcv
 			gl_entries = frappe.db.get_all(
 				"GL Entry", filters={"voucher_no": pcv.name, "is_cancelled": 0}, fields=["*"]
 			)
@@ -37,20 +38,31 @@
 				entry["closing_date"] = pcv_doc.posting_date
 				entry["period_closing_voucher"] = pcv_doc.name
 
+			# get all gl entries for the year
 			closing_entries = frappe.db.get_all(
 				"GL Entry",
 				filters={
 					"is_cancelled": 0,
 					"voucher_no": ["!=", pcv.name],
-					"posting_date": ["<=", pcv.posting_date],
+					"posting_date": ["between", [pcv_doc.year_start_date, pcv.posting_date]],
+					"is_opening": "No",
 				},
 				fields=["*"],
 			)
 
+			if i == 0:
+				# add opening entries only for the first pcv
+				closing_entries += frappe.db.get_all(
+					"GL Entry",
+					filters={"is_cancelled": 0, "is_opening": "Yes"},
+					fields=["*"],
+				)
+
 			for entry in closing_entries:
 				entry["closing_date"] = pcv_doc.posting_date
 				entry["period_closing_voucher"] = pcv_doc.name
 
 			make_closing_entries(gl_entries + closing_entries, voucher_name=pcv.name)
 			company_wise_order[pcv.company].append(pcv.posting_date)
-			get_opening_entries = False
+
+			i += 1
diff --git a/erpnext/public/js/controllers/accounts.js b/erpnext/public/js/controllers/accounts.js
index d943126..47b88a0 100644
--- a/erpnext/public/js/controllers/accounts.js
+++ b/erpnext/public/js/controllers/accounts.js
@@ -91,6 +91,12 @@
 });
 
 frappe.ui.form.on('Purchase Invoice', {
+	setup: (frm) => {
+		frm.make_methods = {
+			'Landed Cost Voucher': function () { frm.trigger('create_landedcost_voucher') },
+		}
+	},
+
 	mode_of_payment: function(frm) {
 		get_payment_mode_account(frm, frm.doc.mode_of_payment, function(account){
 			frm.set_value('cash_bank_account', account);
@@ -99,6 +105,20 @@
 
 	payment_terms_template: function() {
 		cur_frm.trigger("disable_due_date");
+	},
+
+	create_landedcost_voucher: function (frm) {
+		let lcv = frappe.model.get_new_doc('Landed Cost Voucher');
+		lcv.company = frm.doc.company;
+
+		let lcv_receipt = frappe.model.get_new_doc('Landed Cost Purchase Invoice');
+		lcv_receipt.receipt_document_type = 'Purchase Invoice';
+		lcv_receipt.receipt_document = frm.doc.name;
+		lcv_receipt.supplier = frm.doc.supplier;
+		lcv_receipt.grand_total = frm.doc.grand_total;
+		lcv.purchase_receipts = [lcv_receipt];
+
+		frappe.set_route("Form", lcv.doctype, lcv.name);
 	}
 });
 
diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py
index 693fc92..61969fe 100644
--- a/erpnext/selling/doctype/quotation/quotation.py
+++ b/erpnext/selling/doctype/quotation/quotation.py
@@ -288,7 +288,7 @@
 			)
 
 		# sales team
-		for d in customer.get("sales_team"):
+		for d in customer.get("sales_team") or []:
 			target.append(
 				"sales_team",
 				{
diff --git a/erpnext/setup/doctype/employee/employee.py b/erpnext/setup/doctype/employee/employee.py
index ece5a7d..566392c 100755
--- a/erpnext/setup/doctype/employee/employee.py
+++ b/erpnext/setup/doctype/employee/employee.py
@@ -257,7 +257,9 @@
 
 def get_holiday_list_for_employee(employee, raise_exception=True):
 	if employee:
-		holiday_list, company = frappe.db.get_value("Employee", employee, ["holiday_list", "company"])
+		holiday_list, company = frappe.get_cached_value(
+			"Employee", employee, ["holiday_list", "company"]
+		)
 	else:
 		holiday_list = ""
 		company = frappe.db.get_single_value("Global Defaults", "default_company")
diff --git a/erpnext/setup/doctype/holiday_list/holiday_list.py b/erpnext/setup/doctype/holiday_list/holiday_list.py
index fad827a..84d0d35 100644
--- a/erpnext/setup/doctype/holiday_list/holiday_list.py
+++ b/erpnext/setup/doctype/holiday_list/holiday_list.py
@@ -115,6 +115,8 @@
 	if date is None:
 		date = today()
 	if holiday_list:
-		return bool(frappe.get_all("Holiday List", dict(name=holiday_list, holiday_date=date)))
+		return bool(
+			frappe.db.exists("Holiday", {"parent": holiday_list, "holiday_date": date}, cache=True)
+		)
 	else:
 		return False
diff --git a/erpnext/setup/module_onboarding/home/home.json b/erpnext/setup/module_onboarding/home/home.json
index f02fc45..1fd9679 100644
--- a/erpnext/setup/module_onboarding/home/home.json
+++ b/erpnext/setup/module_onboarding/home/home.json
@@ -22,25 +22,16 @@
  "creation": "2021-11-22 12:19:15.888642",
  "docstatus": 0,
  "doctype": "Module Onboarding",
- "documentation_url": "https://docs.erpnext.com/docs/v13/user/manual/en/setting-up/company-setup",
+ "documentation_url": "https://docs.erpnext.com/docs/v14/user/manual/en/setting-up/company-setup",
  "idx": 0,
  "is_complete": 0,
- "modified": "2022-06-07 14:31:00.575193",
+ "modified": "2023-05-20 19:45:03.936741",
  "modified_by": "Administrator",
  "module": "Setup",
  "name": "Home",
  "owner": "Administrator",
  "steps": [
   {
-   "step": "Company Set Up"
-  },
-  {
-   "step": "Navigation Help"
-  },
-  {
-   "step": "Data import"
-  },
-  {
    "step": "Create an Item"
   },
   {
@@ -51,12 +42,9 @@
   },
   {
    "step": "Create a Quotation"
-  },
-  {
-   "step": "Letterhead"
   }
  ],
- "subtitle": "Company, Item, Customer, Supplier, Navigation Help, Data Import, Letter Head, Quotation",
- "success_message": "Masters are all set up!",
- "title": "Let's Set Up Some Masters"
+ "subtitle": "Item, Customer, Supplier and Quotation",
+ "success_message": "You're ready to start your journey with ERPNext",
+ "title": "Let's begin your journey with ERPNext"
 }
\ No newline at end of file
diff --git a/erpnext/setup/onboarding_step/company_set_up/company_set_up.json b/erpnext/setup/onboarding_step/company_set_up/company_set_up.json
index 6f65832..fae2de0 100644
--- a/erpnext/setup/onboarding_step/company_set_up/company_set_up.json
+++ b/erpnext/setup/onboarding_step/company_set_up/company_set_up.json
@@ -5,11 +5,11 @@
  "description": "# Set Up a Company\n\nA company is a legal entity for which you will set up your books of account and create accounting transactions. In ERPNext, you can create multiple companies, and establish relationships (group/subsidiary) among them.\n\nWithin the company master, you can capture various default accounts for that Company and set crucial settings related to the accounting methodology followed for a company.\n",
  "docstatus": 0,
  "doctype": "Onboarding Step",
- "idx": 0,
+ "idx": 1,
  "is_complete": 0,
  "is_single": 0,
  "is_skipped": 0,
- "modified": "2021-12-15 14:22:18.317423",
+ "modified": "2023-05-15 09:18:42.895537",
  "modified_by": "Administrator",
  "name": "Company Set Up",
  "owner": "Administrator",
diff --git a/erpnext/setup/onboarding_step/create_a_customer/create_a_customer.json b/erpnext/setup/onboarding_step/create_a_customer/create_a_customer.json
index f74d745..5b0fd41 100644
--- a/erpnext/setup/onboarding_step/create_a_customer/create_a_customer.json
+++ b/erpnext/setup/onboarding_step/create_a_customer/create_a_customer.json
@@ -5,17 +5,17 @@
  "description": "# Create a Customer\n\nThe Customer master is at the heart of your sales transactions. Customers are linked in Quotations, Sales Orders, Invoices, and Payments. Customers can be either numbered or identified by name (you would typically do this based on the number of customers you have).\n\nThrough Customer\u2019s master, you can effectively track essentials like:\n - Customer\u2019s multiple address and contacts\n - Account Receivables\n - Credit Limit and Credit Period\n",
  "docstatus": 0,
  "doctype": "Onboarding Step",
- "idx": 0,
+ "idx": 1,
  "is_complete": 0,
  "is_single": 0,
  "is_skipped": 0,
- "modified": "2021-12-15 14:20:31.197564",
+ "modified": "2023-05-16 20:01:34.202622",
  "modified_by": "Administrator",
  "name": "Create a Customer",
  "owner": "Administrator",
  "reference_document": "Customer",
  "show_form_tour": 0,
  "show_full_form": 0,
- "title": "Manage Customers",
+ "title": "Create a Customer",
  "validate_action": 1
 }
\ No newline at end of file
diff --git a/erpnext/setup/onboarding_step/create_a_quotation/create_a_quotation.json b/erpnext/setup/onboarding_step/create_a_quotation/create_a_quotation.json
index 8bdb621..92b45b4 100644
--- a/erpnext/setup/onboarding_step/create_a_quotation/create_a_quotation.json
+++ b/erpnext/setup/onboarding_step/create_a_quotation/create_a_quotation.json
@@ -5,11 +5,11 @@
  "description": "# Create a Quotation\n\nLet\u2019s get started with business transactions by creating your first Quotation. You can create a Quotation for an existing customer or a prospect. It will be an approved document, with items you sell and the proposed price + taxes applied. After completing the instructions, you will get a Quotation in a ready to share print format.",
  "docstatus": 0,
  "doctype": "Onboarding Step",
- "idx": 0,
+ "idx": 1,
  "is_complete": 0,
  "is_single": 0,
  "is_skipped": 0,
- "modified": "2021-12-15 14:21:31.675330",
+ "modified": "2023-05-15 09:18:42.984170",
  "modified_by": "Administrator",
  "name": "Create a Quotation",
  "owner": "Administrator",
diff --git a/erpnext/setup/onboarding_step/create_a_supplier/create_a_supplier.json b/erpnext/setup/onboarding_step/create_a_supplier/create_a_supplier.json
index 9574141..4ac26e2 100644
--- a/erpnext/setup/onboarding_step/create_a_supplier/create_a_supplier.json
+++ b/erpnext/setup/onboarding_step/create_a_supplier/create_a_supplier.json
@@ -5,17 +5,17 @@
  "description": "# Create a Supplier\n\nAlso known as Vendor, is a master at the center of your purchase transactions. Suppliers are linked in Request for Quotation, Purchase Orders, Receipts, and Payments. Suppliers can be either numbered or identified by name.\n\nThrough Supplier\u2019s master, you can effectively track essentials like:\n - Supplier\u2019s multiple address and contacts\n - Account Receivables\n - Credit Limit and Credit Period\n",
  "docstatus": 0,
  "doctype": "Onboarding Step",
- "idx": 0,
+ "idx": 1,
  "is_complete": 0,
  "is_single": 0,
  "is_skipped": 0,
- "modified": "2021-12-15 14:21:23.518301",
+ "modified": "2023-05-19 15:32:55.069257",
  "modified_by": "Administrator",
  "name": "Create a Supplier",
  "owner": "Administrator",
  "reference_document": "Supplier",
  "show_form_tour": 0,
  "show_full_form": 0,
- "title": "Manage Suppliers",
+ "title": "Create a Supplier",
  "validate_action": 1
 }
\ No newline at end of file
diff --git a/erpnext/setup/onboarding_step/create_an_item/create_an_item.json b/erpnext/setup/onboarding_step/create_an_item/create_an_item.json
index cd29683..15f36be 100644
--- a/erpnext/setup/onboarding_step/create_an_item/create_an_item.json
+++ b/erpnext/setup/onboarding_step/create_an_item/create_an_item.json
@@ -6,18 +6,18 @@
  "docstatus": 0,
  "doctype": "Onboarding Step",
  "form_tour": "Item General",
- "idx": 0,
+ "idx": 1,
  "intro_video_url": "",
  "is_complete": 0,
  "is_single": 0,
  "is_skipped": 0,
- "modified": "2021-12-15 14:19:56.297772",
+ "modified": "2023-05-16 12:56:40.355878",
  "modified_by": "Administrator",
  "name": "Create an Item",
  "owner": "Administrator",
  "reference_document": "Item",
  "show_form_tour": 1,
- "show_full_form": 1,
- "title": "Manage Items",
+ "show_full_form": 0,
+ "title": "Create an Item",
  "validate_action": 1
 }
\ No newline at end of file
diff --git a/erpnext/setup/onboarding_step/data_import/data_import.json b/erpnext/setup/onboarding_step/data_import/data_import.json
index 4999a36..e5dd7da 100644
--- a/erpnext/setup/onboarding_step/data_import/data_import.json
+++ b/erpnext/setup/onboarding_step/data_import/data_import.json
@@ -5,11 +5,11 @@
  "description": "# Import Data from Spreadsheet\n\nIn ERPNext, you can easily migrate your historical data using spreadsheets. You can use it for migrating not just masters (like Customer, Supplier, Items), but also for transactions like (outstanding invoices, opening stock and accounting entries, etc).",
  "docstatus": 0,
  "doctype": "Onboarding Step",
- "idx": 0,
+ "idx": 1,
  "is_complete": 0,
  "is_single": 0,
  "is_skipped": 0,
- "modified": "2022-06-07 14:28:51.390813",
+ "modified": "2023-05-15 09:18:42.962231",
  "modified_by": "Administrator",
  "name": "Data import",
  "owner": "Administrator",
diff --git a/erpnext/setup/onboarding_step/letterhead/letterhead.json b/erpnext/setup/onboarding_step/letterhead/letterhead.json
index 8e1bb8c..584fd48 100644
--- a/erpnext/setup/onboarding_step/letterhead/letterhead.json
+++ b/erpnext/setup/onboarding_step/letterhead/letterhead.json
@@ -5,11 +5,11 @@
  "description": "# Create a Letter Head\n\nA Letter Head contains your organization's name, logo, address, etc which appears at the header and footer portion in documents. You can learn more about Setting up Letter Head in ERPNext here.\n",
  "docstatus": 0,
  "doctype": "Onboarding Step",
- "idx": 0,
+ "idx": 1,
  "is_complete": 0,
  "is_single": 0,
  "is_skipped": 0,
- "modified": "2021-12-15 14:21:39.037742",
+ "modified": "2023-05-15 09:18:42.995184",
  "modified_by": "Administrator",
  "name": "Letterhead",
  "owner": "Administrator",
diff --git a/erpnext/setup/onboarding_step/navigation_help/navigation_help.json b/erpnext/setup/onboarding_step/navigation_help/navigation_help.json
index cf07968..f57818b 100644
--- a/erpnext/setup/onboarding_step/navigation_help/navigation_help.json
+++ b/erpnext/setup/onboarding_step/navigation_help/navigation_help.json
@@ -2,14 +2,14 @@
  "action": "Watch Video",
  "action_label": "Learn about  Navigation options",
  "creation": "2021-11-22 12:09:52.233872",
- "description": "# Navigation in ERPNext\n\nEase of navigating and browsing around the ERPNext is one of our core strengths. In the following video, you will learn how to reach a specific feature in ERPNext via module page or awesome bar\u2019s shortcut.\n",
+ "description": "# Navigation in ERPNext\n\nEase of navigating and browsing around the ERPNext is one of our core strengths. In the following video, you will learn how to reach a specific feature in ERPNext via module page or AwesomeBar.",
  "docstatus": 0,
  "doctype": "Onboarding Step",
- "idx": 0,
+ "idx": 1,
  "is_complete": 0,
  "is_single": 0,
  "is_skipped": 0,
- "modified": "2022-06-07 14:28:00.901082",
+ "modified": "2023-05-16 12:53:25.939908",
  "modified_by": "Administrator",
  "name": "Navigation Help",
  "owner": "Administrator",
diff --git a/erpnext/stock/doctype/item/item_list.js b/erpnext/stock/doctype/item/item_list.js
index 534b341..22d38e8 100644
--- a/erpnext/stock/doctype/item/item_list.js
+++ b/erpnext/stock/doctype/item/item_list.js
@@ -1,5 +1,5 @@
 frappe.listview_settings['Item'] = {
-	add_fields: ["item_name", "stock_uom", "item_group", "image", "variant_of",
+	add_fields: ["item_name", "stock_uom", "item_group", "image",
 		"has_variants", "end_of_life", "disabled"],
 	filters: [["disabled", "=", "0"]],
 
diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py
index 46d6e9e..74927c7 100644
--- a/erpnext/stock/doctype/pick_list/pick_list.py
+++ b/erpnext/stock/doctype/pick_list/pick_list.py
@@ -29,6 +29,7 @@
 		self.validate_for_qty()
 
 	def before_save(self):
+		self.update_status()
 		self.set_item_locations()
 
 		# set percentage picked in SO
@@ -89,20 +90,20 @@
 		self.update_reference_qty()
 		self.update_sales_order_picking_status()
 
-	def update_status(self, status=None, update_modified=True):
+	def update_status(self, status=None):
 		if not status:
 			if self.docstatus == 0:
 				status = "Draft"
 			elif self.docstatus == 1:
-				if self.status == "Draft":
-					status = "Open"
-				elif target_document_exists(self.name, self.purpose):
+				if target_document_exists(self.name, self.purpose):
 					status = "Completed"
+				else:
+					status = "Open"
 			elif self.docstatus == 2:
 				status = "Cancelled"
 
 		if status:
-			frappe.db.set_value("Pick List", self.name, "status", status, update_modified=update_modified)
+			self.db_set("status", status)
 
 	def update_reference_qty(self):
 		packed_items = []
diff --git a/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py b/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py
index 96ac435..9c4d997 100644
--- a/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py
+++ b/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py
@@ -376,3 +376,19 @@
 
 		accounts_settings.acc_frozen_upto = ""
 		accounts_settings.save()
+
+	def test_create_repost_entry_for_cancelled_document(self):
+		pr = make_purchase_receipt(
+			company="_Test Company with perpetual inventory",
+			warehouse="Stores - TCP1",
+			get_multiple_items=True,
+		)
+
+		self.assertTrue(pr.docstatus == 1)
+		self.assertFalse(frappe.db.exists("Repost Item Valuation", {"voucher_no": pr.name}))
+
+		pr.load_from_db()
+
+		pr.cancel()
+		self.assertTrue(pr.docstatus == 2)
+		self.assertTrue(frappe.db.exists("Repost Item Valuation", {"voucher_no": pr.name}))
diff --git a/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.js b/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.js
index 7a170be..50a78a8 100644
--- a/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.js
+++ b/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.js
@@ -33,5 +33,40 @@
 			"fieldtype": "Date",
 			"default": frappe.datetime.get_today(),
 		},
-	]
+	],
+
+	get_datatable_options(options) {
+		return Object.assign(options, {
+			checkboxColumn: true,
+		});
+	},
+
+	onload(report) {
+		report.page.add_inner_button(__("Create Reposting Entries"), function() {
+			let message = `<div>
+				<p>
+					Reposting Entries will change the value of
+					accounts Stock In Hand, and Stock Expenses
+					in the Trial Balance report and will also change
+					the Balance Value in the Stock Balance report.
+				</p>
+				<p>Are you sure you want to create Reposting Entries?</p>
+				</div>
+			`;
+
+			frappe.confirm(__(message), () => {
+				let indexes = frappe.query_report.datatable.rowmanager.getCheckedRows();
+				let selected_rows = indexes.map(i => frappe.query_report.data[i]);
+
+				frappe.call({
+					method: "erpnext.stock.report.stock_and_account_value_comparison.stock_and_account_value_comparison.create_reposting_entries",
+					args: {
+						rows: selected_rows,
+						company: frappe.query_report.get_filter_values().company
+					}
+				});
+
+			});
+		});
+	}
 };
diff --git a/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py b/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py
index 106e877..b1da3ec 100644
--- a/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py
+++ b/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py
@@ -4,6 +4,7 @@
 
 import frappe
 from frappe import _
+from frappe.utils import get_link_to_form, parse_json
 
 import erpnext
 from erpnext.accounts.utils import get_currency_precision, get_stock_accounts
@@ -134,3 +135,35 @@
 			"width": "120",
 		},
 	]
+
+
+@frappe.whitelist()
+def create_reposting_entries(rows, company):
+	if isinstance(rows, str):
+		rows = parse_json(rows)
+
+	entries = []
+	for row in rows:
+		row = frappe._dict(row)
+
+		try:
+			doc = frappe.get_doc(
+				{
+					"doctype": "Repost Item Valuation",
+					"based_on": "Transaction",
+					"status": "Queued",
+					"voucher_type": row.voucher_type,
+					"voucher_no": row.voucher_no,
+					"posting_date": row.posting_date,
+					"company": company,
+					"allow_nagative_stock": 1,
+				}
+			).submit()
+
+			entries.append(get_link_to_form("Repost Item Valuation", doc.name))
+		except frappe.DuplicateEntryError:
+			pass
+
+	if entries:
+		entries = ", ".join(entries)
+		frappe.msgprint(_(f"Reposting entries created: {entries}"))
diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py
index fb52697..10654dd 100644
--- a/erpnext/stock/utils.py
+++ b/erpnext/stock/utils.py
@@ -256,8 +256,6 @@
 	if isinstance(args, str):
 		args = json.loads(args)
 
-	voucher_no = args.get("voucher_no") or args.get("name")
-
 	in_rate = None
 	if (args.get("serial_no") or "").strip():
 		in_rate = get_avg_purchase_rate(args.get("serial_no"))
@@ -280,12 +278,13 @@
 				in_rate = (
 					_get_fifo_lifo_rate(previous_stock_queue, args.get("qty") or 0, valuation_method)
 					if previous_stock_queue
-					else 0
+					else None
 				)
 		elif valuation_method == "Moving Average":
-			in_rate = previous_sle.get("valuation_rate") or 0
+			in_rate = previous_sle.get("valuation_rate")
 
 	if in_rate is None:
+		voucher_no = args.get("voucher_no") or args.get("name")
 		in_rate = get_valuation_rate(
 			args.get("item_code"),
 			args.get("warehouse"),
diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js
index 45289b1..4bf008a 100644
--- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js
+++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js
@@ -76,26 +76,14 @@
 			}
 		});
 
-		let batch_no_field = frm.get_docfield("items", "batch_no");
+		let batch_no_field = frm.get_docfield('items', 'batch_no');
 		if (batch_no_field) {
 			batch_no_field.get_route_options_for_new_doc = function(row) {
 				return {
-					"item": row.doc.item_code
+					'item': row.doc.item_code
 				}
 			};
 		}
-
-		frappe.db.get_single_value('Buying Settings', 'backflush_raw_materials_of_subcontract_based_on').then(val => {
-			if (val == 'Material Transferred for Subcontract') {
-				frm.fields_dict['supplied_items'].grid.grid_rows.forEach((grid_row) => {
-					grid_row.docfields.forEach((df) => {
-						if (df.fieldname == 'consumed_qty') {
-							df.read_only = 0;
-						}
-					});
-				});
-			}
-		});
 	},
 
 	refresh: (frm) => {
@@ -157,6 +145,8 @@
 					}
 				});
 			}, __('Get Items From'));
+
+			frm.fields_dict.supplied_items.grid.update_docfield_property('consumed_qty', 'read_only', frm.doc.__onload && frm.doc.__onload.backflush_based_on === 'BOM');
 		}
 	},
 
diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py
index 2c84262..416f4f8 100644
--- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py
+++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py
@@ -28,6 +28,14 @@
 			},
 		]
 
+	def onload(self):
+		self.set_onload(
+			"backflush_based_on",
+			frappe.db.get_single_value(
+				"Buying Settings", "backflush_raw_materials_of_subcontract_based_on"
+			),
+		)
+
 	def update_status_updater_args(self):
 		if cint(self.is_return):
 			self.status_updater.extend(