Merge pull request #33682 from AnandBaburajan/misc_asset_fixes

fix: handle asset depr entries posting failure, improve asset depr schedules cancel notes and fix asset repair link [develop]
diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js
index b8185c9..8f5b85d 100644
--- a/erpnext/assets/doctype/asset/asset.js
+++ b/erpnext/assets/doctype/asset/asset.js
@@ -135,6 +135,10 @@
 				}, __("Manage"));
 			}
 
+			if (frm.doc.depr_entry_posting_status === "Failed") {
+				frm.trigger("set_depr_posting_failure_alert");
+			}
+
 			frm.trigger("setup_chart");
 		}
 
@@ -145,6 +149,19 @@
 		}
 	},
 
+	set_depr_posting_failure_alert: function (frm) {
+		const alert = `
+			<div class="row">
+				<div class="col-xs-12 col-sm-6">
+					<span class="indicator whitespace-nowrap red">
+						<span>Failed to post depreciation entries</span>
+					</span>
+				</div>
+			</div>`;
+
+		frm.dashboard.set_headline_alert(alert);
+	},
+
 	toggle_reference_doc: function(frm) {
 		if (frm.doc.purchase_receipt && frm.doc.purchase_invoice && frm.doc.docstatus === 1) {
 			frm.set_df_property('purchase_invoice', 'read_only', 1);
diff --git a/erpnext/assets/doctype/asset/asset.json b/erpnext/assets/doctype/asset/asset.json
index 4bac303..8a64a95 100644
--- a/erpnext/assets/doctype/asset/asset.json
+++ b/erpnext/assets/doctype/asset/asset.json
@@ -68,6 +68,7 @@
   "column_break_51",
   "purchase_receipt_amount",
   "default_finance_book",
+  "depr_entry_posting_status",
   "amended_from"
  ],
  "fields": [
@@ -473,6 +474,16 @@
    "fieldtype": "Int",
    "label": "Asset Quantity",
    "read_only_depends_on": "eval:!doc.is_existing_asset"
+  },
+  {
+   "fieldname": "depr_entry_posting_status",
+   "fieldtype": "Select",
+   "hidden": 1,
+   "label": "Depreciation Entry Posting Status",
+   "no_copy": 1,
+   "options": "\nSuccessful\nFailed",
+   "print_hide": 1,
+   "read_only": 1
   }
  ],
  "idx": 72,
@@ -487,7 +498,7 @@
   {
    "group": "Repair",
    "link_doctype": "Asset Repair",
-   "link_fieldname": "asset_name"
+   "link_fieldname": "asset"
   },
   {
    "group": "Value",
@@ -500,7 +511,7 @@
    "link_fieldname": "asset"
   }
  ],
- "modified": "2022-11-25 12:47:19.689702",
+ "modified": "2023-01-17 00:25:30.387242",
  "modified_by": "Administrator",
  "module": "Assets",
  "name": "Asset",
diff --git a/erpnext/assets/doctype/asset/depreciation.py b/erpnext/assets/doctype/asset/depreciation.py
index 7686c34..5337fd6 100644
--- a/erpnext/assets/doctype/asset/depreciation.py
+++ b/erpnext/assets/doctype/asset/depreciation.py
@@ -5,6 +5,7 @@
 import frappe
 from frappe import _
 from frappe.utils import add_months, cint, flt, get_link_to_form, getdate, nowdate, today
+from frappe.utils.user import get_users_with_role
 
 from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
 	get_checks_for_pl_and_bs_accounts,
@@ -18,7 +19,7 @@
 )
 
 
