Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions nestedLoops/README.md
Original file line number Diff line number Diff line change
@@ -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:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@RickyAmparoCloud "top" should be "to" ;)


```
let arr = steps.aggregator ? steps.aggregator.arr : [];

<insert custom logic>

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 <acctNickName> -f nestedLoops.json
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@RickyAmparoCloud It likes like you've called the formula "formula.json" in the doctor folder. I think you'll either have to rename it or change this line to refer to "formula.json" instead of "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.
7 changes: 7 additions & 0 deletions nestedLoops/TheDoctor/aggregator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
let arr = steps.aggregator ? steps.aggregator.arr : [];

arr.push(steps.loopThroughInvoiceLines.entry.amount)

done({
arr: arr
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@RickyAmparoCloud @nick-bair I'll let Nick confirm but I want to say you don't need "arr:arr" inside of the done callback. Instead you can just pass in done ({ arr });

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's correct.

})
97 changes: 97 additions & 0 deletions nestedLoops/TheDoctor/formula.json
Original file line number Diff line number Diff line change
@@ -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"
}
]
}
1 change: 1 addition & 0 deletions nestedLoops/TheDoctor/gracefulExit.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
done();
3 changes: 3 additions & 0 deletions nestedLoops/TheDoctor/makePaginationQuery.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
let pageInfo = {"pageSize": 3}

done({"pagination":pageInfo})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@RickyAmparoCloud Not sure it matters much, but I think you can pass in done({ pageInfo }) and just refer to .pageInfo.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It depends how you pass it to done:

done({ pageInfo }) => access later by calling stepName.pageInfo
done(pageInfo) => access later by calling stepName

1 change: 1 addition & 0 deletions nestedLoops/nestedLoops.json
Original file line number Diff line number Diff line change
@@ -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}]}