Merge pull request #25971 from rutwikhdev/discard-transactions
feat: Discard transactions
This commit is contained in:
commit
ff31290d33
8 changed files with 140 additions and 9 deletions
|
|
@ -57,7 +57,7 @@
|
|||
"fieldname": "doctype_event",
|
||||
"fieldtype": "Select",
|
||||
"label": "DocType Event",
|
||||
"options": "Before Insert\nBefore Validate\nBefore Save\nAfter Insert\nAfter Save\nBefore Rename\nAfter Rename\nBefore Submit\nAfter Submit\nBefore Cancel\nAfter Cancel\nBefore Delete\nAfter Delete\nBefore Save (Submitted Document)\nAfter Save (Submitted Document)\nBefore Print\nOn Payment Authorization\nOn Payment Paid\nOn Payment Failed"
|
||||
"options": "Before Insert\nBefore Validate\nBefore Save\nAfter Insert\nAfter Save\nBefore Rename\nAfter Rename\nBefore Submit\nAfter Submit\nBefore Cancel\nAfter Cancel\nBefore Discard\nAfter Discard\nBefore Delete\nAfter Delete\nBefore Save (Submitted Document)\nAfter Save (Submitted Document)\nBefore Print\nOn Payment Authorization\nOn Payment Paid\nOn Payment Failed"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.script_type==='API'",
|
||||
|
|
@ -151,7 +151,7 @@
|
|||
"link_fieldname": "server_script"
|
||||
}
|
||||
],
|
||||
"modified": "2024-04-08 16:18:52.901097",
|
||||
"modified": "2024-04-15 20:12:41.971315",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "Server Script",
|
||||
|
|
|
|||
|
|
@ -42,6 +42,8 @@ class ServerScript(Document):
|
|||
"After Submit",
|
||||
"Before Cancel",
|
||||
"After Cancel",
|
||||
"Before Discard",
|
||||
"After Discard",
|
||||
"Before Delete",
|
||||
"After Delete",
|
||||
"Before Save (Submitted Document)",
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ EVENT_MAP = {
|
|||
"on_submit": "After Submit",
|
||||
"before_cancel": "Before Cancel",
|
||||
"on_cancel": "After Cancel",
|
||||
"before_discard": "Before Discard",
|
||||
"on_discard": "After Discard",
|
||||
"on_trash": "Before Delete",
|
||||
"after_delete": "After Delete",
|
||||
"before_update_after_submit": "Before Save (Submitted Document)",
|
||||
|
|
|
|||
|
|
@ -59,6 +59,17 @@ def cancel(doctype=None, name=None, workflow_state_fieldname=None, workflow_stat
|
|||
frappe.msgprint(frappe._("Cancelled"), indicator="red", alert=True)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def discard(doctype: str, name: str | int):
|
||||
"""discard a draft document"""
|
||||
doc = frappe.get_doc(doctype, name)
|
||||
capture_doc(doc, "Discard")
|
||||
|
||||
doc.discard()
|
||||
send_updated_docs(doc)
|
||||
frappe.msgprint(frappe._("Discarded"), indicator="red", alert=True)
|
||||
|
||||
|
||||
def send_updated_docs(doc):
|
||||
from .load import get_docinfo
|
||||
|
||||
|
|
|
|||
|
|
@ -809,11 +809,12 @@ class Document(BaseDocument):
|
|||
|
||||
self.load_doc_before_save(raise_exception=True)
|
||||
|
||||
self._action = "save"
|
||||
previous = self._doc_before_save
|
||||
if not hasattr(self, "_action"):
|
||||
self._action = "save"
|
||||
|
||||
previous = self._doc_before_save
|
||||
# previous is None for new document insert
|
||||
if not previous:
|
||||
if not previous and self._action != "discard":
|
||||
self.check_docstatus_transition(0)
|
||||
return
|
||||
|
||||
|
|
@ -825,7 +826,7 @@ class Document(BaseDocument):
|
|||
raise_exception=frappe.TimestampMismatchError,
|
||||
)
|
||||
|
||||
if not self.meta.issingle:
|
||||
if not self.meta.issingle and self._action != "discard":
|
||||
self.check_docstatus_transition(previous.docstatus)
|
||||
|
||||
def check_docstatus_transition(self, to_docstatus):
|
||||
|
|
@ -1058,6 +1059,25 @@ class Document(BaseDocument):
|
|||
"""Cancel the document. Sets `docstatus` = 2, then saves."""
|
||||
return self._cancel()
|
||||
|
||||
@frappe.whitelist()
|
||||
def discard(self):
|
||||
"""Discard the draft document. Sets `docstatus` = 2 with db_set."""
|
||||
self._action = "discard"
|
||||
|
||||
self.check_if_locked()
|
||||
self.set_user_and_timestamp()
|
||||
self.check_if_latest()
|
||||
|
||||
if not self.docstatus == DocStatus.draft():
|
||||
raise frappe.ValidationError(_("Only draft documents can be discarded"), self.docstatus)
|
||||
|
||||
self.check_permission("write")
|
||||
|
||||
self.run_method("before_discard")
|
||||
self.db_set("docstatus", DocStatus.cancelled())
|
||||
delattr(self, "_action")
|
||||
self.run_method("on_discard")
|
||||
|
||||
@frappe.whitelist()
|
||||
def rename(self, name: str, merge=False, force=False, validate_rename=True):
|
||||
"""Rename the document to `name`. This transforms the current object."""
|
||||
|
|
|
|||
|
|
@ -833,6 +833,17 @@ frappe.ui.form.Form = class FrappeForm {
|
|||
}
|
||||
}
|
||||
|
||||
discard(btn, callback, on_error) {
|
||||
const me = this;
|
||||
return new Promise((resolve) => {
|
||||
frappe.confirm(__("Discard {0}", [this.docname]), function () {
|
||||
me.script_manager.trigger("before_discard").then(function () {
|
||||
return me._discard(btn, callback, on_error, false); // ?
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
savesubmit(btn, callback, on_error) {
|
||||
var me = this;
|
||||
return new Promise((resolve) => {
|
||||
|
|
@ -1015,6 +1026,52 @@ frappe.ui.form.Form = class FrappeForm {
|
|||
}
|
||||
}
|
||||
|
||||
_discard(btn, on_error, skip_confirm) {
|
||||
const me = this;
|
||||
const discard_doc = () => {
|
||||
frappe.validated = true;
|
||||
me.script_manager.trigger("before_discard").then(() => {
|
||||
if (!frappe.validated) {
|
||||
return me.handle_save_fail(btn, on_error);
|
||||
}
|
||||
|
||||
var after_discard = function (r) {
|
||||
if (r.exc) {
|
||||
me.handle_save_fail(btn, on_error);
|
||||
} else {
|
||||
frappe.utils.play_sound("cancel");
|
||||
me.refresh();
|
||||
me.script_manager.trigger("after_discard");
|
||||
}
|
||||
me.reload_doc();
|
||||
};
|
||||
//frappe.ui.form.discard(me, after_discard, btn);
|
||||
frappe.call({
|
||||
freeze: true,
|
||||
method: "frappe.desk.form.save.discard",
|
||||
args: {
|
||||
doctype: me.doc.doctype,
|
||||
name: me.doc.name,
|
||||
},
|
||||
btn: btn,
|
||||
callback: function (r) {
|
||||
after_discard(r);
|
||||
},
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
if (skip_confirm) {
|
||||
discard_doc();
|
||||
} else {
|
||||
frappe.confirm(
|
||||
__("Permanently Discard {0}?", [this.docname]),
|
||||
discard_doc,
|
||||
me.handle_save_fail(btn, on_error)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
savetrash() {
|
||||
this.validate_form_action("Delete");
|
||||
frappe.model.delete_doc(this.doctype, this.docname, function () {
|
||||
|
|
|
|||
|
|
@ -310,6 +310,16 @@ frappe.ui.form.Toolbar = class Toolbar {
|
|||
const allow_print_for_draft = cint(print_settings.allow_print_for_draft);
|
||||
const allow_print_for_cancelled = cint(print_settings.allow_print_for_cancelled);
|
||||
|
||||
if (is_submittable && docstatus == 0 && !this.has_workflow()) {
|
||||
this.page.add_menu_item(
|
||||
__("Discard"),
|
||||
function () {
|
||||
me.frm._discard();
|
||||
},
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
!is_submittable ||
|
||||
docstatus == 1 ||
|
||||
|
|
@ -584,9 +594,7 @@ frappe.ui.form.Toolbar = class Toolbar {
|
|||
}
|
||||
has_workflow() {
|
||||
if (this._has_workflow === undefined)
|
||||
this._has_workflow = frappe.get_list("Workflow", {
|
||||
document_type: this.frm.doctype,
|
||||
}).length;
|
||||
this._has_workflow = frappe.model.has_workflow(this.frm.doctype);
|
||||
return this._has_workflow;
|
||||
}
|
||||
get_docstatus() {
|
||||
|
|
|
|||
|
|
@ -98,6 +98,37 @@ class TestDocument(FrappeTestCase):
|
|||
|
||||
self.assertEqual(frappe.db.get_value(d.doctype, d.name, "subject"), "subject changed")
|
||||
|
||||
def test_discard_transitions(self):
|
||||
d = self.test_insert()
|
||||
self.assertEqual(d.docstatus, 0)
|
||||
|
||||
# invalid: Submit > Discard, Cancel > Discard
|
||||
d.submit()
|
||||
self.assertRaises(frappe.ValidationError, d.discard)
|
||||
d.reload()
|
||||
|
||||
d.cancel()
|
||||
self.assertRaises(frappe.ValidationError, d.discard)
|
||||
|
||||
# valid: Draft > Discard
|
||||
d2 = self.test_insert()
|
||||
d2.discard()
|
||||
self.assertEqual(d2.docstatus, 2)
|
||||
|
||||
def test_save_on_discard_throws(self):
|
||||
from frappe.desk.doctype.event.event import Event
|
||||
|
||||
d3 = self.test_insert()
|
||||
|
||||
def test_on_discard(d3):
|
||||
d3.subject = d3.subject + "update"
|
||||
d3.save()
|
||||
|
||||
d3.on_discard = (test_on_discard)(d3)
|
||||
d3.on_discard = test_on_discard.__get__(d3, Event)
|
||||
|
||||
self.assertRaises(frappe.ValidationError, d3.discard)
|
||||
|
||||
def test_value_changed(self):
|
||||
d = self.test_insert()
|
||||
d.subject = "subject changed again"
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue