diff --git a/frappe/core/doctype/doctype/doctype.json b/frappe/core/doctype/doctype/doctype.json index 14ef2fd8fb..671a6e86e6 100644 --- a/frappe/core/doctype/doctype/doctype.json +++ b/frappe/core/doctype/doctype/doctype.json @@ -604,6 +604,7 @@ { "default": "0", "depends_on": "eval: doc.is_submittable", + "description": "Enabling this will submit documents in background", "fieldname": "queue_in_background", "fieldtype": "Check", "label": "Queue in Background" @@ -707,7 +708,7 @@ "link_fieldname": "reference_doctype" } ], - "modified": "2022-12-14 09:47:27.315351", + "modified": "2023-01-04 17:23:09.206018", "modified_by": "Administrator", "module": "Core", "name": "DocType", @@ -744,4 +745,4 @@ "states": [], "track_changes": 1, "translated_doctype": 1 -} +} \ No newline at end of file diff --git a/frappe/core/doctype/submission_queue/submission_queue.js b/frappe/core/doctype/submission_queue/submission_queue.js index 93d6b981dc..6e64be780a 100644 --- a/frappe/core/doctype/submission_queue/submission_queue.js +++ b/frappe/core/doctype/submission_queue/submission_queue.js @@ -3,11 +3,17 @@ frappe.ui.form.on("Submission Queue", { refresh: function (frm) { - if (frm.doc.status === "Queued" && frm.doc.job_id) { + if (frm.doc.status === "Queued" && frappe.boot.user.roles.includes("System Manager")) { frm.add_custom_button(__("Unlock Reference Document"), () => { - frappe.confirm(__("Are you sure you want to go ahead with this action?"), () => { - frm.call("unlock_doc"); - }); + frappe.confirm( + ` + Are you sure you want to go ahead with this action? + Doing this could unlock other submissions of this document which are in queue (if present) + and could lead to non-ideal conditions.`, + () => { + frm.call("unlock_doc"); + } + ); }); } }, diff --git a/frappe/core/doctype/submission_queue/submission_queue.json b/frappe/core/doctype/submission_queue/submission_queue.json index d1f66ffa13..04668e1c76 100644 --- a/frappe/core/doctype/submission_queue/submission_queue.json +++ b/frappe/core/doctype/submission_queue/submission_queue.json @@ -20,8 +20,9 @@ "fields": [ { "fieldname": "job_id", - "fieldtype": "Data", + "fieldtype": "Link", "label": "Job Id", + "options": "RQ Job", "read_only": 1 }, { @@ -80,14 +81,14 @@ }, { "fieldname": "exception", - "fieldtype": "Text", + "fieldtype": "Long Text", "label": "Exception", "read_only": 1 } ], "index_web_pages_for_search": 1, "links": [], - "modified": "2022-11-12 16:48:37.797232", + "modified": "2023-01-23 12:45:53.997708", "modified_by": "Administrator", "module": "Core", "name": "Submission Queue", @@ -102,6 +103,11 @@ "report": 1, "role": "System Manager", "share": 1 + }, + { + "if_owner": 1, + "read": 1, + "role": "All" } ], "sort_field": "modified", diff --git a/frappe/core/doctype/submission_queue/submission_queue.py b/frappe/core/doctype/submission_queue/submission_queue.py index 2bb4200a87..be0c20fc32 100644 --- a/frappe/core/doctype/submission_queue/submission_queue.py +++ b/frappe/core/doctype/submission_queue/submission_queue.py @@ -4,8 +4,6 @@ from urllib.parse import quote from rq import get_current_job -from rq.exceptions import NoSuchJobError -from rq.job import Job import frappe from frappe import _ @@ -13,7 +11,6 @@ from frappe.desk.doctype.notification_log.notification_log import enqueue_create from frappe.model.document import Document from frappe.monitor import add_data_to_monitor from frappe.utils import now, time_diff_in_seconds -from frappe.utils.background_jobs import get_redis_conn from frappe.utils.data import cint @@ -39,6 +36,7 @@ class SubmissionQueue(Document): frappe.db.delete(table, filters=(table.modified < (Now() - Interval(days=days)))) def insert(self, to_be_queued_doc: Document, action: str): + self.status = "Queued" self.to_be_queued_doc = to_be_queued_doc self.action_for_queuing = action super().insert(ignore_permissions=True) @@ -70,6 +68,7 @@ class SubmissionQueue(Document): def background_submission(self, to_be_queued_doc: Document, action_for_queuing: str): # Set the job id for that submission doctype self.update_job_id(get_current_job().id) + _action = action_for_queuing.lower() if _action == "update": _action = "submit" @@ -85,7 +84,7 @@ class SubmissionQueue(Document): ) values = {"status": "Finished"} except Exception: - values = {"status": "Failed", "exception": frappe.get_traceback()} + values = {"status": "Failed", "exception": frappe.get_traceback(with_context=True)} frappe.db.rollback() values["ended_at"] = now() @@ -96,22 +95,27 @@ class SubmissionQueue(Document): if submission_status == "Failed": doctype = self.doctype docname = self.name - message = _("Submission of {0} {1} with action {2} failed") + message = _("Action {0} failed on {1} {2}. View it {3}") else: doctype = self.ref_doctype docname = self.ref_docname - message = _("Submission of {0} {1} with action {2} completed successfully") + message = _("Action {0} completed successfully on {1} {2}. View it {3}") - message = message.format( - frappe.bold(str(self.ref_doctype)), frappe.bold(self.ref_docname), frappe.bold(action) + message_replacements = ( + frappe.bold(action), + frappe.bold(str(self.ref_doctype)), + frappe.bold(str(self.ref_docname)), ) + time_diff = time_diff_in_seconds(now(), self.created_at) if cint(time_diff) <= 60: frappe.publish_realtime( "msgprint", { - "message": message - + f". View it here", + "message": message.format( + *message_replacements, + f"here", + ), "alert": True, "indicator": "red" if submission_status == "Failed" else "green", }, @@ -122,50 +126,27 @@ class SubmissionQueue(Document): "type": "Alert", "document_type": doctype, "document_name": docname, - "subject": message, + "subject": message.format(*message_replacements, "here"), } notify_to = frappe.db.get_value("User", self.enqueued_by, fieldname="email") enqueue_create_notification([notify_to], notification_doc) - def _unlock_reference_doc(self): - """ - Only execute if self.job_id is defined. - """ - try: - job = Job.fetch(self.job_id, connection=get_redis_conn()) - status = job.get_status(refresh=True) - exc = job.exc_info - except NoSuchJobError: - exc = None - status = "failed" - - if status in ("queued", "started"): - frappe.msgprint(_("Document in queue for execution!")) - return - - self.queued_doc.unlock() - values = ( - {"status": "Finished"} if status == "finished" else {"status": "Failed", "exception": exc} - ) - frappe.db.set_value(self.doctype, self.name, values, update_modified=False) - frappe.msgprint(_("Document Unlocked")) - @frappe.whitelist() def unlock_doc(self): # NOTE: this can lead to some weird unlocking/locking behaviours. # for example: hitting unlock on a submission could lead to unlocking of another submission # of the same reference document. - if self.status != "Queued" and not self.job_id: + if self.status != "Queued": return - self._unlock_reference_doc() + self.queued_doc.unlock() + frappe.msgprint(_("Document Unlocked")) def queue_submission(doc: Document, action: str, alert: bool = True): queue = frappe.new_doc("Submission Queue") - queue.state = "Queued" queue.ref_doctype = doc.doctype queue.ref_docname = doc.name queue.insert(doc, action) @@ -185,9 +166,25 @@ def get_latest_submissions(doctype, docname): # NOTE: not used creation as orderby intentianlly as we have used update_modified=False everywhere # hence assuming modified will be equal to creation for submission queue documents - dt = "Submission Queue" - filters = {"ref_doctype": doctype, "ref_docname": docname} - return { - "latest_submission": frappe.db.get_value(dt, filters), - "latest_failed_submission": frappe.db.get_value(dt, filters | {"status": "Failed"}), - } + latest_submission = frappe.db.get_value( + "Submission Queue", + filters={"ref_doctype": doctype, "ref_docname": docname}, + fieldname=["name", "exception", "status"], + ) + + out = None + if latest_submission: + out = { + "latest_submission": latest_submission[0], + "exc": format_tb(latest_submission[1]), + "status": latest_submission[2], + } + + return out + + +def format_tb(traceback: str | None = None): + if not traceback: + return + + return traceback.strip().split("\n")[-1] diff --git a/frappe/public/js/frappe/form/form.js b/frappe/public/js/frappe/form/form.js index 48384fbb9b..8893c4b69e 100644 --- a/frappe/public/js/frappe/form/form.js +++ b/frappe/public/js/frappe/form/form.js @@ -2047,16 +2047,12 @@ frappe.ui.form.Form = class FrappeForm { this.doc.docstatus === 0 ) ) { - if (wrapper.length) { - wrapper.hide(); - wrapper.html(""); - } - + wrapper.length && wrapper.remove(); return; } if (!wrapper.length) { - wrapper = $('