CRM and CX Blogs by SAP
Stay up-to-date on the latest developments and product news about intelligent customer experience and CRM technologies through blog posts from SAP experts.
cancel
Showing results for 
Search instead for 
Did you mean: 
priti_mittal
Product and Topic Expert
Product and Topic Expert
1,350
In commerce implementation project , sometimes there is a need to customize workflow to achieve and fulfil business requirement . This blog is to talk about specific use case where order approval workflow is customised to achieve two level of approval  .

 

Context


Let say , B2B customer places  an order on B2B Portal and does not have specific role to get his order auto-approved . And business wants such order to go through the 2 level of approvals . Every level has more than one authorised users assigned .

Below flowchart gives better idea to describe the requirement .



Pre-requisite :


Following example is set based upon ,

  1. SAP Commerce 2211

  2. SAP Commerce Integration pack 2211

  3. Basic understanding of workflow


Solution :





    • As per standard flow , order approval process has one of the action called  "startworkflow"

    • Reference of "startworkflow" has intern logic to execute <>WorkflowTemplateStrategy . This strategy file has the logic to create workflow and its steps on fly.

    • We will introduce new strategy for this use case.




Step-1 : Extend enumType WorkflowTemplateType and add new template type let say "DOUBLE_ORDER_APPROVAL"
<enumtype code="WorkflowTemplateType" autocreate="false" generate="false" dynamic="true">
<value code="DOUBLE_ORDER_APPROVAL"/>
</enumtype>

 

Step-2 :Create new strategy let say DoubleOrderApprovalWorkflowTemplateStrategy
public class DoubleOrderApprovalWorkflowTemplateStrategy extends AbstractWorkflowTemplateStrategy
{
....
}

 

