> For the complete documentation index, see [llms.txt](https://boomi.markusschmidt.pro/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://boomi.markusschmidt.pro/boomi-scriptease/examples/aggregate-prices-example.md).

# 9 - Aggregate Prices Example

{% hint style="success" %}
The Script and the Test code is available in your project in the ***training*** folder!
{% endhint %}

<details>

<summary>The Use-Case</summary>

We have any number of incoming JSON documents of the following profile (type):

```json
{
  "articleNo" : "string",
  "price" : 1.23
}
```

There can be more than one document referring to the same article number but a different price.

All documents should be consolidated into one outbound document containing an array of unique articles as follows:

```json
[
    {
        "articleNo" : "...",
        "maxPrice"  : 1.23,
        "minPrice"  : 0.98,
        "priceCount" : 2,
        "prices" : [ 0.98, 1.23 ]
    }
]
```

</details>

<details>

<summary>Example Documents</summary>

Four documents referring to two different articles

**Document 1**

```json
{
  "articleNo" : "a001",
  "price" : 1.23
}
```

**Document 2**

```json
{
  "articleNo" : "a002",
  "price" : 2.00
}
```

**Document 3**

```json
{
  "articleNo" : "a001",
  "price" : 1.05
}
```

**Document 4**

```json
{
  "articleNo" : "a001",
  "price" : 0.98
}
```

</details>

<details>

<summary>Expected Result</summary>

```json
[
    {
        "articleNo" : "a001",
        "maxPrice"  : 1.23,
        "minPrice"  : 0.98,
        "priceCount" : 3,
        "prices" : [ 1.23, 1.05, 0.98 ]
    },
    {
        "articleNo" : "a002",
        "maxPrice"  : 2.00,
        "minPrice"  : 2.00,
        "priceCount" : 1,
        "prices" : [ 2 ]
    }
]

```

</details>

<details>

<summary>Script setup</summary>

Create a folder (aka Package) called ***AggregatePrices***

<img src="/files/dYGvLqLLXoaEHr6BmE7D" alt="" data-size="original">

and add a Script and a Test Script from a [Script Templates](/boomi-scriptease/knowlede-base/process-script-templates.md)

<img src="/files/9VjmzUtChMeaZ0Pn2Ym7" alt="" data-size="original">

</details>

## Step-By-Step

### Setup a Test Context

The first things we need to do is to setup a test context that provides the test data, as it is normally provided by the Boomi run-time. In our case, we must create the documents that we want to pass into the script for testing( see [#example-documents](#example-documents "mention")).

<figure><img src="/files/3FpMOjAdWi3NHGPmkx8Y" alt=""><figcaption><p>The documents passed to the script are defined in the Test class</p></figcaption></figure>

This is simple as that:

```groovy
def context = new ProcessScriptContext(
  inputDocuments: [
    Document.fromText('{ "articleNo" : "a001", "price" : 1.23 }'),
    Document.fromText('{ "articleNo" : "a002", "price" : 2.00 }'),
    Document.fromText('{ "articleNo" : "a001", "price" : 1.02 }'),
    Document.fromText('{ "articleNo" : "a001", "price" : 0.98 }')
  ])
_testScript.run(context)
```

{% hint style="info" %}
We do not need any *Execution Property* or *Dynamic Property* or *Process Property* in our script, but the *Context* would the right place to add those.&#x20;

*Dynamic Document Properties* would be added to each document, not to the *Context* itself.
{% endhint %}

### Set Test expectations

Unlike in the template, we expect only one document as output, and we have to **amend our test assertion** in the Test class. Feel free to add more assertions to ensure the script works as expected.

```groovy
println("\r\n--- Test Output ----------")
int docCount = context.outputDocuments.size()
println(docCount + " Document(s) after script execution")

assert 1 == docCount // <<<<<< expect one document
```

## Implement the Script

{% hint style="danger" %}
**Maintaining the script header information is mandatory,** especially the date and author tags. This is even more important when you work in a team!
{% endhint %}

<details>

<summary>The Script Header</summary>

```groovy
/* **************************************************************************
    Aggregate prices on JSON documents.
        
    IN : JSON documents of profile j.Price
         { "articleNo" : "a001", "price" : 1.23 }
         
    OUT: JSON documents of profile j.Prices (plural=array)
        [
		{
		        "articleNo" : "a001",
		        "maxPrice"  : 1.23, "minPrice"  : 0.98, "priceCount" : 3,
		        "prices" : [ 1.23, 1.05, 0.98 ]
		},{
		        "articleNo" : "a002",
		        "maxPrice"  : 2.00, "minPrice"  : 2.00, "priceCount" : 1,
		        "prices" : [ 2 ]
		}
        ]
    
    ------------------------------------------------
    13.06.2024  mspro -   Created
************************************************************************** */
```

</details>

We need the **JsonSlurper** object to work with JSON data. We create the `js` object instance before the documents loop because there is no need to have a new instance for each document. One `js` is enough for all documents.

<figure><img src="/files/ynGRycZU69V7sU3eZrEu" alt=""><figcaption><p>JsonSlurper instance</p></figcaption></figure>

You recognize that IntelliJ is smart enough to automatically insert the requires *Imports:*

<figure><img src="/files/3BkzcwCjkzUQ3WtTDJRU" alt=""><figcaption><p>IntelliJ adding Imports</p></figcaption></figure>

Navigate to the script logic and replace *Your document related code here ...* with:

```groovy
// *********** Document related functionality ************
Map jsonDoc = js.parseText( textDoc)
_logger.info( "DOC[$docNo]: ${jsonDoc.articleNo} = ${jsonDoc.price}")
// ******** end of Document related functionality ********

```

We parse the text document into a JSON (map), so that we can access the JSON elements as properties (see [Working with JSON](https://docs.groovy-lang.org/latest/html/documentation/json-userguide.html)). The scipt's logic so far is to log the incoming data. Good enough for a first Debug Run. Set a breakpoint on line 52, navigate to the Test and **Debug test01()**.

<figure><img src="/files/ulGxNFqTxrQEcBR9cuqi" alt=""><figcaption><p>Prepare to debug the script</p></figcaption></figure>

The execution stops at the breakpoint. See the variables and output.

<figure><img src="/files/QAeSMX5q0H8lw1vDEeYI" alt=""><figcaption><p>Debugging the script</p></figcaption></figure>

<details>

<summary>Test your Script in Boomi AtomSphere</summary>

![](/files/IFEl6niGfAFhYdEooJ8U)\
You can copy and paste the script code into the `psgAggregatePrices`script component

<img src="/files/ollvdM4RjfagepKJQRre" alt="" data-size="original">

and use it in your test process.

![](/files/Tfwb1w8zhhMBtUxC69Xj)\
Check the process logs to see the log entires your script has written![](/files/7tRUCgIiufX21OBwtxbP)

</details>

Let's improve the scripts functionality and build the business logic. Amend your code, define the `articles` result list and business logic, and set a breakpoint on the line *// << set breakpoint here*

```groovy
// https://www.tutorialspoint.com/groovy/groovy_lists.htm
List articles = []

// Documents loop
for (int docNo = 0; docNo < docCount; docNo++) 
{
		...
		
		
		// *********** Document related functionality ************
		
		Map jsonDoc = js.parseText(textDoc)
		_logger.info("DOC[$docNo]: ${jsonDoc.articleNo} = ${jsonDoc.price}")
		
		// Business Logic
		
		// Check if there is already an item with the same articleNo.
		// If yes, we must update that item. 
		// If no, we create a new item and add it to the prices list.
		// it - iterator = List element (of type)
		
		def article = prices.find { it.articleNo == jsonDoc.articleNo }
		// article TYPE 
		// {    
		//      articleNo
		//      minPrice, maxPrice, priceCount, 
		//      prices[]
		// }
		
		if (article == null) {  // << set breakpoint here
			articles.add([
					articleNo: jsonDoc.articleNo,
					priceCount : 1,
					minPrice : jsonDoc.price,
					maxPrice : jsonDoc.price,
					prices : [ jsonDoc.price]
			])
		} else { }
		
		// ******** end of Document related functionality ********
```

Debug your script and see the execution stopping with `article == null`. This is expected because - look at the *Variables* window - the `prices`list is yet emtpy.

<figure><img src="/files/0KeDeL46yue8WwbrlDLN" alt=""><figcaption></figcaption></figure>

I think, you will find out yourself how to step over the next two lines (Single Step - F10) to stop at `_setTextDocument()`. While your colors might be different (depending in the IntelliJ theme you have chosen), the red line is the breakpoint and the yellow line marks the next line for execution.

<figure><img src="/files/rsR5Ttw24M7jd1q9SO8f" alt=""><figcaption></figcaption></figure>

More interesting to see is the *Variables* window where you can observe that the expect *article* object was added to the `articles` list.

<figure><img src="/files/QXYzGtUOBqCoMgUyJ1Xs" alt="" width="516"><figcaption><p>Variables</p></figcaption></figure>

Amend you code again and implement the `else` branch.

```groovy
if (article == null) { // << set breakpoint here
	articles.add([
			articleNo: jsonDoc.articleNo,
			priceCount : 1,
			minPrice : jsonDoc.price,
			maxPrice : jsonDoc.price,
			prices :  [ jsonDoc.price]
	])
} else {
	// article found -> update
	article.priceCount++        // increment
	if( jsonDoc.price < article.minPrice) article.minPrice = jsonDoc.price 
	if( jsonDoc.price > article.maxPrice) article.maxPrice = jsonDoc.price
	article.prices.add( jsonDoc.price ) // We do _not_ check if the same articles already exists!
}
```

Set a breakpoint on the `_setTextDocument()` line and debug a bit: document by document. After the third document you should check the console output and the variables. You will recognize the variables are close to what we expect to see in the output JSON.

<figure><img src="/files/NTG0yRjZRrexuEeWFfFX" alt=""><figcaption></figcaption></figure>

Last but not least, we must write back the `articles` map into one output document. So, you must move the `_setTextDocument()` to outside of the document loop. Before, we must convert the `articles` map to a Json String.

```groovy
	// ******** end of Document related functionality ********
}   // documents loop

// Your process related code (process properties etc.) here
String outputDoc = JsonOutput.toJson(articles)
_setTextDocument(dataContext, outputDoc, new Properties()) // <<<<<
```

As the very last step, you can update your Test class and `prettyPrint()` the returned Json document:

<figure><img src="/files/12Lqm0jNFcRFaPJSR0YA" alt=""><figcaption><p>The Test Class output</p></figcaption></figure>

## Use the script

Do you rememeber why we did all that effort? We wanted to use that script in Boomi! Copy and paste all the script code 1:1 into the script component. Of course, the Test class is not needed in Boomi.

<figure><img src="/files/GhT6YDRmoRnCkfKkgWK6" alt=""><figcaption><p>Script Code in Boomi</p></figcaption></figure>

<figure><img src="/files/snJsrUFMI7zw2d5Zn6wH" alt=""><figcaption><p>q.e.d.</p></figcaption></figure>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://boomi.markusschmidt.pro/boomi-scriptease/examples/aggregate-prices-example.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
