{"id":110973,"date":"2018-08-15T06:24:56","date_gmt":"2018-08-15T13:24:56","guid":{"rendered":"https:\/\/www.microsoft.com\/en-us\/power-platform\/blog\/power-automate\/approval-writeback\/"},"modified":"2018-08-15T06:24:56","modified_gmt":"2018-08-15T13:24:56","slug":"approval-writeback","status":"publish","type":"power-automate","link":"https:\/\/www.microsoft.com\/en-us\/power-platform\/blog\/power-automate\/approval-writeback\/","title":{"rendered":"Advanced | Flow of The Week: Advanced Approval Write back"},"content":{"rendered":"
Hey Flow Fans! Welcome to this Flow of The Week Written by Community Member Phillip Guglielmi!<\/p>\n
Phillip is a Senior Enterprise Business Intelligence and Analytics Architect with my Microsoft Certified Solutions Associate in Data Management and Analytics and Certified Public Accountant designation. As a diverse hybrid, I have a deep understanding of the interface between business and IT with advanced technical skills in cloud based and on premise applications including SQL, Azure SQL Data Warehousing, enterprise data warehousing, OLAP cubes, SAP HANA, Dynamics 365, OBIEE, Azure Machine Learning, Azure SSAS, SSRS, Tableau, Power BI, SSIS, PowerPivot, Power Query, PowerApps, Flow, and Excel. Currently, He is developing with program language including C#, R, jQuery, JSON, REST API\u2019s, OData, HTML5, JavaScript, MDX, and DAX. My goal is to support others in the community through shared knowledge as I seek to strengthen my skills in key areas of enterprise operations and the technologies transforming them, including data architecture, process automation, governance and strategy, future state enterprise data warehousing, and computer science.<\/em><\/strong><\/p>\n Be sure and leave some comments below, letting him know what you thought of the post!<\/p>\n A robust, Client-specific approval workflow and write-back engine is not a standard feature of many ERP systems, which requires BI architects and data engineers to think outside the box to deliver scalable solutions. Microsoft Flow can meet these enterprise level requirements, and can do it at a fraction of the time and cost of any other solution on the market. This article will tackle the current business requirements pertaining to enterprise level solutions around on-premise SQL data warehouse write-backs, robust tiered approval cycles, and sales order approval requests.<\/p>\n \u00a0<\/p>\n \u00a0<\/p>\n \u00a0<\/p>\n \u00a0<\/p>\n The scenario described in this tutorial will operate on a SQL stored procedure and Approvals, but you are more than welcome to substitute any data that produces an array within Flow so that you can follow along: a SharePoint list, a list of documents in OneDrive, tags from Computer Vision API, etc.<\/p>\n Please see attached link to the AdventureWorks data warehouse<\/a> to follow along with the blog post.<\/p>\n The next article I write will use the new Excel Connector<\/a><\/strong> and Common Data Service.<\/strong> \u00a0<\/p>\n <\/p>\n I will walk through how to build this flow from scratch, but\u00a0you can also use this template to follow along yourself<\/a>.<\/p>\n You can use the\u00a0Recurrence\u00a0<\/strong>trigger as we will be using an OData<\/a> filter to find open transactions only.<\/p>\n <\/p>\n Insert a step after the Recurrence and search for “get row” and select the option for “SQL Server \u2013 Get rows.”<\/p>\n Use an OData<\/a> filter to return Sales Orders that are \u2018Open\u2019. We will immediately mark these rows as \u2018Pending\u2019 with the Execute Stored Procedure.<\/strong><\/p>\n Insert a step after the Get rows and search for \u201cApply to each.\u201d<\/p>\n Insert a step after the Apply to each and search for “SQL Server – Execute stored procedure.”<\/p>\n <\/p>\n Running a SQL Procedure to update the rows that are \u2018Open\u2019 to \u2018Pending\u2019 is important because we do not want those records re-entering the Approval cycle again, thereby effectively keeping them in the Approval cycle until approved (or rejected). Upon rejection, they will remain in the loop. Therefore, only new sales orders will be picked up by the approval cycle.<\/p>\n \u00a0<\/p>\n Insert a step after the \u2018Apply to Each\u2019 and search for “Data Operations – Select.”<\/p>\n Click into the From field and use the dynamic content box to choose the results of the “Get Rows” step.<\/p>\n Based on the business requirements, we need to group all Sales Orders by the column SalesOrderNumber as the approvers would like to see the comprehensive list of products that make up the sales order.<\/p>\n There are two columns in the section for Map: “Enter key” on the left and “Enter value” on the right.<\/p>\n <\/p>\n Click into each row in the “Enter value” column and use the formula below:<\/p>\n Navigate to https:\/\/docs.microsoft.com\/en-us\/powerapps\/maker\/canvas-apps\/functions\/function-isblank-isempty<\/a> for more information on the coalesce<\/a> function. This is one of my favorite functions because if all of the arguments are\u00a0blank<\/em>, then the function returns\u00a0blank<\/em>, rather than an error.<\/p>\n Insert a step after the \u2018Select\u2019 and search for “Data Operations – Compose.”<\/p>\n The select step has reduced the get rows step to the sales order number, but the data is still a table. In order to turn the table into deduplicated list and finally an array to be grouped, we need to union<\/strong> each row.<\/p>\n <\/p>\n This union<\/strong> function returns a single array or object with all the elements that are in the array or object passed in. For parameters, this function only requires either the array or object from where we want all the items. The parameters for this function can either be a set of objects or a set of arrays, not a mixture of both. If there are two objects with the same name, the last item with that name appears in the result.<\/p>\n Insert a step and search for “Variables – Set Variable.”<\/p>\n The Variables connector is one of the more important connectors. To use a variable you will first have to initialize a variable. Here, we will start our flow with a list of initialize variable actions as we not yet able to initialize variable actions in a single scope box.<\/p>\n To do this, we will initiate a variable to track whether the owner and the approver have approved a sales order.<\/p>\n <\/p>\n Type will be Integer and the Value will be set to 0.<\/p>\n Insert a step and search for “Variables – Set Variable.”<\/p>\n Add variable->\u201dInitialize variable\u201d action, Name set to Initialize LIDCODE, Type set to Array and set Value to the Output from the Compose.<\/p>\n <\/p>\n Below is an extra screenshot to show where which Output to use.<\/p>\n <\/p>\n Now that we have set up the ex-ante variables and arrays, we will now start the approval process, which will proceed until all the sales orders are approved. Watch this awesome tutorial<\/a> by Jonathon Levesque<\/a> about kicking off the approval process to include timeouts and escalations, read this article<\/a> by Merwan Hade<\/a> on setting an approval expiry<\/a>, and this article<\/a> by David Blyth<\/a> on approval reminders.<\/a><\/p>\n \u00a0<\/p>\n Insert a new step and navigate \u201cApply to each.\u201d<\/p>\n Now that we have a unique list of sales order numbers, we can add an \u201cApply to each\u201d on the list of sales orders and create an approval and write-back the response to SQL. An apply to each loop makes it possible to control the list that you repeat over.<\/p>\n <\/p>\n Insert a step and search for “Data Operations – Compose.”<\/p>\n Use the compose operation to retrieve the sales order number from the apply to each.<\/p>\n It is very important not to forget to make your \u201cApple to each\u201d execute in parallel by going to options and enabling \u201cConcurrency control\u201d.<\/p>\n <\/p>\n \u00a0<\/p>\n Insert a step and navigate \u201cAdd a do until\u201d<\/p>\n We set up the \u201cDo Until\u201d where the exit condition is when the isSecondApproved variable becomes 1 and the sales orders array is empty (meaning they have all been sent out). The variable will be checked at the start of every loop iteration, and once the approval is complete and variable set to 1, it will exit the loop iteration.\u00a0<\/p>\n <\/p>\n Do until loop. This loop will continue until both the approver(s) have approved the sales order. That will start out looking like this, once you add the approval step in:<\/p>\n \u00a0<\/p>\n <\/p>\n Insert a step and search for “Data Operations – Filter array.”<\/p>\n The From will be the original get rows step and we will click on the Edit in advanced mode.<\/p>\n \u00a0<\/p>\n <\/p>\n Insert a step and search for “Data Operations – Select.”<\/p>\n <\/p>\n Again, we use coalesce to avoid any errors in the output.<\/p>\n \u00a0<\/p>\n Insert a step and search for “Data Operations – Compose.”<\/p>\n We are checking if anything is currently open for the owner. If there is, we will set up the approval to begin with the owner, otherwise, the approval process will start with the secondary approver.<\/p>\n <\/p>\n We need to create an array that captures the approvers who will be responsible for approving the sales order. By creating an array and then joining them, we will be able to add the output to the approval.<\/p>\n Similar to step 3 above, we will select the columns we need to deduplicate and join.<\/p>\n Insert a step and search for “Data Operations – Select.”<\/p>\n <\/p>\n Insert a step and search for “Data Operations – Compose.”<\/p>\n \u00a0<\/p>\n <\/p>\n Insert a step and search for “Approval.”<\/p>\n Utilizing the output from the compose data operation above, we join all potential approvers that are on the sales order.<\/p>\n Full shot:<\/p>\n <\/p>\n Insert a step and search for “Condition.”<\/p>\n We are going to set the condition as Response is equal to Approve.<\/p>\n If you are working in sharepoint and would like to know how to get a dynamic list of approvers, check out this flow of the week about sending parallel approval requests for a dynamic set of approvers<\/a>.<\/p>\n Once the owner approves the sales order, there is a stored procedure that writes back the on-premise SQL Data Warehouse the results, and the second approver is set to pending.<\/p>\n <\/p>\n \u00a0<\/p>\n We will not worry at this point what happens when the Owner rejects in this scenario. However, we will cover what happens when the second owner rejects the sales order. We will cover that now.<\/p>\n Insert a step after the SQL stored procedure and search for “Data Operations – Select.”<\/p>\n Once the owner has approved the sales order, it is now the second owners turn to approve it. We are going to follow the steps we performed in setting up the owner:<\/p>\n <\/p>\n \u00a0<\/p>\n <\/p>\n <\/p>\n The important part here is to make sure to use the selected e-mail columns and compose action to create an array for the secondary approvers e-mails. Finally, we will use the join <\/em>function to combine all potential approvers on the sales order.<\/p>\n <\/p>\n The final difference will be in how we handle is the second approver rejects the sales order. We will send the owner an e-mail notifying them that the sales order has been rejected, and then we will set the variable so that the loop will resend the owner the approval.<\/p>\n Finally, just as we did with the owner we will make sure to set our variables depending on the outcome of the secondary approver\u2019s decision.<\/p>\n Sales Order is Approved <\/strong>by Approver:<\/p>\n <\/p>\n <\/p>\n Sales Order is Rejected <\/strong>by Approver:<\/p>\n The Owner will receive a notification the sales order has been rejected along with comments from the Approver on the modifications that are required for approval.\u00a0<\/p>\n <\/p>\n <\/p>\n <\/p>\n <\/p>\n \u00a0<\/p>\n <\/p>\n \u00a0<\/p>\n Future developments will look at allowing the manager to delete a rejected record or cancelling the sales order. Maybe even a purge process that has been out there for an extended amount of time. There are many avenues to take the process, and the goal to inspire and encourage curiosity with the Power Platform and Flow.<\/p>\n My next posts will be around Approvals in the Common Data Service and Dynamics 365. <\/strong><\/p>\n Let us know in the comments if you like this post as I will be developing enhancements to this Flow. \u00a0<\/p>\n https:\/\/flow.microsoft.com\/en-us\/blog\/approvals-in-cds-and-seven-updates\/<\/a><\/p>\n For formatting approvals to, use markdown<\/a> language at: https:\/\/docs.microsoft.com\/en-us\/flow\/approvals-markdown-support<\/a><\/p>\n For an amazing article by Stephen Siciliano<\/a> on Rich text formatting with Approvals, please see the link below:<\/p>\nThe Approval Scenario<\/strong><\/h2>\n
\n
Overview of the flow<\/strong><\/h1>\n
\n
The trigger<\/strong><\/h1>\n
\n
\n
Creating variables and arrays for the approval<\/strong><\/h2>\n
\n
\ncoalesce(item()?['SalesOrderNumber'],null)<\/pre>\n<\/div>\n
\n
\nunion(body('SelectLIDCODE'),body('SelectLIDCODE'))<\/pre>\n<\/div>\n
\n
\n
Setting-Up the Approvals<\/strong><\/h2>\n
\n
\n@items('Send_each_sales_order_indivdually')['LIDCODE']<\/pre>\n<\/div>\n
\n
\n@equals(variables('isSecondApproved'), 1))<\/pre>\n<\/div>\n
\n
\n@equals(item()?['SalesOrderNumber'], outputs('SalesOrderNumber'))<\/pre>\n<\/div>\n
\n
First level of approval – Owner approval<\/strong><\/h2>\n
\n
\nlength(body('MasterSelect'))<\/pre>\n<\/div>\n
\n
\njoin(outputs('a1EmailUnion'),';')<\/pre>\n<\/div>\n
Second level of approval – Approver<\/strong><\/h2>\n
\n
\n
\n
\nconcat(item()?['ApproverEmail'],'')<\/pre>\n<\/div>\n
\n
\n
\nunion(body('Selecta2Emails'),body('Selecta2Emails'))<\/pre>\n<\/div>\n
\n
\n
\njoin(outputs('a2EmailUnion'),';')<\/pre>\n<\/div>\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
Next Steps<\/strong><\/h2>\n