-def post_depreciation_entries(date=None, commit=True):
+def post_depreciation_entries(date=None):
 	# Return if automatic booking of asset depreciation is disabled
 	if not cint(
 		frappe.db.get_value("Accounts Settings", None, "book_asset_depreciation_entry_automatically")
@@ -27,13 +28,24 @@
 
 	if not date:
 		date = today()
+
+	failed_asset_names = []
+
 	for asset_name in get_depreciable_assets(date):
 		asset_doc = frappe.get_doc("Asset", asset_name)
 
-		make_depreciation_entry_for_all_asset_depr_schedules(asset_doc, date)
-
-		if commit:
+		try:
+			make_depreciation_entry_for_all_asset_depr_schedules(asset_doc, date)
 			frappe.db.commit()
+		except Exception as e:
+			frappe.db.rollback()
+			failed_asset_names.append(asset_name)
+
+	if failed_asset_names:
+		set_depr_entry_posting_status_for_failed_assets(failed_asset_names)
+		notify_depr_entry_posting_error(failed_asset_names)
+
+	frappe.db.commit()
 
 
 def get_depreciable_assets(date):
@@ -146,6 +158,8 @@
 			row.value_after_depreciation -= d.depreciation_amount
 			row.db_update()
 
+	frappe.db.set_value("Asset", asset_name, "depr_entry_posting_status", "Successful")
+
 	asset.set_status()
 
 	return asset_depr_schedule_doc
@@ -209,6 +223,42 @@
 	return credit_account, debit_account
 
 
+def set_depr_entry_posting_status_for_failed_assets(failed_asset_names):
+	for asset_name in failed_asset_names:
+		frappe.db.set_value("Asset", asset_name, "depr_entry_posting_status", "Failed")
+
+
+def notify_depr_entry_posting_error(failed_asset_names):
+	recipients = get_users_with_role("Accounts Manager")
+
+	if not recipients:
+		recipients = get_users_with_role("System Manager")
+
+	subject = _("Error while posting depreciation entries")
+
+	asset_links = get_comma_separated_asset_links(failed_asset_names)
+
+	message = (
+		_("Hi,")
+		+ "<br>"
+		+ _("The following assets have failed to post depreciation entries: {0}").format(asset_links)
+		+ "."
+	)
+
+	frappe.sendmail(recipients=recipients, subject=subject, message=message)
+
+
+def get_comma_separated_asset_links(asset_names):
+	asset_links = []
+
+	for asset_name in asset_names:
+		asset_links.append(get_link_to_form("Asset", asset_name))
+
+	asset_links = ", ".join(asset_links)
+
+	return asset_links
+
+
 @frappe.whitelist()
 def scrap_asset(asset_name):
 	asset = frappe.get_doc("Asset", asset_name)
@@ -295,12 +345,12 @@
 		asset_doc, notes, date_of_return=date
 	)
 
-	modify_depreciation_schedule_for_asset_repairs(asset_doc)
+	modify_depreciation_schedule_for_asset_repairs(asset_doc, notes)
 
 	asset_doc.save()
 
 
-def modify_depreciation_schedule_for_asset_repairs(asset):
+def modify_depreciation_schedule_for_asset_repairs(asset, notes):
 	asset_repairs = frappe.get_all(
 		"Asset Repair", filters={"asset": asset.name}, fields=["name", "increase_in_asset_life"]
 	)
@@ -309,10 +359,6 @@
 		if repair.increase_in_asset_life:
 			asset_repair = frappe.get_doc("Asset Repair", repair.name)
 			asset_repair.modify_depreciation_schedule()
-			notes = _("This schedule was created when Asset {0} went through Asset Repair {1}.").format(
-				get_link_to_form(asset.doctype, asset.name),
-				get_link_to_form(asset_repair.doctype, asset_repair.name),
-			)
 			make_new_active_asset_depr_schedules_and_cancel_current_ones(asset, notes)
 
 
diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py
index d61ef8e..51a2b52 100644
--- a/erpnext/assets/doctype/asset/test_asset.py
+++ b/erpnext/assets/doctype/asset/test_asset.py
@@ -1549,6 +1549,7 @@
 			"asset_owner": args.asset_owner or "Company",
 			"is_existing_asset": args.is_existing_asset or 1,
 			"asset_quantity": args.get("asset_quantity") or 1,
+			"depr_entry_posting_status": args.depr_entry_posting_status or "",
 		}
 	)
 
diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py
index 62a3483..821accf 100644
--- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py
+++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py
@@ -430,7 +430,7 @@
 
 			if asset.calculate_depreciation:
 				notes = _(
-					"This schedule was created when Asset {0} was consumed when Asset Capitalization {1} was submitted."
+					"This schedule was created when Asset {0} was consumed through Asset Capitalization {1}."
 				).format(
 					get_link_to_form(asset.doctype, asset.name), get_link_to_form(self.doctype, self.get("name"))
 				)
