[BPMN] เพิ่มเติมเกี่ยวกับ multiInstanceLoopCharacteristics

จาก Blog ที่แล้วที่ผมได้เขียนเกี่ยวกับตัวอย่างของ multiInstanceLoopCharacteristics โดยใช้ตัวอย่างเรื่อง กระบวนการของการขอเสนอรางวัลให้กับพนักงานดีเด่น ซึ่งลองเล่นไป เล่นมาแล้วพบปัญหา ดังนี้

  • Task "Evaluate Award" ได้ถูก Assign ให้เป็น Group โดยที่กรรมการแต่ละท่านเลือกไปทำครับ แต่มันสามารถ Claim ซ้ำกันได้ครับ แบบนี้ไม่ดีแน่ครับ
  • ทางแก้ไขหล่ะ
    • แบบที่ 1: แก้ไข Object ของ MultiInstance แล้วให้กำหนด User เป็น Assignee สำหรับ Task นั้นเลย
    • แบบที่ 2:แก้ไขโดยการเพิ่ม Listener แล้วเขียน Code ขึ้นมาตรวจสอบครับ
  • ** สำหรับใน Blog นี้ ผมใช้วิธีแรกครับ แต่จะแทรก ตัวอย่างของ Listener เข้าไปด้วยครับ

ลุยกันเลยครับ

  • สำหรับภาพรวมของกระบวนการ (Process) เหมือนเดิมครับ แต่จะแก้ Coding และ Config ข้างในมากกว่าครับ
  • มาเริ่มกันเลยครับ
  • ใน Script Task "Setup Reviews" ผมแก้จากการใช้ List มาเป็น Dictionary ครับ โดยให้ key = ชื่อคณะกรรมการ และ value = ผลการประเมินครับ ตาม Code ตัวอย่างเลย
    //create a collection to hold our multi-instance results.
    //ดึงข้อมูลผู้ใช้ออกมา
    def committee = [ 'jame', 'john', 'peter'];
    //สร้าง MAP เป็นข้อมูลแบบ Key/Value
    def reviewResultMap = [:];
    for (i = 0; i <RequiredNumOfPeers; i++) {
        reviewResultMap.put(committee[i], "NOT_REVIEW");
    }
    
    execution.setVariable ("reviewResultMap", reviewResultMap);
    //ดึง Id ของ Process มาแสดงผล
    RequestNo= execution.getProcessInstanceId();
    execution.setVariable ("RequestNo", RequestNo);
  • มาที่พระเอกของแรกแล้วครับ ตรง Sub-Process "EvaluateProcess" ที่ถูกกำหนดเป็น MultiInstance ครับ
    • แก้ไข MultiInstance ครับ
      • มาดูที่รูปก่อนครับ
      • แล้วที่นี้กำหนดข้อมูลของแต่ะส่วน ดังนี้ครับ
        • Loop Cadinality - อันนี้เหมือนเดิมครับ คือ เอาค่าจากตัวแปร ${RequiredNumOfPeers} ที่กำหนดมาจาก Task Associate Reviews 
        • Collection - Data ที่ใช้ใน multiInstanceLoopCharacteristics ครับ โดยช้อมูลชุดนี้เกิดจาก Task Setup Reviews ที่สร้าง MAP ชื่อ ${reviewResultMap} ที่มีขนาดเท่ากับ ${RequiredNumOfPeers} ถ้าในตัวอย่างเท่ากับ 3 ซึ่งในแต่ละ Instance ของ Task เห็นข้อมูล reviewsResult(Object แต่ละอันใน MAP reviewsResults โดยดึงมาเฉพาะ Key ถ้าดูในแบบจำลองในส่วน Collection กำหนดให้ค่าเท่ากับ ${reviewResultMap.keySet()}  ) ซึ่งประกาศไว้ในส่วน element ในรูปแบบ 1 Instance ต่อ 1 Object ครับ
        • completionCondition ครับ ซึ่งถ้าเงื่อนไขเป็นจริง ก็จะออกจาก  multiInstanceLoopCharacteristics ครับ โดยเงื่อนไขทาง Business คือ กรรมการ 2 ใน 3 ท่าน ได้เข้าร่วมการประเมินครับ ซื่งสามารถกำหนดเงื่อนไขได้ ดังนี้
          //เงื่อนไขทาง Business คือ กรรมการ 2 ใน 3 ท่าน ได้เข้าร่วมการประเมินครับ ส่วนอันหลังผมกำหนดขึ้น เพราะตรวจสอบว่าระหว่างที่ทำ Process ข้อมูลไม่ได้ถูกเพิ่มปรับแต่งอะไร
          ${(nrOfCompletedInstances >= RequiredMinNumofPeer) && (reviewResultMap.size() == nrOfInstances)}
    • เพิ่ม Listener เพื่อกำหนดตัวแปรระดับ Local ให้ Engine รู้จักครับ โดยผมเพิ่มตัวแปร reviewer(ชื่อผู้ประเมิน) และ reviewResult(ผลการประเมิน) ซื้อยู่ใน Scope ของ Sub-Process "EvaluateProcess" ดังนี้ครับ
      • มาดูที่รูปก่อนครับ
      • ในส่วนของ Script มีการแก้ไข โดยจะ Execute ต่อเมื่อมีการเรียกใช้ Sub Process "EvaluateProcess" ครับ โดยมีรายละเอียดของ Script ดังนี้
        execution.setVariable("reviewer", theCommitee,"EvaluateProcess");
        execution.setVariable("reviewResult", "NOT_REVIEW","EvaluateProcess");
  • Note: อย่าลืมแก้ไขชื่อตัวแปรให้ครับนะครับ ถ้าไป Run ที่ตัว BPMN Engine แล้วเสียเวลาพอสมควรครับ

