After workflow action

Hello everyone,

I am trying to implement an after workflow action in client-side JS. What I want to achieve is to have the form automatically record the user and date where that triggered the workflow action. Here is my code below;

frappe.ui.form.on(‘Expense Claim’, {
after_workflow_action: (frm) => {
if(frm.doc.workflow_state === ‘Advance Verified By Finance’){
frm.set_value({
approved_by_f: frappe.session.user_fullname,
approval_date_f: frappe.datetime.nowdate()
});
frm.refresh();
frm.save();
}}});

The values are getting set but I either get an error that the document has been refreshed before the values get the chance to save or the error does not show up but the document fails to save the set values after the workflow action.

Can someone please correct the code for me? Thank you

I have another way to do it, using a check field to trigger the username and date to populate before the workflow. However, there has to be a before_workflow_action to make sure that the approvers have checked the field before approving but I have not been able to figure this part in bold and italics.

frappe.ui.form.on(‘Expense Claim’, {
before_workflow_action: (frm) => {
if(frm.doc.workflow_state === ‘Advance Verified By Finance’){
if( the selected workflow action is ‘Approve’ and checkfield ‘is not checked’ )
frappe.throw(“Please check the approval checkfield before approving the payment request”);
frappe.validated = false;
}}});

Looking forward to a solution, thank you

I don’t have this in front of me to test, but I wouldn’t have thought you’d want a frm.refresh() event before the save. My guess would be that the doc refresh is still in progress while the save gets called. If you remove the refresh, does it work?

Thanks for the reply, but it shows this error if I remove frm.refresh(); If I leave it there, sometimes it shows, other times it executes the workflow but fails to save the fields

Hmm…what version are you using, and do you have any other custom event triggers for this doctype? I just tested on my staging site and it worked fine (without the refresh instruction)

Installed Apps

ERPNext: v13.11.1 (version-13)

Frappe Framework: v13.11.0 (version-13)

Yes, I have three of these fields as there are three approvers. After each approval, the fields are to be populated and move to the next state where the next approver’s action will populate other fields, as shown in my first post.

I’m not totally following this part. Are you saying that the fields being populated depend on the current workflow state?

Regardless, I’m not encountering the problem you are with the code above. If you have more code active than what you’ve shown here, you might consider commenting it all out, starting with this, and adding the rest in one piece at a time. If you’ve got only and exactly the code listed above under after_workflow_action, I’m really not sure why it works fine for you but not for me.

So the workflow state goes:

  1. Approved By Manager
  2. Verified By Finance
  3. Authorized By Managing Partner

If the present workflow state is “Advance Verified By Financer”, then the managing partner is the role to make the next change, hence

{if(frm.doc.workflow_state === ‘Advance Verified By Finance’){
frm.set_value({
approved_by_f: frappe.session.user_fullname,
approval_date_f: frappe.datetime.nowdate()
});}

The fields, “approved_by_f” and “approval_date_f” actually get populated, but the form is not saved before the workflow is executed. Take a look at the screenshot below:

The form changes to “Not Saved” so that once the I refresh, I lose the contents of the fields.

Also, is there any way to capture the workflow action? Whether Approve or Reject. If there is a way then I can do this with another approach

You wouldn’t expect it to be saved before the workflow is executed. This trigger is getting called after it finishes being executed. That’s why you need to call frm.save() afterwards, to commit the changes made after the workflow transition.

This code:

frappe.ui.form.on(‘Expense Claim’, {
    after_workflow_action: (frm) => {
        if (frm.doc.workflow_state === ‘Advance Verified By Finance’) {
            frm.set_value({
                approved_by_f: frappe.session.user_fullname,
                approval_date_f: frappe.datetime.nowdate()
            });
            frm.save();
        }
    }
});

…works absolutely fine for me on v13.11. I’m very surprised it’s not working for you. There must be something else going on interfering, creating a race condition of some sort.

To see which action has been called, you can check the variable frm.selected_workflow_action. This variable is only available during before_workflow_action event handlers, I believe.

Is this the correct use of frm.selected_workflow_action? It is not working for me, it is not throwing the error. Here is my code.

frappe.ui.form.on('Expense Claim', {
before_workflow_action: (frm) => {
if(frm.doc.workflow_state === 'Advance Validated By Finance'){
 if (frm.doc.checkbox_m == 1){
    if(frm.selected_workflow_action == "Approve Advance Request"){
        frappe.throw("Please check the 'approval' checkbox before approving");
        frappe.validated = false;    
        }
}}}});

(Friendly request: your code will be a lot easier to read if you use code fences (```) in the line above and the line below your code to preserve formatting)

Can you this with console.log(frm.selected_workflow_action)? What value do you see in output?

Is this what you mean?

Sort of. Are you unfamiliar with using console.log to debug? You need to put this into your actual before_workflow_action event trigger, where the variable frm is defined. That will allow you to see what the value of selected_workflow_action is when it runs and, consequently, why your error message isn’t triggering.

Unfortunately, I am not. If you could show me a resource to understand this I would be happy to execute it.

Also, my apologies, I am very new to programming (erpnext being my first experience). I am still learning the basics. Is this what you mean?

frappe.ui.form.on('Expense Claim', {
before_workflow_action: (frm) => {
if(frm.doc.workflow_state === 'Advance Validated By Finance'){
    if(frm.selected_workflow_action == "Approve Advance Request"){
        if (frm.doc.checkbox_m == 1)
        {
        frappe.throw("Please check the 'approval' checkbox before approving");
        frappe.validated = false;    
        }
}}}});
1 Like

The console.log function writes whatever argument you send it to the dev console.

So, try something like this:

frappe.ui.form.on("Expense Claim", {
    before_workflow_action: (frm) => {
        console.log(frm.selected_workflow_action);
        if (
            frm.doc.workflow_state === "Advance Validated By Finance" &&
            frm.selected_workflow_action === "Approve Advance Request" &&
            frm.doc.checkbox_m == 1
        ) {
            frappe.throw("Please check the 'approval' checkbox before approving");
            frappe.validated = false;
        }
    },
});

You should see the value of frm.selected_workflow_action output to the console before the workflow action triggers. (I’ve also tidied your code up a bit, removing the nested if statements and the unindended bracket closures.)

4 Likes

It WORKED!!!
Thank You!!!

1 Like

One last thing,
I would like to write a server-side script to automatically delete all the forms of a custom doctype every 1 hour.

How do I go about that? How do I execute the cron job?

If you have your own app, you can define scheduler jobs in your hooks.py definition.

https://frappeframework.com/docs/user/en/python-api/hooks#scheduler-events

Otherwise, if your site is configured for server scripts, you can just do it there.

https://frappeframework.com/docs/user/en/desk/scripting/server-script

Thank you so much!!