@@ -521,7 +521,7 @@
 			asset_doc.gross_purchase_amount = total_target_asset_value
 			asset_doc.purchase_receipt_amount = total_target_asset_value
 			notes = _(
-				"This schedule was created when target Asset {0} was updated when Asset Capitalization {1} was submitted."
+				"This schedule was created when target Asset {0} was updated through Asset Capitalization {1}."
 			).format(
 				get_link_to_form(asset_doc.doctype, asset_doc.name), get_link_to_form(self.doctype, self.name)
 			)
@@ -537,7 +537,7 @@
 				if asset.calculate_depreciation:
 					reverse_depreciation_entry_made_after_disposal(asset, self.posting_date)
 					notes = _(
-						"This schedule was created when Asset {0} was restored when Asset Capitalization {1} was cancelled."
+						"This schedule was created when Asset {0} was restored on Asset Capitalization {1}'s cancellation."
 					).format(
 						get_link_to_form(asset.doctype, asset.name), get_link_to_form(self.doctype, self.name)
 					)
diff --git a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.json b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.json
index af09cda..898c482 100644
--- a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.json
+++ b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.json
@@ -159,7 +159,7 @@
  "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2023-01-02 15:38:30.766779",
+ "modified": "2023-01-16 21:08:21.421260",
  "modified_by": "Administrator",
  "module": "Assets",
  "name": "Asset Depreciation Schedule",
diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.py b/erpnext/assets/doctype/asset_repair/asset_repair.py
index b8cd115..9a05a74 100644
--- a/erpnext/assets/doctype/asset_repair/asset_repair.py
+++ b/erpnext/assets/doctype/asset_repair/asset_repair.py
@@ -56,8 +56,11 @@
 			):
 				self.modify_depreciation_schedule()
 
-		notes = _("This schedule was created when Asset Repair {0} was submitted.").format(
-			get_link_to_form(self.doctype, self.name)
+		notes = _(
+			"This schedule was created when Asset {0} was repaired through Asset Repair {1}."
+		).format(
+			get_link_to_form(self.asset_doc.doctype, self.asset_doc.name),
+			get_link_to_form(self.doctype, self.name),
 		)
 		self.asset_doc.flags.ignore_validate_update_after_submit = True
 		make_new_active_asset_depr_schedules_and_cancel_current_ones(self.asset_doc, notes)
@@ -80,8 +83,9 @@
 			):
 				self.revert_depreciation_schedule_on_cancellation()
 
-		notes = _("This schedule was created when Asset Repair {0} was cancelled.").format(
-			get_link_to_form(self.doctype, self.name)
+		notes = _("This schedule was created when Asset {0}'s Asset Repair {1} was cancelled.").format(
+			get_link_to_form(self.asset_doc.doctype, self.asset_doc.name),
+			get_link_to_form(self.doctype, self.name),
 		)
 		self.asset_doc.flags.ignore_validate_update_after_submit = True
 		make_new_active_asset_depr_schedules_and_cancel_current_ones(self.asset_doc, notes)
diff --git a/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py b/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py
index 262d552..6cfbe53 100644
--- a/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py
+++ b/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py
@@ -127,12 +127,20 @@
 			current_asset_depr_schedule_doc.flags.should_not_cancel_depreciation_entries = True
 			current_asset_depr_schedule_doc.cancel()
 
-			notes = _(
-				"This schedule was created when Asset {0} was adjusted through Asset Value Adjustment {1}."
-			).format(
-				get_link_to_form(asset.doctype, asset.name),
-				get_link_to_form(self.get("doctype"), self.get("name")),
-			)
+			if self.docstatus == 1:
+				notes = _(
+					"This schedule was created when Asset {0} was adjusted through Asset Value Adjustment {1}."
+				).format(
+					get_link_to_form(asset.doctype, asset.name),
+					get_link_to_form(self.get("doctype"), self.get("name")),
+				)
+			elif self.docstatus == 2:
+				notes = _(
+					"This schedule was created when Asset {0}'s Asset Value Adjustment {1} was cancelled."
+				).format(
+					get_link_to_form(asset.doctype, asset.name),
+					get_link_to_form(self.get("doctype"), self.get("name")),
+				)
 			new_asset_depr_schedule_doc.notes = notes
 
 			new_asset_depr_schedule_doc.insert()