2.7.2. Contract Editor Controller

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.