diff --git a/frappe/model/workflow.py b/frappe/model/workflow.py index 282063696f..f8b9c2501c 100644 --- a/frappe/model/workflow.py +++ b/frappe/model/workflow.py @@ -5,6 +5,7 @@ from __future__ import unicode_literals import frappe from frappe.utils import cint from frappe import _ +import json class WorkflowStateError(frappe.ValidationError): pass class WorkflowTransitionError(frappe.ValidationError): pass @@ -52,7 +53,6 @@ def get_transitions(doc, workflow = None): if not success: continue transitions.append(transition.as_dict()) - print(transitions) return transitions @frappe.whitelist() @@ -149,7 +149,7 @@ def get_workflow(doctype): def has_approval_access(user, doc, transition): return (user == 'Administrator' or transition.get('allow_self_approval') - or user != doc.owner) + or user != doc.get('owner')) def get_workflow_state_field(workflow_name): return get_workflow_field_value(workflow_name, 'workflow_state_field') @@ -166,11 +166,33 @@ def get_workflow_field_value(workflow_name, field): @frappe.whitelist() def bulk_workflow_approval(docs, action, doctype): - import json docs = json.loads(docs) - for doc in docs: + for (i, doc) in enumerate(docs, 1): doc['doctype'] = doctype try: + show_progress(docs, _('Approving {0}').format(doctype), i, doc.get('name')) apply_workflow(doc, action) - except Exception as e: - print(e) \ No newline at end of file + except frappe.ValidationError: + pass + +@frappe.whitelist() +def get_common_transition_actions(docs, doctype): + common_actions = [] + docs = json.loads(docs) + for (i, doc) in enumerate(docs, 1): + doc['doctype'] = doctype + actions = [t.get('action') for t in get_transitions(doc) if has_approval_access(frappe.session.user, doc, t)] + if not actions: return [] + common_actions = actions if i == 1 else set(common_actions).intersection(actions) + if not common_actions: return [] + + return common_actions + +def show_progress(docnames, message, i, description): + n = len(docnames) + if n >= 10: + frappe.publish_progress( + float(i) * 100 / n, + title = message, + description = description + ) diff --git a/frappe/public/js/frappe/list/list_view.js b/frappe/public/js/frappe/list/list_view.js index dba27230e8..b7ab9b87ba 100644 --- a/frappe/public/js/frappe/list/list_view.js +++ b/frappe/public/js/frappe/list/list_view.js @@ -119,14 +119,19 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList { set_actions_menu_items() { this.actions_menu_items = this.get_actions_menu_items(); - this.get_workflow_actions_menu_items(); + this.workflow_action_menu_items = this.get_workflow_action_menu_items(); + this.workflow_action_items = {}; - const actions = this.actions_menu_items.concat(this.workflow_actions_menu_items); + const actions = this.actions_menu_items.concat(this.workflow_action_menu_items); actions.map(item => { const $item = this.page.add_actions_menu_item(item.label, item.action, item.standard); if (item.class) { $item.addClass(item.class); } + if (item.is_workflow_action) { + // can be used to dynamically show or hide action + this.workflow_action_items[item.name] = $item; + } }); } @@ -189,7 +194,6 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList { }, interval); } - set_primary_action() { if (this.can_create) { this.page.set_primary_action(__('New'), () => { @@ -344,6 +348,7 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList { if (toggle) { this.page.show_actions_menu(); this.page.clear_primary_action(); + this.toggle_workflow_actions(); } else { this.page.hide_actions_menu(); this.set_primary_action(); @@ -1115,14 +1120,14 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList { }); } - get_workflow_actions_menu_items() { - if (this.workflow_action_setup) return + get_workflow_action_menu_items() { + const workflow_actions = []; if (frappe.model.has_workflow(this.doctype)) { - this.workflow_actions_menu_items = []; const actions = frappe.workflow.get_all_transition_actions(this.doctype); actions.forEach(action => { - this.workflow_actions_menu_items.push({ + workflow_actions.push({ label: __(action), + name: action, action: () => { frappe.xcall('frappe.model.workflow.bulk_workflow_approval', { docs: this.get_checked_items(), @@ -1135,40 +1140,19 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList { }); }); } - this.workflow_action_setup = true; - // const check_requirement = () => { - // const checked_items = this.get_checked_items(); - // const doc_states = []; - // checked_items.forEach(doc => { - // doc.doctype = this.doctype; - // doc_states.push(frappe.workflow.get_state(doc)); - // }); - - // const all_docs_have_same_state = frappe.utils.unique(doc_states).length === 1; - - // if (all_docs_have_same_state) { - // this.actions_menu_items.push({ - // label: __('Next Action'), - // action: () => { - // frappe.msgprint('IN'); - // }, - // standard: true - // }); - // } - // }; - - // if (!this.transitions) { - // frappe.workflow.get_transitions(this.doctype) - // .then(transitions => { - // this.transitions = transitions; - // check_requirement(); - // }); - // } else { - // check_requirement(); - // } - // check_requirement(); - console.log('--'); + return workflow_actions; + } + toggle_workflow_actions() { + const checked_items = this.get_checked_items(); + frappe.xcall('frappe.model.workflow.get_common_transition_actions', { + docs: checked_items, + doctype: this.doctype + }).then(actions => { + Object.keys(this.workflow_action_items).forEach((key) => { + this.workflow_action_items[key].toggle(actions.includes(key)); + }); + }); } get_actions_menu_items() { @@ -1279,12 +1263,12 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList { actions_menu_items.push(bulk_printing()); } - // Bulk submit + // bulk submit if (frappe.model.is_submittable(doctype) && has_submit_permission(doctype)) { actions_menu_items.push(bulk_submit()); } - // Bulk cancel + // bulk cancel if (frappe.model.can_cancel(doctype)) { actions_menu_items.push(bulk_cancel()); } diff --git a/frappe/public/js/frappe/model/workflow.js b/frappe/public/js/frappe/model/workflow.js index fa71f94f0f..904101698d 100644 --- a/frappe/public/js/frappe/model/workflow.js +++ b/frappe/public/js/frappe/model/workflow.js @@ -78,10 +78,13 @@ frappe.workflow = { } return state; }, + get_all_transitions(doctype) { + return frappe.workflow.workflows[doctype].transitions || []; + }, get_all_transition_actions(doctype) { - const transitions = frappe.workflow.workflows[doctype].transitions || []; + const transitions = this.get_all_transitions(doctype); return transitions.map(transition => { return transition.action; }); - } + }, };