fix(UX): record reason for skipping attendance or marking absent for auto attendance (#30844)

diff --git a/erpnext/hr/doctype/employee_checkin/employee_checkin.py b/erpnext/hr/doctype/employee_checkin/employee_checkin.py
index 64eb019..e07b5e5 100644
--- a/erpnext/hr/doctype/employee_checkin/employee_checkin.py
+++ b/erpnext/hr/doctype/employee_checkin/employee_checkin.py
@@ -5,7 +5,7 @@
 import frappe
 from frappe import _
 from frappe.model.document import Document
-from frappe.utils import cint, get_datetime
+from frappe.utils import cint, get_datetime, get_link_to_form
 
 from erpnext.hr.doctype.attendance.attendance import (
 	get_duplicate_attendance_record,
@@ -130,14 +130,11 @@
 	"""
 	log_names = [x.name for x in logs]
 	employee = logs[0].employee
+
 	if attendance_status == "Skip":
-		frappe.db.sql(
-			"""update `tabEmployee Checkin`
-			set skip_auto_attendance = %s
-			where name in %s""",
-			("1", log_names),
-		)
+		skip_attendance_in_checkins(log_names)
 		return None
+
 	elif attendance_status in ("Present", "Absent", "Half Day"):
 		employee_doc = frappe.get_doc("Employee", employee)
 		duplicate = get_duplicate_attendance_record(employee, attendance_date, shift)
@@ -159,6 +156,12 @@
 			}
 			attendance = frappe.get_doc(doc_dict).insert()
 			attendance.submit()
+
+			if attendance_status == "Absent":
+				attendance.add_comment(
+					text=_("Employee was marked Absent for not meeting the working hours threshold.")
+				)
+
 			frappe.db.sql(
 				"""update `tabEmployee Checkin`
 				set attendance = %s
@@ -167,13 +170,10 @@
 			)
 			return attendance
 		else:
-			frappe.db.sql(
-				"""update `tabEmployee Checkin`
-				set skip_auto_attendance = %s
-				where name in %s""",
-				("1", log_names),
-			)
+			skip_attendance_in_checkins(log_names)
+			add_comment_in_checkins(log_names, duplicate, overlapping)
 			return None
+
 	else:
 		frappe.throw(_("{} is an invalid Attendance Status.").format(attendance_status))
 
@@ -241,3 +241,34 @@
 
 def find_index_in_dict(dict_list, key, value):
 	return next((index for (index, d) in enumerate(dict_list) if d[key] == value), None)
+
+
+def add_comment_in_checkins(log_names, duplicate, overlapping):
+	if duplicate:
+		text = _("Auto Attendance skipped due to duplicate attendance record: {}").format(
+			get_link_to_form("Attendance", duplicate[0].name)
+		)
+	else:
+		text = _("Auto Attendance skipped due to overlapping attendance record: {}").format(
+			get_link_to_form("Attendance", overlapping.name)
+		)
+
+	for name in log_names:
+		frappe.get_doc(
+			{
+				"doctype": "Comment",
+				"comment_type": "Comment",
+				"reference_doctype": "Employee Checkin",
+				"reference_name": name,
+				"content": text,
+			}
+		).insert(ignore_permissions=True)
+
+
+def skip_attendance_in_checkins(log_names):
+	EmployeeCheckin = frappe.qb.DocType("Employee Checkin")
+	(
+		frappe.qb.update(EmployeeCheckin)
+		.set("skip_auto_attendance", 1)
+		.where(EmployeeCheckin.name.isin(log_names))
+	).run()
diff --git a/erpnext/hr/doctype/shift_type/shift_type.py b/erpnext/hr/doctype/shift_type/shift_type.py
index 5e214cf..a61bb9e 100644
--- a/erpnext/hr/doctype/shift_type/shift_type.py
+++ b/erpnext/hr/doctype/shift_type/shift_type.py
@@ -134,7 +134,17 @@
 			shift_details = get_employee_shift(employee, timestamp, True)
 
 			if shift_details and shift_details.shift_type.name == self.name:
-				mark_attendance(employee, date, "Absent", self.name)
+				attendance = mark_attendance(employee, date, "Absent", self.name)
+				if attendance:
+					frappe.get_doc(
+						{
+							"doctype": "Comment",
+							"comment_type": "Comment",
+							"reference_doctype": "Attendance",
+							"reference_name": attendance,
+							"content": frappe._("Employee was marked Absent due to missing Employee Checkins."),
+						}
+					).insert(ignore_permissions=True)
 
 	def get_start_and_end_dates(self, employee):
 		"""Returns start and end dates for checking attendance and marking absent