State Based E1 Page

One challenge of Browser App development is state management. A drill-down functionality is a good example where this is crucial. Without state management it’s one-way – you can’t go back if don’t know where you came from.

Water

The first place to look for any solution to a JavaScript problem is NPM. The sheer number of packages for solving state management (1,090 in my last search) gives an idea of its prevalence.

Sometimes a piece just falls into place. I’ve been pondering state management for the e1p-templates for some time, and traversing thousand-plus components sounded daunting. And then one morning an article about Effector dropped into my inbox.

Along with store2, which I had picked up somewhere along the journey, I was convinced that I had the right pieces for a state managed E1 Page.

Route Sample

The Route Sample demonstrates state management in an E1 Page. An initial Chart of Account page that responds to branch selection with the account balance. Each selection creates a state that can be revisited from the sidebar.

The objective of this demo is state management, and along the way demonstrates couple of points to note.

Aggregate

The native JDE Toolset language is limited, to put it nicely. And contradictory it’s also one of its strength. The language gives you just enough to get the job done while limiting the risk of blowing your whole leg off (BYWLO).

The language’s limitations are offset with AIS by enabling E1 functionality in Browser Apps and added functionality. One of the added functionality is Aggregate – an implementation of the SQL Aggregate Functions.

The account balance is retrieved by summing up the selected branch and all sub-branches with a single AIS request. Although doable in the native language, it’s certainly not a one-liner and a potential BYWLO. The flipside of the above contradiction is that the risk of BYWLO increases significantly when trying to solve a problem that the language is not designed for – the old square-peg and round-hole dilemma.

JavaScrip Array

One of my almost comical discoveries in JavaScript is the resurrection of the humble array. With the exception of RPG, which I had the privilege of coding sometime in the last century, array’s have been around forever as the poor man’s data storage. In the days back in last century, memory was a precious commodity so loading thousands of rows of data into an array was a very poor technique and deemed to fail. Subsequently all sorts of programming techniques were developed to manage data, but the array always stayed in the background as a convenient data storage where no trickeries were needed.

There is probably a book in the evolution of the array and maybe one has already been written – I haven’t quite got to the nerd-level of looking.

Fast forward to modern JavaScript and you discover that if you need to transpose some data, then use an array!

The Route Sample page has array transposition firstly for the account master (F0901) into the tree structure and secondly the account balance (F0902) into nested tables.

The first one is in the coa-tree component:

addChildren(lda: number, objFrom: string, objTo: string, rows: any[]): any[] {
    if (lda > 9) return [];
    const count = rows.filter((r: any) => r.F0901_LDA == lda).length;
    if (count == 0) return this.addChildren(lda + 1, objFrom, objTo, rows);
    return rows
        .filter((r: any) => r.F0901_LDA == lda)
        .filter((r: any) => r.F0901_OBJ >= objFrom && r.F0901_OBJ < objTo)
        .map((r: any, i: number, a: any[]) => {
            const next = a[i + 1];
            const obj = next ? next.F0901_OBJ : objTo;
            let children = [];
            let nxtLda = lda;
            while (children.length === 0 && nxtLda < 9) {
                nxtLda++;
                children = this.addChildren(nxtLda, r.F0901_OBJ, obj, rows)
            }
            return {
                id: r.F0901_AID,
                text: `${r.F0901_MCU}.${r.F0901_OBJ}${r.F0901_SUB > ' ' ? '.' : ''}${r.F0901_SUB} - ${r.F0901_DL01}`,
                icon: children.length > 0
                    ? 'fas fa-layer-group icon'
                    : r.F0901_PEC == 'N' ? 'fas fa-times small-icon' : 'fas fa-check small-icon',
                data: {
                    aid: r.F0901_AID,
                    lda: Number(r.F0901_LDA),
                    pec: r.F0901_PEC,
                    co: r.F0901_CO,
                    mcu: r.F0901_MCU.trim(),
                    sub: r.F0901_SUB,
                    obj: r.F0901_OBJ
                },
                children
            }
        });
}

What this piece of code does is that it reads through an array of the account master and uses the level of detail (LDA) along with the object (OBJ) to place the account in its proper place in the hierarchy. The technique used is to filter and map the array with recursive adds.

The second one is in the store:

page.data.save = Object.values<any>(response)
    .filter(g => g.output)
    .map(g => g.output)
    .map(g => {
        return g.map(r => {
            return {
                fy: r.groupBy['F0902.FY'],
                an: [
                    r['F0902.AN01_SUM'],
                    r['F0902.AN02_SUM'],
                    r['F0902.AN03_SUM'],
                    r['F0902.AN04_SUM'],
                    r['F0902.AN05_SUM'],
                    r['F0902.AN06_SUM'],
                    r['F0902.AN07_SUM'],
                    r['F0902.AN08_SUM'],
                    r['F0902.AN09_SUM'],
                    r['F0902.AN10_SUM'],
                    r['F0902.AN11_SUM'],
                    r['F0902.AN12_SUM']
                ]
            };
        })
        .map(r => {
            const sum = r.an.reduce((s, a) => Numeral(s).add(a).value());
            const max = Math.max(...r.an);
            const min = Math.min(...r.an);
            const range = Numeral(max).subtract(min).value() || 1;
            const offset = r.an.map(a => Numeral(a).divide(range).value());
            return {
                fy: r.fy,
                an: r.an,
                offset,
                sum,
                max,
                min,
                range
            };
        });
    });

In addition to filter and map, the reduce method is used to sum up the postings.

It took me some time to get my head around the programming technique above. How you can efficiently transpose an array by method chaining. But once you get the hang of this, you feel powerless without it.

Incidentally, arrays are one of those things left out of the Toolset’s native language. Probably because JDE has it’s origin in RPG so it wasn’t deemed important enough for ‘One World’. This reminds me of an enthusiastic RPG-ist once telling me, again sometime last century, about the revolutionary feature in the latest release of the language: “We now have an ‘if’ statement!” So maybe RGP has array now?