Go to the Controller tab and replace its content with the following code:
ContractEdit.java.
package com.company.demo.gui.contract; import com.haulmont.bpm.entity.ProcDefinition; import com.haulmont.bpm.entity.ProcInstance; import com.haulmont.bpm.gui.action.ProcAction; import com.haulmont.bpm.gui.procactions.ProcActionsFrame; import com.haulmont.cuba.core.global.*; import com.haulmont.cuba.gui.WindowManager; import com.haulmont.cuba.gui.app.core.file.FileDownloadHelper; import com.haulmont.cuba.gui.components.*; import com.company.demo.entity.Contract; import com.haulmont.cuba.gui.components.actions.BaseAction; import com.haulmont.cuba.gui.data.DsContext; import com.haulmont.cuba.gui.xml.layout.ComponentsFactory; import javax.annotation.Nullable; import javax.inject.Inject; import java.util.Map; public class ContractEdit extends AbstractEditor<Contract> { private static final String PROCESS_CODE = "contractApproval"; @Inject private DataManager dataManager; private ProcDefinition procDefinition; private ProcInstance procInstance; @Inject private ProcActionsFrame procActionsFrame; @Inject private GroupBoxLayout procActionsBox; @Inject private ComponentsFactory componentsFactory; @Inject private Table attachmentsTable; @Inject private Metadata metadata; @Override protected void postInit() { super.postInit(); procDefinition = findProcDefinition(); if (procDefinition != null) { procInstance = findProcInstance(); if (procInstance == null) { procInstance = metadata.create(ProcInstance.class); procInstance.setProcDefinition(procDefinition); procInstance.setEntityName("demo$Contract"); procInstance.setEntityId(getItem().getId()); } initProcActionsFrame(); } getDsContext().addListener(new DsContext.CommitListenerAdapter() { @Override public void beforeCommit(CommitContext context) { if (procInstance != null && PersistenceHelper.isNew(procInstance)) { context.getCommitInstances().add(procInstance); } } }); FileDownloadHelper.initGeneratedColumn(attachmentsTable, "file"); } private void initProcActionsFrame() { procActionsFrame.setBeforeStartProcessPredicate(new ProcAction.BeforeActionPredicate() { @Override public boolean evaluate() { if (PersistenceHelper.isNew(getItem())) { showNotification(getMessage("saveContract"), NotificationType.WARNING); return false; } return true; } }); procActionsFrame.setAfterStartProcessListener(new ProcAction.AfterActionListener() { @Override public void actionCompleted() { showNotification(getMessage("processStarted"), NotificationType.HUMANIZED); close(COMMIT_ACTION_ID); } }); procActionsFrame.setBeforeCompleteTaskPredicate(new ProcAction.BeforeActionPredicate() { @Override public boolean evaluate() { return commit(); } }); procActionsFrame.setAfterCompleteTaskListener(new ProcAction.AfterActionListener() { @Override public void actionCompleted() { showNotification(getMessage("taskCompleted"), NotificationType.HUMANIZED); close(COMMIT_ACTION_ID); } }); procActionsFrame.setCancelProcessEnabled(false); procActionsFrame.init(procInstance); } @Nullable private ProcDefinition findProcDefinition() { LoadContext ctx = new LoadContext(ProcDefinition.class); ctx.setQueryString("select pd from bpm$ProcDefinition pd where pd.code = :code") .setParameter("code", PROCESS_CODE); return dataManager.load(ctx); } @Nullable private ProcInstance findProcInstance() { LoadContext ctx = new LoadContext(ProcInstance.class).setView("procInstance-start"); ctx.setQueryString("select pi from bpm$ProcInstance pi where pi.procDefinition.id = :procDefinition and pi.entityId = :entityId") .setParameter("procDefinition", procDefinition) .setParameter("entityId", getItem()); return dataManager.load(ctx); } }
Press OK to save the changes.
Let’s examine the controller code in details.
To start the process, we have to create an instance of the process (ProcInstance object), link it to a process definition (ProcDefinition object), and perform start. The process instance can be started both without a link to any project entity and with this link. In our case a link to the contract is mandatory.
In the beginning of the postInit()
method an instance of contract approval process is searched by findProcDefinition()
method, which searches for a process definition with the contractApproval
code. Next, there is a check whether a ProcInstance object linked with the contract exists in the database (findProcInstance()
method). If the process instance object doesn’t exist, then it is created, the relation to ProcDefinition is set, and a linked
entity name and identifier are filled.
if (procInstance == null) { procInstance = metadata.create(ProcInstance.class); procInstance.setProcDefinition(procDefinition); procInstance.setEntityName("demo$Contract"); procInstance.setEntityId(getItem().getId()); }
CommitListener
adds the created ProcInstance object to the list of entities
that will be sent to the middleware for be committed.
getDsContext().addListener(new DsContext.CommitListenerAdapter() { @Override public void beforeCommit(CommitContext context) { if (procInstance != null && PersistenceHelper.isNew(procInstance)) { context.getCommitInstances().add(procInstance); } } });
Next, go to the initProcActionsFrame()
method.
A ProcActionsFrame
is a standard frame displaying the buttons with available process actions. ProcActionsFrame
is linked with a ProcInstance
instance. If the process is not started yet, the frame will display the start process button. If the process is started and
there are active tasks for the current user, then the frame will display buttons for task completion according to the task
outcomes defined in the process model. For the detailed information about ProcActionsFrame see Section 6.1, “ProcActionsFrame”.
private void initProcActionsFrame() { procActionsFrame.setBeforeStartProcessPredicate(new ProcAction.BeforeActionPredicate() { @Override public boolean evaluate() { if (PersistenceHelper.isNew(getItem())) { showNotification(getMessage("saveContract"), NotificationType.WARNING); return false; } return true; } }); procActionsFrame.setAfterStartProcessListener(new ProcAction.AfterActionListener() { @Override public void actionCompleted() { showNotification(getMessage("processStarted"), NotificationType.HUMANIZED); close(COMMIT_ACTION_ID); } }); procActionsFrame.setBeforeCompleteTaskPredicate(new ProcAction.BeforeActionPredicate() { @Override public boolean evaluate() { return commit(); } }); procActionsFrame.setAfterCompleteTaskListener(new ProcAction.AfterActionListener() { @Override public void actionCompleted() { showNotification(getMessage("taskCompleted"), NotificationType.HUMANIZED); close(COMMIT_ACTION_ID); } }); procActionsFrame.setCancelProcessEnabled(false); procActionsFrame.init(procInstance); }
The procActionsFrame.setBeforeStartProcessPredicate()
method adds the check that is performed before the process start. If the contract is not saved yet, the process will not
start and the warning message will be shown.
The procActionsFrame.setBeforeCompleteTaskPredicate()
method invokes an editor commit and allows to complete a process action only if the editor commit was successful.
setAfterProcessStartListener
and setAfterCompleteTaskListener
methods will be invoked after corresponding events. They will show the notification and close the contract editor.
After all necessary listeners and predicates are set up, the initialization frame is invoked.
procActionsFrame.init(procInstance);
UI components are created during the frame initialization.