The third use case in the Bugzilla business scenario is bug management. This page describes the relevant tasks and the operations you use in the Bugzilla connector and the other ESB connectors. It contains the following sections:
Overview
The flow for bug management is illustrated in the following diagram. The ESB connectors for TSheets, Zoho Books and FreshDesk will be used to connect to each service.
Retrieving assignees of bugs with the status of 'In Progress' and assigning tasks
Prerequisites
There should be bugs which are in the 'In-progress' status with user(s) assigned to it in Bugzilla.
Retrieve bugs which are in the ‘In-progress’ status and its assignee from the Bugzilla API using the searchBugs operation. Retrieve users from the TSheets API using the listUsers operation.
Using the listJobCodeAssignments operation, check if the user has been assigned to the relevant task in the TSheets API.
Assign the relevant task in the TSheets API to that assignee using the addJobCodeAssignments operation.
Bugzilla operations
TSheets operations
Samples
<?xml version="1.0" encoding="UTF-8"?> <proxy xmlns="http://ws.apache.org/ns/synapse" name="bugzilla_assignUsersToTasks" transports="https,http" statistics="disable" trace="disable" startOnLoad="true"> <target> <inSequence> <!--Bugzilla Properties --> <property name="bugzilla.apiUrl" expression="json-eval($.bugzillaApiUrl)" /> <property name="bugzilla.apiKey" expression="json-eval($.bugzillaApiKey)" /> <!--TSheets Properties--> <property name="tsheets.apiUrl" expression="json-eval($.tsheetsApiUrl)" /> <property name="tsheets.accessToken" expression="json-eval($.tsheetsAccessToken)" /> <property name="responseString" value="" scope="operation" /> <!-- Call bugzilla connector searchBugs method to retrieve confirmed bugs --> <bugzilla.init> <apiUrl>{$ctx:bugzilla.apiUrl}</apiUrl> <apiKey>{$ctx:bugzilla.apiKey}</apiKey> </bugzilla.init> <bugzilla.searchBugs> <status>IN_PROGRESS</status> </bugzilla.searchBugs> <filter source="$axis2:HTTP_SC" regex="200"> <then> <property name="bugsCount" expression="count(//bugs)" scope="operation" /> <property name="bugIndex" expression="0" scope="operation" /> <filter xpath="get-property('operation', 'bugsCount') = 0"> <then> <property name="id" value="{}" /> <property name="message" value="There are no bugs to process." /> <property name="status" value="Skipped" /> <call-template target="responseHandlerTemplate"> <with-param name="id" value="{$ctx:id}" /> <with-param name="activity" value="bugzilla_retrieveBugs" /> <with-param name="status" value="{$ctx:status}" /> <with-param name="message" value="{$ctx:message}" /> </call-template> <loopback /> </then> <else> <!--FOR EACH bug : BEGIN--> <iterate continueParent="false" id="bugIterator" expression="//bugs" sequential="true"> <target> <sequence> <property name="messageType" value="application/json" scope="axis2" /> <property name="bugzilla.id" expression="json-eval($.bugs.id)" /> <property name="bugzilla.username" expression="json-eval($.bugs.assigned_to)" /> <property name="bugzilla.summary" expression="json-eval($.bugs.summary)" /> <property name="tsheets.taskId" expression="json-eval($.bugs.cf_taskid)" /> <filter xpath="get-property('tsheets.taskId') = 0 "> <then> <property name="id" expression="fn:concat('bugId:',get-property('bugzilla.id'))" /> <property name="status" value="Skipped" /> <property name="message" value="Tsheet child job code ID is not given for the bug." /> <call-template target="responseHandlerTemplate"> <with-param name="id" value="{$ctx:id}" /> <with-param name="activity" value="tsheets_JobCodeAssignments" /> <with-param name="status" value="{$ctx:status}" /> <with-param name="message" value="{$ctx:message}" /> </call-template> </then> <else> <property name="uri.var.urlQuery" action="remove" /> <!-- Call tsheets connector listUsers method to get user details of the particular user.--> <tsheets.init> <accessToken>{$ctx:tsheets.accessToken}</accessToken> <apiUrl>{$ctx:tsheets.apiUrl}</apiUrl> </tsheets.init> <tsheets.listUsers> </tsheets.listUsers> <script language="js"> <![CDATA[ payload = mc.getPayloadJSON(); var users = payload.results.users; var bugAssignee = mc.getProperty('bugzilla.username'); for(var key in users) { var userIds = users[key]; var userEmail = userIds['email']; if(userEmail == bugAssignee ) { var userId = userIds['id']; mc.setProperty("tsheet.userId",userId); break; } } ]]></script> <filter source="boolean(get-property('tsheet.userId') )" regex="false"> <then> <property name="id" expression="fn:concat('bugId:',get-property('bugzilla.id'))" /> <property name="status" value="Skipped" /> <property name="message" value="Tsheet user related to the bug, is not found." /> <call-template target="responseHandlerTemplate"> <with-param name="id" value="{$ctx:id}" /> <with-param name="activity" value="tsheets_JobCodeAssignments" /> <with-param name="status" value="{$ctx:status}" /> <with-param name="message" value="{$ctx:message}" /> </call-template> </then> <else> <!-- Call tsheets connector listJobCodeAssignments method to get jobe code assignments for the particular user.--> <tsheets.init> <accessToken>{$ctx:tsheets.accessToken}</accessToken> <apiUrl>{$ctx:tsheets.apiUrl}</apiUrl> </tsheets.init> <tsheets.listJobCodeAssignments> <userIds>{$ctx:tsheet.userId}</userIds> </tsheets.listJobCodeAssignments> <script language="js"> <![CDATA[ payload = mc.getPayloadJSON(); var jobCodeAssignments = payload.results.jobcode_assignments; var taskId = mc.getProperty('tsheets.taskId'); for(var key in jobCodeAssignments) { var assignmentIds = jobCodeAssignments[key]; var jobcodeId = assignmentIds['jobcode_id']; if(jobcodeId == taskId ) { var assignmentStatus = assignmentIds['active']; if(assignmentStatus == true) { mc.setProperty("tsheet.assignmentFlag","true"); break; } } } ]]></script> <filter source="boolean(get-property('tsheet.assignmentFlag'))" regex="false"> <then> <script language="js"> <![CDATA[ var userId=mc.getProperty('tsheet.userId'); var jobcodeId=mc.getProperty('tsheets.taskId'); var jobCodeAssignObj = {}; jobCodeAssignObj.user_id = userId; jobCodeAssignObj.jobcode_id = jobcodeId; mc.setPayloadJSON(jobCodeAssignObj); ]]> </script> <property name="tsheets.jobCodeAssign" expression="json-eval($.)" /> <!-- Call tsheets connector addJobCodeAssignments method to assign user to the created task.--> <tsheets.init> <accessToken>{$ctx:tsheets.accessToken}</accessToken> <apiUrl>{$ctx:tsheets.apiUrl}</apiUrl> </tsheets.init> <tsheets.addJobCodeAssignments> <jobCodeAssignments>{$ctx:tsheets.jobCodeAssign}</jobCodeAssignments> </tsheets.addJobCodeAssignments> <filter source="$axis2:HTTP_SC" regex="200"> <then> <property name="tsheets.jobCodeAssignmentId" expression="json-eval($.results.jobcode_assignments.1.id)" /> <property name="jobCodeIdObject" expression="fn:concat('jobCodeAssignmentId:',get-property('tsheets.jobCodeAssignmentId'))" /> <property name="message" expression="fn:concat('Job code [',get-property('bugzilla.summary'),'] has been assigned to the user [',get-property('bugzilla.username'),'] successfully.')" /> <property name="status" value="success" /> </then> <else> <property name="jobCodeIdObject" expression="get-property('projectNameObj')" /> <property name="status" value="error" /> <property name="message" expression="json-eval($.)" /> </else> </filter> <call-template target="responseHandlerTemplate"> <with-param name="id" value="{$ctx:jobCodeIdObject}" /> <with-param name="activity" value="tsheets_JobCodeAssignments" /> <with-param name="status" value="{$ctx:status}" /> <with-param name="message" value="{$ctx:message}" /> </call-template> </then> </filter> </else> </filter> </else> </filter> <property name="bugIndex" expression="get-property('operation','bugIndex') + 1" scope="operation" /> <filter xpath="(get-property('operation','bugIndex') = get-property('operation', 'bugsCount'))"> <then> <loopback /> </then> </filter> </sequence> </target> </iterate> <!--FOR EACH bug : END--> </else> </filter> </then> </filter> </inSequence> <outSequence> <filter source="boolean(get-property('operation', 'responseString'))" regex="false"> <then> <payloadFactory media-type="json"> <format>{ "Response":{ "process":"bugzilla-assignUsersToTasks", "activityResponse":"All users have been assigned to tasks." } } </format> </payloadFactory> </then> <else> <payloadFactory media-type="json"> <format>{ "Response":{ "process":"bugzilla-assignUsersToTasks", "activityResponse":[$1] } } </format> <args> <arg expression="get-property('operation', 'responseString')" /> </args> </payloadFactory> </else> </filter> <send /> </outSequence> </target> <description /> </proxy>
{ "bugzillaApiUrl":"http://172.22.217.15/bugiya/rest.cgi", "bugzillaApiKey":"jgvuN3V9aWqMLWFo4gs2UkezswIUCn9qezgOmibG", "tsheetsApiUrl":"https://bugzilla.tsheets.com", "tsheetsAccessToken":"S.1__5702cdc8639ffa44e9d935d1736d4979598a15e9" }
Retrieving resolved products, updating the ticket ID and creating time entries and invoices
- Retrieve tasks from Zoho Books using the listTasks operation. If task not available use createTask to create it.
- Update the ticket as 'Resolved' in the FreshDesk API using the updateTicket operation.
- Retrieve resolved bugs for each product from the Bugzilla API using the searchBugs operation and if it is billable, retrieve associated time sheets from the TSheets API using the listTimeSheets operation. Create associated time entries in the Zoho Books APi using the createTimeEntry operation.
- Retrieve project details and contact details from the Zoho Books API using the getProject and getContact operations respectively. Create invoices for the above time entries in the Zoho Books API using the createInvoice operation and mail the invoice to the customer.
Using the updateBug operation, update the custom fields of the bug in the Bugzilla API.
Note
An invoice is created for a component, which can have multiple bugs and the sum of the time entries for a particular bug is shown as a single entry in the invoice.
Bugzilla operations
TSheets operations
Zoho Books operations
FreshDesk operations
Samples
<?xml version="1.0" encoding="UTF-8"?> <!-- This template responsible for creating a tasks and time entries in zohobooks according to given access credentials with other parameters --> <template xmlns="http://ws.apache.org/ns/synapse" name="createTasksAndTimeEntries"> <!-- ZohoBooks related template parameters --> <parameter name="zohobooks.apiUrl" description="Base endpoint URL of ZohoBooks API." /> <parameter name="zohobooks.authToken" description="Auth token to access the ZohoBooks API." /> <parameter name="zohobooks.organizationId" description="ID of the organization." /> <parameter name="zohobooks.projectId" description="Project ID of the zohobooks." /> <parameter name="zohobooks.taskName" description="Task name of the task associated with zohobook project." /> <parameter name="zohobooks.timeEntryDate" description="Date which time entry should be created." /> <parameter name="zohobooks.userId" description="User ID of the zohobook user." /> <parameter name="zohobooks.logTime" description="log time for the time entry." /> <parameter name="zohobooks.invoiceHours" description="quantity for the line item of the invoice." /> <parameter name="zohobooks.taskRate" description="Rate of the task." /> <!--Common parameter--> <parameter name="bugzilla.id" description="ID of the bug in bugzilla." /> <parameter name="bugzilla.description" description="Description of the invoice to create." /> <sequence> <!-- ZohoBooks related template properties --> <property name="zohobooks.apiUrl" expression="$func:zohobooks.apiUrl" /> <property name="zohobooks.authToken" expression="$func:zohobooks.authToken" /> <property name="zohobooks.organizationId" expression="$func:zohobooks.organizationId" /> <property name="zohobooks.projectId" expression="$func:zohobooks.projectId" /> <property name="zohobooks.timeEntryDate" expression="$func:zohobooks.timeEntryDate" /> <property name="zohobooks.userId" expression="$func:zohobooks.userId" /> <property name="zohobooks.taskName" expression="$func:zohobooks.taskName" /> <property name="zohobooks.logTime" expression="$func:zohobooks.logTime" /> <property name="zohobooks.invoiceHours" expression="$func:zohobooks.invoiceHours" /> <property name="zohobooks.taskRate" expression="$func:zohobooks.taskRate" /> <!-- Common template properties --> <property name="bugzilla.id" expression="$func:bugzilla.id" /> <property name="bugzilla.description" expression="$func:bugzilla.description" /> <property name="invoiceObjects" value="" scope="operation" /> <filter source="boolean(get-property('zohobooks.authToken'))" regex="true"> <then> <!-- Call zohobooks connector listTasks method to list tasks for the given project --> <zohobooks.init> <authToken>{$ctx:zohobooks.authToken}</authToken> <apiUrl>{$ctx:zohobooks.apiUrl}</apiUrl> <organizationId>{$ctx:zohobooks.organizationId}</organizationId> </zohobooks.init> <zohobooks.listTasks> <projectId>{$ctx:zohobooks.projectId}</projectId> </zohobooks.listTasks> <property name="tasks" expression="json-eval($.task)" /> <filter source="$axis2:HTTP_SC" regex="200"> <then> <script language="js"> <![CDATA[payload = mc.getPayloadJSON(); var taskName = mc.getProperty('zohobooks.taskName'); var taskArray = payload.task; var taskId = ""; var taskFlag ="false"; for(var i=0;i<taskArray.length;i++) { var taskObject = taskArray[i]; for(var key in taskObject) { var name = taskObject['task_name']; if(name == taskName) { taskFlag="true"; taskId = taskObject['task_id']; break; } } } mc.setProperty("zohobooks.taskId",taskId); mc.setProperty("taskFlag",taskFlag); ]]></script> <filter xpath="get-property('taskFlag') = 'false'"> <then> <!-- Call zohobooks connector createTask method to create a task if the relevant task is not exsisting --> <zohobooks.init> <authToken>{$ctx:zohobooks.authToken}</authToken> <apiUrl>{$ctx:zohobooks.apiUrl}</apiUrl> <organizationId>{$ctx:zohobooks.organizationId}</organizationId> </zohobooks.init> <zohobooks.createTask> <projectId>{$ctx:zohobooks.projectId}</projectId> <taskName>{$ctx:zohobooks.taskName}</taskName> </zohobooks.createTask> <property name="zohobooks.taskId" expression="json-eval($.task.task_id)" /> </then> </filter> <!-- Call zohobooks connector createTimeEntry method create a time entry --> <zohobooks.init> <authToken>{$ctx:zohobooks.authToken}</authToken> <apiUrl>{$ctx:zohobooks.apiUrl}</apiUrl> <organizationId>{$ctx:zohobooks.organizationId}</organizationId> </zohobooks.init> <zohobooks.createTimeEntry> <taskId>{$ctx:zohobooks.taskId}</taskId> <userId>{$ctx:zohobooks.userId}</userId> <logDate>{$ctx:zohobooks.timeEntryDate}</logDate> <projectId>{$ctx:zohobooks.projectId}</projectId> <logTime>{$ctx:zohobooks.logTime}</logTime> </zohobooks.createTimeEntry> <property name="zohobooks.timeEntryId" expression="json-eval($.time_entry.time_entry_id)" /> <filter source="boolean(get-property('zohobooks.timeEntryId'))" regex="false"> <then> <property name="id" expression="fn:concat('bugzillaId:',get-property('bugzilla.id'))" /> <property name="status" value="error" /> <property name="message" expression="json-eval($)" /> </then> <else> <property name="id" expression="fn:concat('bugzillaId:',get-property('bugzilla.id'))" /> <property name="status" value="success" /> <property name="message" value="Time entry has been created for the bug." /> </else> </filter> <!--Call the responseHandler template--> <call-template target="responseHandlerTemplate"> <with-param name="activity" value="zohobooks_logTimeEntries" /> <with-param name="id" value="{$ctx:id}" /> <with-param name="status" value="{$ctx:status}" /> <with-param name="message" value="{$ctx:message}" /> </call-template> <filter source="boolean(get-property('zohobooks.timeEntryId'))" regex="true"> <then> <!-- Call zohobooks connector getProject method to retrieve project related details --> <zohobooks.init> <authToken>{$ctx:zohobooks.authToken}</authToken> <apiUrl>{$ctx:zohobooks.apiUrl}</apiUrl> <organizationId>{$ctx:zohobooks.organizationId}</organizationId> </zohobooks.init> <zohobooks.getProject> <projectId>{$ctx:zohobooks.projectId}</projectId> </zohobooks.getProject> <property name="zohobooks.customerId" expression="json-eval($.project.customer_id)" /> <!-- Call zohobooks connector getContact method to retrieve contact related details --> <zohobooks.init> <authToken>{$ctx:zohobooks.authToken}</authToken> <apiUrl>{$ctx:zohobooks.apiUrl}</apiUrl> <organizationId>{$ctx:zohobooks.organizationId}</organizationId> </zohobooks.init> <zohobooks.getContact> <contactId>{$ctx:zohobooks.customerId}</contactId> </zohobooks.getContact> <property name="zohobooks.contactPersonId" expression="json-eval($.contact.contact_persons[0].contact_person_id)" /> <payloadFactory media-type="json"> <format> { "project_id": "$1", "time_entry_ids":["$2"], "name": "$3", "quantity":"$4", "rate":"$5", "description":"$6" } </format> <args> <arg expression="get-property('zohobooks.projectId')" /> <arg expression="get-property('zohobooks.timeEntryId')" /> <arg expression="get-property('zohobooks.taskName')" /> <arg expression="get-property('zohobooks.invoiceHours')" /> <arg expression="get-property('zohobooks.taskRate')" /> <arg expression="get-property('bugzilla.description')" /> </args> </payloadFactory> <property name="zohobooks.invoiceItems" expression="json-eval($.)" /> <property name="bugIdString" expression="fn:concat(get-property('operation','bugIdString'),get-property('bugzilla.id'),',')" scope="operation" /> <property name="invoiceObjects" expression="fn:concat(get-property('operation','invoiceObjects'),get-property('zohobooks.invoiceItems'),',')" scope="operation" /> <property name="zohobooks.contactPerson" expression="fn:concat('["',get-property('zohobooks.contactPersonId'),'"]')" /> </then> </filter> </then> <else> <property name="id" value="{}" /> <property name="status" value="Skipped" /> <property name="message" value="ZohoBooks project ID is not valid." /> <call-template target="responseHandlerTemplate"> <with-param name="id" value="{$ctx:id}" /> <with-param name="activity" value="zohobooks_retrieveTasks" /> <with-param name="status" value="{$ctx:status}" /> <with-param name="message" value="{$ctx:message}" /> </call-template> <loopback /> </else> </filter> </then> </filter> </sequence> </template>
<?xml version="1.0" encoding="UTF-8"?> <proxy xmlns="http://ws.apache.org/ns/synapse" name="bugzilla_manageResolvedBugs" transports="https,http" statistics="disable" trace="disable" startOnLoad="true"> <target> <inSequence> <!--Bugzilla Properties --> <property name="bugzilla.apiUrl" expression="json-eval($.bugzillaApiUrl)" /> <property name="bugzilla.apiKey" expression="json-eval($.bugzillaApiKey)" /> <property name="bugzilla.date" expression="json-eval($.bugzillaLastInvoicedDate)" /> <property name="bugzilla.product" expression="json-eval($.bugzillaProduct)" /> <!--TSheets Properties--> <property name="tsheets.apiUrl" expression="json-eval($.tsheetsApiUrl)" /> <property name="tsheets.accessToken" expression="json-eval($.tsheetsAccessToken)" /> <!--ZohoBooks Properties--> <property name="zohobooks.apiUrl" value="https://books.zoho.com" /> <property name="zohobooks.authToken" expression="json-eval($.zohoBooksAuthToken)" /> <property name="zohobooks.organizationId" expression="json-eval($.zohoBooksOrganizationId)" /> <property name="zohobooks.projectId" expression="json-eval($.zohoBookProjectId)" /> <!--Freshdesk Properties --> <property name="freshdesk.apiUrl" expression="json-eval($.freshdeskApiUrl)" /> <property name="freshdesk.apiKey" expression="json-eval($.freshdeskApiKey)" /> <property name="timeEntryMap" expression="json-eval($.timeEntryMap)" /> <property name="productMap" expression="json-eval($.productMap)" /> <property name="responseString" value="" scope="operation" /> <property name="bugIdString" value="" scope="operation" /> <filter source="boolean(get-property('zohobooks.projectId'))" regex="false"> <then> <property name="id" expression="fn:concat('bugId:',get-property('bugzilla.id'))" /> <property name="status" value="Skipped" /> <property name="message" expression="fn:concat('Mapping project ID in Zohobooks for the product of the bug ',get-property('bugzilla.product'),' cannot be found in the request.')" /> <call-template target="responseHandlerTemplate"> <with-param name="id" value="{$ctx:id}" /> <with-param name="activity" value="zohobooks_getProject" /> <with-param name="status" value="{$ctx:status}" /> <with-param name="message" value="{$ctx:message}" /> </call-template> </then> <else> <!-- Call bugzilla connector searchBugs method to retrieve resolved bugs --> <bugzilla.init> <apiUrl>{$ctx:bugzilla.apiUrl}</apiUrl> <apiKey>{$ctx:bugzilla.apiKey}</apiKey> </bugzilla.init> <bugzilla.searchBugs> <status>RESOLVED</status> <lastChangeTime>{$ctx:bugzilla.date}</lastChangeTime> <product>{$ctx:bugzilla.product}</product> </bugzilla.searchBugs> <filter source="$axis2:HTTP_SC" regex="200"> <then> <property name="bugsCount" expression="count(//bugs)" scope="operation" /> <property name="bugIndex" expression="0" scope="operation" /> <filter xpath="get-property('operation', 'bugsCount') = 0"> <then> <property name="id" value="{}" /> <property name="status" value="Skipped" /> <property name="message" value="There are no bugs to process." /> <call-template target="responseHandlerTemplate"> <with-param name="id" value="{$ctx:id}" /> <with-param name="activity" value="bugzilla_retrieveBugs" /> <with-param name="status" value="{$ctx:status}" /> <with-param name="message" value="{$ctx:message}" /> </call-template> <loopback /> </then> <else> <!--FOR EACH Bug : BEGIN--> <iterate continueParent="false" id="bugIterator" expression="//bugs" sequential="true"> <target> <sequence> <property name="messageType" value="application/json" scope="axis2" /> <property name="bugzilla.id" expression="json-eval($.bugs.id)" /> <property name="bugzilla.isBillable" expression="json-eval($.bugs.cf_isbugbillable)" /> <property name="tsheets.taskId" expression="json-eval($.bugs.cf_taskid)" /> <property name="bugzilla.description" expression="json-eval($.bugs.summary)" /> <property name="bugzilla.creationTime" expression="json-eval($.bugs.creation_time)" /> <property name="bugzilla.product" expression="json-eval($.bugs.product)" /> <property name="bugzilla.component" expression="json-eval($.bugs.component)" /> <property name="bugzilla.hasInvoiced" expression="json-eval($.bugs.cf_hasinvoiced)" /> <script language="js"> <![CDATA[ var startDate=mc.getProperty('bugzilla.creationTime'); startDate=startDate.split('T')[0]; mc.setProperty("tsheets.startDate",startDate); var currentDate=new Date(); var currentday=""+currentDate.getDate(); if(currentday.length==1){ currentday="0"+currentday; } var month=""+Number(currentDate.getMonth()+1); if(month.length==1){ month="0"+month; } var dateString=currentDate.getFullYear() + "-" + month + "-" + currentday ; mc.setProperty('tsheets.endDate', dateString); ]]> </script> <property name="freshdesk.ticketId" expression="json-eval($.bugs.cf_freshdesk_ticketid)" /> <filter xpath="get-property('bugzilla.hasInvoiced') !='null'"> <then> <property name="id" expression="fn:concat('bugId:',get-property('bugzilla.id'))" /> <property name="status" value="Skipped" /> <property name="message" value="This bug has been invoiced already." /> <call-template target="responseHandlerTemplate"> <with-param name="id" value="{$ctx:id}" /> <with-param name="activity" value="zohobooks_createInvoice" /> <with-param name="status" value="{$ctx:status}" /> <with-param name="message" value="{$ctx:message}" /> </call-template> </then> <else> <!-- Calling freshDesk_updateTicketsSeq sequence to update tickets as resolved in FreshDesk doesn't exist --> <sequence key="freshDesk_updateTicketsSeq" /> <filter xpath="get-property('bugzilla.isBillable') !='true'"> <then> <property name="id" expression="fn:concat('bugId:',get-property('bugzilla.id'))" /> <property name="message" value="This bug is not billable." /> <property name="status" value="Skipped" /> <call-template target="responseHandlerTemplate"> <with-param name="id" value="{$ctx:id}" /> <with-param name="activity" value="zohobooks_createInvoice" /> <with-param name="status" value="{$ctx:status}" /> <with-param name="message" value="{$ctx:message}" /> </call-template> </then> <else> <filter xpath="get-property('tsheets.taskId') = '0'"> <then> <property name="id" expression="fn:concat('bugId:',get-property('bugzilla.id'))" /> <property name="status" value="Skipped" /> <property name="message" value="There is no task ID given for the bug." /> <call-template target="responseHandlerTemplate"> <with-param name="id" value="{$ctx:id}" /> <with-param name="activity" value="tsheet_retrieveTimeSheets" /> <with-param name="status" value="{$ctx:status}" /> <with-param name="message" value="{$ctx:message}" /> </call-template> </then> <else> <!-- Call tsheets connector listTimeSheets method to list timesheets --> <tsheets.init> <accessToken>{$ctx:tsheets.accessToken}</accessToken> <apiUrl>{$ctx:tsheets.apiUrl}</apiUrl> </tsheets.init> <tsheets.listTimeSheets> <jobCodeIds>{$ctx:tsheets.taskId}</jobCodeIds> <startDate>{$ctx:tsheets.startDate}</startDate> <endDate>{$ctx:tsheets.endDate}</endDate> </tsheets.listTimeSheets> <property name="tsheets.timeSheets" expression="json-eval($.results.timesheets)" /> <filter xpath="get-property('tsheets.timeSheets') = '[]'"> <then> <property name="id" expression="fn:concat('bugId:',get-property('bugzilla.id'))" /> <property name="status" value="Skipped" /> <property name="message" value="There are no timesheets associated with the bug." /> <call-template target="responseHandlerTemplate"> <with-param name="id" value="{$ctx:id}" /> <with-param name="activity" value="tsheets_retrieveTimeSheets" /> <with-param name="status" value="{$ctx:status}" /> <with-param name="message" value="{$ctx:message}" /> </call-template> </then> <else> <script language="js"><![CDATA[ payload = mc.getPayloadJSON(); var timeSheet = payload.results.timesheets; var allJobCodes = payload.supplemental_data.jobcodes; var bugId = mc.getProperty('bugzilla.id'); var bugComponent = mc.getProperty('bugzilla.component'); var tDate = ""; var tDuration = 0 ; var jobCodeId = ""; var childJobName = ""; var parentJobCode = ""; var jobCodeRate = ""; var datesArray = []; for(var key in timeSheet) { var timeSheetId = timeSheet[key]; tDate = timeSheetId['date']; datesArray.push(tDate); tDuration += timeSheetId['duration']; jobCodeId = timeSheetId['jobcode_id']; } var maxDate = datesArray[0]; for(var i=0;i<datesArray.length;i++) { if (datesArray[i] > maxDate) maxDate = datesArray[i]; } for(var key in allJobCodes) { var allJobCodeIds = allJobCodes[key]; var jobCodes = allJobCodeIds['id']; if(jobCodes == jobCodeId) { childJobName = allJobCodeIds['name']; parentJobCode = allJobCodeIds['parent_id']; break; } } for(var key in allJobCodes) { var allJobCodeIds = allJobCodes[key]; var jobCodes = allJobCodeIds['id']; if(jobCodes == parentJobCode) { jobCodeRate = allJobCodeIds['billable_rate']; break; } } var noOfHours = tDuration/3600; var roundedHours = noOfHours.toFixed(2); var hours = roundedHours.split('.')[0]; var minutes = roundedHours.split('.')[1]; if(hours.length == 1) { hours = "0"+hours; } var logTime = hours + ":" + minutes ; mc.setProperty("zohobooks.logTime",logTime); mc.setProperty("zohobooks.invoiceHours",roundedHours); var timeEntryObject = eval("(" + mc.getProperty('timeEntryMap') + ")"); var userId = timeEntryObject[bugComponent]; mc.setProperty("zohobooks.userId", userId); mc.setProperty("tsheet.date",maxDate); mc.setProperty("zohobooks.taskName",childJobName); mc.setProperty("zohobooks.rate",jobCodeRate); ]]></script> <filter source="boolean(get-property('zohobooks.userId') )" regex="false"> <then> <property name="id" expression="fn:concat('bugId:',get-property('bugzilla.id'))" /> <property name="status" value="Skipped" /> <property name="message" expression="fn:concat('Mapping user ID in ZohoBooks for the component of the bug ',get-property('bugzilla.component'),' cannot be found in the request.')" /> <call-template target="responseHandlerTemplate"> <with-param name="id" value="{$ctx:id}" /> <with-param name="activity" value="zohobooks_getUser" /> <with-param name="status" value="{$ctx:status}" /> <with-param name="message" value="{$ctx:message}" /> </call-template> </then> <else> <property name="lineItemDescription" expression="fn:concat('[ Component :',get-property('bugzilla.component'),' ; Bug ID :',get-property('bugzilla.id'),' ]')" /> <!-- Calling createTasksAndTimeEntries Template to create tasks, time entries in zohobooks --> <call-template target="createTasksAndTimeEntries"> <!-- parameter values will be passed on to a sequence template --> ( <with-param name="zohobooks.apiUrl" value="{$ctx:zohobooks.apiUrl}" /> | <with-param name="zohobooks.authToken" value="{$ctx:zohobooks.authToken}" /> | <with-param name="zohobooks.organizationId" value="{$ctx:zohobooks.organizationId}" /> | <with-param name="zohobooks.projectId" value="{$ctx:zohobooks.projectId}" /> | <with-param name="zohobooks.timeEntryDate" value="{$ctx:tsheet.date}" /> | <with-param name="zohobooks.logTime" value="{$ctx:zohobooks.logTime}" /> | <with-param name="zohobooks.invoiceHours" value="{$ctx:zohobooks.invoiceHours}" /> | <with-param name="zohobooks.userId" value="{$ctx:zohobooks.userId}" /> | <with-param name="zohobooks.taskName" value="{$ctx:zohobooks.taskName}" /> | <with-param name="tsheet.timesheetId" value="{$ctx:timeSheet.id}" /> | <with-param name="zohobooks.taskRate" value="{$ctx:zohobooks.rate}" /> | <with-param name="bugzilla.id" value="{$ctx:bugzilla.id}" /> | <with-param name="bugzilla.description" value="{$ctx:lineItemDescription}" /> | ) * </call-template> </else> </filter> </else> </filter> </else> </filter> </else> </filter> </else> </filter> <property name="bugIndex" expression="get-property('operation','bugIndex') + 1" scope="operation" /> <filter xpath="(get-property('operation','bugIndex') = get-property('operation', 'bugsCount'))"> <then> <property name="invoiceObjects" expression="fn:substring(get-property('operation','invoiceObjects'), 1, fn:string-length(get-property('operation','invoiceObjects'))-1)" /> <filter xpath="get-property('invoiceObjects') = ''"> <then> <property name="id" value="{}" /> <property name="status" value="Skipped" /> <property name="message" value="There are no bugs to create an invoice." /> <call-template target="responseHandlerTemplate"> <with-param name="id" value="{$ctx:id}" /> <with-param name="activity" value="zohobooks_createInvoice" /> <with-param name="status" value="{$ctx:status}" /> <with-param name="message" value="{$ctx:message}" /> </call-template> <loopback/> </then> <else> <payloadFactory media-type="json"> <format>{ "line_items": [$1] } </format> <args> <arg expression="get-property('invoiceObjects')" /> </args> </payloadFactory> <property name="zohobooks.invoiceItems" expression="json-eval($.line_items)" /> <!-- Call zohobooks connector createInvoice method to create invoice for the bug.--> <zohobooks.init> <authToken>{$ctx:zohobooks.authToken}</authToken> <apiUrl>{$ctx:zohobooks.apiUrl}</apiUrl> <organizationId>{$ctx:zohobooks.organizationId}</organizationId> </zohobooks.init> <zohobooks.createInvoice> <customerId>{$ctx:zohobooks.customerId}</customerId> <contactPersons>{$ctx:zohobooks.contactPerson}</contactPersons> <lineItems>{$ctx:zohobooks.invoiceItems}</lineItems> <send>true</send> </zohobooks.createInvoice> <property name="zohobooks.invoiceId" expression="json-eval($.invoice.invoice_id)" /> <filter source="boolean(get-property('zohobooks.invoiceId'))" regex="false"> <then> <property name="id" value="{}" /> <property name="status" value="error" /> <property name="message" expression="json-eval($)" /> </then> <else> <property name="id" expression="fn:concat('zohobooksInvoiceId:',get-property('zohobooks.invoiceId'))" /> <property name="status" value="success" /> <property name="message" value="Invoice has been created for resolved bugs." /> </else> </filter> <!--Call the responseHandler template--> <call-template target="responseHandlerTemplate"> <with-param name="activity" value="zohobooks_createInvoices" /> <with-param name="id" value="{$ctx:id}" /> <with-param name="status" value="{$ctx:status}" /> <with-param name="message" value="{$ctx:message}" /> </call-template> <filter source="boolean(get-property('zohobooks.invoiceId'))" regex="true"> <then> <!-- Calling bugzilla-updateBugsSeq sequence to update bugs with invoice Id--> <sequence key="bugzilla-updateBugsSeq" /> </then> </filter> </else> </filter> <filter xpath="(get-property('operation','updatedBugIndex') = get-property('operation', 'updatedBugsCount'))"> <then> <loopback /> </then> </filter> </then> </filter> </sequence> </target> </iterate> <!--FOR EACH Bug : END--> </else> </filter> </then> </filter> </else> </filter> </inSequence> <outSequence> <payloadFactory media-type="json"> <format>{ "Response":{ "process":"bugzilla-manageResolvedBugs", "activityResponse":[$1] } } </format> <args> <arg expression="get-property('operation', 'responseString')" /> </args> </payloadFactory> <send /> </outSequence> </target> <description /> </proxy>
{ "bugzillaApiUrl":"http://172.22.217.15/bugiya/rest.cgi", "bugzillaApiKey":"jgvuN3V9aWqMLWFo4gs2UkezswIUCn9qezgOmibG", "bugzillaLastInvoicedDate":"2015-05-11", "bugzillaProduct":"Tour Management", "zohoBookProjectId":"148716000000041001", "tsheetsApiUrl":"https://bugiya.tsheets.com", "tsheetsAccessToken":"S.1__abb193c1d6144f9dbdb94c39d4d163d957f10550", "zohoBooksAuthToken":"c11a87210138ff2e0bf6d049abd3c27e", "zohoBooksOrganizationId":"52064035", "timeEntryMap": { "Book Hotels":"148716000000039027" }, "freshdeskApiUrl":"https://yasasi.freshdesk.com", "freshdeskApiKey":"ElSv9dEcOIC1XsfYZOVE" }
Invoice is created for a component, which can have multiple bugs and the sum of the time entries for a particular bug is shown as a single entry in the invoice.