Merge branch 'staging' into develop

This commit is contained in:
Ameya Shenoy 2018-09-26 07:21:28 +00:00
commit e551153ea0
No known key found for this signature in database
GPG key ID: AC016A555657D0A3
19 changed files with 324 additions and 109 deletions

View file

@ -17,7 +17,7 @@ from faker import Faker
from .exceptions import *
from .utils.jinja import (get_jenv, get_template, render_template, get_email_from_template, get_jloader)
__version__ = '10.1.48'
__version__ = '10.1.49'
__title__ = "Frappe Framework"
local = Local()

View file

@ -17,6 +17,9 @@ frappe.ui.form.on('Data Import', {
};
});
// should never check public
frm.fields_dict["import_file"].df.is_private = 1;
frappe.realtime.on("data_import_progress", function(data) {
if (data.data_import === frm.doc.name) {
if (data.reload && data.reload === true) {

View file

@ -73,6 +73,9 @@ class Report(Document):
return True
def update_report_json(self):
if not self.json:
self.json = '{}'
if self.json:
data = json.loads(self.json)
data["add_total_row"] = self.add_total_row
@ -121,7 +124,15 @@ class Report(Document):
else:
# standard report
params = json.loads(self.json)
columns = params.get('columns')
if params.get('columns'):
columns = params.get('columns')
else:
columns = [['name', self.ref_doctype]]
for df in frappe.get_meta(self.ref_doctype).fields:
if df.in_list_view:
columns.append([df.fieldname, self.ref_doctype])
_filters = params.get('filters') or []
if filters:
@ -135,7 +146,11 @@ class Report(Document):
# sort by is saved as DocType.fieldname, covert it to sql
return '`tab{0}`.`{1}`'.format(*parts)
order_by = _format(params.get('sort_by').split('.')) + ' ' + params.get('sort_order')
if params.get('sort_by'):
order_by = _format(params.get('sort_by').split('.')) + ' ' + params.get('sort_order')
else:
order_by = _format(self.ref_doctype, 'modified') + ' desc'
if params.get('sort_by_next'):
order_by += ', ' + _format(params.get('sort_by_next').split('.')) + ' ' + params.get('sort_order_next')

View file

@ -14,6 +14,7 @@
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -40,10 +41,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -69,10 +72,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -99,10 +104,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -129,10 +136,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -158,10 +167,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -188,10 +199,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -218,10 +231,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -247,10 +262,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -277,10 +294,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -307,10 +326,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -337,16 +358,18 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "data",
"fieldtype": "Text",
"fieldtype": "Long Text",
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
@ -367,10 +390,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -397,6 +422,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}
],
@ -410,7 +436,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-02-06 11:56:43.519455",
"modified": "2018-09-21 08:49:07.915376",
"modified_by": "Administrator",
"module": "Core",
"name": "Transaction Log",
@ -419,7 +445,6 @@
"permissions": [
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 0,
"delete": 0,
@ -445,5 +470,6 @@
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0
"track_seen": 0,
"track_views": 0
}

View file

@ -20,7 +20,7 @@ class TransactionLog(Document):
if prev_hash:
self.previous_hash = prev_hash[0][0]
else:
self.previous_hash = _("Indexing broken")
self.previous_hash = "Indexing broken"
else:
self.previous_hash = self.hash_line()
self.transaction_hash = self.hash_line()

View file

@ -59,11 +59,11 @@ def generate_report_result(report, filters=None, user=None):
method_name = get_report_module_dotted_path(module, report.name) + ".execute"
threshold = 10
res = []
start_time = datetime.datetime.now()
# The JOB
res = frappe.get_attr(method_name)(frappe._dict(filters))
end_time = datetime.datetime.now()
if (end_time - start_time).seconds > threshold and not report.prepared_report:
@ -159,6 +159,8 @@ def run(report_name, filters=None, user=None):
frappe.msgprint(_("Must have report permission to access this report."),
raise_exception=True)
result = None
if report.prepared_report:
if filters:
if isinstance(filters, string_types):
@ -167,9 +169,13 @@ def run(report_name, filters=None, user=None):
dn = filters.get("prepared_report_name")
else:
dn = ""
return get_prepared_report_result(report, filters, dn)
result = get_prepared_report_result(report, filters, dn)
else:
return generate_report_result(report, filters, user)
result = generate_report_result(report, filters, user)
result["add_total_row"] = report.add_total_row
return result
def get_prepared_report_result(report, filters, dn=""):

View file

@ -165,9 +165,10 @@ def get_context(context):
if not frappe.safe_eval(recipient.condition, None, context):
continue
if recipient.email_by_document_field:
if validate_email_add(doc.get(recipient.email_by_document_field)):
recipient.email_by_document_field = doc.get(recipient.email_by_document_field).replace(",", "\n")
recipients = recipients + recipient.email_by_document_field.split("\n")
email_ids_value = doc.get(recipient.email_by_document_field)
if validate_email_add(email_ids_value):
email_ids = email_ids_value.replace(",", "\n")
recipients = recipients + email_ids.split("\n")
# else:
# print "invalid email"

View file

@ -88,6 +88,13 @@ def send(recipients=None, sender=None, subject=None, message=None, text_content=
recipients = [r for r in list(set(recipients)) if r and r not in unsubscribed]
if cc:
cc = [r for r in list(set(cc)) if r and r not in unsubscribed]
if not recipients and not cc:
# Recipients may have been unsubscribed, exit quietly
return
email_text_context = text_content
should_append_unsubscribe = (add_unsubscribe_link

View file

@ -12,7 +12,7 @@ source_link = "https://github.com/frappe/frappe"
app_license = "MIT"
develop_version = '11.x.x-develop'
staging_version = '11.0.2'
staging_version = '11.0.3'
app_email = "info@frappe.io"
@ -125,7 +125,10 @@ doc_events = {
"frappe.desk.notifications.clear_doctype_notifications",
"frappe.workflow.doctype.workflow_action.workflow_action.process_workflow_actions"
],
"on_trash": "frappe.desk.notifications.clear_doctype_notifications",
"on_trash": [
"frappe.desk.notifications.clear_doctype_notifications",
"frappe.workflow.doctype.workflow_action.workflow_action.process_workflow_actions"
],
"on_change": [
"frappe.core.doctype.feedback_trigger.feedback_trigger.trigger_feedback_request",
]

View file

@ -161,7 +161,7 @@ def get_role_permissions(doctype_meta, user=None, verbose=False):
def has_permission_without_if_owner_enabled(ptype):
return any(p.get(ptype, 0) and not p.get('if_owner', 0) for p in applicable_permissions)
applicable_permissions = list(filter(is_perm_applicable, doctype_meta.permissions))
applicable_permissions = list(filter(is_perm_applicable, getattr(doctype_meta, 'permissions', [])))
has_if_owner_enabled = any(p.get('if_owner', 0) for p in applicable_permissions)
for ptype in rights:

View file

@ -305,38 +305,6 @@
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "enable_print_server",
"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": "Enable Print Server",
"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_in_quick_entry": 0,
@ -473,7 +441,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.enable_print_server == 1",
"depends_on": "",
"fieldname": "server_printer",
"fieldtype": "Section Break",
"hidden": 0,
@ -499,6 +467,38 @@
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "enable_print_server",
"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": "Enable Print Server",
"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_in_quick_entry": 0,
@ -507,6 +507,7 @@
"collapsible": 0,
"columns": 0,
"default": "localhost",
"depends_on": "enable_print_server",
"fieldname": "server_ip",
"fieldtype": "Data",
"hidden": 0,
@ -539,6 +540,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "enable_print_server",
"fieldname": "printer_name",
"fieldtype": "Select",
"hidden": 0,
@ -564,37 +566,6 @@
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_17",
"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_in_quick_entry": 0,
@ -603,6 +574,7 @@
"collapsible": 0,
"columns": 0,
"default": "631",
"depends_on": "enable_print_server",
"fieldname": "port",
"fieldtype": "Int",
"hidden": 0,
@ -832,7 +804,7 @@
"issingle": 1,
"istable": 0,
"max_attachments": 0,
"modified": "2018-06-29 01:34:57.508272",
"modified": "2018-09-20 12:10:14.440598",
"modified_by": "Administrator",
"module": "Printing",
"name": "Print Settings",

View file

@ -495,7 +495,13 @@ frappe.ui.form.Layout = Class.extend({
var parent = this.frm ? this.frm.doc : null;
if(expression.substr(0,5)=='eval:') {
if(typeof(expression) === 'boolean') {
out = expression;
} else if(typeof(expression) === 'function') {
out = expression(doc);
} else if(expression.substr(0,5)=='eval:') {
try {
out = eval(expression.substr(5));
} catch(e) {

View file

@ -265,6 +265,7 @@ frappe.ui.BaseList = Class.extend({
clear: function () {
this.data = [];
this.wrapper.find('.list-select-all').prop('checked', false);
this.wrapper.find('.result-list').empty();
this.wrapper.find('.result').show();
this.wrapper.find('.no-result').hide();

View file

@ -98,10 +98,17 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList {
() => this.get_report_doc(),
() => this.get_report_settings(),
() => this.setup_page_head(),
() => this.refresh_report()
() => this.refresh_report(),
() => this.add_make_chart_button()
]);
}
add_make_chart_button(){
this.page.add_inner_button(__("Set Chart"), () => {
this.get_possible_chart_options();
});
}
refresh_report() {
this.toggle_message(true);
@ -247,9 +254,17 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList {
this.add_prepared_report_buttons(data.doc);
}
this.toggle_message(false);
if (data.result && data.result.length) {
this.render_chart(data);
this.render_report(data);
this.prepare_report_data(data);
const chart_options = this.get_chart_options(data);
this.$chart.empty();
if(chart_options) {
this.render_chart(chart_options);
}
this.render_datatable();
} else {
this.toggle_nothing_to_show(true);
}
@ -303,12 +318,15 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList {
}
}
render_report(data) {
prepare_report_data(data) {
this.raw_data = data;
this.columns = this.prepare_columns(data.columns);
this.data = this.prepare_data(data.result);
this.tree_report = this.data.some(d => 'indent' in d);
}
render_datatable() {
if (this.datatable) {
this.datatable.refresh(this.data, this.columns);
return;
@ -323,21 +341,157 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList {
});
}
render_chart(data) {
this.$chart.empty();
let opts = this.report_settings.get_chart_data
get_chart_options(data) {
let options = this.report_settings.get_chart_data
? this.report_settings.get_chart_data(data.columns, data.result)
: data.chart
? data.chart
: {};
if (!(opts.data && opts.data.labels && opts.data.labels.length > 0)) return;
: undefined;
Object.assign(opts, {
height: 200
if (!(options && options.data && options.data.labels && options.data.labels.length > 0)) return;
return options;
}
render_chart(options, height=200) {
Object.assign(options, {
height: height
});
this.$chart.empty();
this.chart = new Chart(this.$chart[0], options);
this.$chart.show();
this.chart = new Chart(this.$chart[0], opts);
}
get_possible_chart_options() {
const columns = this.raw_data.columns;
const rows = this.raw_data.result;
const first_row = rows[0];
const has_total_row = this.raw_data.add_total_row;
const indices = first_row.reduce((accumulator, current_value, current_index) => {
if(!isNaN(Number(current_value))) {
accumulator.push(current_index);
}
return accumulator;
}, []);
function get_column_values(column_name) {
const column_index = columns.indexOf(column_name);
return rows.map(row => row[column_index]);
}
function get_chart_options({ y_field, x_field, chart_type, color }) {
const type = chart_type.toLowerCase();
const colors = color ? [color] : undefined;
let labels = get_column_values(x_field)
.filter(Boolean)
.map(d => d.trim())
.filter(Boolean);
let dataset_values = get_column_values(y_field).map(d => Number(d));
if(has_total_row) {
labels = labels.slice(0, -1);
dataset_values = dataset_values.slice(0, -1);
}
return {
data: {
labels: labels,
datasets: [
{ values: dataset_values }
]
},
type: type,
colors: colors
};
}
function preview_chart() {
const wrapper = $(dialog.fields_dict["chart_preview"].wrapper);
const values = dialog.get_values(true);
let options = get_chart_options(values);
Object.assign(options, {
height: 150
});
wrapper.empty();
new Chart(wrapper[0], options);
wrapper.find('.chart-container .title, .chart-container .sub-title').hide();
wrapper.show();
}
const numeric_fields = columns.filter((col, i) => indices.includes(i));
const non_numeric_fields = columns.filter((col, i) => !indices.includes(i))
const dialog = new frappe.ui.Dialog({
title: __('Make Chart'),
fields: [
{
fieldname: 'y_field',
label: 'Y Field',
fieldtype: 'Select',
options: numeric_fields,
default: numeric_fields[0],
onchange: preview_chart
},
{
fieldname: 'x_field',
label: 'X Field',
fieldtype: 'Select',
options: non_numeric_fields,
default: non_numeric_fields[0],
onchange: preview_chart
},
{
fieldname: 'cb_1',
fieldtype: 'Column Break'
},
{
fieldname: 'chart_type',
label: 'Type of Chart',
fieldtype: 'Select',
options: ['Bar', 'Line', 'Percentage', 'Pie'],
default: 'Bar',
onchange: preview_chart
},
{
fieldname: 'color',
label: 'Color',
fieldtype: 'Color',
depends_on: doc => ['Bar', 'Line'].includes(doc.chart_type),
onchange: preview_chart,
},
{
fieldname: 'sb_1',
fieldtype: 'Section Break',
label: 'Chart Preview'
},
{
fieldname: 'chart_preview',
label: 'Chart Preview',
fieldtype: 'HTML',
}
],
primary_action_label: __('Make'),
primary_action: (values) => {
let options = get_chart_options(values);
options.title = __(`${this.report_name}: ${values.y_field} vs ${values.x_field}`);
this.render_chart(options);
dialog.hide();
}
});
dialog.show();
// load preview after dialog animation
setTimeout(preview_chart, 500);
}
get_user_settings() {

View file

@ -306,7 +306,7 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView {
this.chart = new Chart(this.$charts_wrapper[0], {
title: __("{0} Chart", [this.doctype]),
data: data,
type: args.chart_type, // 'bar', 'line', 'scatter', 'pie', 'percentage'
type: args.chart_type,
height: 150,
colors: ['violet', 'light-blue', 'orange', 'red'],
@ -365,7 +365,7 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView {
{
label: __('Chart Type'),
fieldtype: 'Select',
options: ['Bar', 'Line', 'Scatter', 'Pie', 'Percentage'],
options: ['Bar', 'Line', 'Pie', 'Percentage'],
fieldname: 'chart_type',
default: toTitle(defaults.chart_type || 'Bar')
}
@ -396,6 +396,9 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView {
// look like strings and also
// monkey patch the doc
// javascript is awesome
// O.o
return {
doc: d,
toString() {

View file

@ -386,6 +386,10 @@ h6.uppercase, .h6.uppercase {
}
}
.timeline-item .media-body {
max-width: 100%;
}
.timeline-item.user-content {
margin: 30px 0px 30px 27px;

View file

@ -21,7 +21,7 @@ queue_timeout = {
redis_connection = None
def enqueue(method, queue='default', timeout=300, event=None,
def enqueue(method, queue='default', timeout=None, event=None,
is_async=True, job_name=None, now=False, enqueue_after_commit=False, **kwargs):
'''
Enqueue method to be executed using a background worker

View file

@ -10,8 +10,8 @@ import requests
import subprocess # nosec
from frappe.utils import cstr
from frappe.utils.gitutils import get_app_last_commit_ref, get_app_branch
from frappe import _
import subprocess # nosec
from frappe import _, safe_decode
def get_change_log(user=None):
if not user: user = frappe.session.user
@ -118,15 +118,21 @@ def get_versions():
def get_app_branch(app):
'''Returns branch of an app'''
try:
return subprocess.check_output('cd ../apps/{0} && git rev-parse --abbrev-ref HEAD'.format(app),
shell=True).strip()
result = subprocess.check_output('cd ../apps/{0} && git rev-parse --abbrev-ref HEAD'.format(app),
shell=True)
result = safe_decode(result)
result = result.strip()
return result
except Exception as e:
return ''
def get_app_last_commit_ref(app):
try:
return subprocess.check_output('cd ../apps/{0} && git rev-parse HEAD'.format(app),
shell=True).strip()[:7]
result = subprocess.check_output('cd ../apps/{0} && git rev-parse HEAD --short 7'.format(app),
shell=True)
result = safe_decode(result)
result = result.strip()
return result
except Exception as e:
return ''
@ -159,7 +165,7 @@ def check_release_on_github(app):
# Check if repo remote is on github
from subprocess import CalledProcessError
try:
remote_url = subprocess.check_output("cd ../apps/{} && git ls-remote --get-url".format(app), shell=True)
remote_url = subprocess.check_output("cd ../apps/{} && git ls-remote --get-url".format(app), shell=True).decode()
except CalledProcessError:
# Passing this since some apps may not have git initializaed in them
return None

View file

@ -24,17 +24,18 @@ def get_permission_query_conditions(user):
return "(`tabWorkflow Action`.`user`='{user}')".format(user=user)
def has_permission(doc, user):
if user not in ['Administrator', doc.user]:
return False
def process_workflow_actions(doc, state):
workflow = get_workflow_name(doc.get('doctype'))
if not workflow: return
if state == "on_trash":
clear_workflow_actions(doc.get('doctype'), doc.get('name'))
return
if is_workflow_action_already_created(doc): return
clear_old_workflow_actions(doc)
@ -54,7 +55,6 @@ def process_workflow_actions(doc, state):
if send_email_alert(workflow):
enqueue(send_workflow_action_email, queue='short', users_data=list(user_data_map.values()), doc=doc)
@frappe.whitelist(allow_guest=True)
def apply_action(action, doctype, docname, current_state, user=None, last_modified=None):
if not verify_request():
@ -234,6 +234,14 @@ def is_workflow_action_already_created(doc):
'workflow_state': get_doc_workflow_state(doc)
})
def clear_workflow_actions(doctype, name):
if not (doctype and name):
return
frappe.db.sql('''delete from `tabWorkflow Action`
where reference_doctype=%s and reference_name=%s''',
(doctype, name))
def get_doc_workflow_state(doc):
workflow_name = get_workflow_name(doc.get('doctype'))
workflow_state_field = get_workflow_state_field(workflow_name)