fix(Task): Do not create/schedule task after project end (#19184)
* fix: do not create/schedule task after project end
* fix: check difference between dates
* fix: check project date
* fix: task creation
* fix: tests
diff --git a/erpnext/agriculture/doctype/crop_cycle/crop_cycle.py b/erpnext/agriculture/doctype/crop_cycle/crop_cycle.py
index bb9045c..3e51933 100644
--- a/erpnext/agriculture/doctype/crop_cycle/crop_cycle.py
+++ b/erpnext/agriculture/doctype/crop_cycle/crop_cycle.py
@@ -51,27 +51,25 @@
self.create_task(disease_doc.treatment_task, self.name, start_date)
def create_project(self, period, crop_tasks):
- project = frappe.new_doc("Project")
- project.update({
+ project = frappe.get_doc({
+ "doctype": "Project",
"project_name": self.title,
"expected_start_date": self.start_date,
"expected_end_date": add_days(self.start_date, period - 1)
- })
- project.insert()
+ }).insert()
return project.name
def create_task(self, crop_tasks, project_name, start_date):
for crop_task in crop_tasks:
- task = frappe.new_doc("Task")
- task.update({
+ frappe.get_doc({
+ "doctype": "Task",
"subject": crop_task.get("task_name"),
"priority": crop_task.get("priority"),
"project": project_name,
"exp_start_date": add_days(start_date, crop_task.get("start_day") - 1),
"exp_end_date": add_days(start_date, crop_task.get("end_day") - 1)
- })
- task.insert()
+ }).insert()
def reload_linked_analysis(self):
linked_doctypes = ['Soil Texture', 'Soil Analysis', 'Plant Analysis']
diff --git a/erpnext/projects/doctype/task/task.py b/erpnext/projects/doctype/task/task.py
index 90e9f05..54fce8d 100755
--- a/erpnext/projects/doctype/task/task.py
+++ b/erpnext/projects/doctype/task/task.py
@@ -10,6 +10,7 @@
from frappe.utils import add_days, cstr, date_diff, get_link_to_form, getdate
from frappe.utils.nestedset import NestedSet
from frappe.desk.form.assign_to import close_all_assignments, clear
+from frappe.utils import date_diff
class CircularReferenceError(frappe.ValidationError): pass
class EndDateCannotBeGreaterThanProjectEndDateError(frappe.ValidationError): pass
@@ -28,16 +29,29 @@
def validate(self):
self.validate_dates()
+ self.validate_parent_project_dates()
self.validate_progress()
self.validate_status()
self.update_depends_on()
def validate_dates(self):
if self.exp_start_date and self.exp_end_date and getdate(self.exp_start_date) > getdate(self.exp_end_date):
- frappe.throw(_("'Expected Start Date' can not be greater than 'Expected End Date'"))
+ frappe.throw(_("{0} can not be greater than {1}").format(frappe.bold("Expected Start Date"), \
+ frappe.bold("Expected End Date")))
if self.act_start_date and self.act_end_date and getdate(self.act_start_date) > getdate(self.act_end_date):
- frappe.throw(_("'Actual Start Date' can not be greater than 'Actual End Date'"))
+ frappe.throw(_("{0} can not be greater than {1}").format(frappe.bold("Actual Start Date"), \
+ frappe.bold("Actual End Date")))
+
+ def validate_parent_project_dates(self):
+ if not self.project or frappe.flags.in_test:
+ return
+
+ expected_end_date = getdate(frappe.db.get_value("Project", self.project, "expected_end_date"))
+
+ if expected_end_date:
+ validate_project_dates(expected_end_date, self, "exp_start_date", "exp_end_date", "Expected")
+ validate_project_dates(expected_end_date, self, "act_start_date", "act_end_date", "Actual")
def validate_status(self):
if self.status!=self.get_db_value("status") and self.status == "Completed":
@@ -255,3 +269,10 @@
def on_doctype_update():
frappe.db.add_index("Task", ["lft", "rgt"])
+
+def validate_project_dates(project_end_date, task, task_start, task_end, actual_or_expected_date):
+ if task.get(task_start) and date_diff(project_end_date, getdate(task.get(task_start))) < 0:
+ frappe.throw(_("Task's {0} Start Date cannot be after Project's End Date.").format(actual_or_expected_date))
+
+ if task.get(task_end) and date_diff(project_end_date, getdate(task.get(task_end))) < 0:
+ frappe.throw(_("Task's {0} End Date cannot be after Project's End Date.").format(actual_or_expected_date))
\ No newline at end of file