Repository: frappe/frappe
Pull request: Workflow actions
![workflow-actions-3](https://user-images.githubusercontent.com/13928957/40418621-7fd64d46-5ea0-11e8-92c5-fa03b6940239.gif)

Closes : https://github.com/frappe/frappe/issues/5391, https://github.com/frappe/frappe/issues/5553


Problem statement:
Workflow Action
### Motivation

The user has created workflows on various transactions like leaves, purchase orders etc. There may be multiple users who need to approve and there are multiple stages of approval. The goal is to facilitate communication to the users via email and also create a list of pending actions. 

Workflows are assigned to roles and they may be separated by user permissions.

The design involves creating a new document "Pending Workflow Action" that can be created for each user that is involved in the workflow step. A list view of pending actions will also be useful for approving in case the user needs to approve multiple actions.

### Tasks

- [x] New DocType "Workflow Action"
  - [x] Fields - reference_doctype, reference_name, workflow_status, status, user - (Open, Completed)
  - [x] Notification based on status
- [x] Create a new Workflow Action after each transaction for each user based on role and user permissions
- [x] Email notification
  - [x] Send email notification to each user
  - [x] Create routes for user to apply action via URL
  - [x] Attach print format of the reference document for action
  - [x] Show buttons in email to apply action via URL
- [x] List View
  - [x] Open host document when workflow action is clicked via list view (for example if the action is "Approve Purchase Order", then open purchase order rather than the pending action document
- [x] Clearing
  - [x] Clear actions if any one of the users has changed the state
  - [x] Clear old + completed workflow actions (which were not acted upon)

Hints from issue discussion:
This is awesome
@rmehta Possibly add an "expected time to complete by" and "importance level 1-5" for tasks.
There are 2 benefits -
1. Easy prioritization of tasks for user
2. Useful for HR department/managers to review on-time/delays in completion of tasks  
I have implemented most functionality,
I will make pr at the end of the week 
Awesome thanks! Can you start the PR right now so we can give you feedback.

Sent from phone

> On 12-May-2018, at 2:57 PM, Ahmad M Abdelrahman <notifications@github.com> wrote:
> 
> I have implemented most functionality,
> I will make pr at the end of the week
> 
> —
> You are receiving this because you were mentioned.
> Reply to this email directly, view it on GitHub, or mute the thread.
> 

the Names are different in my version, the main doctypes are

User Notification
User Notification Settings 

almost the same fields with addition of target_doc , action_date, due_date ,expire_date , handled_by 

the doc is used for the normal notification  (notification.py) by adding new flags 

"Leave Application": {
"use_user_notification":1,
"active_workflow": "My leave application workflow",
"send_email":0,
"docstatus": 0
},

active_workflow if not set its create User Notification for all employee with the normal notifcation method like 
"Leave Application": {"docstatus": 0},

then i add a hock that target create event for all doctypes and check if the doctype  use_user_notification 
flag is active (or exist )
and create User Notification for all matching user 

on update and on update_after_submit 
if use_user_notification flag is active (or exist )
if there active_workflow
create User Notification based on the new workflow
Old User Notification ('s)  status change into "Handled"  and the handeled_by change 
(Many users may have the same role so they have to know who handel it )

the code i made it 2 years ago so its need refactoring . 
i will share it asap . 
if you have any changes on the scinarion or altartion please share it . 
best regards 
Ahmad Abderlrahman

Commit message: Fix incorrect 'this' reference

Patch to review:
diff --git a/frappe/core/notifications.py b/frappe/core/notifications.py
index f156bb9e5a4e..d4111b77b5f7 100644
--- a/frappe/core/notifications.py
+++ b/frappe/core/notifications.py
@@ -12,6 +12,7 @@ def get_notification_config():
 			"ToDo": "frappe.core.notifications.get_things_todo",
 			"Event": "frappe.core.notifications.get_todays_events",
 			"Error Snapshot": {"seen": 0, "parent_error_snapshot": None},
+			"Workflow Action": {"status": 'Open'}
 		},
 		"for_other": {
 			"Likes": "frappe.core.notifications.get_unseen_likes",
diff --git a/frappe/core/page/permission_manager/permission_manager.py b/frappe/core/page/permission_manager/permission_manager.py
index 549faee38343..b9313d05f8d4 100644
--- a/frappe/core/page/permission_manager/permission_manager.py
+++ b/frappe/core/page/permission_manager/permission_manager.py
@@ -100,7 +100,9 @@ def reset(doctype):
 
 @frappe.whitelist()
 def get_users_with_role(role):
-	frappe.only_for("System Manager")
+	if frappe.request and frappe.local.form_dict.cmd == 'get_users_with_role':
+		frappe.only_for("System Manager")
+
 	return [p[0] for p in frappe.db.sql("""select distinct tabUser.name
 		from `tabHas Role`, tabUser where
 			`tabHas Role`.role=%s
diff --git a/frappe/desk/notifications.py b/frappe/desk/notifications.py
index a7f9220d6447..6a9f993e4efe 100644
--- a/frappe/desk/notifications.py
+++ b/frappe/desk/notifications.py
@@ -6,6 +6,7 @@
 import frappe
 from frappe.utils import time_diff_in_seconds, now, now_datetime, DATETIME_FORMAT
 from dateutil.relativedelta import relativedelta
+from six import string_types
 
 @frappe.whitelist()
 def get_notifications():
@@ -180,7 +181,10 @@ def delete_notification_count_for(doctype):
 
 def clear_doctype_notifications(doc, method=None, *args, **kwargs):
 	config = get_notification_config()
-	doctype = doc.doctype
+	if isinstance(doc, string_types):
+		doctype = doc # assuming doctype name was passed directly
+	else:
+		doctype = doc.doctype
 
 	if doctype in config.for_doctype:
 		delete_notification_count_for(doctype)
diff --git a/frappe/hooks.py b/frappe/hooks.py
index e5b27b6e4540..5693d5c79aae 100755
--- a/frappe/hooks.py
+++ b/frappe/hooks.py
@@ -86,7 +86,8 @@
 	"Kanban Board": "frappe.desk.doctype.kanban_board.kanban_board.get_permission_query_conditions",
 	"Contact": "frappe.contacts.address_and_contact.get_permission_query_conditions_for_contact",
 	"Address": "frappe.contacts.address_and_contact.get_permission_query_conditions_for_address",
-	"Communication": "frappe.core.doctype.communication.communication.get_permission_query_conditions_for_communication"
+	"Communication": "frappe.core.doctype.communication.communication.get_permission_query_conditions_for_communication",
+	"Workflow Action": "frappe.workflow.doctype.workflow_action.workflow_action.get_permission_query_conditions"
 }
 
 has_permission = {
@@ -98,6 +99,7 @@
 	"Contact": "frappe.contacts.address_and_contact.has_permission",
 	"Address": "frappe.contacts.address_and_contact.has_permission",
 	"Communication": "frappe.core.doctype.communication.communication.has_permission",
+	"Workflow Action": "frappe.workflow.doctype.workflow_action.workflow_action.has_permission"
 }
 
 has_website_permission = {
@@ -112,14 +114,18 @@
 	"*": {
 		"on_update": [
 			"frappe.desk.notifications.clear_doctype_notifications",
-			"frappe.core.doctype.activity_log.feed.update_feed"
+			"frappe.core.doctype.activity_log.feed.update_feed",
+			"frappe.workflow.doctype.workflow_action.workflow_action.create_workflow_actions"
 		],
 		"after_rename": "frappe.desk.notifications.clear_doctype_notifications",
 		"on_cancel": [
 			"frappe.desk.notifications.clear_doctype_notifications",
+			"frappe.workflow.doctype.workflow_action.workflow_action.create_workflow_actions"
 		],
 		"on_trash": "frappe.desk.notifications.clear_doctype_notifications",
-		"on_change": "frappe.core.doctype.feedback_trigger.feedback_trigger.trigger_feedback_request"
+		"on_change": [
+			"frappe.core.doctype.feedback_trigger.feedback_trigger.trigger_feedback_request",
+		]
 	},
 	"Email Group Member": {
 		"validate": "frappe.email.doctype.email_group.email_group.restrict_email_group"
diff --git a/frappe/model/workflow.py b/frappe/model/workflow.py
index 6de67e0dac89..b4543dec23e4 100644
--- a/frappe/model/workflow.py
+++ b/frappe/model/workflow.py
@@ -61,6 +61,10 @@ def apply_workflow(doc, action):
 	doc = frappe.get_doc(frappe.parse_json(doc))
 	workflow = get_workflow(doc.doctype)
 	transitions = get_transitions(doc, workflow)
+	user = frappe.session.user
+
+	if not has_approval_access(doc, workflow, user):
+		frappe.throw(_("Self approval is not allowed"))
 
 	# find the transition
 	transition = None
@@ -133,3 +137,8 @@ def validate_workflow(doc):
 
 def get_workflow(doctype):
 	return frappe.get_doc('Workflow', get_workflow_name(doctype))
+
+def has_approval_access(doc, workflow, user):
+	return (user == 'Administrator'
+		or workflow.get('allow_self_approval')
+		or user != doc.owner)
\ No newline at end of file
diff --git a/frappe/patches.txt b/frappe/patches.txt
index c3859ec738ec..56c29a191b98 100644
--- a/frappe/patches.txt
+++ b/frappe/patches.txt
@@ -216,3 +216,4 @@ frappe.patches.v10_0.increase_single_table_column_length
 frappe.patches.v11_0.create_contact_for_user
 frappe.patches.v11_0.sync_stripe_settings_before_migrate
 frappe.patches.v11_0.update_list_user_settings
+frappe.patches.v11_0.rename_workflow_action_to_workflow_action_master
diff --git a/frappe/patches/v11_0/rename_workflow_action_to_workflow_action_master.py b/frappe/patches/v11_0/rename_workflow_action_to_workflow_action_master.py
new file mode 100644
index 000000000000..48990a3e3a17
--- /dev/null
+++ b/frappe/patches/v11_0/rename_workflow_action_to_workflow_action_master.py
@@ -0,0 +1,5 @@
+import frappe
+
+def execute():
+	if not frappe.db.table_exists("Workflow Action Master"):
+		frappe.db.sql("RENAME TABLE `tabWorkflow Action` to `tabWorkflow Action Master`")
diff --git a/frappe/public/js/frappe/form/workflow.js b/frappe/public/js/frappe/form/workflow.js
index 4e428c0fae4e..5e2af60b0c7a 100644
--- a/frappe/public/js/frappe/form/workflow.js
+++ b/frappe/public/js/frappe/form/workflow.js
@@ -56,9 +56,22 @@ frappe.ui.form.States = Class.extend({
 		}
 
 		// state text
-		var state = this.get_state();
+		const state = this.get_state();
+
+		let doctype = this.frm.doctype;
+
+		function has_approval_access() {
+			let approval_access = false;
+			const user = frappe.session.user;
+			if (user === 'Administrator'
+				|| frappe.workflow.is_self_approval_enabled(doctype)
+				|| user !== this.frm.doc.owner) {
+				approval_access = true;
+			}
+			return approval_access;
+		}
 
-		if(state) {
+		if(state && has_approval_access()) {
 			// show actions from that state
 			this.show_actions(state);
 		}
diff --git a/frappe/public/js/frappe/list/list_view.js b/frappe/public/js/frappe/list/list_view.js
index 0d7a157d5f56..dca3d4963362 100644
--- a/frappe/public/js/frappe/list/list_view.js
+++ b/frappe/public/js/frappe/list/list_view.js
@@ -590,6 +590,10 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
 	}
 
 	get_form_link(doc) {
+		if (this.settings.get_form_link) {
+			return this.settings.get_form_link(doc);
+		}
+
 		const docname = doc.name.match(/[%'"]/)
 			? encodeURIComponent(doc.name)
 			: doc.name;
diff --git a/frappe/public/js/frappe/model/workflow.js b/frappe/public/js/frappe/model/workflow.js
index 4a22e9c387ed..dfad3a93e823 100644
--- a/frappe/public/js/frappe/model/workflow.js
+++ b/frappe/public/js/frappe/model/workflow.js
@@ -40,6 +40,9 @@ frappe.workflow = {
 		frappe.workflow.setup(doctype);
 		return frappe.get_children(frappe.workflow.workflows[doctype], "states", {state:state})[0];
 	},
+	is_self_approval_enabled: function(doctype) {
+		return frappe.workflow.workflows[doctype].allow_self_approval;
+	},
 	is_read_only: function(doctype, name) {
 		var state_fieldname = frappe.workflow.get_state_fieldname(doctype);
 		if(state_fieldname) {
diff --git a/frappe/templates/emails/workflow_action.html b/frappe/templates/emails/workflow_action.html
new file mode 100644
index 000000000000..3cc011638a54
--- /dev/null
+++ b/frappe/templates/emails/workflow_action.html
@@ -0,0 +1,8 @@
+<p>{{ message }}</p>
+<div>
+	<p style="margin: 2em 0 !important">
+		{% for action in actions %}
+		<a class="btn" style="margin-right: 1em" href="{{ action.action_link }}">{{_(action.action_name)}}</a>
+		{% endfor %}
+	</p>
+</div>
\ No newline at end of file
diff --git a/frappe/utils/install.py b/frappe/utils/install.py
index a822bfbb9808..8dd06194eb38 100644
--- a/frappe/utils/install.py
+++ b/frappe/utils/install.py
@@ -60,9 +60,9 @@ def install_basic_docs():
 			"icon": "ok-sign", "style": "Success"},
 		{'doctype': "Workflow State", "workflow_state_name": "Rejected",
 			"icon": "remove", "style": "Danger"},
-		{'doctype': "Workflow Action", "workflow_action_name": "Approve"},
-		{'doctype': "Workflow Action", "workflow_action_name": "Reject"},
-		{'doctype': "Workflow Action", "workflow_action_name": "Review"},
+		{'doctype': "Workflow Action Master", "workflow_action_name": "Approve"},
+		{'doctype': "Workflow Action Master", "workflow_action_name": "Reject"},
+		{'doctype': "Workflow Action Master", "workflow_action_name": "Review"},
 		{'doctype': "Email Domain", "domain_name":"example.com", "email_id": "account@example.com", "password": "pass", "email_server": "imap.example.com","use_imap": 1, "smtp_server": "smtp.example.com"},
 		{'doctype': "Email Account", "domain":"example.com", "email_id": "notifications@example.com", "default_outgoing": 1},
 		{'doctype': "Email Account", "domain":"example.com", "email_id": "replies@example.com", "default_incoming": 1}
diff --git a/frappe/workflow/doctype/workflow/workflow.json b/frappe/workflow/doctype/workflow/workflow.json
index b33bf242aeb1..7e411a3cb6b2 100644
--- a/frappe/workflow/doctype/workflow/workflow.json
+++ b/frappe/workflow/doctype/workflow/workflow.json
@@ -1,328 +1,378 @@
 {
- "allow_copy": 0, 
- "allow_import": 0, 
- "allow_rename": 1, 
- "autoname": "field:workflow_name", 
- "beta": 0, 
- "creation": "2012-12-28 10:49:55", 
- "custom": 0, 
- "description": "Defines workflow states and rules for a document.", 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "Document", 
- "editable_grid": 0, 
- "engine": "InnoDB", 
+ "allow_copy": 0,
+ "allow_guest_to_view": 0,
+ "allow_import": 0,
+ "allow_rename": 1,
+ "autoname": "field:workflow_name",
+ "beta": 0,
+ "creation": "2012-12-28 10:49:55",
+ "custom": 0,
+ "description": "Defines workflow states and rules for a document.",
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "Document",
+ "editable_grid": 0,
+ "engine": "InnoDB",
  "fields": [
   {
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "workflow_name", 
-   "fieldtype": "Data", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Workflow Name", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
+   "allow_bulk_edit": 0,
+   "allow_on_submit": 0,
+   "bold": 0,
+   "collapsible": 0,
+   "columns": 0,
+   "fieldname": "workflow_name",
+   "fieldtype": "Data",
+   "hidden": 0,
+   "ignore_user_permissions": 0,
+   "ignore_xss_filter": 0,
+   "in_filter": 0,
+   "in_global_search": 0,
+   "in_list_view": 1,
+   "in_standard_filter": 0,
+   "label": "Workflow Name",
+   "length": 0,
+   "no_copy": 0,
+   "permlevel": 0,
+   "print_hide": 0,
+   "print_hide_if_no_value": 0,
+   "read_only": 0,
+   "remember_last_selected_value": 0,
+   "report_hide": 0,
+   "reqd": 1,
+   "search_index": 0,
+   "set_only_once": 0,
+   "translatable": 0,
    "unique": 0
-  }, 
+  },
   {
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "description": "DocType on which this Workflow is applicable.", 
-   "fieldname": "document_type", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 1, 
-   "label": "Document Type", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "DocType", 
-   "permlevel": 0, 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
+   "allow_bulk_edit": 0,
+   "allow_on_submit": 0,
+   "bold": 0,
+   "collapsible": 0,
+   "columns": 0,
+   "description": "DocType on which this Workflow is applicable.",
+   "fieldname": "document_type",
+   "fieldtype": "Link",
+   "hidden": 0,
+   "ignore_user_permissions": 0,
+   "ignore_xss_filter": 0,
+   "in_filter": 0,
+   "in_global_search": 0,
+   "in_list_view": 1,
+   "in_standard_filter": 1,
+   "label": "Document Type",
+   "length": 0,
+   "no_copy": 0,
+   "options": "DocType",
+   "permlevel": 0,
+   "print_hide": 0,
+   "print_hide_if_no_value": 0,
+   "read_only": 0,
+   "remember_last_selected_value": 0,
+   "report_hide": 0,
+   "reqd": 1,
+   "search_index": 0,
+   "set_only_once": 0,
+   "translatable": 0,
    "unique": 0
-  }, 
+  },
   {
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "description": "If checked, all other workflows become inactive.", 
-   "fieldname": "is_active", 
-   "fieldtype": "Check", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Is Active", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
+   "allow_bulk_edit": 0,
+   "allow_on_submit": 0,
+   "bold": 0,
+   "collapsible": 0,
+   "columns": 0,
+   "description": "If checked, all other workflows become inactive.",
+   "fieldname": "is_active",
+   "fieldtype": "Check",
+   "hidden": 0,
+   "ignore_user_permissions": 0,
+   "ignore_xss_filter": 0,
+   "in_filter": 0,
+   "in_global_search": 0,
+   "in_list_view": 0,
+   "in_standard_filter": 0,
+   "label": "Is Active",
+   "length": 0,
+   "no_copy": 0,
+   "permlevel": 0,
+   "print_hide": 0,
+   "print_hide_if_no_value": 0,
+   "read_only": 0,
+   "remember_last_selected_value": 0,
+   "report_hide": 0,
+   "reqd": 0,
+   "search_index": 0,
+   "set_only_once": 0,
+   "translatable": 0,
    "unique": 0
-  }, 
+  },
   {
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "description": "If Checked workflow status will not override status in list view", 
-   "fieldname": "override_status", 
-   "fieldtype": "Check", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Don't Override Status", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
+   "allow_bulk_edit": 0,
+   "allow_on_submit": 0,
+   "bold": 0,
+   "collapsible": 0,
+   "columns": 0,
+   "description": "Allow approval for creator of the document",
+   "fieldname": "allow_self_approval",
+   "fieldtype": "Check",
+   "hidden": 0,
+   "ignore_user_permissions": 0,
+   "ignore_xss_filter": 0,
+   "in_filter": 0,
+   "in_global_search": 0,
+   "in_list_view": 0,
+   "in_standard_filter": 0,
+   "label": "Allow Self Approval",
+   "length": 0,
+   "no_copy": 0,
+   "permlevel": 0,
+   "precision": "",
+   "print_hide": 0,
+   "print_hide_if_no_value": 0,
+   "read_only": 0,
+   "remember_last_selected_value": 0,
+   "report_hide": 0,
+   "reqd": 0,
+   "search_index": 0,
+   "set_only_once": 0,
+   "translatable": 0,
    "unique": 0
-  }, 
+  },
   {
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "description": "Different \"States\" this document can exist in. Like \"Open\", \"Pending Approval\" etc.", 
-   "fieldname": "states_head", 
-   "fieldtype": "Section Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "States", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
+   "allow_bulk_edit": 0,
+   "allow_on_submit": 0,
+   "bold": 0,
+   "collapsible": 0,
+   "columns": 0,
+   "description": "If Checked workflow status will not override status in list view",
+   "fieldname": "override_status",
+   "fieldtype": "Check",
+   "hidden": 0,
+   "ignore_user_permissions": 0,
+   "ignore_xss_filter": 0,
+   "in_filter": 0,
+   "in_global_search": 0,
+   "in_list_view": 0,
+   "in_standard_filter": 0,
+   "label": "Don't Override Status",
+   "length": 0,
+   "no_copy": 0,
+   "permlevel": 0,
+   "precision": "",
+   "print_hide": 0,
+   "print_hide_if_no_value": 0,
+   "read_only": 0,
+   "remember_last_selected_value": 0,
+   "report_hide": 0,
+   "reqd": 0,
+   "search_index": 0,
+   "set_only_once": 0,
+   "translatable": 0,
    "unique": 0
-  }, 
+  },
   {
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "description": "All possible Workflow States and roles of the workflow. Docstatus Options: 0 is\"Saved\", 1 is \"Submitted\" and 2 is \"Cancelled\"", 
-   "fieldname": "states", 
-   "fieldtype": "Table", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Document States", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Workflow Document State", 
-   "permlevel": 0, 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
+   "allow_bulk_edit": 0,
+   "allow_on_submit": 0,
+   "bold": 0,
+   "collapsible": 0,
+   "columns": 0,
+   "description": "Different \"States\" this document can exist in. Like \"Open\", \"Pending Approval\" etc.",
+   "fieldname": "states_head",
+   "fieldtype": "Section Break",
+   "hidden": 0,
+   "ignore_user_permissions": 0,
+   "ignore_xss_filter": 0,
+   "in_filter": 0,
+   "in_global_search": 0,
+   "in_list_view": 0,
+   "in_standard_filter": 0,
+   "label": "States",
+   "length": 0,
+   "no_copy": 0,
+   "permlevel": 0,
+   "print_hide": 0,
+   "print_hide_if_no_value": 0,
+   "read_only": 0,
+   "remember_last_selected_value": 0,
+   "report_hide": 0,
+   "reqd": 0,
+   "search_index": 0,
+   "set_only_once": 0,
+   "translatable": 0,
    "unique": 0
-  }, 
+  },
   {
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "description": "Rules for how states are transitions, like next state and which role is allowed to change state etc.", 
-   "fieldname": "transition_rules", 
-   "fieldtype": "Section Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Transition Rules", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
+   "allow_bulk_edit": 0,
+   "allow_on_submit": 0,
+   "bold": 0,
+   "collapsible": 0,
+   "columns": 0,
+   "description": "All possible Workflow States and roles of the workflow. Docstatus Options: 0 is\"Saved\", 1 is \"Submitted\" and 2 is \"Cancelled\"",
+   "fieldname": "states",
+   "fieldtype": "Table",
+   "hidden": 0,
+   "ignore_user_permissions": 0,
+   "ignore_xss_filter": 0,
+   "in_filter": 0,
+   "in_global_search": 0,
+   "in_list_view": 0,
+   "in_standard_filter": 0,
+   "label": "Document States",
+   "length": 0,
+   "no_copy": 0,
+   "options": "Workflow Document State",
+   "permlevel": 0,
+   "print_hide": 0,
+   "print_hide_if_no_value": 0,
+   "read_only": 0,
+   "remember_last_selected_value": 0,
+   "report_hide": 0,
+   "reqd": 1,
+   "search_index": 0,
+   "set_only_once": 0,
+   "translatable": 0,
    "unique": 0
-  }, 
+  },
   {
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "description": "Rules defining transition of state in the workflow.", 
-   "fieldname": "transitions", 
-   "fieldtype": "Table", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Transitions", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Workflow Transition", 
-   "permlevel": 0, 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
+   "allow_bulk_edit": 0,
+   "allow_on_submit": 0,
+   "bold": 0,
+   "collapsible": 0,
+   "columns": 0,
+   "description": "Rules for how states are transitions, like next state and which role is allowed to change state etc.",
+   "fieldname": "transition_rules",
+   "fieldtype": "Section Break",
+   "hidden": 0,
+   "ignore_user_permissions": 0,
+   "ignore_xss_filter": 0,
+   "in_filter": 0,
+   "in_global_search": 0,
+   "in_list_view": 0,
+   "in_standard_filter": 0,
+   "label": "Transition Rules",
+   "length": 0,
+   "no_copy": 0,
+   "permlevel": 0,
+   "print_hide": 0,
+   "print_hide_if_no_value": 0,
+   "read_only": 0,
+   "remember_last_selected_value": 0,
+   "report_hide": 0,
+   "reqd": 0,
+   "search_index": 0,
+   "set_only_once": 0,
+   "translatable": 0,
    "unique": 0
-  }, 
+  },
   {
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "default": "workflow_state", 
-   "description": "Field that represents the Workflow State of the transaction (if field is not present, a new hidden Custom Field will be created)", 
-   "fieldname": "workflow_state_field", 
-   "fieldtype": "Data", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Workflow State Field", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
+   "allow_bulk_edit": 0,
+   "allow_on_submit": 0,
+   "bold": 0,
+   "collapsible": 0,
+   "columns": 0,
+   "description": "Rules defining transition of state in the workflow.",
+   "fieldname": "transitions",
+   "fieldtype": "Table",
+   "hidden": 0,
+   "ignore_user_permissions": 0,
+   "ignore_xss_filter": 0,
+   "in_filter": 0,
+   "in_global_search": 0,
+   "in_list_view": 0,
+   "in_standard_filter": 0,
+   "label": "Transitions",
+   "length": 0,
+   "no_copy": 0,
+   "options": "Workflow Transition",
+   "permlevel": 0,
+   "print_hide": 0,
+   "print_hide_if_no_value": 0,
+   "read_only": 0,
+   "remember_last_selected_value": 0,
+   "report_hide": 0,
+   "reqd": 1,
+   "search_index": 0,
+   "set_only_once": 0,
+   "translatable": 0,
+   "unique": 0
+  },
+  {
+   "allow_bulk_edit": 0,
+   "allow_on_submit": 0,
+   "bold": 0,
+   "collapsible": 0,
+   "columns": 0,
+   "default": "workflow_state",
+   "description": "Field that represents the Workflow State of the transaction (if field is not present, a new hidden Custom Field will be created)",
+   "fieldname": "workflow_state_field",
+   "fieldtype": "Data",
+   "hidden": 0,
+   "ignore_user_permissions": 0,
+   "ignore_xss_filter": 0,
+   "in_filter": 0,
+   "in_global_search": 0,
+   "in_list_view": 0,
+   "in_standard_filter": 0,
+   "label": "Workflow State Field",
+   "length": 0,
+   "no_copy": 0,
+   "permlevel": 0,
+   "print_hide": 0,
+   "print_hide_if_no_value": 0,
+   "read_only": 0,
+   "remember_last_selected_value": 0,
+   "report_hide": 0,
+   "reqd": 0,
+   "search_index": 0,
+   "set_only_once": 0,
+   "translatable": 0,
    "unique": 0
   }
- ], 
- "hide_heading": 0, 
- "hide_toolbar": 0, 
- "icon": "fa fa-random", 
- "idx": 1, 
- "image_view": 0, 
- "in_create": 0, 
-
- "is_submittable": 0, 
- "issingle": 0, 
- "istable": 0, 
- "max_attachments": 0, 
- "menu_index": 0, 
- "modified": "2017-02-20 13:33:38.497751", 
- "modified_by": "Administrator", 
- "module": "Workflow", 
- "name": "Workflow", 
- "owner": "Administrator", 
+ ],
+ "has_web_view": 0,
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "icon": "fa fa-random",
+ "idx": 1,
+ "image_view": 0,
+ "in_create": 0,
+ "is_submittable": 0,
+ "issingle": 0,
+ "istable": 0,
+ "max_attachments": 0,
+ "menu_index": 0,
+ "modified": "2018-05-21 09:49:54.487644",
+ "modified_by": "Administrator",
+ "module": "Workflow",
+ "name": "Workflow",
+ "owner": "Administrator",
  "permissions": [
   {
-   "amend": 0, 
-   "apply_user_permissions": 0, 
-   "cancel": 0, 
-   "create": 1, 
-   "delete": 1, 
-   "email": 1, 
-   "export": 0, 
-   "if_owner": 0, 
-   "import": 0, 
-   "permlevel": 0, 
-   "print": 1, 
-   "read": 1, 
-   "report": 0, 
-   "role": "System Manager", 
-   "set_user_permissions": 0, 
-   "share": 1, 
-   "submit": 0, 
+   "amend": 0,
+   "cancel": 0,
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 0,
+   "if_owner": 0,
+   "import": 0,
+   "permlevel": 0,
+   "print": 1,
+   "read": 1,
+   "report": 0,
+   "role": "System Manager",
+   "set_user_permissions": 0,
+   "share": 1,
+   "submit": 0,
    "write": 1
   }
- ], 
- "quick_entry": 0, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "show_name_in_global_search": 1, 
- "sort_order": "ASC", 
- "track_changes": 1, 
+ ],
+ "quick_entry": 0,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "show_name_in_global_search": 1,
+ "sort_order": "ASC",
+ "track_changes": 1,
  "track_seen": 0
 }
\ No newline at end of file
diff --git a/frappe/workflow/doctype/workflow_action/test_workflow_action.py b/frappe/workflow/doctype/workflow_action/test_workflow_action.py
deleted file mode 100644
index d70ee9151bbd..000000000000
--- a/frappe/workflow/doctype/workflow_action/test_workflow_action.py
+++ /dev/null
@@ -1,12 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-from __future__ import unicode_literals
-
-import frappe
-import unittest
-
-# test_records = frappe.get_test_records('Workflow Action')
-
-class TestWorkflowAction(unittest.TestCase):
-	pass
diff --git a/frappe/workflow/doctype/workflow_action/workflow_action.json b/frappe/workflow/doctype/workflow_action/workflow_action.json
index 744471f0410f..5f8d16dbe86f 100644
--- a/frappe/workflow/doctype/workflow_action/workflow_action.json
+++ b/frappe/workflow/doctype/workflow_action/workflow_action.json
@@ -1,86 +1,254 @@
 {
  "allow_copy": 0, 
+ "allow_guest_to_view": 0, 
  "allow_import": 0, 
  "allow_rename": 0, 
- "autoname": "field:workflow_action_name", 
+ "autoname": "WFA.####", 
  "beta": 0, 
- "creation": "2012-12-28 10:49:56", 
+ "creation": "2018-05-17 18:29:03.923384", 
  "custom": 0, 
- "description": "Workflow Action Master", 
  "docstatus": 0, 
  "doctype": "DocType", 
- "editable_grid": 0, 
+ "document_type": "", 
+ "editable_grid": 1, 
+ "engine": "InnoDB", 
  "fields": [
   {
+   "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
    "columns": 0, 
-   "fieldname": "workflow_action_name", 
+   "fieldname": "status", 
+   "fieldtype": "Select", 
+   "hidden": 0, 
+   "ignore_user_permissions": 0, 
+   "ignore_xss_filter": 0, 
+   "in_filter": 0, 
+   "in_global_search": 0, 
+   "in_list_view": 1, 
+   "in_standard_filter": 0, 
+   "label": "Status", 
+   "length": 0, 
+   "no_copy": 0, 
+   "options": "Open\nCompleted", 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 0, 
+   "print_hide_if_no_value": 0, 
+   "read_only": 0, 
+   "remember_last_selected_value": 0, 
+   "report_hide": 0, 
+   "reqd": 0, 
+   "search_index": 0, 
+   "set_only_once": 0, 
+   "translatable": 0, 
+   "unique": 0
+  }, 
+  {
+   "allow_bulk_edit": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
+   "fieldname": "reference_name", 
+   "fieldtype": "Dynamic Link", 
+   "hidden": 0, 
+   "ignore_user_permissions": 0, 
+   "ignore_xss_filter": 0, 
+   "in_filter": 0, 
+   "in_global_search": 0, 
+   "in_list_view": 0, 
+   "in_standard_filter": 0, 
+   "label": "Reference Name", 
+   "length": 0, 
+   "no_copy": 0, 
+   "options": "reference_doctype", 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 0, 
+   "print_hide_if_no_value": 0, 
+   "read_only": 0, 
+   "remember_last_selected_value": 0, 
+   "report_hide": 0, 
+   "reqd": 0, 
+   "search_index": 0, 
+   "set_only_once": 0, 
+   "translatable": 0, 
+   "unique": 0
+  }, 
+  {
+   "allow_bulk_edit": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
+   "fieldname": "reference_doctype", 
+   "fieldtype": "Link", 
+   "hidden": 0, 
+   "ignore_user_permissions": 0, 
+   "ignore_xss_filter": 0, 
+   "in_filter": 0, 
+   "in_global_search": 0, 
+   "in_list_view": 1, 
+   "in_standard_filter": 0, 
+   "label": "Reference Doctype", 
+   "length": 0, 
+   "no_copy": 0, 
+   "options": "DocType", 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 0, 
+   "print_hide_if_no_value": 0, 
+   "read_only": 0, 
+   "remember_last_selected_value": 0, 
+   "report_hide": 0, 
+   "reqd": 0, 
+   "search_index": 0, 
+   "set_only_once": 0, 
+   "translatable": 0, 
+   "unique": 0
+  }, 
+  {
+   "allow_bulk_edit": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
+   "fieldname": "user", 
+   "fieldtype": "Link", 
+   "hidden": 1, 
+   "ignore_user_permissions": 0, 
+   "ignore_xss_filter": 0, 
+   "in_filter": 0, 
+   "in_global_search": 0, 
+   "in_list_view": 0, 
+   "in_standard_filter": 0, 
+   "label": "User", 
+   "length": 0, 
+   "no_copy": 0, 
+   "options": "User", 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 0, 
+   "print_hide_if_no_value": 0, 
+   "read_only": 0, 
+   "remember_last_selected_value": 0, 
+   "report_hide": 0, 
+   "reqd": 0, 
+   "search_index": 0, 
+   "set_only_once": 0, 
+   "translatable": 0, 
+   "unique": 0
+  }, 
+  {
+   "allow_bulk_edit": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
+   "fieldname": "workflow_state", 
    "fieldtype": "Data", 
+   "hidden": 1, 
+   "ignore_user_permissions": 0, 
+   "ignore_xss_filter": 0, 
+   "in_filter": 0, 
+   "in_global_search": 0, 
+   "in_list_view": 0, 
+   "in_standard_filter": 0, 
+   "label": "Workflow State", 
+   "length": 0, 
+   "no_copy": 0, 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 0, 
+   "print_hide_if_no_value": 0, 
+   "read_only": 0, 
+   "remember_last_selected_value": 0, 
+   "report_hide": 0, 
+   "reqd": 0, 
+   "search_index": 0, 
+   "set_only_once": 0, 
+   "translatable": 0, 
+   "unique": 0
+  }, 
+  {
+   "allow_bulk_edit": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
+   "fieldname": "completed_by", 
+   "fieldtype": "Link", 
    "hidden": 0, 
    "ignore_user_permissions": 0, 
    "ignore_xss_filter": 0, 
    "in_filter": 0, 
+   "in_global_search": 0, 
    "in_list_view": 0, 
    "in_standard_filter": 0, 
-   "label": "Workflow Action Name", 
+   "label": "Completed By", 
    "length": 0, 
    "no_copy": 0, 
+   "options": "User", 
    "permlevel": 0, 
+   "precision": "", 
    "print_hide": 0, 
    "print_hide_if_no_value": 0, 
    "read_only": 0, 
    "remember_last_selected_value": 0, 
    "report_hide": 0, 
-   "reqd": 1, 
+   "reqd": 0, 
    "search_index": 0, 
    "set_only_once": 0, 
+   "translatable": 0, 
    "unique": 0
   }
  ], 
+ "has_web_view": 0, 
  "hide_heading": 0, 
  "hide_toolbar": 0, 
- "icon": "fa fa-flag", 
- "idx": 1, 
+ "idx": 0, 
  "image_view": 0, 
  "in_create": 0, 
-
  "is_submittable": 0, 
  "issingle": 0, 
  "istable": 0, 
  "max_attachments": 0, 
- "modified": "2016-12-29 14:40:34.796187", 
+ "modified": "2018-05-22 13:53:46.788548", 
  "modified_by": "Administrator", 
  "module": "Workflow", 
  "name": "Workflow Action", 
+ "name_case": "", 
  "owner": "Administrator", 
  "permissions": [
   {
    "amend": 0, 
-   "apply_user_permissions": 0, 
    "cancel": 0, 
-   "create": 1, 
+   "create": 0, 
    "delete": 1, 
-   "email": 1, 
+   "email": 0, 
    "export": 0, 
    "if_owner": 0, 
    "import": 0, 
-   "is_custom": 0, 
    "permlevel": 0, 
-   "print": 1, 
+   "print": 0, 
    "read": 1, 
    "report": 0, 
-   "role": "System Manager", 
+   "role": "All", 
    "set_user_permissions": 0, 
-   "share": 1, 
+   "share": 0, 
    "submit": 0, 
-   "write": 1
+   "write": 0
   }
  ], 
- "quick_entry": 1, 
+ "quick_entry": 0, 
  "read_only": 0, 
  "read_only_onload": 0, 
+ "show_name_in_global_search": 0, 
+ "sort_field": "modified", 
+ "sort_order": "DESC", 
+ "title_field": "reference_name", 
  "track_changes": 1, 
  "track_seen": 0
 }
\ No newline at end of file
diff --git a/frappe/workflow/doctype/workflow_action/workflow_action.py b/frappe/workflow/doctype/workflow_action/workflow_action.py
index 4b0c3a4ca1a6..ef88bcf217d2 100644
--- a/frappe/workflow/doctype/workflow_action/workflow_action.py
+++ b/frappe/workflow/doctype/workflow_action/workflow_action.py
@@ -3,8 +3,164 @@
 
 from __future__ import unicode_literals
 import frappe
-
 from frappe.model.document import Document
+from frappe.core.page.permission_manager.permission_manager import get_users_with_role
+from frappe.utils.background_jobs import enqueue
+from frappe.utils import get_url
+from frappe.utils.verified_command import get_signed_params, verify_request
+from frappe import _
+from frappe.model.workflow import apply_workflow, get_workflow_name, has_approval_access
+from frappe.desk.notifications import clear_doctype_notifications
 
 class WorkflowAction(Document):
-	pass
\ No newline at end of file
+	pass
+
+
+def get_permission_query_conditions(user):
+	if not user: user = frappe.session.user
+
+	if user == "Administrator": return ""
+
+	return "(`tabWorkflow Action`.user='{user}')".format(user=user)
+
+
+def has_permission(doc, user):
+	if user not in ['Administrator', doc.user]:
+		return False
+
+
+def create_workflow_actions(doc, state):
+
+	reference_doctype = doc.doctype
+	reference_name = doc.name
+	workflow = get_workflow_name(reference_doctype)
+
+	if not workflow: return
+
+	workflow = frappe.get_doc('Workflow', workflow)
+
+	doc_current_state = doc.get(workflow.workflow_state_field)
+
+	# hack to check if workflow action was already created and is this method called again due some field changes
+	workflow_action_already_created = frappe.db.count('Workflow Action', {
+		'reference_doctype': reference_doctype,
+		'reference_name': reference_name,
+		'workflow_state': doc_current_state
+	})
+
+	if workflow_action_already_created: return
+
+	immediate_next_possible_transitions = [d for d in workflow.transitions if d.state == doc_current_state]
+
+	current_user = frappe.session.user
+
+	# update status of completed workflow action of user
+	frappe.db.sql('''update `tabWorkflow Action` set status='Completed', completed_by=%s
+		where reference_doctype=%s and reference_name=%s and user=%s''',
+		(current_user, reference_doctype, reference_name, current_user))
+
+	# delete irrelevant workflow actions
+	frappe.db.sql('''delete from `tabWorkflow Action`
+		where reference_doctype=%s and reference_name=%s and user!=%s''',
+		(reference_doctype, reference_name, current_user))
+
+	clear_doctype_notifications('Workflow Action')
+
+	from frappe.permissions import has_permission
+
+	email_map = {}
+
+	for transition in immediate_next_possible_transitions:
+		users = get_users_with_role(transition.allowed)
+		for user in users:
+			if (has_approval_access(doc, workflow, user) and
+				has_permission(doctype=doc, ptype='read', user=user)): pass
+			else: continue
+
+			if not email_map.get(user):
+				email_map[user] = {
+					'possible_actions': [],
+					'email': frappe.db.get_value('User', user, 'email'),
+				}
+
+			email_map[user].get('possible_actions').append({
+				'action_name': transition.action,
+				'action_link': get_workflow_action_url(
+					transition.action,
+					reference_doctype,
+					reference_name,
+					doc_current_state)
+			})
+
+	for user in email_map.keys():
+		newdoc = frappe.get_doc({
+				'doctype': 'Workflow Action',
+				'reference_doctype': reference_doctype,
+				'reference_name': reference_name,
+				'workflow_state': doc_current_state,
+				'status': 'Open',
+				'user': user
+			})
+		newdoc.insert(ignore_permissions=True)
+
+	frappe.db.commit()
+	if email_map:
+		send_workflow_action_email(email_map, reference_doctype, reference_name)
+
+
+def send_workflow_action_email(email_map, doctype, docname):
+	email_message = _('{0}:{1}'.format(doctype, docname))
+	common_args = {
+		'template': 'workflow_action',
+		'attachments': [frappe.attach_print(doctype, docname , file_name=docname)],
+		'header': [_("Workflow Action"), "orange"],
+		'subject': _('Workflow Action')
+	}
+
+	for d in email_map.values():
+		email_args = {
+			'recipients': [d.get('email')],
+			'args': {
+				'actions': d.get('possible_actions'),
+				'message': email_message
+			},
+		}
+		email_args.update(common_args)
+		enqueue(method=frappe.sendmail, queue='short', **email_args)
+
+
+@frappe.whitelist()
+def apply_action(action, doctype, docname, current_state):
+	verify_request()
+	doc = frappe.get_doc(doctype, docname)
+	workflow = get_workflow_name(doctype)
+	doc_workflow_state = doc.get(
+		frappe.get_value('Workflow', workflow, 'workflow_state_field')
+	)
+
+	if doc_workflow_state == current_state:
+		try:
+			apply_workflow(doc, action)
+			frappe.respond_as_web_page(_("Success"),
+				_("Done"),
+				indicator_color='green')
+		except Exception as e:
+			frappe.respond_as_web_page(_("Error"), e,
+				indicator_color='red')
+	else:
+		frappe.respond_as_web_page(_("Link Expired"),
+			_("Document {0} has been updated by {1}".format(docname, doc.get("modified_by"))),
+			indicator_color='blue')
+
+
+def get_workflow_action_url(action, doctype, docname, current_state):
+	apply_action_method = "/api/method/frappe.workflow.doctype.workflow_action.workflow_action.apply_action"
+
+	params = {
+		"doctype": doctype,
+		"docname": docname,
+		"action": action,
+		"current_state": current_state
+	}
+
+	return get_url(apply_action_method + "?" + get_signed_params(params))
\ No newline at end of file
diff --git a/frappe/workflow/doctype/workflow_action/workflow_action_list.js b/frappe/workflow/doctype/workflow_action/workflow_action_list.js
new file mode 100644
index 000000000000..d1774e7bb9fe
--- /dev/null
+++ b/frappe/workflow/doctype/workflow_action/workflow_action_list.js
@@ -0,0 +1,19 @@
+frappe.listview_settings['Workflow Action'] = {
+	get_form_link: (doc) => {
+		let doctype = '';
+		let docname = '';
+		if(doc.status === 'Open') {
+			doctype = doc.reference_doctype;
+			docname = doc.reference_name;
+		} else {
+			doctype = 'Workflow Action';
+			docname = doc.name;
+		}
+		docname = docname.match(/[%'"]/)
+			? encodeURIComponent(docname)
+			: docname;
+
+		const link = '#Form/' + doctype + '/' + docname;
+		return link;
+	}
+};
\ No newline at end of file
diff --git a/frappe/workflow/doctype/workflow_action_master/__init__.py b/frappe/workflow/doctype/workflow_action_master/__init__.py
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/frappe/workflow/doctype/workflow_action_master/workflow_action_master.js b/frappe/workflow/doctype/workflow_action_master/workflow_action_master.js
new file mode 100644
index 000000000000..dce775b1cc5c
--- /dev/null
+++ b/frappe/workflow/doctype/workflow_action_master/workflow_action_master.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2018, Frappe Technologies and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Workflow Action Master', {
+	refresh: function() {
+
+	}
+});
diff --git a/frappe/workflow/doctype/workflow_action_master/workflow_action_master.json b/frappe/workflow/doctype/workflow_action_master/workflow_action_master.json
new file mode 100644
index 000000000000..4931ac5b13d1
--- /dev/null
+++ b/frappe/workflow/doctype/workflow_action_master/workflow_action_master.json
@@ -0,0 +1,89 @@
+{
+ "allow_copy": 0, 
+ "allow_guest_to_view": 0, 
+ "allow_import": 0, 
+ "allow_rename": 0, 
+ "autoname": "field:workflow_action_name", 
+ "beta": 0, 
+ "creation": "2012-12-28 10:49:56", 
+ "custom": 0, 
+ "description": "Workflow Action Master", 
+ "docstatus": 0, 
+ "doctype": "DocType", 
+ "editable_grid": 0, 
+ "fields": [
+  {
+   "allow_bulk_edit": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
+   "fieldname": "workflow_action_name", 
+   "fieldtype": "Data", 
+   "hidden": 0, 
+   "ignore_user_permissions": 0, 
+   "ignore_xss_filter": 0, 
+   "in_filter": 0, 
+   "in_global_search": 0, 
+   "in_list_view": 1, 
+   "in_standard_filter": 0, 
+   "label": "Workflow Action Name", 
+   "length": 0, 
+   "no_copy": 0, 
+   "permlevel": 0, 
+   "print_hide": 0, 
+   "print_hide_if_no_value": 0, 
+   "read_only": 0, 
+   "remember_last_selected_value": 0, 
+   "report_hide": 0, 
+   "reqd": 1, 
+   "search_index": 0, 
+   "set_only_once": 0, 
+   "translatable": 0, 
+   "unique": 0
+  }
+ ], 
+ "has_web_view": 0, 
+ "hide_heading": 0, 
+ "hide_toolbar": 0, 
+ "icon": "fa fa-flag", 
+ "idx": 1, 
+ "image_view": 0, 
+ "in_create": 0, 
+ "is_submittable": 0, 
+ "issingle": 0, 
+ "istable": 0, 
+ "max_attachments": 0, 
+ "modified": "2018-05-18 10:31:09.040701", 
+ "modified_by": "Administrator", 
+ "module": "Workflow", 
+ "name": "Workflow Action Master", 
+ "owner": "Administrator", 
+ "permissions": [
+  {
+   "amend": 0, 
+   "cancel": 0, 
+   "create": 1, 
+   "delete": 1, 
+   "email": 1, 
+   "export": 0, 
+   "if_owner": 0, 
+   "import": 0, 
+   "permlevel": 0, 
+   "print": 1, 
+   "read": 1, 
+   "report": 0, 
+   "role": "System Manager", 
+   "set_user_permissions": 0, 
+   "share": 1, 
+   "submit": 0, 
+   "write": 1
+  }
+ ], 
+ "quick_entry": 1, 
+ "read_only": 0, 
+ "read_only_onload": 0, 
+ "show_name_in_global_search": 0, 
+ "track_changes": 1, 
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/frappe/workflow/doctype/workflow_action_master/workflow_action_master.py b/frappe/workflow/doctype/workflow_action_master/workflow_action_master.py
new file mode 100644
index 000000000000..df1123f786c8
--- /dev/null
+++ b/frappe/workflow/doctype/workflow_action_master/workflow_action_master.py
@@ -0,0 +1,9 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2018, Frappe Technologies and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+from frappe.model.document import Document
+
+class WorkflowActionMaster(Document):
+	pass
diff --git a/frappe/workflow/doctype/workflow_transition/workflow_transition.json b/frappe/workflow/doctype/workflow_transition/workflow_transition.json
index 835ac29c8de2..8797d2ab9f0f 100644
--- a/frappe/workflow/doctype/workflow_transition/workflow_transition.json
+++ b/frappe/workflow/doctype/workflow_transition/workflow_transition.json
@@ -1,293 +1,293 @@
 {
- "allow_copy": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "beta": 0,
- "creation": "2013-02-22 01:27:36",
- "custom": 0,
- "description": "Defines actions on states and the next step and allowed roles.",
- "docstatus": 0,
- "doctype": "DocType",
- "editable_grid": 1,
+ "allow_copy": 0, 
+ "allow_guest_to_view": 0, 
+ "allow_import": 0, 
+ "allow_rename": 0, 
+ "beta": 0, 
+ "creation": "2013-02-22 01:27:36", 
+ "custom": 0, 
+ "description": "Defines actions on states and the next step and allowed roles.", 
+ "docstatus": 0, 
+ "doctype": "DocType", 
+ "editable_grid": 1, 
  "fields": [
   {
-   "allow_bulk_edit": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fieldname": "state",
-   "fieldtype": "Link",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 1,
-   "in_standard_filter": 0,
-   "label": "State",
-   "length": 0,
-   "no_copy": 0,
-   "options": "Workflow State",
-   "permlevel": 0,
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "print_width": "200px",
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 1,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0,
+   "allow_bulk_edit": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
+   "fieldname": "state", 
+   "fieldtype": "Link", 
+   "hidden": 0, 
+   "ignore_user_permissions": 0, 
+   "ignore_xss_filter": 0, 
+   "in_filter": 0, 
+   "in_global_search": 0, 
+   "in_list_view": 1, 
+   "in_standard_filter": 0, 
+   "label": "State", 
+   "length": 0, 
+   "no_copy": 0, 
+   "options": "Workflow State", 
+   "permlevel": 0, 
+   "print_hide": 0, 
+   "print_hide_if_no_value": 0, 
+   "print_width": "200px", 
+   "read_only": 0, 
+   "remember_last_selected_value": 0, 
+   "report_hide": 0, 
+   "reqd": 1, 
+   "search_index": 0, 
+   "set_only_once": 0, 
+   "translatable": 0, 
+   "unique": 0, 
    "width": "200px"
-  },
+  }, 
   {
-   "allow_bulk_edit": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fieldname": "action",
-   "fieldtype": "Link",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 1,
-   "in_standard_filter": 0,
-   "label": "Action",
-   "length": 0,
-   "no_copy": 0,
-   "options": "Workflow Action",
-   "permlevel": 0,
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "print_width": "200px",
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 1,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0,
+   "allow_bulk_edit": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
+   "fieldname": "action", 
+   "fieldtype": "Link", 
+   "hidden": 0, 
+   "ignore_user_permissions": 0, 
+   "ignore_xss_filter": 0, 
+   "in_filter": 0, 
+   "in_global_search": 0, 
+   "in_list_view": 1, 
+   "in_standard_filter": 0, 
+   "label": "Action", 
+   "length": 0, 
+   "no_copy": 0, 
+   "options": "Workflow Action Master", 
+   "permlevel": 0, 
+   "print_hide": 0, 
+   "print_hide_if_no_value": 0, 
+   "print_width": "200px", 
+   "read_only": 0, 
+   "remember_last_selected_value": 0, 
+   "report_hide": 0, 
+   "reqd": 1, 
+   "search_index": 0, 
+   "set_only_once": 0, 
+   "translatable": 0, 
+   "unique": 0, 
    "width": "200px"
-  },
+  }, 
   {
-   "allow_bulk_edit": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fieldname": "next_state",
-   "fieldtype": "Link",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 1,
-   "in_standard_filter": 0,
-   "label": "Next State",
-   "length": 0,
-   "no_copy": 0,
-   "options": "Workflow State",
-   "permlevel": 0,
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "print_width": "200px",
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 1,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0,
+   "allow_bulk_edit": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
+   "fieldname": "next_state", 
+   "fieldtype": "Link", 
+   "hidden": 0, 
+   "ignore_user_permissions": 0, 
+   "ignore_xss_filter": 0, 
+   "in_filter": 0, 
+   "in_global_search": 0, 
+   "in_list_view": 1, 
+   "in_standard_filter": 0, 
+   "label": "Next State", 
+   "length": 0, 
+   "no_copy": 0, 
+   "options": "Workflow State", 
+   "permlevel": 0, 
+   "print_hide": 0, 
+   "print_hide_if_no_value": 0, 
+   "print_width": "200px", 
+   "read_only": 0, 
+   "remember_last_selected_value": 0, 
+   "report_hide": 0, 
+   "reqd": 1, 
+   "search_index": 0, 
+   "set_only_once": 0, 
+   "translatable": 0, 
+   "unique": 0, 
    "width": "200px"
-  },
+  }, 
   {
-   "allow_bulk_edit": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fieldname": "allowed",
-   "fieldtype": "Link",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 1,
-   "in_standard_filter": 0,
-   "label": "Allowed",
-   "length": 0,
-   "no_copy": 0,
-   "options": "Role",
-   "permlevel": 0,
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "print_width": "200px",
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 1,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0,
+   "allow_bulk_edit": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
+   "fieldname": "allowed", 
+   "fieldtype": "Link", 
+   "hidden": 0, 
+   "ignore_user_permissions": 0, 
+   "ignore_xss_filter": 0, 
+   "in_filter": 0, 
+   "in_global_search": 0, 
+   "in_list_view": 1, 
+   "in_standard_filter": 0, 
+   "label": "Allowed", 
+   "length": 0, 
+   "no_copy": 0, 
+   "options": "Role", 
+   "permlevel": 0, 
+   "print_hide": 0, 
+   "print_hide_if_no_value": 0, 
+   "print_width": "200px", 
+   "read_only": 0, 
+   "remember_last_selected_value": 0, 
+   "report_hide": 0, 
+   "reqd": 1, 
+   "search_index": 0, 
+   "set_only_once": 0, 
+   "translatable": 0, 
+   "unique": 0, 
    "width": "200px"
-  },
+  }, 
   {
-   "allow_bulk_edit": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fieldname": "conditions",
-   "fieldtype": "Section Break",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
-   "label": "Conditions",
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
+   "allow_bulk_edit": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
+   "fieldname": "conditions", 
+   "fieldtype": "Section Break", 
+   "hidden": 0, 
+   "ignore_user_permissions": 0, 
+   "ignore_xss_filter": 0, 
+   "in_filter": 0, 
+   "in_global_search": 0, 
+   "in_list_view": 0, 
+   "in_standard_filter": 0, 
+   "label": "Conditions", 
+   "length": 0, 
+   "no_copy": 0, 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 0, 
+   "print_hide_if_no_value": 0, 
+   "read_only": 0, 
+   "remember_last_selected_value": 0, 
+   "report_hide": 0, 
+   "reqd": 0, 
+   "search_index": 0, 
+   "set_only_once": 0, 
+   "translatable": 0, 
    "unique": 0
-  },
+  }, 
   {
-   "allow_bulk_edit": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fieldname": "condition",
-   "fieldtype": "Code",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
-   "label": "Condition",
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
+   "allow_bulk_edit": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
+   "fieldname": "condition", 
+   "fieldtype": "Code", 
+   "hidden": 0, 
+   "ignore_user_permissions": 0, 
+   "ignore_xss_filter": 0, 
+   "in_filter": 0, 
+   "in_global_search": 0, 
+   "in_list_view": 0, 
+   "in_standard_filter": 0, 
+   "label": "Condition", 
+   "length": 0, 
+   "no_copy": 0, 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 0, 
+   "print_hide_if_no_value": 0, 
+   "read_only": 0, 
+   "remember_last_selected_value": 0, 
+   "report_hide": 0, 
+   "reqd": 0, 
+   "search_index": 0, 
+   "set_only_once": 0, 
+   "translatable": 0, 
    "unique": 0
-  },
+  }, 
   {
-   "allow_bulk_edit": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fieldname": "column_break_7",
-   "fieldtype": "Column Break",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
+   "allow_bulk_edit": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
+   "fieldname": "column_break_7", 
+   "fieldtype": "Column Break", 
+   "hidden": 0, 
+   "ignore_user_permissions": 0, 
+   "ignore_xss_filter": 0, 
+   "in_filter": 0, 
+   "in_global_search": 0, 
+   "in_list_view": 0, 
+   "in_standard_filter": 0, 
+   "length": 0, 
+   "no_copy": 0, 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 0, 
+   "print_hide_if_no_value": 0, 
+   "read_only": 0, 
+   "remember_last_selected_value": 0, 
+   "report_hide": 0, 
+   "reqd": 0, 
+   "search_index": 0, 
+   "set_only_once": 0, 
+   "translatable": 0, 
    "unique": 0
-  },
+  }, 
   {
-   "allow_bulk_edit": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fieldname": "example",
-   "fieldtype": "HTML",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
-   "label": "Example",
-   "length": 0,
-   "no_copy": 0,
-   "options": "<pre><code>doc.grand_total &gt; 0</code></pre>\n\n<p>Conditions should be written in simple Python. Please use properties available in the form only.</p>",
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
+   "allow_bulk_edit": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
+   "fieldname": "example", 
+   "fieldtype": "HTML", 
+   "hidden": 0, 
+   "ignore_user_permissions": 0, 
+   "ignore_xss_filter": 0, 
+   "in_filter": 0, 
+   "in_global_search": 0, 
+   "in_list_view": 0, 
+   "in_standard_filter": 0, 
+   "label": "Example", 
+   "length": 0, 
+   "no_copy": 0, 
+   "options": "<pre><code>doc.grand_total &gt; 0</code></pre>\n\n<p>Conditions should be written in simple Python. Please use properties available in the form only.</p>", 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 0, 
+   "print_hide_if_no_value": 0, 
+   "read_only": 0, 
+   "remember_last_selected_value": 0, 
+   "report_hide": 0, 
+   "reqd": 0, 
+   "search_index": 0, 
+   "set_only_once": 0, 
+   "translatable": 0, 
    "unique": 0
   }
- ],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
- "idx": 1,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 0,
- "issingle": 0,
- "istable": 1,
- "max_attachments": 0,
- "modified": "2018-04-04 23:41:58.233754",
- "modified_by": "Administrator",
- "module": "Workflow",
- "name": "Workflow Transition",
- "owner": "Administrator",
- "permissions": [],
- "quick_entry": 0,
- "read_only": 0,
- "read_only_onload": 0,
- "show_name_in_global_search": 0,
- "track_changes": 0,
+ ], 
+ "has_web_view": 0, 
+ "hide_heading": 0, 
+ "hide_toolbar": 0, 
+ "idx": 1, 
+ "image_view": 0, 
+ "in_create": 0, 
+ "is_submittable": 0, 
+ "issingle": 0, 
+ "istable": 1, 
+ "max_attachments": 0, 
+ "modified": "2018-05-18 10:30:54.706642", 
+ "modified_by": "Administrator", 
+ "module": "Workflow", 
+ "name": "Workflow Transition", 
+ "owner": "Administrator", 
+ "permissions": [], 
+ "quick_entry": 0, 
+ "read_only": 0, 
+ "read_only_onload": 0, 
+ "show_name_in_global_search": 0, 
+ "track_changes": 0, 
  "track_seen": 0
 }
\ No newline at end of file


Code review comments:
diff_hunk: @@ -3,8 +3,164 @@
 
 from __future__ import unicode_literals
 import frappe
-
 from frappe.model.document import Document
+from frappe.core.page.permission_manager.permission_manager import get_users_with_role
+from frappe.utils.background_jobs import enqueue
+from frappe.utils import get_url
+from frappe.utils.verified_command import get_signed_params, verify_request
+from frappe import _
+from frappe.model.workflow import apply_workflow, get_workflow_name, has_approval_access
+from frappe.desk.notifications import clear_doctype_notifications
 
 class WorkflowAction(Document):
-	pass
\ No newline at end of file
+	pass
+
+
+def get_permission_query_conditions(user):
+	if not user: user = frappe.session.user
+
+	if user == "Administrator": return ""
+
+	return "(`tabWorkflow Action`.user='{user}')".format(user=user)
+
+
+def has_permission(doc, user):
+	if user not in ['Administrator', doc.user]:
+		return False
+
+
+def create_workflow_actions(doc, state):
+
+	reference_doctype = doc.doctype
+	reference_name = doc.name
+	workflow = get_workflow_name(reference_doctype)
+
+	if not workflow: return
+
+	workflow = frappe.get_doc('Workflow', workflow)
+
+	doc_current_state = doc.get(workflow.workflow_state_field)
+
+	# hack to check if workflow action was already created and is this method called again due some field changes
+	workflow_action_already_created = frappe.db.count('Workflow Action', {
+		'reference_doctype': reference_doctype,
+		'reference_name': reference_name,
+		'workflow_state': doc_current_state
+	})
+
+	if workflow_action_already_created: return
+
+	immediate_next_possible_transitions = [d for d in workflow.transitions if d.state == doc_current_state]
+
+	current_user = frappe.session.user
+
+	# update status of completed workflow action of user
+	frappe.db.sql('''update `tabWorkflow Action` set status='Completed', completed_by=%s
+		where reference_doctype=%s and reference_name=%s and user=%s''',
+		(current_user, reference_doctype, reference_name, current_user))
+
+	# delete irrelevant workflow actions
+	frappe.db.sql('''delete from `tabWorkflow Action`
+		where reference_doctype=%s and reference_name=%s and user!=%s''',
+		(reference_doctype, reference_name, current_user))
+
+	clear_doctype_notifications('Workflow Action')
+
+	from frappe.permissions import has_permission
+
+	email_map = {}
path: frappe/workflow/doctype/workflow_action/workflow_action.py
text: @user1:
`user_map`

diff_hunk: @@ -3,8 +3,164 @@
 
 from __future__ import unicode_literals
 import frappe
-
 from frappe.model.document import Document
+from frappe.core.page.permission_manager.permission_manager import get_users_with_role
+from frappe.utils.background_jobs import enqueue
+from frappe.utils import get_url
+from frappe.utils.verified_command import get_signed_params, verify_request
+from frappe import _
+from frappe.model.workflow import apply_workflow, get_workflow_name, has_approval_access
+from frappe.desk.notifications import clear_doctype_notifications
 
 class WorkflowAction(Document):
-	pass
\ No newline at end of file
+	pass
+
+
+def get_permission_query_conditions(user):
+	if not user: user = frappe.session.user
+
+	if user == "Administrator": return ""
+
+	return "(`tabWorkflow Action`.user='{user}')".format(user=user)
+
+
+def has_permission(doc, user):
+	if user not in ['Administrator', doc.user]:
+		return False
+
+
+def create_workflow_actions(doc, state):
+
+	reference_doctype = doc.doctype
+	reference_name = doc.name
+	workflow = get_workflow_name(reference_doctype)
+
+	if not workflow: return
+
+	workflow = frappe.get_doc('Workflow', workflow)
+
+	doc_current_state = doc.get(workflow.workflow_state_field)
+
+	# hack to check if workflow action was already created and is this method called again due some field changes
+	workflow_action_already_created = frappe.db.count('Workflow Action', {
+		'reference_doctype': reference_doctype,
+		'reference_name': reference_name,
+		'workflow_state': doc_current_state
+	})
+
+	if workflow_action_already_created: return
+
+	immediate_next_possible_transitions = [d for d in workflow.transitions if d.state == doc_current_state]
+
+	current_user = frappe.session.user
+
+	# update status of completed workflow action of user
+	frappe.db.sql('''update `tabWorkflow Action` set status='Completed', completed_by=%s
+		where reference_doctype=%s and reference_name=%s and user=%s''',
+		(current_user, reference_doctype, reference_name, current_user))
+
+	# delete irrelevant workflow actions
+	frappe.db.sql('''delete from `tabWorkflow Action`
+		where reference_doctype=%s and reference_name=%s and user!=%s''',
+		(reference_doctype, reference_name, current_user))
+
+	clear_doctype_notifications('Workflow Action')
+
+	from frappe.permissions import has_permission
+
+	email_map = {}
+
+	for transition in immediate_next_possible_transitions:
+		users = get_users_with_role(transition.allowed)
+		for user in users:
+			if (has_approval_access(doc, workflow, user) and
+				has_permission(doctype=doc, ptype='read', user=user)): pass
+			else: continue
+
+			if not email_map.get(user):
+				email_map[user] = {
+					'possible_actions': [],
+					'email': frappe.db.get_value('User', user, 'email'),
+				}
+
+			email_map[user].get('possible_actions').append({
+				'action_name': transition.action,
+				'action_link': get_workflow_action_url(
+					transition.action,
+					reference_doctype,
+					reference_name,
+					doc_current_state)
+			})
+
+	for user in email_map.keys():
+		newdoc = frappe.get_doc({
+				'doctype': 'Workflow Action',
+				'reference_doctype': reference_doctype,
+				'reference_name': reference_name,
+				'workflow_state': doc_current_state,
+				'status': 'Open',
+				'user': user
+			})
+		newdoc.insert(ignore_permissions=True)
+
+	frappe.db.commit()
+	if email_map:
+		send_workflow_action_email(email_map, reference_doctype, reference_name)
+
+
+def send_workflow_action_email(email_map, doctype, docname):
+	email_message = _('{0}:{1}'.format(doctype, docname))
path: frappe/workflow/doctype/workflow_action/workflow_action.py
text: @user1:
make this configurable in State table (?) `next action email template` -> link to `Email Template`

diff_hunk: @@ -3,8 +3,164 @@
 
 from __future__ import unicode_literals
 import frappe
-
 from frappe.model.document import Document
+from frappe.core.page.permission_manager.permission_manager import get_users_with_role
+from frappe.utils.background_jobs import enqueue
+from frappe.utils import get_url
+from frappe.utils.verified_command import get_signed_params, verify_request
+from frappe import _
+from frappe.model.workflow import apply_workflow, get_workflow_name, has_approval_access
+from frappe.desk.notifications import clear_doctype_notifications
 
 class WorkflowAction(Document):
-	pass
\ No newline at end of file
+	pass
+
+
+def get_permission_query_conditions(user):
+	if not user: user = frappe.session.user
+
+	if user == "Administrator": return ""
+
+	return "(`tabWorkflow Action`.user='{user}')".format(user=user)
+
+
+def has_permission(doc, user):
+	if user not in ['Administrator', doc.user]:
+		return False
+
+
+def create_workflow_actions(doc, state):
+
+	reference_doctype = doc.doctype
+	reference_name = doc.name
+	workflow = get_workflow_name(reference_doctype)
+
+	if not workflow: return
+
+	workflow = frappe.get_doc('Workflow', workflow)
+
+	doc_current_state = doc.get(workflow.workflow_state_field)
+
+	# hack to check if workflow action was already created and is this method called again due some field changes
+	workflow_action_already_created = frappe.db.count('Workflow Action', {
+		'reference_doctype': reference_doctype,
+		'reference_name': reference_name,
+		'workflow_state': doc_current_state
+	})
+
+	if workflow_action_already_created: return
+
+	immediate_next_possible_transitions = [d for d in workflow.transitions if d.state == doc_current_state]
+
+	current_user = frappe.session.user
+
+	# update status of completed workflow action of user
+	frappe.db.sql('''update `tabWorkflow Action` set status='Completed', completed_by=%s
+		where reference_doctype=%s and reference_name=%s and user=%s''',
+		(current_user, reference_doctype, reference_name, current_user))
+
+	# delete irrelevant workflow actions
+	frappe.db.sql('''delete from `tabWorkflow Action`
+		where reference_doctype=%s and reference_name=%s and user!=%s''',
+		(reference_doctype, reference_name, current_user))
+
+	clear_doctype_notifications('Workflow Action')
+
+	from frappe.permissions import has_permission
+
+	email_map = {}
+
+	for transition in immediate_next_possible_transitions:
+		users = get_users_with_role(transition.allowed)
+		for user in users:
+			if (has_approval_access(doc, workflow, user) and
+				has_permission(doctype=doc, ptype='read', user=user)): pass
+			else: continue
+
+			if not email_map.get(user):
+				email_map[user] = {
+					'possible_actions': [],
+					'email': frappe.db.get_value('User', user, 'email'),
+				}
+
+			email_map[user].get('possible_actions').append({
+				'action_name': transition.action,
+				'action_link': get_workflow_action_url(
+					transition.action,
+					reference_doctype,
+					reference_name,
+					doc_current_state)
+			})
+
+	for user in email_map.keys():
+		newdoc = frappe.get_doc({
+				'doctype': 'Workflow Action',
+				'reference_doctype': reference_doctype,
+				'reference_name': reference_name,
+				'workflow_state': doc_current_state,
+				'status': 'Open',
+				'user': user
+			})
+		newdoc.insert(ignore_permissions=True)
+
+	frappe.db.commit()
+	if email_map:
+		send_workflow_action_email(email_map, reference_doctype, reference_name)
+
+
+def send_workflow_action_email(email_map, doctype, docname):
+	email_message = _('{0}:{1}'.format(doctype, docname))
+	common_args = {
+		'template': 'workflow_action',
+		'attachments': [frappe.attach_print(doctype, docname , file_name=docname)],
+		'header': [_("Workflow Action"), "orange"],
+		'subject': _('Workflow Action')
+	}
+
+	for d in email_map.values():
+		email_args = {
+			'recipients': [d.get('email')],
+			'args': {
+				'actions': d.get('possible_actions'),
+				'message': email_message
+			},
+		}
+		email_args.update(common_args)
+		enqueue(method=frappe.sendmail, queue='short', **email_args)
+
+
+@frappe.whitelist()
+def apply_action(action, doctype, docname, current_state):
+	verify_request()
+	doc = frappe.get_doc(doctype, docname)
+	workflow = get_workflow_name(doctype)
+	doc_workflow_state = doc.get(
+		frappe.get_value('Workflow', workflow, 'workflow_state_field')
+	)
+
+	if doc_workflow_state == current_state:
+		try:
+			apply_workflow(doc, action)
+			frappe.respond_as_web_page(_("Success"),
path: frappe/workflow/doctype/workflow_action/workflow_action.py
text: @user1:
More descriptive.

`Leave Application is set to "Approved"` : `{doctype} is set to "{state}"`

diff_hunk: @@ -3,8 +3,164 @@
 
 from __future__ import unicode_literals
 import frappe
-
 from frappe.model.document import Document
+from frappe.core.page.permission_manager.permission_manager import get_users_with_role
+from frappe.utils.background_jobs import enqueue
+from frappe.utils import get_url
+from frappe.utils.verified_command import get_signed_params, verify_request
+from frappe import _
+from frappe.model.workflow import apply_workflow, get_workflow_name, has_approval_access
+from frappe.desk.notifications import clear_doctype_notifications
 
 class WorkflowAction(Document):
-	pass
\ No newline at end of file
+	pass
+
+
+def get_permission_query_conditions(user):
+	if not user: user = frappe.session.user
+
+	if user == "Administrator": return ""
+
+	return "(`tabWorkflow Action`.user='{user}')".format(user=user)
+
+
+def has_permission(doc, user):
+	if user not in ['Administrator', doc.user]:
+		return False
+
+
+def create_workflow_actions(doc, state):
+
path: frappe/workflow/doctype/workflow_action/workflow_action.py
text: @user1:
`process_workflow_actions`

break up into small functions

diff_hunk: @@ -3,8 +3,164 @@
 
 from __future__ import unicode_literals
 import frappe
-
 from frappe.model.document import Document
+from frappe.core.page.permission_manager.permission_manager import get_users_with_role
+from frappe.utils.background_jobs import enqueue
+from frappe.utils import get_url
+from frappe.utils.verified_command import get_signed_params, verify_request
+from frappe import _
+from frappe.model.workflow import apply_workflow, get_workflow_name, has_approval_access
+from frappe.desk.notifications import clear_doctype_notifications
 
 class WorkflowAction(Document):
-	pass
\ No newline at end of file
+	pass
+
+
+def get_permission_query_conditions(user):
+	if not user: user = frappe.session.user
+
+	if user == "Administrator": return ""
+
+	return "(`tabWorkflow Action`.user='{user}')".format(user=user)
+
+
+def has_permission(doc, user):
+	if user not in ['Administrator', doc.user]:
+		return False
+
+
+def create_workflow_actions(doc, state):
+
+	reference_doctype = doc.doctype
+	reference_name = doc.name
+	workflow = get_workflow_name(reference_doctype)
+
+	if not workflow: return
+
+	workflow = frappe.get_doc('Workflow', workflow)
+
+	doc_current_state = doc.get(workflow.workflow_state_field)
+
+	# hack to check if workflow action was already created and is this method called again due some field changes
+	workflow_action_already_created = frappe.db.count('Workflow Action', {
path: frappe/workflow/doctype/workflow_action/workflow_action.py
text: @user1:
use `frappe.db.exists`

diff_hunk: @@ -100,7 +100,9 @@ def reset(doctype):
 
 @frappe.whitelist()
 def get_users_with_role(role):
-	frappe.only_for("System Manager")
+	if frappe.request and frappe.local.form_dict.cmd == 'get_users_with_role':
path: frappe/core/page/permission_manager/permission_manager.py
text: @user1:
create another method that will be called internally

diff_hunk: @@ -3,8 +3,164 @@
 
 from __future__ import unicode_literals
 import frappe
-
 from frappe.model.document import Document
+from frappe.core.page.permission_manager.permission_manager import get_users_with_role
+from frappe.utils.background_jobs import enqueue
+from frappe.utils import get_url
+from frappe.utils.verified_command import get_signed_params, verify_request
+from frappe import _
+from frappe.model.workflow import apply_workflow, get_workflow_name, has_approval_access
+from frappe.desk.notifications import clear_doctype_notifications
 
 class WorkflowAction(Document):
-	pass
\ No newline at end of file
+	pass
+
+
+def get_permission_query_conditions(user):
+	if not user: user = frappe.session.user
+
+	if user == "Administrator": return ""
+
+	return "(`tabWorkflow Action`.user='{user}')".format(user=user)
+
+
+def has_permission(doc, user):
+	if user not in ['Administrator', doc.user]:
+		return False
+
+
+def create_workflow_actions(doc, state):
+
+	reference_doctype = doc.doctype
+	reference_name = doc.name
+	workflow = get_workflow_name(reference_doctype)
+
+	if not workflow: return
+
+	workflow = frappe.get_doc('Workflow', workflow)
+
+	doc_current_state = doc.get(workflow.workflow_state_field)
+
+	# hack to check if workflow action was already created and is this method called again due some field changes
+	workflow_action_already_created = frappe.db.count('Workflow Action', {
+		'reference_doctype': reference_doctype,
+		'reference_name': reference_name,
+		'workflow_state': doc_current_state
+	})
+
+	if workflow_action_already_created: return
+
+	immediate_next_possible_transitions = [d for d in workflow.transitions if d.state == doc_current_state]
path: frappe/workflow/doctype/workflow_action/workflow_action.py
text: @user1:
`next_possible_transactions` ?