Step-3 : Override method createWorkflowTemplate() . Write custom logic to create workflow and steps within .
@Override
public WorkflowTemplateModel createWorkflowTemplate(final List<? extends UserModel> users, final String code,
final String description)
{
//2nd level of approvers
List<UserModel> secondLevelofApprovers = filterSecondLevelofApprovers(users);

//1st level of approvers
List<UserModel> firstLevelofApprovers = filterFirstLevelofApprovers(users);

return (WorkflowTemplateModel) getSessionService().executeInLocalView(new SessionExecutionBody()
{
@Override
public Object execute()
{
final UserModel admin = getUserService().getAdminUser();
getUserService().setCurrentUser(admin);

if (LOG.isDebugEnabled())
{
LOG.debug(String.format("Creating WorkflowTemplate for code %s with description %s", code, description));
}
final WorkflowTemplateModel workflowTemplateModel = createBlankWorkflowTemplate(code, description, admin);

final AutomatedWorkflowActionTemplateModel wakeUpProcessEngineAutomatedAction = createAutomatedWorkflowActionTemplate(
code, B2BWorkflowIntegrationService.ACTIONCODES.BACK_TO_PROCESSENGINE.name(), WorkflowActionType.NORMAL, admin,
workflowTemplateModel, null, "b2bAfterWorkflowAction");

final AutomatedWorkflowActionTemplateModel afterApproveOrderWorkflowDecisionAction = createAutomatedWorkflowActionTemplate(
code, B2BWorkflowIntegrationService.ACTIONCODES.APPROVED.name() + "_END", WorkflowActionType.NORMAL, admin,
workflowTemplateModel, null, "afterApproveOrderWorkflowDecisionAction");
final AutomatedWorkflowActionTemplateModel afterRejectOrderWorkflowDecisionAction = createAutomatedWorkflowActionTemplate(
code, B2BWorkflowIntegrationService.ACTIONCODES.REJECTED.name() + "_END", WorkflowActionType.NORMAL, admin,
workflowTemplateModel, null, "afterRejectOrderWorkflowDecisionAction");


final AutomatedWorkflowActionTemplateModel adminApproveDecisionAutomatedAction = createAutomatedWorkflowActionTemplate(
code, B2BWorkflowIntegrationService.ACTIONCODES.APPROVED.name(), WorkflowActionType.NORMAL, admin,
workflowTemplateModel, null, "approveDecisionAutomatedAction");



for (final UserModel approver : firstLevelofApprovers)
{
final AutomatedWorkflowActionTemplateModel approveDecisionAutomatedAction = createAutomatedWorkflowActionTemplate(
code, B2BWorkflowIntegrationService.ACTIONCODES.APPROVED.name(), WorkflowActionType.NORMAL, approver,
workflowTemplateModel, null, "approveDecisionAutomatedAction");

final AutomatedWorkflowActionTemplateModel rejectDecisionAutomatedAction = createAutomatedWorkflowActionTemplate(
code, B2BWorkflowIntegrationService.ACTIONCODES.REJECTED.name(), WorkflowActionType.NORMAL, approver,
workflowTemplateModel, null, "rejectDecisionAutomatedAction");

final WorkflowActionTemplateModel action = createWorkflowActionTemplateModel(code,
B2BWorkflowIntegrationService.ACTIONCODES.APPROVAL.name(), WorkflowActionType.START, approver,
workflowTemplateModel);

// approve decision links and proceed to next level incase any of the approver approves
createLink(action, approveDecisionAutomatedAction, B2BWorkflowIntegrationService.DECISIONCODES.APPROVE.name(),
Boolean.FALSE);
createLink(approveDecisionAutomatedAction, adminApproveDecisionAutomatedAction,
B2BWorkflowIntegrationService.DECISIONCODES.APPROVE.name() + "_END", Boolean.FALSE);

// reject decision links
createLink(action, rejectDecisionAutomatedAction, B2BWorkflowIntegrationService.DECISIONCODES.REJECT.name(),
Boolean.FALSE);
createLink(rejectDecisionAutomatedAction, afterRejectOrderWorkflowDecisionAction,
B2BWorkflowIntegrationService.DECISIONCODES.REJECT.name() + "_END", Boolean.FALSE);
createLink(afterRejectOrderWorkflowDecisionAction, wakeUpProcessEngineAutomatedAction,
B2BWorkflowIntegrationService.ACTIONCODES.BACK_TO_PROCESSENGINE.name(), Boolean.FALSE);
}


List<WorkflowActionTemplateModel> toActions = new ArrayList<>();

WorkflowActionType workflowActionType = WorkflowActionType.NORMAL;

if(firstLevelofApprovers.isEmpty()){
workflowActionType = WorkflowActionType.START;
}

for (final UserModel approver : secondLevelofApprovers)
{
final AutomatedWorkflowActionTemplateModel salesApproveDecisionAutomatedAction = createAutomatedWorkflowActionTemplate(
code, B2BWorkflowIntegrationService.ACTIONCODES.APPROVED.name(), WorkflowActionType.NORMAL, approver,
workflowTemplateModel, null, "approveDecisionAutomatedAction");

final AutomatedWorkflowActionTemplateModel salesRejectDecisionAutomatedAction = createAutomatedWorkflowActionTemplate(
code, B2BWorkflowIntegrationService.ACTIONCODES.REJECTED.name(), WorkflowActionType.NORMAL, approver,
workflowTemplateModel, null, "rejectDecisionAutomatedAction");

final WorkflowActionTemplateModel salesAction = createWorkflowActionTemplateModel(code,
B2BWorkflowIntegrationService.ACTIONCODES.APPROVAL.name(), workflowActionType, approver,
workflowTemplateModel);

if(!firstLevelofApprovers.isEmpty()){
toActions.add(salesAction);
}

// approve decision links
createLink(salesAction, salesApproveDecisionAutomatedAction, B2BWorkflowIntegrationService.DECISIONCODES.APPROVE.name(),
Boolean.FALSE);

createLink(salesApproveDecisionAutomatedAction, afterApproveOrderWorkflowDecisionAction,
B2BWorkflowIntegrationService.DECISIONCODES.APPROVE.name() + "_END", Boolean.FALSE);
createLink(afterApproveOrderWorkflowDecisionAction, wakeUpProcessEngineAutomatedAction,
B2BWorkflowIntegrationService.ACTIONCODES.BACK_TO_PROCESSENGINE.name(), Boolean.FALSE);


// reject decision links
createLink(salesAction, salesRejectDecisionAutomatedAction, B2BWorkflowIntegrationService.DECISIONCODES.REJECT.name(),
Boolean.FALSE);
createLink(salesRejectDecisionAutomatedAction, afterRejectOrderWorkflowDecisionAction,
B2BWorkflowIntegrationService.DECISIONCODES.REJECT.name() + "_END", Boolean.FALSE);
createLink(afterRejectOrderWorkflowDecisionAction, wakeUpProcessEngineAutomatedAction,
B2BWorkflowIntegrationService.ACTIONCODES.BACK_TO_PROCESSENGINE.name(), Boolean.FALSE);
}
if(!firstLevelofApprovers.isEmpty())
{
createManyToActionsLink(adminApproveDecisionAutomatedAction, toActions,
B2BWorkflowIntegrationService.ACTIONCODES.APPROVED.name(), Boolean.FALSE);
}

//end finishAction to end workflow
final WorkflowActionTemplateModel finishAction = createWorkflowActionTemplateModel(code,
B2BWorkflowIntegrationService.ACTIONCODES.END.name(), WorkflowActionType.END, admin, workflowTemplateModel);
createLink(wakeUpProcessEngineAutomatedAction, finishAction, "WORKFLOW_FINISHED", Boolean.FALSE);

return workflowTemplateModel;
}
});
}

