Merge pull request #29362 from nextchamp-saqib/fix-cancel-jv

fix: cannot cancel jv after migrating from v12
diff --git a/erpnext/projects/doctype/timesheet/test_timesheet.py b/erpnext/projects/doctype/timesheet/test_timesheet.py
index 148d8ba..989bcd1 100644
--- a/erpnext/projects/doctype/timesheet/test_timesheet.py
+++ b/erpnext/projects/doctype/timesheet/test_timesheet.py
@@ -5,7 +5,7 @@
 import unittest
 
 import frappe
-from frappe.utils import add_months, now_datetime, nowdate
+from frappe.utils import add_months, add_to_date, now_datetime, nowdate
 
 from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
 from erpnext.hr.doctype.employee.test_employee import make_employee
@@ -151,6 +151,27 @@
 		settings.ignore_employee_time_overlap = initial_setting
 		settings.save()
 
+	def test_to_time(self):
+		emp = make_employee("test_employee_6@salary.com")
+		from_time = now_datetime()
+
+		timesheet = frappe.new_doc("Timesheet")
+		timesheet.employee = emp
+		timesheet.append(
+			'time_logs',
+			{
+				"billable": 1,
+				"activity_type": "_Test Activity Type",
+				"from_time": from_time,
+				"hours": 2,
+				"company": "_Test Company"
+			}
+		)
+		timesheet.save()
+
+		to_time = timesheet.time_logs[0].to_time
+		self.assertEqual(to_time, add_to_date(from_time, hours=2, as_datetime=True))
+
 
 def make_salary_structure_for_timesheet(employee, company=None):
 	salary_structure_name = "Timesheet Salary Structure Test"
diff --git a/erpnext/projects/doctype/timesheet/timesheet.py b/erpnext/projects/doctype/timesheet/timesheet.py
index e92785e..dd0b5f9 100644
--- a/erpnext/projects/doctype/timesheet/timesheet.py
+++ b/erpnext/projects/doctype/timesheet/timesheet.py
@@ -7,7 +7,7 @@
 import frappe
 from frappe import _
 from frappe.model.document import Document
-from frappe.utils import flt, getdate, time_diff_in_hours
+from frappe.utils import add_to_date, flt, getdate, time_diff_in_hours
 
 from erpnext.controllers.queries import get_match_cond
 from erpnext.hr.utils import validate_active_employee
@@ -136,10 +136,19 @@
 
 	def validate_time_logs(self):
 		for data in self.get('time_logs'):
+			self.set_to_time(data)
 			self.validate_overlap(data)
 			self.set_project(data)
 			self.validate_project(data)
 
+	def set_to_time(self, data):
+		if not (data.from_time and data.hours):
+			return
+
+		_to_time = add_to_date(data.from_time, hours=data.hours, as_datetime=True)
+		if data.to_time != _to_time:
+			data.to_time = _to_time
+
 	def validate_overlap(self, data):
 		settings = frappe.get_single('Projects Settings')
 		self.validate_overlap_for("user", data, self.user, settings.ignore_user_time_overlap)