ทดสอบกันเลยครับ

  • Mary ได้เริ่มการทำงานของ Reward Process แล้วครับ
  • Mary กรอกข้อมูลเพื่อเสนอชื่อของผมไปครับ (ในชีวิตจริงๆ ก็อยากได้เงินก้อนแบบนี้บ้าง T__T)
  • กำหนดรายละเอียดของ Task Associate Reviews  ครับ
  • สังเกตุดีๆว่าตอนนี้ Task "Evaluate Award" ได้ถูก Assign เข้าไปที่คณะกรรมการแต่ละท่านแล้วครับ เมื่อ Mary ได้เขียน Flow มาจนถึง Sub Process "EvaluateProcess" พบว่า User ที่เป็นกรรมการทั้ง 3 ท่านได้ถูก Assign Task เรียบร้อยครับ
    • User jame
    • User john
    • User peter
  • และเมื่อ 2 ใน 3 ของคณะกรรมการได้พิจารณา Task เรียบร้อยแล้ว พบ่วาระบบทำการ Clear อีก 1 Instance ให้อัตโนมัติครับ
  • เมื่อจบการตัดสินของคณะกรรมการแล้ง ระบบตัดสินผลลัพธ์ และแจ้งให้ Mary รับทราบผลลัพธ์ครับ