/**
* To link one fromActionTemplate to list of toTemplateActions(target)
* @param fromAction
* @param toActions
* @param qualifier
* @param isAndConnection
*/
private void createManyToActionsLink(final WorkflowActionTemplateModel fromAction, final Collection<WorkflowActionTemplateModel> toActions,
final String qualifier, final Boolean isAndConnection)
{
final WorkflowDecisionTemplateModel workflowDecisionTemplate = getModelService()
.create(WorkflowDecisionTemplateModel.class);
workflowDecisionTemplate.setName(qualifier, Locale.ENGLISH);
workflowDecisionTemplate.setCode(fromAction.getCode());
workflowDecisionTemplate.setQualifier(qualifier);
workflowDecisionTemplate.setActionTemplate(fromAction);
workflowDecisionTemplate.setToTemplateActions(toActions);
getModelService().save(workflowDecisionTemplate);
final Collection<WorkflowDecisionTemplateModel> decisionTemplates = new ArrayList<WorkflowDecisionTemplateModel>(
fromAction.getDecisionTemplates());
decisionTemplates.add(workflowDecisionTemplate);
fromAction.setDecisionTemplates(decisionTemplates);
this.getModelService().save(fromAction);
setConnectionBetweenActionAndDecision(null, isAndConnection, workflowDecisionTemplate);
}

 

Step-4 : Override Method getWorkflowTemplateType() . provide the templateCode to refer further.
	@Override
public String getWorkflowTemplateType()
{
return WorkflowTemplateType.DOUBLE_ORDER_APPROVAL.getCode();
}

 

Step-5 : Create the bean reference  in *-spring.xml file
<bean id="doubleOrderApprovalWorkflowStrategy" class="<replace_with_currect_package>.DoubleOrderApprovalWorkflowTemplateStrategy" parent="abstractWorkflowTemplateStrategy"/>

 

Step-6 : Refer below code to  refer custom template and strategy in StartWorkFlow .
WorkflowTemplateType templateType = WorkflowTemplateType.DOUBLE_ORDER_APPROVAL;

final WorkflowTemplateModel workflowTemplate = b2bWorkflowIntegrationService.createWorkflowTemplate(approvers,workflowTemplateCode, "Generated B2B Double Order Approval Workflow", templateType);

final WorkflowModel workflow = workflowService.createWorkflow(workflowTemplate.getName(), workflowTemplate,Collections.singletonList(aprovalProcess), workflowTemplate.getOwner());

workflowProcessingService.startWorkflow(workflow);
this.modelService.saveAll();
order.setWorkflow(workflow);
order.setStatus(OrderStatus.PENDING_APPROVAL);
this.modelService.save(order);

Conclusion


This post describes very specific use case to give a flavour and thought process to customize Hybris workflow . This can further customised or modified as per need and business ask .

 

Finally, share your feedback. This would encourage me to add more content in the coming days.

 

Thank You

 
1 Comment