From 00ae52ac2eb481e7d810f505d8e7deb3962fbea2 Mon Sep 17 00:00:00 2001 From: Ricky Amparo Date: Tue, 17 Nov 2020 16:06:22 -0700 Subject: [PATCH] Add nested loop formulas for both Doctor and UI import --- nestedLoops/README.md | 39 ++++++++ nestedLoops/TheDoctor/aggregator.js | 7 ++ nestedLoops/TheDoctor/formula.json | 97 ++++++++++++++++++++ nestedLoops/TheDoctor/gracefulExit.js | 1 + nestedLoops/TheDoctor/makePaginationQuery.js | 3 + nestedLoops/nestedLoops.json | 1 + 6 files changed, 148 insertions(+) create mode 100644 nestedLoops/README.md create mode 100644 nestedLoops/TheDoctor/aggregator.js create mode 100644 nestedLoops/TheDoctor/formula.json create mode 100644 nestedLoops/TheDoctor/gracefulExit.js create mode 100644 nestedLoops/TheDoctor/makePaginationQuery.js create mode 100644 nestedLoops/nestedLoops.json diff --git a/nestedLoops/README.md b/nestedLoops/README.md new file mode 100644 index 0000000..e9a2ff3 --- /dev/null +++ b/nestedLoops/README.md @@ -0,0 +1,39 @@ +# Creating a Loop Step Within Another Loop Step + +## Problem this solves: +* "How should I setup a loopstep from within another loop and aggragate data from the nested loop?" + +## Solution: +You will need to call the primary loop as the on failure branch of the nested loop. You will then need top use the standard aggregator approach to maintain the data from the nested loop. This approach is outlined below: + + ``` + let arr = steps.aggregator ? steps.aggregator.arr : []; + + + + done({arr:arr}) + ``` + +This will look to see if the step has already run and if it has it will get the results from the step and you can add to them or if it has not run it will start with an empty array. + +Included in this folder is [nestedLoops.json](nestedLoops.json), which is a formula json that can be imported and used to test with. With only a handful of steps, this formula will get invoices (from an Element Instance), loop through all those invoices and then through each invoice's line items, retrieve the line item's amount, and add it to an array. After is has finished looping through it will log an array of all of the line items' amount. This formula uses a manual trigger for easy testing. + +## IMPORTANT NOTES: +* You will need an authenticated Element instance with a GET /invoices API (endpoint). Additionally, the invoices in the response will also need to have line items so that we could loop through those as well. This formula was built using Quickbooks Online as their inovice records have a line items array. You can authenticate one in the UI. +* Your Element needs to return an array of line items that each contain a field called **Amount**. Alternatively, you can update the javascript step called `aggregator` to refer to a field that exists on the API you choose (i.e. for Zoho CRM v2, the field **Full_Name** could be used instead). You can update this step easily in the Formula UI. +* There is no paging in this formula. Additionally there is a pagination query in place to limit the number of invoices that get returned to 20. This extra step was added to make testing slightly easier as there are only a handful of records to work with. Please be mindful of this deliberate limitation as you test. + +## Import Using the UI: +1. Go to the Formulas UI and click the `BUILD NEW FORMULA` button, then select `Import` from the dropdown. +2. Find the json file `nestedLoops.json` in the nestedLLoops directory and use it to import the formula. +3. You can now interact with the formula in the UI. You can update the `aggregator` step as needed. Use the `TRY IT OUT` button to select (and/or create) a formula instance, as well as the Element instance (Finance System) you want to use. Then run the formula. + +## Import Using the doctor: +1. Insure you have the latest non-beta version of the doctor installed via npm. You can follow the directions found [here](https://www.npmjs.com/package/ce-util) or run the command `npm install -g ce-util`. You'll need a version of node that is >= v6.3.0. You will need to upload the file from TheDoctor directory, as files uploaded with the doctor have a different structure than files imported via UI. +2. From the TheDoctor directory, run the command: + ``` + doctor upload formulas -f nestedLoops.json + ``` + This will upload the Nested Loops Formula into the specified account. + +3. You can now interact with the formula in the UI. You can update the `aggregator` step as needed. Use the `TRY IT OUT` button to select (and/or create) a formula instance, as well as the Element instance (Finance System) you want to use. Then run the formula. \ No newline at end of file diff --git a/nestedLoops/TheDoctor/aggregator.js b/nestedLoops/TheDoctor/aggregator.js new file mode 100644 index 0000000..26778d8 --- /dev/null +++ b/nestedLoops/TheDoctor/aggregator.js @@ -0,0 +1,7 @@ +let arr = steps.aggregator ? steps.aggregator.arr : []; + +arr.push(steps.loopThroughInvoiceLines.entry.amount) + +done({ + arr: arr +}) \ No newline at end of file diff --git a/nestedLoops/TheDoctor/formula.json b/nestedLoops/TheDoctor/formula.json new file mode 100644 index 0000000..d4ec1f0 --- /dev/null +++ b/nestedLoops/TheDoctor/formula.json @@ -0,0 +1,97 @@ +{ + "active": true, + "configuration": [ + { + "description": "This is the QBO element that you will be retrieving invoices from. ", + "key": "QBOInstance", + "name": "QBOInstance", + "required": true, + "type": "elementInstance" + } + ], + "debugLoggingEnabled": false, + "debugLoggingExpires": "2020-11-14T02:55:23Z", + "engine": "v3", + "id": 41889, + "name": "nestedLoops", + "singleThreaded": false, + "steps": [ + { + "name": "makePaginationQuery", + "onFailure": [], + "onSuccess": [ + "getInvoices" + ], + "properties": {}, + "type": "script" + }, + { + "name": "getInvoices", + "onFailure": [], + "onSuccess": [ + "loopThroughInvoices" + ], + "properties": { + "api": "/invoices", + "elementInstanceId": "${config.QBOInstance}", + "method": "GET", + "query": "${steps.makePaginationQuery.pagination}" + }, + "type": "elementRequest" + }, + { + "name": "loopThroughInvoices", + "onFailure": [ + "gracefulExit" + ], + "onSuccess": [ + "loopThroughInvoiceLines" + ], + "properties": { + "list": "${steps.getInvoices.response.body}" + }, + "type": "loop" + }, + { + "name": "loopThroughInvoiceLines", + "onFailure": [ + "loopThroughInvoices" + ], + "onSuccess": [ + "aggregator" + ], + "properties": { + "list": "${steps.loopThroughInvoices.entry.line}" + }, + "type": "loop" + }, + { + "name": "aggregator", + "onFailure": [], + "onSuccess": [ + "loopThroughInvoiceLines" + ], + "properties": {}, + "type": "script" + }, + { + "name": "gracefulExit", + "onFailure": [], + "onSuccess": [], + "properties": {}, + "type": "script" + } + ], + "triggers": [ + { + "async": true, + "name": "trigger", + "onFailure": [], + "onSuccess": [ + "makePaginationQuery" + ], + "properties": {}, + "type": "manual" + } + ] +} \ No newline at end of file diff --git a/nestedLoops/TheDoctor/gracefulExit.js b/nestedLoops/TheDoctor/gracefulExit.js new file mode 100644 index 0000000..90a34fc --- /dev/null +++ b/nestedLoops/TheDoctor/gracefulExit.js @@ -0,0 +1 @@ +done(); \ No newline at end of file diff --git a/nestedLoops/TheDoctor/makePaginationQuery.js b/nestedLoops/TheDoctor/makePaginationQuery.js new file mode 100644 index 0000000..0b3a2ec --- /dev/null +++ b/nestedLoops/TheDoctor/makePaginationQuery.js @@ -0,0 +1,3 @@ +let pageInfo = {"pageSize": 3} + +done({"pagination":pageInfo}) \ No newline at end of file diff --git a/nestedLoops/nestedLoops.json b/nestedLoops/nestedLoops.json new file mode 100644 index 0000000..910a2b9 --- /dev/null +++ b/nestedLoops/nestedLoops.json @@ -0,0 +1 @@ +{"id":41889,"name":"nestedLoops","debugLoggingExpires":"2020-11-14T02:55:23Z","userId":8584,"accountId":7612,"createdDate":"2020-11-13T18:46:20Z","steps":[{"id":445708,"onSuccess":["loopThroughInvoiceLines"],"onFailure":[],"name":"aggregator","type":"script","properties":{"body":"let arr = steps.aggregator ? steps.aggregator.arr : [];\n\narr.push(steps.loopThroughInvoiceLines.entry.amount)\n\ndone({\n arr: arr\n})"}},{"id":445684,"onSuccess":["loopThroughInvoices"],"onFailure":[],"name":"getInvoices","type":"elementRequest","properties":{"api":"/invoices","query":"${steps.makePaginationQuery.pagination}","method":"GET","elementInstanceId":"${config.QBOInstance}"}},{"id":445709,"onSuccess":[],"onFailure":[],"name":"gracefulExit","type":"script","properties":{"body":"done();"}},{"id":445710,"onSuccess":["aggregator"],"onFailure":["loopThroughInvoices"],"name":"loopThroughInvoiceLines","type":"loop","properties":{"list":"${steps.loopThroughInvoices.entry.line}"}},{"id":445707,"onSuccess":["loopThroughInvoiceLines"],"onFailure":["gracefulExit"],"name":"loopThroughInvoices","type":"loop","properties":{"list":"${steps.getInvoices.response.body}"}},{"id":445685,"onSuccess":["getInvoices"],"onFailure":[],"name":"makePaginationQuery","type":"script","properties":{"body":"let pageInfo = {\"pageSize\": 3}\n\ndone({\"pagination\":pageInfo})"}}],"triggers":[{"id":38003,"onSuccess":["makePaginationQuery"],"onFailure":[],"type":"manual","async":true,"name":"trigger","properties":{}}],"engine":"v3","active":true,"singleThreaded":false,"debugLoggingEnabled":false,"configuration":[{"id":82541,"key":"QBOInstance","name":"QBOInstance","type":"elementInstance","description":"This is the QBO element that you will be retrieving invoices from. ","required":true}]} \ No newline at end of file