Merge pull request #10548 from prssanna/workflow-warning-v13

feat: Show warning if documents have workflow states that do not exist in the workflow
This commit is contained in:
Prssanna Desai 2020-06-02 13:40:35 +05:30 committed by GitHub
commit 9cabe788d2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 191 additions and 3 deletions

View file

@ -42,6 +42,8 @@ frappe.ui.Dialog = class Dialog extends frappe.ui.FieldGroup {
this.body = this.$body.get(0);
this.$message = $('<div class="hide modal-message"></div>').appendTo(this.modal_body);
this.header = this.$wrapper.find(".modal-header");
this.buttons = this.header.find('.buttons');
this.set_indicator();
// make fields (if any)
super.make();
@ -164,6 +166,11 @@ frappe.ui.Dialog = class Dialog extends frappe.ui.FieldGroup {
set_title(t) {
this.$wrapper.find(".modal-title").html(t);
}
set_indicator() {
if (this.indicator) {
this.header.find('.indicator').removeClass().addClass('indicator ' + this.indicator);
}
}
show() {
// show it
if ( this.animate ) {

View file

@ -53,6 +53,33 @@ frappe.confirm = function(message, ifyes, ifno) {
return d;
}
frappe.warn = function(title, message_html, proceed_action, primary_label) {
const d = new frappe.ui.Dialog({
title: title,
indicator: 'red',
fields: [
{
fieldtype: 'HTML',
fieldname: 'warning_message',
options: `<div class="frappe-warning-message">${message_html}</div>`
}
],
primary_action_label: primary_label,
primary_action: () => {
if (proceed_action) proceed_action();
d.hide();
},
secondary_action_label: __("Cancel"),
});
d.buttons.find('.btn-primary').removeClass('btn-primary').addClass('btn-danger');
const modal_footer = $(`<div class="modal-footer"></div>`).insertAfter($(d.modal_body));
modal_footer.html(d.buttons);
d.show();
return d;
};
frappe.prompt = function(fields, callback, title, primary_label) {
if (typeof fields === "string") {
fields = [{

View file

@ -292,6 +292,25 @@ Object.assign(frappe.utils, {
return frappe.utils.guess_style(text, null, true);
},
get_indicator_color: function(state) {
return frappe.db.get_list('Workflow State', {filters: {name: state}, fields: ['name', 'style']}).then(res => {
const state = res[0];
if (!state.style) {
return frappe.utils.guess_colour(state.name);
}
const style = state.style;
const colour_map = {
"Success": "green",
"Warning": "orange",
"Danger": "red",
"Primary": "blue",
};
return colour_map[style];
});
},
sort: function(list, key, compare_type, reverse) {
if(!list || list.length < 2)
return list || [];

View file

@ -5,7 +5,35 @@ frappe.ui.form.on("Workflow", {
frm.set_query("document_type", {"issingle": 0, "istable": 0});
},
refresh: function(frm) {
if (frm.doc.document_type) {
frm.add_custom_button(__('Go to {0} List', [frm.doc.document_type]), () => {
frappe.set_route('List', frm.doc.document_type);
});
}
frm.events.update_field_options(frm);
frm.ignore_warning = frm.is_new() ? true : false;
if (frm.is_new()) {
return;
}
frm.states = null;
frm.trigger('make_state_table');
frm.trigger('get_orphaned_states_and_count').then(() => {
frm.trigger('render_state_table');
});
},
validate: (frm) => {
if (frm.ignore_warning) {
return;
}
return frm.trigger('get_orphaned_states_and_count').then(() => {
if (frm.states && frm.states.length) {
frappe.validated = false;
frm.trigger('create_warning_dialog');
}
});
},
document_type: function(frm) {
frm.events.update_field_options(frm);
@ -19,6 +47,101 @@ frappe.ui.form.on("Workflow", {
frappe.meta.get_docfield("Workflow Document State", "update_field", frm.doc.name).options = [""].concat(resp);
})
}
}
})
},
create_warning_dialog: function(frm) {
const warning_html =
`<p class="bold">
${__('Are you sure you want to save this document?')}
</p>
<p>${__(`There are documents which have workflow states that do not exist in this Workflow.
It is recommended that you add these states to the Workflow and change their states
before removing these states.`)}
</p>`;
const message_html = warning_html + frm.state_table_html;
let proceed_action = () => {
frm.ignore_warning = true;
frm.save();
};
frappe.warn(__(`Worflow States Don't Exist`), message_html, proceed_action, __(`Save Anyway`));
},
set_table_html: function(frm) {
const promises = frm.states.map(r => {
const state = r[frm.doc.workflow_state_field];
return frappe.utils.get_indicator_color(state).then(color => {
return `<tr>
<td>
<div class="indicator ${color}">
<a class="text-muted orphaned-state">${r[frm.doc.workflow_state_field]}</a>
</div>
</td>
<td>${r.count}</td></tr>`;
});
});
Promise.all(promises).then(rows => {
const rows_html = rows.join('');
frm.state_table_html = (`<table class="table state-table table-bordered" style="margin:0px; width: 65%">
<thead style="font-size: 12px">
<tr class="text-muted">
<th>${__('State')}</th>
<th>${__('Count')}</th>
</tr>
</thead>
<tbody>
${rows_html}
</tbody>
</table>`);
});
},
get_orphaned_states_and_count: function(frm) {
let states_list = [];
frm.doc.states.map(state => states_list.push(state.state));
return frappe.xcall('frappe.workflow.doctype.workflow.workflow.get_workflow_state_count', {
doctype: frm.doc.document_type,
workflow_state_field: frm.doc.workflow_state_field,
states: states_list
}).then(result => {
if (result && result.length) {
frm.states = result;
return frm.trigger('set_table_html');
}
});
},
make_state_table: function(frm) {
const wrapper = frm.get_field('states').$wrapper;
if (frm.state_table) {
frm.state_table.empty();
}
frm.state_table = $(`<div class="state-table"><div>`).insertAfter(wrapper);
},
render_state_table: function(frm) {
if (frm.states && frm.states.length) {
const form_state_table_html =
`<p class="text-muted small" style="margin-top: 30px">
${'Document States that do not exist in your Workflow'}
</p>
${frm.state_table_html}
</div>`;
frm.state_table.html(form_state_table_html);
$(frm.state_table).find('a.orphaned-state').on('click', (e) => {
const state = $(e.currentTarget).text();
let filters = {};
filters[frm.doc.workflow_state_field] = state;
frappe.set_route('List', frm.doc.document_type, filters);
});
}
}
});
frappe.ui.form.on("Workflow Document State", {
states_remove: function(frm) {
frm.trigger('get_orphaned_states_and_count').then(() => {
frm.trigger('render_state_table');
});
}
});

View file

@ -59,7 +59,7 @@ class Workflow(Document):
def update_doc_status(self):
'''
Checks if the docstatus of a state was updated.
Checks if the docstatus of a state was updated.
If yes then the docstatus of the document with same state will be updated
'''
doc_before_save = self.get_doc_before_save()
@ -112,3 +112,15 @@ class Workflow(Document):
def get_fieldnames_for(doctype):
return [f.fieldname for f in frappe.get_meta(doctype).fields \
if f.fieldname not in no_value_fields]
@frappe.whitelist()
def get_workflow_state_count(doctype, workflow_state_field, states):
states = frappe.parse_json(states)
result = frappe.get_all(
doctype,
fields=[workflow_state_field, 'count(*) as count', 'docstatus'],
filters = {'workflow_state': ['not in', states]},
group_by = workflow_state_field
)
return [r for r in result if r[workflow_state_field]]