diff --git a/frappe/core/doctype/server_script/server_script.json b/frappe/core/doctype/server_script/server_script.json index 3aedd4f542..67cb6e75ea 100644 --- a/frappe/core/doctype/server_script/server_script.json +++ b/frappe/core/doctype/server_script/server_script.json @@ -9,6 +9,7 @@ "script_type", "reference_doctype", "event_frequency", + "cron_format", "doctype_event", "api_method", "allow_guest", @@ -99,7 +100,7 @@ "fieldtype": "Select", "label": "Event Frequency", "mandatory_depends_on": "eval:doc.script_type == \"Scheduler Event\"", - "options": "All\nHourly\nDaily\nWeekly\nMonthly\nYearly\nHourly Long\nDaily Long\nWeekly Long\nMonthly Long" + "options": "All\nHourly\nDaily\nWeekly\nMonthly\nYearly\nHourly Long\nDaily Long\nWeekly Long\nMonthly Long\nCron" }, { "fieldname": "module", @@ -132,6 +133,12 @@ "fieldname": "rate_limit_seconds", "fieldtype": "Int", "label": "Time Window (Seconds)" + }, + { + "depends_on": "eval:doc.event_frequency==='Cron'", + "fieldname": "cron_format", + "fieldtype": "Data", + "label": "Cron Format" } ], "index_web_pages_for_search": 1, @@ -141,7 +148,7 @@ "link_fieldname": "server_script" } ], - "modified": "2023-05-16 11:03:58.282680", + "modified": "2023-05-27 16:33:16.595424", "modified_by": "Administrator", "module": "Core", "name": "Server Script", diff --git a/frappe/core/doctype/server_script/server_script.py b/frappe/core/doctype/server_script/server_script.py index a9b870e240..07808d619b 100644 --- a/frappe/core/doctype/server_script/server_script.py +++ b/frappe/core/doctype/server_script/server_script.py @@ -52,11 +52,16 @@ class ServerScript(Document): def sync_scheduler_events(self): """Create or update Scheduled Job Type documents for Scheduler Event Server Scripts""" if not self.disabled and self.event_frequency and self.script_type == "Scheduler Event": - setup_scheduler_events(script_name=self.name, frequency=self.event_frequency) + cron_format = self.cron_format if self.event_frequency == "Cron" else None + setup_scheduler_events( + script_name=self.name, frequency=self.event_frequency, cron_format=cron_format + ) def clear_scheduled_events(self): - """Deletes existing scheduled jobs by Server Script if self.event_frequency has changed""" - if self.script_type == "Scheduler Event" and self.has_value_changed("event_frequency"): + """Deletes existing scheduled jobs by Server Script if self.event_frequency or self.cron_format has changed""" + if self.script_type == "Scheduler Event" and ( + self.has_value_changed("event_frequency") or self.has_value_changed("cron_format") + ): for scheduled_job in self.scheduled_jobs: frappe.delete_doc("Scheduled Job Type", scheduled_job.name) @@ -171,7 +176,7 @@ class ServerScript(Document): return items -def setup_scheduler_events(script_name, frequency): +def setup_scheduler_events(script_name: str, frequency: str, cron_format: str | None = None): """Creates or Updates Scheduled Job Type documents based on the specified script name and frequency Args: @@ -188,6 +193,7 @@ def setup_scheduler_events(script_name, frequency): "method": method, "frequency": frequency, "server_script": script_name, + "cron_format": cron_format, } ).insert() @@ -200,6 +206,7 @@ def setup_scheduler_events(script_name, frequency): return doc.frequency = frequency + doc.cron_format = cron_format doc.save() frappe.msgprint(_("Scheduled execution for script {0} has updated").format(script_name)) diff --git a/frappe/core/doctype/server_script/test_server_script.py b/frappe/core/doctype/server_script/test_server_script.py index 3d11a02ca4..4371806b32 100644 --- a/frappe/core/doctype/server_script/test_server_script.py +++ b/frappe/core/doctype/server_script/test_server_script.py @@ -3,6 +3,7 @@ import requests import frappe +from frappe.core.doctype.scheduled_job_type.scheduled_job_type import sync_jobs from frappe.frappeclient import FrappeClient, FrappeException from frappe.tests.utils import FrappeTestCase from frappe.utils import get_site_url @@ -283,3 +284,37 @@ frappe.qb.from_(todo).select(todo.name).where(todo.name == "{todo.name}").run() script1.delete() script2.delete() frappe.db.commit() + + def test_server_script_scheduled(self): + scheduled_script = frappe.get_doc( + doctype="Server Script", + name="scheduled_script_wo_cron", + script_type="Scheduler Event", + script="""frappe.flags = {"test": True}""", + event_frequency="Hourly", + ).insert() + + cron_script = frappe.get_doc( + doctype="Server Script", + name="scheduled_script_w_cron", + script_type="Scheduler Event", + script="""frappe.flags = {"test": True}""", + event_frequency="Cron", + cron_format="0 0 1 1 *", # 1st january + ).insert() + + # Ensure that jobs remain in DB after migrate + sync_jobs() + self.assertTrue(frappe.db.exists("Scheduled Job Type", {"server_script": scheduled_script.name})) + + cron_job_name = frappe.db.get_value("Scheduled Job Type", {"server_script": cron_script.name}) + self.assertTrue(cron_job_name) + + cron_job = frappe.get_doc("Scheduled Job Type", cron_job_name) + self.assertEqual(cron_job.next_execution.day, 1) + self.assertEqual(cron_job.next_execution.month, 1) + + cron_script.cron_format = "0 0 2 1 *" # 2nd january + cron_script.save() + cron_job.reload() + self.assertEqual(cron_job.next_execution.day, 2)