Retrieve the id of a process and navigate to a new form/process on the action of a button
This question may be a detailed one as I need to understand the anatomy of an application through it.
So, I have an instantiation form that shows the list of customers onboarded to our system so far; there is a "Add new customer" button which should open another form containing the onboarding formalities. I need to provide the URL of the latter form in the action of the button. I have been following the documentation to understand how to form a URL but I am not able to get the missing pieces right - namely, the processInstanceId and the taskId of the next process/form to navigate to.
Moreover, I want to know the difference between processDefinitionId and processInstanceId.
For instance, for the above case, I provide a URL like this - /bonita/portal/form/processInstance/{{processId}}/task/Basic%20information%20ingestion
I am not able to pass the processId correctly. I get an error if I run this from my studio.
Another aspect of my application is that after filling the form, I need to run a sequence of system tasks which will do some operations (business logic, DB,etc.). I already have python scripts written for these tasks, is there a way I can run those in my system tasks.
I am attaching the diagram for a visual understanding of my use case -
I need a bit of guidance to move forward as currently I am stuck on how to proceed.
Thanks in advance!
EDIT:
I am somehow unable to show the image so I'll just chart out the process as follows:
CustomerList (instantiationForm) ---> Basic information Ingestion (nextForm/nextStep) ---> (sequence of system tasks)
Basically from instantiationForm I need to give a button to navigate to "nextForm" in order to add a new customer.
There are multiple things here so I will do the best I can to help you.
Your diagram image does not display...
1) The anatomy of the application, in my opinion it's the developed process that needs looking at as this is where the opportunity lies.
I have an instantiation form that shows the list of customers onboarded to our system so far; there is a "Add new customer" button which should open another form containing the onboarding formalities. I need to provide the URL of the latter form in the action of the button. I have been following the documentation to understand how to form a URL but I am not able to get the missing pieces right - namely, the processInstanceId and the taskId of the next process/form to navigate to.
I have an instantiation form that shows the list of customers onboarded to our system so far; there is a "Add new customer" button which should open another form containing the onboarding formalities.
Just out of interest how do you show your existing customers on the Instantiation form? We're having trouble doing that...
So your process is supposed to work as follows:
- Open Instantiation form
- User clicks ADD Customer button
- Open a Onboarding form
What is supposed to happen AFTER the Onboarding form is completed and the user presumably hits save?
Is the process supposed to go back to the Instantiation form with the list of customers already onboarded, or something else? You don't say.
In my mind you have two choices:
- Show a Modal Form ( MF Link ) and then when clicking save add the Onboarded Customer to the collection for update and close the modal window, or
- Hide all other fields on the form and show the new form ( Hidden Form Link ) from a hidden container and then when clicking save add the Onboarded Customer to the collection for update, hide the Onboarding container and unhide the other fields.
This way there is no need for specific URL's or breaking the chain of the process. You could also use both these methods to update BDM and start a background task doing all the business operations (business logic, DB,etc.) which are not online tasks.
Personally I like the Hidden Form, it works very well for what we want it to do.
Regarding Python, you can run these from a Script Connector as "System Script" however I've not tried it. You can also run python in Tomcat with various caveats. See the following links:
https://www.quora.com/Can-I-use-Tomcat-server-to-deploy-Python-rest-web-...
http://lekshmideepu.blogspot.se/2013/03/configure-tomcat-7-to-run-python...
regards
Seán
PS: As this reply offers an answer your question, and if you like it, please Mark UP and/or as Resolved.
Hi,
I feel that is a duplicate of this question, no?: http://community.bonitasoft.com/questions-and-answers/retrieve-id-proces...
Cheers
Comments
Thank you for the useful links and precise answer to my question.
First to attempt to answer your question - Just out of interest how do you show your existing customers on the Instantiation form? We're having trouble doing that...
I am basically populating a table widget based on a JSON response from a a REST API call. Then I have just specified the properties (content, column keys) of the widget to show the content of the response. The way the entire process is defined (similar to what you mentioned in the reply) is as follows: The customers list shows existing customers (CustomerList Form) and on clicking "Add New" the new form (Onboarding Form) should open up, which in turn has a submit button to add the customer to DB and then the same API call will render the new customer in the list. I will try and use the "Hidden Form" link and apply the concept. But I just wanted to use the URL linking and see if that helped. I have been reading the documentation but unable to wrap my head around it.
Actually, my challenge is to find a way to start processes or background tasks (as you mentioned) from other forms.
One way to solve the running of Python functions/scripts is to host these as services (REST APIs) and then start them in the background but how should I perform these actions.
If I can get some help regarding these things, I would gladly appreciate it!
Thank you.
Is the process supposed to go back to the Instantiation form with the list of customers already onboarded, or something else?
Form the onboarding form, the user will have two options either to go back to the instantiation form or to another form that requires him to perform another task but I think if I figure it out for one form navigation, I'll be able to perform the others as well.
P.S. Sorry for the mulitple comments!!
Hey Sean,
Thanks for the link on Hidden Forms. Using the custom widget, I was able to make a step form. Ic an configure the submit button on each of these forms to store the records in DB and also update the BDM through the contract.
I have a small query, though - now I have all my pages in one form and I have removed the case instantiation form. So when I run this process through Studio, I am navigated to Bonita Portal with an auto-logged in user and I can see the task list depending on my process diagram. If I take this task, I am able to see the form and I think this looks fine. But now, after filling the last form (which is essentially a multi-document upload), I want to run the MongoDB connection script task. So should I configure this connection script in the task itself or link it to a separate script task. Currently, I am doing the latter and unable to understand how to run the script from the last form.
The way I presume it should be done is:
1. Upload docs through the Step Form and store it in a fileContainer contract.
2. Retrieve the value of the fileContainer contract (using contexts??) in the next task which is essentially a script task.
Please tell me if this understanding is correct.
Thanks in advance!!
It took us a while to appreciate the power of the "Page Hidden Forms", they are very good and underrated.
We actually make the "Page Hidden Forms" our instantiation form so when we run it it automatically runs. It's almost tlike a one page application (excluding management approval which is the following step.
Anyway:
But now, after filling the last form (which is essentially a multi-document upload), I want to run the MongoDB connection script task. So should I configure this connection script in the task itself or link it to a separate script task.
The way we do this is:
Hidden form 1 -> data input only -> Next
Hidden form 2 -> data input only -> Next
Hidden form 3 -> document upload -> Submit
The document Upload is MULTIPLE documents and defined in the Contract as follows:
Execution Tab -> Contract -> Add -> Name uploadedFiles -> Type File -> Multiple ticked
Then go to the POOL -> Data Tab -> Documents -> Add Document -> Multiple selected -> Initial content -> From Contract -> Select the Contract Name uploadedFiles.
Then Add a SCRIPT TASK to the Pool immediately after the task for the upload form. Here you can do the Database Upload.
It is much easier to do it this way and you are separating the two functions without them breaking each other. Makes for easier debugging as well.
Tasks in the diagram don't use contexts, they have direct access to the variables where the information is stored.
Currently, I am doing the latter and unable to understand how to run the script from the last form.
Doing the above will ensure the tasks run automatically and you don't have to run the script, it's already run for you.
Hope that helps
regards
Seán
Thank you for the prompt reply. I have done exactly the way you suggested (except the configuration of the Documents in the Data tab). I shall execute this as two separate tasks (decoupling user input from logic - seems intuitive).
I still have a doubt, though - earlier I had made this form my case Instantiation form but then I realised that the case instantiation as an initFunc() for every contract but no "Operations" tab. So, if I were to update the BDM through the contract, then how do I achieve it in case instantiation?? And can I start my script task (DB connection and upload) immediately after the case instantiation to facilitate the upload operation.
Thanks a lot for sharing your expertise. It has helped me a great deal to move forward with the process flow implementation.
Regards
Megha
The way it seems to work is as follows, and it confused me for a while as well...
7.1 Connectors in
7.2 Task form
7.3 Task operations
7.4 Connectors Out
The initFunc() "is" the Operations Tab associated with Instantiation Form (notice is in quotes:)
... if I were to update the BDM through the contract, then how do I achieve it in case instantiation?...
As you can see the BDM is populated automatically at Case Instantiation by the initFunc();
And yes you can start your MongoDB upload immediately after.
Hopefully the following is what you'll have (a short example):
That's how it works for me anyway, yes takes time to understand but once you get it, it makes sense (sort of)
regards
Seán
All right, so in a way the process diagram, defining the sequential process flow and is triggered with the case instantiation form and all the processes after it will execute (assuming there are no logical errors) right till the end.
Thank you so much for this valuable post. It has clarified a lot of confusion regarding the Bonita processes.
This one-page approach is definitely simpler to work with than the page navigation among tasks. However, just of curiosity, the hidden form approach will not work if each hidden form has a separate (say, script/service) task associated with it.
Thank you again!!!
Regards
Megha
1) Yes...
2) Absolutely, it took me a while
3) The Hidden Form approach do not/cannot have script/service tasks associated with them it is after all - just one form. So no. If you really need this then you need multiple tasks and separate forms.
And Finally :) I've just remembered something about how to do Files: Checked and yes I will have to tell you how to do it right tomorrow. It's late here and I need to go and remember exactly how we did it.
If I remember rightly:
Step1 -> Output Operation to documentList
Step2 -> MultiInstantiation on documentList calling subroutine
Subroutine calls Upload to DB Connector
Till then,
regards
Seán
All right, that's really helpful. You have already helped loads. I will try my current approach to capture the documents in the contract and starting the next task as a Script task.
If that doesn't work, then I can test the multi-instantiation approach you have suggested.
The Hidden Form approach do not/cannot have script/service tasks associated with them it is after all - just one form. So no. If you really need this then you need multiple tasks and separate forms.
Yes, I understand. For the current use case, the a single-page application will work as I just need to call our product API stack to store the results in DB. Perhaps, I will need to dig deeper to know how to work with multiple tasks and navigating among them. :)
Thanks a lot!
Regards
Megha
OK, how to do the document upload.
Create the subroutine to do the uploads
Now to send the data to the subroutine:
import java.util.logging.Logger;
int dI = 0;
boolean debug = true;
ProcessRuntimeAPI processRuntimeAPI = apiAccessor.getProcessAPI();
String processName = processRuntimeAPI.getProcessInstance(processInstanceId).getName();
//set the name of the routine
String thisTrace = " "+processName+ " getUpdatedOutputs: "
Logger logger= Logger.getLogger("org.bonitasoft");
if(debug){dI++; logger.severe(dI+thisTrace+"Trace Start");}
//TODO - Code goes in here
List<Long> xList = new ArrayList<Long>();
if(debug){
dI++;if(0<=outputFiles){logger.severe(dI+thisTrace+"outputFiles: "+outputFiles.toString())}else{logger.severe(dI+thisTrace+"outputFiles: "+outputFiles.toString())}
dI++;if(1<=outputFiles){logger.severe(dI+thisTrace+"outputDoc01: "+outputDoc01.getVersion()+" "+outputDoc01.getContentFileName())}
dI++;if(2<=outputFiles){logger.severe(dI+thisTrace+"outputDoc02: "+outputDoc02.getVersion()+" "+outputDoc02.getContentFileName())}
dI++;if(3<=outputFiles){logger.severe(dI+thisTrace+"outputDoc03: "+outputDoc03.getVersion()+" "+outputDoc03.getContentFileName())}
dI++;if(4<=outputFiles){logger.severe(dI+thisTrace+"outputDoc04: "+outputDoc04.getVersion()+" "+outputDoc04.getContentFileName())}
dI++;if(5<=outputFiles){logger.severe(dI+thisTrace+"outputDoc05: "+outputDoc05.getVersion()+" "+outputDoc05.getContentFileName())}
dI++;if(6<=outputFiles){logger.severe(dI+thisTrace+"outputDoc06: "+outputDoc06.getVersion()+" "+outputDoc06.getContentFileName())}
dI++;if(7<=outputFiles){logger.severe(dI+thisTrace+"outputDoc07: "+outputDoc07.getVersion()+" "+outputDoc07.getContentFileName())}
dI++;if(8<=outputFiles){logger.severe(dI+thisTrace+"outputDoc08: "+outputDoc08.getVersion()+" "+outputDoc08.getContentFileName())}
dI++;if(9<=outputFiles){logger.severe(dI+thisTrace+"outputDoc09: "+outputDoc09.getVersion()+" "+outputDoc09.getContentFileName())}
dI++;if(10<=outputFiles){logger.severe(dI+thisTrace+"outputDoc10: "+outputDoc10.getVersion()+" "+outputDoc10.getContentFileName())}
}
def x;
if(1<=outputFiles){if (outputDoc01 == null){ /* do nothing */ } else{x = apiAccessor.getProcessAPI().getLastDocument(processInstanceId, "outputDoc01").getId(); xList.add(x);}}
if(2<=outputFiles){if (outputDoc02 == null){ /* do nothing */ } else{x = apiAccessor.getProcessAPI().getLastDocument(processInstanceId, "outputDoc02").getId(); xList.add(x);}}
if(3<=outputFiles){if (outputDoc03 == null){ /* do nothing */ } else{x = apiAccessor.getProcessAPI().getLastDocument(processInstanceId, "outputDoc03").getId(); xList.add(x);}}
if(4<=outputFiles){if (outputDoc04 == null){ /* do nothing */ } else{x = apiAccessor.getProcessAPI().getLastDocument(processInstanceId, "outputDoc04").getId(); xList.add(x);}}
if(5<=outputFiles){if (outputDoc05 == null){ /* do nothing */ } else{x = apiAccessor.getProcessAPI().getLastDocument(processInstanceId, "outputDoc05").getId(); xList.add(x);}}
if(6<=outputFiles){if (outputDoc06 == null){ /* do nothing */ } else{x = apiAccessor.getProcessAPI().getLastDocument(processInstanceId, "outputDoc06").getId(); xList.add(x);}}
if(7<=outputFiles){if (outputDoc07 == null){ /* do nothing */ } else{x = apiAccessor.getProcessAPI().getLastDocument(processInstanceId, "outputDoc07").getId(); xList.add(x);}}
if(8<=outputFiles){if (outputDoc08 == null){ /* do nothing */ } else{x = apiAccessor.getProcessAPI().getLastDocument(processInstanceId, "outputDoc08").getId(); xList.add(x);}}
if(9<=outputFiles){if (outputDoc09 == null){ /* do nothing */ } else{x = apiAccessor.getProcessAPI().getLastDocument(processInstanceId, "outputDoc09").getId(); xList.add(x);}}
if(10<=outputFiles){if(outputDoc10 == null){ /* do nothing */ } else{x = apiAccessor.getProcessAPI().getLastDocument(processInstanceId, "outputDoc10").getId(); xList.add(x);}}
if(debug){dI++; logger.severe(dI+thisTrace+"xList"+xList.toString());}
if(debug){dI++; logger.severe(dI+thisTrace+"Trace End");}
return xList;
NOTE(1): Debug code is included and can be read on the log
NOTE(2): the code is for hardcoded documents we have - you need to change the two major code sections to loop over the Document List variable that you've defined. The first section prints all the information on the log, the second created the usable outputDocuments variable previously defined
6. After the Script/Service Task add an ABSTRACT Task->General->Iteration
7. Select Parallel or Sequential multi-Instantiation as required
8. Select Create instances from a list
9. Input is variable outputDocuments
10. Iterator is docID
11. goto General->Process to call and give the name of pushDocumentToDatabase subroutine
12. Execution->data to send and add docID Assigned to Data docID
That gives you everything except the last bit to turn the data into a blob that can be uploaded:
String documentname = d1.getContentFileName();
String mimeType = d1.getContentMimeType();
// use fileName if title is empty
title = (title==null||title.isEmpty()) ? d1.getContentFileName():title;
final byte[] contentByte = ((DocumentAPI) processRuntimeAPI).getDocumentContent(d1.getContentStorageId());
final ByteArrayInputStream gubedocument = new ByteArrayInputStream(contentByte);
int doclength = contentByte.length;
and the final prepared statement that uploads the data:
+ " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
//1 documentid
//2 tdocumentname
//3 documentname
//4 versionmajor
//5 versionminor
//6 versionmini
//7 uploader
//8 approver
//9 uploaddate
//10 approvaldate
//11 status
//12 document - the blob
ps.setLong(1, 0);
ps.setString(2, removeExtension(documentname.replace("_", " "))); // remove
ps.setString(3, gubedocumentname);
ps.setLong(4, 0);
ps.setLong(5, 0);
ps.setLong(6, 0);
ps.setString(7, "Uploader");
ps.setString(8, "Approver");
ps.setTimestamp(9, date);
ps.setTimestamp(10, date);
ps.setString(11, "Pending");
ps.setBinaryStream(12, document, doclength);
ps.executeUpdate();
ps.close();
con.close();
It's all up to you now,
regards
Seán
All right, this is something interesting to work with!
I had a similar code for looping over my documents and calling a function in the script connector itself that will upload my documents to the DB Server (sequentially). After looking at the code you have sent, there are clearly several merits of this approach:
1. Decoupling logic for input (document retrieval from forms) and the insertion logic.
2. Parallel or sequential multi-instantiation
But since, I have a deliverable due for this week, I just wanted to know if the following code will work as per my requirements:
`
If this is executed, probably I can try the multi-instantiated subroutine method as well.
Also, on a different note, in my one-page application with multiple (hidden) forms, if I click Submit button the information will get stored in the contract associated with the BDM and it will get updated (correct?); if in addition to that I also want to send the data to an external API call which takes some headers as well in the request. Now there is a property where I can mention the URL to send to, my query is this URL supposed to point to my BDM or I can use it to point to an external API (but still the issue of specifying custom headers (Authorization, etc.)). Since, I had this confusion, I decided that it will be best to create a custom button that has a special onClick function to handle GET/POST requests along with headers. I need to know if this is a correct thing to do.
Thank you!
Regards
Megha
In the context of hidden forms (multiple steps), does each form need a submit button to save the contract necessary to update the BDM or can I have a button at the last form to update all contracts at once?
I actually ran a small test with the Submit Button on one of my hidden forms. I get the following error on clicking the Submit button:
"exception":"class org.bonitasoft.engine.bpm.flownode.UserTaskNotFoundException","message":"USERNAME=helen.kelly | Activity instance with id 8737521854282115197 not found"
Can you please tell me why this error is occuring?
Regards
Nope, but only ONE submit button...at the very end, the next and previous buttons flick between the "pages" of the form that's it.
As to why this user task - no idea...sorry.
Thank you for the quick and useful reply.
Your answer helped me solve the UserTaskNotFoundException as well. I was trying to use the SUBMIT button on each of the hidden forms which do not have a task/process associated with them. Since no context was found on clicking submit, it threw the exception.
Moreover, I added the SUBMIT button at the end of all the steps and I am able to see an output (currently its a contract validation error, but once I move past that, I may be able to test the rest of the process).
In relation to this, I will only be able to move ahead to the next step (in my case, the Doc Upload Script Task) only if I successfully fulfil the instantiation form contract, is that correct??
Thanks a lot, once again for the help!