สำหรับ Source Code ของแบบจำลองครับ

  • มาดูที่ Github ได้เลยครับ
    <?xml version="1.0" encoding="UTF-8"?>
    <bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" id="Definitions_1" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="1.11.3">
    <bpmn:collaboration id="Collaboration_1ukzxwm">
    <bpmn:participant id="Participant_1jytf7q" name="Reward Process" processRef="RewardMultiInstanceAssignee" />
    </bpmn:collaboration>
    <bpmn:process id="RewardMultiInstanceAssignee" isExecutable="true">
    <bpmn:laneSet>
    <bpmn:lane id="Lane_0r1co7i" name="Manager">
    <bpmn:flowNodeRef>Task_0o9iylr</bpmn:flowNodeRef>
    <bpmn:flowNodeRef>EndEvent_111ifpl</bpmn:flowNodeRef>
    <bpmn:flowNodeRef>EndEvent_1mu7bj0</bpmn:flowNodeRef>
    <bpmn:flowNodeRef>Task_1uzfnxh</bpmn:flowNodeRef>
    <bpmn:flowNodeRef>Task_1l0vjpp</bpmn:flowNodeRef>
    <bpmn:flowNodeRef>Task_0p0dthr</bpmn:flowNodeRef>
    <bpmn:flowNodeRef>StartEvent_1</bpmn:flowNodeRef>
    <bpmn:flowNodeRef>ExclusiveGateway_1cfwqqd</bpmn:flowNodeRef>
    <bpmn:childLaneSet xsi:type="bpmn:tLaneSet" />
    </bpmn:lane>
    <bpmn:lane id="Lane_0feow9m" name="Reward Commitee">
    <bpmn:flowNodeRef>EvaluateProcess</bpmn:flowNodeRef>
    <bpmn:flowNodeRef>Task_0ofwv70</bpmn:flowNodeRef>
    </bpmn:lane>
    </bpmn:laneSet>
    <bpmn:sequenceFlow id="SequenceFlow_1nhdcp1" sourceRef="StartEvent_1" targetRef="Task_1l0vjpp" />
    <bpmn:sequenceFlow id="SequenceFlow_0y00p8b" sourceRef="Task_1l0vjpp" targetRef="Task_0p0dthr" />
    <bpmn:sequenceFlow id="SequenceFlow_0azgsh3" sourceRef="Task_0p0dthr" targetRef="EvaluateProcess" />
    <bpmn:sequenceFlow id="SequenceFlow_0bslxhz" sourceRef="Task_0ofwv70" targetRef="ExclusiveGateway_1cfwqqd" />
    <bpmn:sequenceFlow id="SequenceFlow_125qqc9" name="Rejected" sourceRef="ExclusiveGateway_1cfwqqd" targetRef="Task_1uzfnxh">
    <bpmn:conditionExpression xsi:type="bpmn:tFormalExpression"><![CDATA[${NumOfApprove < RequiredNumOfApproval}]]></bpmn:conditionExpression>
    </bpmn:sequenceFlow>
    <bpmn:sequenceFlow id="SequenceFlow_0qvflnw" sourceRef="Task_1uzfnxh" targetRef="EndEvent_1mu7bj0" />
    <bpmn:sequenceFlow id="SequenceFlow_06tfs2q" name="Approved" sourceRef="ExclusiveGateway_1cfwqqd" targetRef="Task_0o9iylr">
    <bpmn:conditionExpression xsi:type="bpmn:tFormalExpression"><![CDATA[${NumOfApprove >= RequiredNumOfApproval}]]></bpmn:conditionExpression>
    </bpmn:sequenceFlow>
    <bpmn:sequenceFlow id="SequenceFlow_04bpp14" sourceRef="Task_0o9iylr" targetRef="EndEvent_111ifpl" />
    <bpmn:userTask id="Task_0o9iylr" name="Note Personnel File Appproved" camunda:assignee="mary">
    <bpmn:extensionElements>
    <camunda:formData>
    <camunda:formField id="RequestNo" label="RequestNo" type="string">
    <camunda:validation>
    <camunda:constraint name="readonly" config="true" />
    </camunda:validation>
    </camunda:formField>
    <camunda:formField id="EmployeeId" label="EmployeeId" type="string">
    <camunda:validation>
    <camunda:constraint name="readonly" config="true" />
    </camunda:validation>
    </camunda:formField>
    <camunda:formField id="EmployeeName" label="EmployeeName" type="string">
    <camunda:validation>
    <camunda:constraint name="readonly" config="true" />
    </camunda:validation>
    </camunda:formField>
    <camunda:formField id="Position" label="Position" type="string">
    <camunda:validation>
    <camunda:constraint name="readonly" config="true" />
    </camunda:validation>
    </camunda:formField>
    <camunda:formField id="No1Workmanship" label="No1Workmanship" type="string">
    <camunda:validation>
    <camunda:constraint name="readonly" config="true" />
    </camunda:validation>
    </camunda:formField>
    <camunda:formField id="No2Workmanship" label="No2Workmanship" type="string">
    <camunda:validation>
    <camunda:constraint name="readonly" config="true" />
    </camunda:validation>
    </camunda:formField>
    <camunda:formField id="No3Workmanship" label="No3Workmanship" type="string">
    <camunda:validation>
    <camunda:constraint name="readonly" config="true" />
    </camunda:validation>
    </camunda:formField>
    <camunda:formField id="RewardAmount" label="RewardAmount" type="long" defaultValue="10000">
    <camunda:validation>
    <camunda:constraint name="readonly" config="true" />
    </camunda:validation>
    </camunda:formField>
    <camunda:formField id="RewardStatus" label="RewardStatus" type="string" defaultValue="Approved">
    <camunda:validation>
    <camunda:constraint name="readonly" config="true" />
    </camunda:validation>
    </camunda:formField>
    </camunda:formData>
    </bpmn:extensionElements>
    <bpmn:incoming>SequenceFlow_06tfs2q</bpmn:incoming>
    <bpmn:outgoing>SequenceFlow_04bpp14</bpmn:outgoing>
    </bpmn:userTask>
    <bpmn:endEvent id="EndEvent_111ifpl" name="End Approved">
    <bpmn:incoming>SequenceFlow_04bpp14</bpmn:incoming>
    </bpmn:endEvent>
    <bpmn:endEvent id="EndEvent_1mu7bj0" name="End Reject">
    <bpmn:incoming>SequenceFlow_0qvflnw</bpmn:incoming>
    </bpmn:endEvent>
    <bpmn:userTask id="Task_1uzfnxh" name="Note Personnel File Rejected" camunda:assignee="mary">
    <bpmn:extensionElements>
    <camunda:formData>
    <camunda:formField id="RequestNo" label="RequestNo" type="string">
    <camunda:validation>
    <camunda:constraint name="readonly" config="true" />
    </camunda:validation>
    </camunda:formField>
    <camunda:formField id="EmployeeId" label="EmployeeId" type="string">
    <camunda:validation>
    <camunda:constraint name="readonly" config="true" />
    </camunda:validation>
    </camunda:formField>
    <camunda:formField id="EmployeeName" label="EmployeeName" type="string">
    <camunda:validation>
    <camunda:constraint name="readonly" config="true" />
    </camunda:validation>
    </camunda:formField>
    <camunda:formField id="Position" label="Position" type="string">
    <camunda:validation>
    <camunda:constraint name="readonly" config="true" />
    </camunda:validation>
    </camunda:formField>
    <camunda:formField id="No1Workmanship" label="No1Workmanship" type="string">
    <camunda:validation>
    <camunda:constraint name="readonly" config="true" />
    </camunda:validation>
    </camunda:formField>
    <camunda:formField id="No2Workmanship" label="No2Workmanship" type="string">
    <camunda:validation>
    <camunda:constraint name="readonly" config="true" />
    </camunda:validation>
    </camunda:formField>
    <camunda:formField id="No3Workmanship" label="No3Workmanship" type="string">
    <camunda:validation>
    <camunda:constraint name="readonly" config="true" />
    </camunda:validation>
    </camunda:formField>
    <camunda:formField id="RewardAmount" label="RewardAmount" type="long" defaultValue="10000">
    <camunda:validation>
    <camunda:constraint name="readonly" config="true" />
    </camunda:validation>
    </camunda:formField>
    <camunda:formField id="RewardStatus" label="RewardStatus" type="string" defaultValue="Rejected">
    <camunda:validation>
    <camunda:constraint name="readonly" config="true" />
    </camunda:validation>
    </camunda:formField>
    </camunda:formData>
    </bpmn:extensionElements>
    <bpmn:incoming>SequenceFlow_125qqc9</bpmn:incoming>
    <bpmn:outgoing>SequenceFlow_0qvflnw</bpmn:outgoing>
    </bpmn:userTask>
    <bpmn:sequenceFlow id="SequenceFlow_1a0h9gg" sourceRef="EvaluateProcess" targetRef="Task_0ofwv70" />
    <bpmn:startEvent id="StartEvent_1" name="Enter Employee who shall receive Reward">
    <bpmn:extensionElements>
    <camunda:formData>
    <camunda:formField id="RequestNo" label="RequestNo" type="string" defaultValue="-">
    <camunda:validation>
    <camunda:constraint name="readonly" config="true" />
    </camunda:validation>
    </camunda:formField>
    <camunda:formField id="EmployeeId" label="EmployeeId" type="string">
    <camunda:validation>
    <camunda:constraint name="required" config="true" />
    </camunda:validation>
    </camunda:formField>
    <camunda:formField id="EmployeeName" label="EmployeeName" type="string">
    <camunda:validation>
    <camunda:constraint name="required" config="true" />
    </camunda:validation>
    </camunda:formField>
    <camunda:formField id="Position" label="Position" type="string">
    <camunda:validation>
    <camunda:constraint name="required" config="true" />
    </camunda:validation>
    </camunda:formField>
    <camunda:formField id="No1Workmanship" label="No1Workmanship" type="string">
    <camunda:validation>
    <camunda:constraint name="required" config="true" />
    </camunda:validation>
    </camunda:formField>
    <camunda:formField id="No2Workmanship" label="No2Workmanship" type="string" defaultValue="-">
    <camunda:validation>
    <camunda:constraint name="required" config="true" />
    </camunda:validation>
    </camunda:formField>
    <camunda:formField id="No3Workmanship" label="No3Workmanship" type="string" defaultValue="-">
    <camunda:validation>
    <camunda:constraint name="required" config="true" />
    </camunda:validation>
    </camunda:formField>
    <camunda:formField id="RewardAmount" label="RewardAmount" type="long" defaultValue="10000" />
    </camunda:formData>
    </bpmn:extensionElements>
    <bpmn:outgoing>SequenceFlow_1nhdcp1</bpmn:outgoing>
    </bpmn:startEvent>
    <bpmn:userTask id="Task_1l0vjpp" name="Associate Reviews" camunda:assignee="mary">
    <bpmn:extensionElements>
    <camunda:formData>
    <camunda:formField id="RequestNo" label="RequestNo" type="string">
    <camunda:validation>
    <camunda:constraint name="readonly" config="true" />
    </camunda:validation>
    </camunda:formField>
    <camunda:formField id="EmployeeId" label="ReviewEmployeeId" type="string">
    <camunda:validation>
    <camunda:constraint name="readonly" config="true" />
    </camunda:validation>
    </camunda:formField>
    <camunda:formField id="EmployeeName" label="ReviewEmployeeName" type="string">
    <camunda:validation>
    <camunda:constraint name="readonly" config="true" />
    </camunda:validation>
    </camunda:formField>
    <camunda:formField id="RequiredNumOfPeers" label="RequiredNumOfPeers" type="long" defaultValue="3" />
    <camunda:formField id="RequiredMinNumofPeer" label="RequiredMinNumofPeer" type="long" defaultValue="2" />
    <camunda:formField id="RequiredNumOfApproval" label="RequiredNumOfApproval" type="long" defaultValue="2" />
    </camunda:formData>
    </bpmn:extensionElements>
    <bpmn:incoming>SequenceFlow_1nhdcp1</bpmn:incoming>
    <bpmn:outgoing>SequenceFlow_0y00p8b</bpmn:outgoing>
    </bpmn:userTask>
    <bpmn:scriptTask id="Task_0p0dthr" name="Setup Reviews" scriptFormat="groovy">
    <bpmn:incoming>SequenceFlow_0y00p8b</bpmn:incoming>
    <bpmn:outgoing>SequenceFlow_0azgsh3</bpmn:outgoing>
    <bpmn:script><![CDATA[//create a collection to hold our multi-instance results.
    def committee = [ 'jame', 'john', 'peter'];
    //Create MAP
    def reviewResultMap = [:];
    for (i = 0; i <RequiredNumOfPeers; i++) {
    reviewResultMap.put(committee[i], "NOT_REVIEW");
    }
    execution.setVariable ("reviewResultMap", reviewResultMap);
    RequestNo= execution.getProcessInstanceId();
    execution.setVariable ("RequestNo", RequestNo);]]></bpmn:script>
    </bpmn:scriptTask>
    <bpmn:subProcess id="EvaluateProcess" name="Evaluate Process">
    <bpmn:documentation><![CDATA[
    ]]></bpmn:documentation>
    <bpmn:extensionElements>
    <camunda:executionListener event="start">
    <camunda:script scriptFormat="groovy"><![CDATA[execution.setVariable("reviewer", theCommitee,"EvaluateProcess");
    execution.setVariable("reviewResult", "NOT_REVIEW","EvaluateProcess");
    ]]></camunda:script>
    </camunda:executionListener>
    </bpmn:extensionElements>
    <bpmn:incoming>SequenceFlow_0azgsh3</bpmn:incoming>
    <bpmn:outgoing>SequenceFlow_1a0h9gg</bpmn:outgoing>
    <bpmn:multiInstanceLoopCharacteristics camunda:collection="${reviewResultMap.keySet()}" camunda:elementVariable="theCommitee">
    <bpmn:loopCardinality xsi:type="bpmn:tFormalExpression">${RequiredNumOfPeers}</bpmn:loopCardinality>
    <bpmn:completionCondition xsi:type="bpmn:tFormalExpression"><![CDATA[${(nrOfCompletedInstances >= RequiredMinNumofPeer) && (reviewResultMap.size() == nrOfInstances)}]]></bpmn:completionCondition>
    </bpmn:multiInstanceLoopCharacteristics>
    <bpmn:startEvent id="StartEvent_04zeqi7">
    <bpmn:outgoing>SequenceFlow_1es6rsf</bpmn:outgoing>
    </bpmn:startEvent>
    <bpmn:endEvent id="EndEvent_0ov7unr">
    <bpmn:incoming>SequenceFlow_0r400ep</bpmn:incoming>
    </bpmn:endEvent>
    <bpmn:sequenceFlow id="SequenceFlow_1es6rsf" sourceRef="StartEvent_04zeqi7" targetRef="Task_0eroej3" />
    <bpmn:sequenceFlow id="SequenceFlow_1cbnqvg" sourceRef="Task_0eroej3" targetRef="Task_07hvbtg" />
    <bpmn:userTask id="Task_0eroej3" name="Evaluate Award" camunda:assignee="${theCommitee}">
    <bpmn:documentation>Group RewardCommitee</bpmn:documentation>
    <bpmn:extensionElements>
    <camunda:formData>
    <camunda:formField id="RequestNo" label="RequestNo" type="string">
    <camunda:validation>
    <camunda:constraint name="readonly" config="true" />
    </camunda:validation>
    </camunda:formField>
    <camunda:formField id="EmployeeId" label="EmployeeId" type="string">
    <camunda:validation>
    <camunda:constraint name="readonly" config="true" />
    </camunda:validation>
    </camunda:formField>
    <camunda:formField id="EmployeeName" label="EmployeeName" type="string">
    <camunda:validation>
    <camunda:constraint name="readonly" config="true" />
    </camunda:validation>
    </camunda:formField>
    <camunda:formField id="Position" label="Position" type="string">
    <camunda:validation>
    <camunda:constraint name="readonly" config="true" />
    </camunda:validation>
    </camunda:formField>
    <camunda:formField id="No1Workmanship" label="No1Workmanship" type="string">
    <camunda:validation>
    <camunda:constraint name="readonly" config="true" />
    </camunda:validation>
    </camunda:formField>
    <camunda:formField id="No2Workmanship" label="No2Workmanship" type="string">
    <camunda:validation>
    <camunda:constraint name="readonly" config="true" />
    </camunda:validation>
    </camunda:formField>
    <camunda:formField id="No3Workmanship" label="No3Workmanship" type="string">
    <camunda:validation>
    <camunda:constraint name="readonly" config="true" />
    </camunda:validation>
    </camunda:formField>
    <camunda:formField id="RewardAmount" label="RewardAmount" type="long" defaultValue="10000">
    <camunda:validation>
    <camunda:constraint name="readonly" config="true" />
    </camunda:validation>
    </camunda:formField>
    <camunda:formField id="reviewer" label="reviewer" type="string">
    <camunda:validation>
    <camunda:constraint name="readonly" config="true" />
    </camunda:validation>
    </camunda:formField>
    <camunda:formField id="reviewResult" label="reviewResult" type="enum">
    <camunda:value id="NOT_REVIEW" name="NOT_REVIEW" />
    <camunda:value id="APPROVE" name="APPROVE" />
    <camunda:value id="REJECT" name="REJECT" />
    </camunda:formField>
    </camunda:formData>
    </bpmn:extensionElements>
    <bpmn:incoming>SequenceFlow_1es6rsf</bpmn:incoming>
    <bpmn:outgoing>SequenceFlow_1cbnqvg</bpmn:outgoing>
    </bpmn:userTask>
    <bpmn:scriptTask id="Task_07hvbtg" name="Update Result" scriptFormat="groovy">
    <bpmn:incoming>SequenceFlow_1cbnqvg</bpmn:incoming>
    <bpmn:outgoing>SequenceFlow_0r400ep</bpmn:outgoing>
    <bpmn:script><![CDATA[/*อัพเดต Result */
    reviewResultMap[reviewer] = reviewResult;
    out:println "review result on instance id: " + loopCounter + "-value-"+ reviewResultMap[reviewer] + "-by-" + reviewer;
    execution.setVariable ("reviewResultMap", reviewResultMap);
    ]]></bpmn:script>
    </bpmn:scriptTask>
    <bpmn:sequenceFlow id="SequenceFlow_0r400ep" sourceRef="Task_07hvbtg" targetRef="EndEvent_0ov7unr" />
    </bpmn:subProcess>
    <bpmn:scriptTask id="Task_0ofwv70" name="Calulate results" scriptFormat="groovy">
    <bpmn:incoming>SequenceFlow_1a0h9gg</bpmn:incoming>
    <bpmn:outgoing>SequenceFlow_0bslxhz</bpmn:outgoing>
    <bpmn:script><![CDATA[/*นับจำนวนคนที่ Approved พนักงาน */
    //เอา Review Result ของแต่ละคนออกมา
    //นับผลลัพธ์
    int numofApprove = 0;
    for (review in reviewResultMap) {
    out:println "review result:" + review.key + "--" + review.value;
    if(review.value =="APPROVE")
    {
    numofApprove += 1;
    }
    }
    execution.setVariable ("NumOfApprove", numofApprove);]]></bpmn:script>
    </bpmn:scriptTask>
    <bpmn:exclusiveGateway id="ExclusiveGateway_1cfwqqd" name="Is Appoved Award">
    <bpmn:incoming>SequenceFlow_0bslxhz</bpmn:incoming>
    <bpmn:outgoing>SequenceFlow_06tfs2q</bpmn:outgoing>
    <bpmn:outgoing>SequenceFlow_125qqc9</bpmn:outgoing>
    </bpmn:exclusiveGateway>
    </bpmn:process>
    <bpmndi:BPMNDiagram id="BPMNDiagram_1">
    <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Collaboration_1ukzxwm">
    <bpmndi:BPMNShape id="StartEvent_04zeqi7_di" bpmnElement="StartEvent_04zeqi7">
    <dc:Bounds x="263" y="265" width="36" height="36" />
    <bpmndi:BPMNLabel>
    <dc:Bounds x="236" y="305" width="90" height="12" />
    </bpmndi:BPMNLabel>
    </bpmndi:BPMNShape>
    <bpmndi:BPMNShape id="EndEvent_0ov7unr_di" bpmnElement="EndEvent_0ov7unr">
    <dc:Bounds x="564" y="265" width="36" height="36" />
    <bpmndi:BPMNLabel>
    <dc:Bounds x="537" y="305" width="90" height="12" />
    </bpmndi:BPMNLabel>
    </bpmndi:BPMNShape>
    <bpmndi:BPMNEdge id="SequenceFlow_1es6rsf_di" bpmnElement="SequenceFlow_1es6rsf">
    <di:waypoint xsi:type="dc:Point" x="299" y="283" />
    <di:waypoint xsi:type="dc:Point" x="320" y="283" />
    <bpmndi:BPMNLabel>
    <dc:Bounds x="264.5" y="262" width="90" height="12" />
    </bpmndi:BPMNLabel>
    </bpmndi:BPMNEdge>
    <bpmndi:BPMNEdge id="SequenceFlow_1cbnqvg_di" bpmnElement="SequenceFlow_1cbnqvg">
    <di:waypoint xsi:type="dc:Point" x="420" y="283" />
    <di:waypoint xsi:type="dc:Point" x="444" y="283" />
    <bpmndi:BPMNLabel>
    <dc:Bounds x="387" y="262" width="90" height="12" />
    </bpmndi:BPMNLabel>
    </bpmndi:BPMNEdge>
    <bpmndi:BPMNShape id="UserTask_10a4qhe_di" bpmnElement="Task_0eroej3">
    <dc:Bounds x="320" y="243" width="100" height="80" />
    </bpmndi:BPMNShape>
    <bpmndi:BPMNShape id="Participant_1jytf7q_di" bpmnElement="Participant_1jytf7q">
    <dc:Bounds x="123" y="-60" width="1099" height="465" />
    </bpmndi:BPMNShape>
    <bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
    <dc:Bounds x="227" y="-23" width="36" height="36" />
    <bpmndi:BPMNLabel>
    <dc:Bounds x="202" y="17" width="85" height="36" />
    </bpmndi:BPMNLabel>
    </bpmndi:BPMNShape>
    <bpmndi:BPMNShape id="UserTask_0rmfsuf_di" bpmnElement="Task_1l0vjpp">
    <dc:Bounds x="332" y="-45" width="100" height="80" />
    </bpmndi:BPMNShape>
    <bpmndi:BPMNShape id="ScriptTask_0zxuyho_di" bpmnElement="Task_0p0dthr">
    <dc:Bounds x="487" y="-45" width="100" height="80" />
    </bpmndi:BPMNShape>
    <bpmndi:BPMNShape id="UserTask_1wa2mm2_di" bpmnElement="Task_1uzfnxh">
    <dc:Bounds x="935" y="57" width="100" height="80" />
    </bpmndi:BPMNShape>
    <bpmndi:BPMNShape id="UserTask_0b03w9m_di" bpmnElement="Task_0o9iylr">
    <dc:Bounds x="935" y="-45" width="100" height="80" />
    </bpmndi:BPMNShape>
    <bpmndi:BPMNShape id="EndEvent_111ifpl_di" bpmnElement="EndEvent_111ifpl">
    <dc:Bounds x="1111" y="-23" width="36" height="36" />
    <bpmndi:BPMNLabel>
    <dc:Bounds x="1095" y="17" width="69" height="12" />
    </bpmndi:BPMNLabel>
    </bpmndi:BPMNShape>
    <bpmndi:BPMNShape id="EndEvent_1mu7bj0_di" bpmnElement="EndEvent_1mu7bj0">
    <dc:Bounds x="1111" y="79" width="36" height="36" />
    <bpmndi:BPMNLabel>
    <dc:Bounds x="1103" y="119" width="54" height="12" />
    </bpmndi:BPMNLabel>
    </bpmndi:BPMNShape>
    <bpmndi:BPMNShape id="ExclusiveGateway_1cfwqqd_di" bpmnElement="ExclusiveGateway_1cfwqqd" isMarkerVisible="true">
    <dc:Bounds x="789" y="21" width="50" height="50" />
    <bpmndi:BPMNLabel>
    <dc:Bounds x="722" y="15" width="89" height="12" />
    </bpmndi:BPMNLabel>
    </bpmndi:BPMNShape>
    <bpmndi:BPMNShape id="ScriptTask_1og7wn4_di" bpmnElement="Task_0ofwv70">
    <dc:Bounds x="676" y="241" width="100" height="80" />
    </bpmndi:BPMNShape>
    <bpmndi:BPMNShape id="SubProcess_1sva43h_di" bpmnElement="EvaluateProcess" isExpanded="true">
    <dc:Bounds x="243" y="182" width="377" height="195" />
    </bpmndi:BPMNShape>
    <bpmndi:BPMNEdge id="SequenceFlow_1nhdcp1_di" bpmnElement="SequenceFlow_1nhdcp1">
    <di:waypoint xsi:type="dc:Point" x="263" y="-5" />
    <di:waypoint xsi:type="dc:Point" x="332" y="-5" />
    <bpmndi:BPMNLabel>
    <dc:Bounds x="252.5" y="-26" width="90" height="12" />
    </bpmndi:BPMNLabel>
    </bpmndi:BPMNEdge>
    <bpmndi:BPMNEdge id="SequenceFlow_0y00p8b_di" bpmnElement="SequenceFlow_0y00p8b">
    <di:waypoint xsi:type="dc:Point" x="432" y="-5" />
    <di:waypoint xsi:type="dc:Point" x="487" y="-5" />
    <bpmndi:BPMNLabel>
    <dc:Bounds x="414.5" y="-26" width="90" height="12" />
    </bpmndi:BPMNLabel>
    </bpmndi:BPMNEdge>
    <bpmndi:BPMNEdge id="SequenceFlow_0azgsh3_di" bpmnElement="SequenceFlow_0azgsh3">
    <di:waypoint xsi:type="dc:Point" x="537" y="35" />
    <di:waypoint xsi:type="dc:Point" x="537" y="120" />
    <di:waypoint xsi:type="dc:Point" x="207" y="120" />
    <di:waypoint xsi:type="dc:Point" x="207" y="281" />
    <di:waypoint xsi:type="dc:Point" x="243" y="281" />
    <bpmndi:BPMNLabel>
    <dc:Bounds x="327" y="99" width="90" height="12" />
    </bpmndi:BPMNLabel>
    </bpmndi:BPMNEdge>
    <bpmndi:BPMNEdge id="SequenceFlow_125qqc9_di" bpmnElement="SequenceFlow_125qqc9">
    <di:waypoint xsi:type="dc:Point" x="814" y="71" />
    <di:waypoint xsi:type="dc:Point" x="814" y="97" />
    <di:waypoint xsi:type="dc:Point" x="935" y="97" />
    <bpmndi:BPMNLabel>
    <dc:Bounds x="821" y="79" width="43" height="12" />
    </bpmndi:BPMNLabel>
    </bpmndi:BPMNEdge>
    <bpmndi:BPMNEdge id="SequenceFlow_0qvflnw_di" bpmnElement="SequenceFlow_0qvflnw">
    <di:waypoint xsi:type="dc:Point" x="1035" y="97" />
    <di:waypoint xsi:type="dc:Point" x="1111" y="97" />
    <bpmndi:BPMNLabel>
    <dc:Bounds x="1073" y="76" width="0" height="12" />
    </bpmndi:BPMNLabel>
    </bpmndi:BPMNEdge>
    <bpmndi:BPMNEdge id="SequenceFlow_06tfs2q_di" bpmnElement="SequenceFlow_06tfs2q">
    <di:waypoint xsi:type="dc:Point" x="814" y="21" />
    <di:waypoint xsi:type="dc:Point" x="814" y="-5" />
    <di:waypoint xsi:type="dc:Point" x="935" y="-5" />
    <bpmndi:BPMNLabel>
    <dc:Bounds x="820" y="-25" width="48" height="12" />
    </bpmndi:BPMNLabel>
    </bpmndi:BPMNEdge>
    <bpmndi:BPMNEdge id="SequenceFlow_04bpp14_di" bpmnElement="SequenceFlow_04bpp14">
    <di:waypoint xsi:type="dc:Point" x="1035" y="-5" />
    <di:waypoint xsi:type="dc:Point" x="1091" y="-5" />
    <di:waypoint xsi:type="dc:Point" x="1091" y="-5" />
    <di:waypoint xsi:type="dc:Point" x="1111" y="-5" />
    <bpmndi:BPMNLabel>
    <dc:Bounds x="1106" y="-11" width="0" height="12" />
    </bpmndi:BPMNLabel>
    </bpmndi:BPMNEdge>
    <bpmndi:BPMNEdge id="SequenceFlow_0bslxhz_di" bpmnElement="SequenceFlow_0bslxhz">
    <di:waypoint xsi:type="dc:Point" x="726" y="241" />
    <di:waypoint xsi:type="dc:Point" x="726" y="46" />
    <di:waypoint xsi:type="dc:Point" x="789" y="46" />
    <bpmndi:BPMNLabel>
    <dc:Bounds x="696" y="137.5" width="90" height="12" />
    </bpmndi:BPMNLabel>
    </bpmndi:BPMNEdge>
    <bpmndi:BPMNEdge id="SequenceFlow_1a0h9gg_di" bpmnElement="SequenceFlow_1a0h9gg">
    <di:waypoint xsi:type="dc:Point" x="620" y="281" />
    <di:waypoint xsi:type="dc:Point" x="676" y="281" />
    <bpmndi:BPMNLabel>
    <dc:Bounds x="603" y="260" width="90" height="12" />
    </bpmndi:BPMNLabel>
    </bpmndi:BPMNEdge>
    <bpmndi:BPMNShape id="Lane_0feow9m_di" bpmnElement="Lane_0feow9m">
    <dc:Bounds x="153" y="163" width="1069" height="242" />
    </bpmndi:BPMNShape>
    <bpmndi:BPMNShape id="Lane_0r1co7i_di" bpmnElement="Lane_0r1co7i">
    <dc:Bounds x="153" y="-60" width="1069" height="223" />
    </bpmndi:BPMNShape>
    <bpmndi:BPMNShape id="ScriptTask_0hlg5fz_di" bpmnElement="Task_07hvbtg">
    <dc:Bounds x="444" y="243" width="100" height="80" />
    </bpmndi:BPMNShape>
    <bpmndi:BPMNEdge id="SequenceFlow_0r400ep_di" bpmnElement="SequenceFlow_0r400ep">
    <di:waypoint xsi:type="dc:Point" x="544" y="283" />
    <di:waypoint xsi:type="dc:Point" x="564" y="283" />
    <bpmndi:BPMNLabel>
    <dc:Bounds x="554" y="262" width="0" height="12" />
    </bpmndi:BPMNLabel>
    </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
    </bpmndi:BPMNDiagram>
    </bpmn:definitions>