After workflow action

Hi @peterg,

I tried this code with the before_workflow_action hook, BUT the document status is showing as ‘Not Saved’ after execution of the workflow. The fields are populated, but as the document is not saved, when I refresh the page, the values go away.

Here even for a system user (with full access), the document is not saved after workflow action and values go away as soon as page is reloaded.

frappe.ui.form.on('Purchase Order', {
	before_workflow_action: (frm) => {
    if(frm.selected_workflow_action === 'Submit'){
        frm.set_value({
                custom_created_by: frappe.session.user,
                custom_created_time: frappe.datetime.now_datetime()
            });
    }
    else if(frm.selected_workflow_action === 'Approve'){
        frm.set_value({
                custom_approved_by: frappe.session.user,
                custom_approved_time: frappe.datetime.now_datetime()
            });
    }
    frm.save();
	}
});

Can you point me towards the general save hook handler that you have suggested to me?

Thanks

I don’t have access to a frappe instance at the moment to test, but it sounds like you’re just running into a race condition. I’m pretty sure that the set_value function is asynchronous, but in your code it doesn’t look like you’re waiting for it to return.

What about something like this?

frappe.ui.form.on('Purchase Order', {
  before_workflow_action: (frm) => {
    const my_actions = ['Submit', 'Approve']
    if (my_actions.includes(frm.selected_workflow_action)) {
      frm.set_value({
        custom_created_by: frappe.session.user,
        custom_created_time: frappe.datetime.now_datetime()
      }).then(r => frm.save())
    }
  }
});
1 Like

I tried this code; this is also not saving any values…

frappe.ui.form.on('Purchase Order', {
  before_workflow_action: (frm) => {
    if (frm.selected_workflow_action === 'Submit for Approval') {
      frm.set_value({
        custom_created_by: frappe.session.user,
        custom_created_time: frappe.datetime.now_datetime()
      }).then(r => frm.save())
    }
    else if (frm.selected_workflow_action === 'Approve') {
      frm.set_value({
        custom_approved_by: frappe.session.user,
        custom_approved_time: frappe.datetime.now_datetime()
      }).then(r => frm.save())
    } 
  }
});


Workflow Transition:

You’ll have to do some debugging with console.log statements. I still think it’s a race condition. It may not be possible to edit the document on the client side while it’s already in a save cycle. That makes sense, actually. The server is the source of truth, and client interaction is all via remote procedure calls. It would be challenging to allow client-side changes to data while a workflow transition is in process.

FWIW, this kind of logic probably should be executed on the server side, anyway. As you’re trying to do it, it would be pretty simple for any authorized user to set these values arbitrarily.

Thank you for your explanation.
I’ll try it server side or in a different way.

If I get a concrete solution, I’ll share it here :+1:

Hi @peterg,

I have found a solution to my problem which I have posted here:

I want to throw an error if the checkbox is not ticked, and the verifier tries to move to the next stage.

frappe.ui.form.on('Purchase Order', {
    validate: (frm) => {
        // Doc Creation:
        if (!frm.doc.custom_created_by && frm.doc.workflow_state === 'Draft') {
            frm.doc.custom_created_by = frappe.session.user,
            frm.doc.custom_created_time = frappe.datetime.now_datetime();
        }
        // Doc Verification:
        else if ((frm.doc.custom_read_and_verified == 1) && (frm.doc.workflow_state === 'Verification Pending')){
            frm.doc.custom_verified_by = frappe.session.user,
            frm.doc.custom_verified_time = frappe.datetime.now_datetime();
        }
        else if ((frm.doc.custom_read_and_verified != 1) && (frm.doc.workflow_state === 'Verification Pending')){
            frm.doc.custom_verified_by = null,
            frm.doc.custom_verified_time = null;
        }
        // Doc Approval:
        else if ((frm.doc.custom_read_and_approved == 1) && (frm.doc.workflow_state === 'Approval Pending by Manager')){
            frm.doc.custom_approved_by = frappe.session.user,
            frm.doc.custom_approved_time = frappe.datetime.now_datetime();
        }
        else if ((frm.doc.custom_read_and_approved != 1) && (frm.doc.workflow_state === 'Approval Pending by Manager')){
            frm.doc.custom_approved_by = null,
            frm.doc.custom_approved_time = null;
        }
    },

    before_workflow_action: (frm) => {
            //console.log(frm.selected_workflow_action);
            if ((frm.doc.workflow_state === 'Verification Pending') && (frm.selected_workflow_action === 'Verify') && (frm.custom_read_and_verified != 1))
            {
                frappe.throw("Please tick the Read by Verifier checkbox");
            }
    }
});

It is working, thanks.

Great, glad it’s working. If you want to make it a separate step, that’s fine. Because this is a client-side operation, it will be possible for users with a small amount of javascript knowledge to falsify the user id, but that may not be an issue at your organization.

1 Like

Hi @rps49 ,
Facing same problem
before_workflow_action(frm){
console.log(“Action”,frm.selected_workflow_action);
action = frm.selected_workflow_action
if(action===“Submit” && frm.doc.workflow_state==“Draft”){
let kra_kpi = frm.doc.kra_kpi_mapping
kra_kpi.forEach((ele)=>{
if(ele.self_ratings===0){
frappe.throw(“Please enter Self Ratings for KRA KPI”)
}
})

    }
    else if(action=="Approve" && frm.doc.workflow_state=="Pending with RO for Ratings"){
        let kra_kpi = frm.doc.kra_kpi_mapping
                kra_kpi.forEach((ele)=>{
                        if(ele.self_ratings===0){
                            frappe.throw("Please enter Self Ratings for KRA KPI")
                        }
                    })
        let goal_kra = frm.doc.goal_and_kra
        goal_kra.forEach((ele)=>{
            if(ele.ro_ratings===0){
                frappe.throw("Please enter RO Ratings for Goal and KRA")
            }
        })
    }
    else if(action=="Approve" && self.dro_required && frm.doc.workflow_state=="Pending with DRO for Ratings"){
        let kra_kpi = frm.doc.kra_kpi_mapping
                kra_kpi.forEach((ele)=>{
                        if(ele.dro_ratings===0){
                            frappe.throw("Please enter RO Ratings for KRA KPI")
                        }
                    })
        let goal_kra = frm.doc.goal_and_kra
        goal_kra.forEach((ele)=>{
            if(ele.dro_ratings===0){
                frappe.throw("Please enter DLO Ratings for Goal And KRA")
            }
        })
    }
},

Working code but document gets freezed and need to refresh it.

Yes frappe.throw() freezes the page and the user has to manually refresh it to make it normal again.

I have not found any solution to this, and have just instructed the users to refresh the page if this happens…

Hi @rps49,
Managed to do it on validate event. Thanks for your time and reply.

if (self.workflow_state==“Approved by HR” or self.workflow_state==“Approved by RO” or self.workflow_state==“Pending with DRO for Ratings” or self.workflow_state==“Finalised”) and frappe.db.get_value(“PMS Appraisal”,self.name,“workflow_state”)==“Pending with RO for Ratings”:
for i in self.kra_kpi_mapping:
if i.ro_ratings==0:
frappe.throw(“Please Enter RO Ratings for KRA KPI”)
for i in self.goal_and_kra:
if i.ro_ratings==0:
frappe.throw(“Please Enter RO Ratings for Goal and KRA”)
if not self.ro_behavioural_ratings:
frappe.throw(“Please Submit Behavioural Feedback”)