diff --git a/frappe/desk/form/assign_to.py b/frappe/desk/form/assign_to.py index ce8bb444a1..16f170498d 100644 --- a/frappe/desk/form/assign_to.py +++ b/frappe/desk/form/assign_to.py @@ -32,7 +32,7 @@ def get(args=None): filters={ "reference_type": args.get("doctype"), "reference_name": args.get("name"), - "status": ("!=", "Cancelled"), + "status": ("not in", ("Cancelled", "Closed")), }, limit=5, ) @@ -164,6 +164,14 @@ def remove(doctype, name, assign_to): return set_status(doctype, name, "", assign_to, status="Cancelled") +@frappe.whitelist() +def close(doctype: str, name: str, assign_to: str): + if assign_to != frappe.session.user: + frappe.throw(_("Only the assignee can complete this to-do.")) + + return set_status(doctype, name, "", assign_to, status="Closed") + + def set_status(doctype, name, todo=None, assign_to=None, status="Cancelled"): """remove from todo""" try: @@ -187,7 +195,7 @@ def set_status(doctype, name, todo=None, assign_to=None, status="Cancelled"): pass # clear assigned_to if field exists - if frappe.get_meta(doctype).get_field("assigned_to") and status == "Cancelled": + if frappe.get_meta(doctype).get_field("assigned_to") and status in ("Cancelled", "Closed"): frappe.db.set_value(doctype, name, "assigned_to", None) return get({"doctype": doctype, "name": name}) diff --git a/frappe/desk/form/load.py b/frappe/desk/form/load.py index 56f39aacfb..d6d4c400b2 100644 --- a/frappe/desk/form/load.py +++ b/frappe/desk/form/load.py @@ -345,7 +345,7 @@ def get_assignments(dt, dn): filters={ "reference_type": dt, "reference_name": dn, - "status": ("!=", "Cancelled"), + "status": ("not in", ("Cancelled", "Closed")), "allocated_to": ("is", "set"), }, ) diff --git a/frappe/public/js/frappe/form/sidebar/assign_to.js b/frappe/public/js/frappe/form/sidebar/assign_to.js index cd83705f19..25f259e867 100644 --- a/frappe/public/js/frappe/form/sidebar/assign_to.js +++ b/frappe/public/js/frappe/form/sidebar/assign_to.js @@ -288,6 +288,13 @@ frappe.ui.form.AssignmentDialog = class { assign_to: assignment, }); } + close_assignment(assignment) { + return frappe.xcall("frappe.desk.form.assign_to.close", { + doctype: this.frm.doctype, + name: this.frm.docname, + assign_to: assignment, + }); + } update_assignment(assignment) { const in_the_list = this.assignment_list.find(`[data-user="${assignment}"]`).length; if (!in_the_list) { @@ -295,22 +302,40 @@ frappe.ui.form.AssignmentDialog = class { } } get_assignment_row(assignment) { - let row = $(` + const row = $(`
- +
${frappe.avatar(assignment)} ${frappe.user.full_name(assignment)} - +
+
+
`); - if (assignment === frappe.session.user || this.frm.perm[0].write) { - row.append(` - - ${frappe.utils.icon("close")} - + const btn_group = row.find(".btn-group"); + + if (assignment === frappe.session.user) { + btn_group.append(` + `); - row.find(".remove-btn").click(() => { + btn_group.find(".complete-btn").click(() => { + this.close_assignment(assignment).then((assignments) => { + row.remove(); + this.render(assignments); + }); + }); + } + + if (assignment === frappe.session.user || this.frm.perm[0].write) { + btn_group.append(` + + `); + btn_group.find(".remove-btn").click(() => { this.remove_assignment(assignment).then((assignments) => { row.remove(); this.render(assignments); diff --git a/frappe/public/scss/common/modal.scss b/frappe/public/scss/common/modal.scss index 4b7f028c79..350ccadac9 100644 --- a/frappe/public/scss/common/modal.scss +++ b/frappe/public/scss/common/modal.scss @@ -254,7 +254,6 @@ body.modal-open[style^="padding-right"] { .dialog-assignment-row { display: flex; align-items: center; - justify-content: space-between; padding: 5px 15px; border-radius: var(--border-radius-md); color: var(--text-color); @@ -262,13 +261,21 @@ body.modal-open[style^="padding-right"] { margin-bottom: 5px; } @extend .row; - .remove-btn { - display: none; + .btn-group { + opacity: 0; + transition: opacity .77s ease-in-out; + + button { + display: inline-flex; + } + } + .assignee { + flex: 1; } &:hover { - background: var(--fg-hover-color); - .remove-btn { - display: block; + .btn-group { + opacity: 1; + transition: opacity 0.1s ease-in-out; } } .avatar { diff --git a/frappe/translations/de.csv b/frappe/translations/de.csv index f8bbd8e22e..77c49e25ae 100644 --- a/frappe/translations/de.csv +++ b/frappe/translations/de.csv @@ -1777,6 +1777,7 @@ Only mandatory fields are necessary for new records. You can delete non-mandator Only standard DocTypes are allowed to be customized from Customize Form.,Nur Standard-DocTypes dürfen über Formular anpassen angepasst werden., Only users involved in the document are listed,"Es werden nur Benutzer aufgelistet, die an dem Dokument beteiligt sind", Only {0} emailed reports are allowed per user,Maximal {0} E-Mail-Berichte pro Benutzer erlaubt., +Only the assignee can complete this to-do.,Nur der zugewiesene Benutzer kann diese Aufgabe abschließen., Oops! Something went wrong,Hoppla! Etwas ist schiefgelaufen, "Oops, you are not allowed to know that","Hoppla, dass dürften Sie garnicht wissen", Open Link,Verknüpfung öffnen, @@ -3748,6 +3749,7 @@ Description,Beschreibung, Designation,Bezeichnung, Disabled,Deaktiviert, Doctype,DocType, +Done,Erledigt, Download Template,Vorlage herunterladen, Dr,Soll, Due Date,Fälligkeitsdatum,