@zeniai/client-epic-state 4.20.0 → 4.20.1-beta11ND
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/commonStateTypes/coaBalance/additionalBalances/getSummationBalance.d.ts +1 -1
- package/lib/esm/index.js +2 -1
- package/lib/esm/view/profitAndLossClassesView/horizontalSectionEmptyBalancesSlice.js +17 -0
- package/lib/esm/view/profitAndLossClassesView/profitAndLossClassesByClassHorizontalSelector.js +3 -8
- package/lib/esm/view/profitAndLossClassesView/profitAndLossHorizontalEnrichment.js +132 -73
- package/lib/index.d.ts +2 -1
- package/lib/index.js +27 -25
- package/lib/tsconfig.typecheck.tsbuildinfo +1 -1
- package/lib/view/profitAndLossClassesView/horizontalSectionEmptyBalancesSlice.d.ts +9 -0
- package/lib/view/profitAndLossClassesView/horizontalSectionEmptyBalancesSlice.js +20 -0
- package/lib/view/profitAndLossClassesView/profitAndLossClassesByClassHorizontalSelector.js +3 -8
- package/lib/view/profitAndLossClassesView/profitAndLossHorizontalEnrichment.js +132 -73
- package/package.json +1 -1
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { COABalanceTimeFrame } from '../../commonStateTypes/coaBalance/coaBalance';
|
|
2
|
+
import { COABalancesSlice } from '../../commonStateTypes/coaBalance/coaBalancesFilter';
|
|
3
|
+
import { Month } from '../../commonStateTypes/timePeriod';
|
|
4
|
+
/**
|
|
5
|
+
* Empty `COABalancesSlice` for horizontal P&L-by-class section / calculated headers when
|
|
6
|
+
* enrichment has not produced balances yet, or for UI mapping fallbacks.
|
|
7
|
+
* Structure matches {@link enrichProfitAndLossByClassHorizontalReport} output.
|
|
8
|
+
*/
|
|
9
|
+
export declare function getEmptyHorizontalSectionBalancesSlice(firstMonthOfFY: Month, timeframe?: COABalanceTimeFrame): COABalancesSlice;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getEmptyHorizontalSectionBalancesSlice = getEmptyHorizontalSectionBalancesSlice;
|
|
4
|
+
/**
|
|
5
|
+
* Empty `COABalancesSlice` for horizontal P&L-by-class section / calculated headers when
|
|
6
|
+
* enrichment has not produced balances yet, or for UI mapping fallbacks.
|
|
7
|
+
* Structure matches {@link enrichProfitAndLossByClassHorizontalReport} output.
|
|
8
|
+
*/
|
|
9
|
+
function getEmptyHorizontalSectionBalancesSlice(firstMonthOfFY, timeframe = 'month') {
|
|
10
|
+
return {
|
|
11
|
+
balances: [],
|
|
12
|
+
additionalBalances: [],
|
|
13
|
+
filter: {
|
|
14
|
+
firstMonthOfFY,
|
|
15
|
+
timeframe,
|
|
16
|
+
balancesRange: { numberOfPeriods: 0, orderBy: 'ascending_date' },
|
|
17
|
+
additionalBalances: [],
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
}
|
|
@@ -52,16 +52,11 @@ const getProfitAndLossClassesHorizontalView = (filter, accountState, classState,
|
|
|
52
52
|
const calculatedSections = {};
|
|
53
53
|
let reportCurrency = currencyHelper_1.defaultCustomerCurrency;
|
|
54
54
|
if (fetchState.fetchState === 'Completed' && selectedPeriod != null) {
|
|
55
|
-
// Get section reports using existing selector infrastructure
|
|
56
|
-
const sectionReports = {};
|
|
57
|
-
HORIZONTAL_PANDL_SECTION_IDS.forEach((sectionId) => {
|
|
58
|
-
sectionReports[sectionId] = (0, sectionClassesViewSelector_1.getSectionClassesViewReport)(accountState, classState, sectionsState, { reportId, sectionId }, filter);
|
|
59
|
-
});
|
|
60
55
|
reportCurrency =
|
|
61
56
|
profitAndLossClassesViewState.currency ?? currencyHelper_1.defaultCustomerCurrency;
|
|
62
|
-
//
|
|
57
|
+
// Fetch each section and pivot class→account to account→class in one pass
|
|
63
58
|
HORIZONTAL_PANDL_SECTION_IDS.forEach((sectionId) => {
|
|
64
|
-
const sectionReport =
|
|
59
|
+
const sectionReport = (0, sectionClassesViewSelector_1.getSectionClassesViewReport)(accountState, classState, sectionsState, { reportId, sectionId }, filter);
|
|
65
60
|
if (sectionReport != null) {
|
|
66
61
|
sections[sectionId] = pivotSectionToHorizontal(sectionReport, classColumns, SECTION_TITLES[sectionId] ?? sectionId, reportCurrency);
|
|
67
62
|
}
|
|
@@ -265,7 +260,7 @@ function sumBalancesInSlice(balances) {
|
|
|
265
260
|
if (balances.length === 0) {
|
|
266
261
|
return 0;
|
|
267
262
|
}
|
|
268
|
-
const summed = (0, getSummationBalance_1.getSummationFreeRangeTotalBalance)(
|
|
263
|
+
const summed = (0, getSummationBalance_1.getSummationFreeRangeTotalBalance)(balances);
|
|
269
264
|
return summed?.balance.amount ?? 0;
|
|
270
265
|
}
|
|
271
266
|
/**
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.enrichProfitAndLossByClassHorizontalReport = enrichProfitAndLossByClassHorizontalReport;
|
|
4
4
|
const currencyHelper_1 = require("../../commonStateTypes/currencyHelper");
|
|
5
|
+
const horizontalSectionEmptyBalancesSlice_1 = require("./horizontalSectionEmptyBalancesSlice");
|
|
5
6
|
const profitAndLossClassesByClassHorizontalSelectorTypes_1 = require("./profitAndLossClassesByClassHorizontalSelectorTypes");
|
|
6
7
|
/**
|
|
7
8
|
* Populates `sectionHeaderBalancesSlice`, and per-account `accountReportData`, so the
|
|
@@ -28,30 +29,31 @@ function enrichHorizontalSection(report, section) {
|
|
|
28
29
|
return total?.balance.amount ?? 0;
|
|
29
30
|
});
|
|
30
31
|
section.sectionHeaderBalancesSlice = buildHorizontalClassColumnBalancesSlice(report, classAmounts, section.sectionTotal.amount);
|
|
31
|
-
const
|
|
32
|
+
const parentIndices = computeHorizontalAccountParentIndices(section.accounts);
|
|
33
|
+
const childrenRowIndicesByParent = buildHorizontalAccountChildIndicesByParent(parentIndices);
|
|
32
34
|
const childrenRecord = {};
|
|
33
35
|
childrenRowIndicesByParent.forEach((childIndices, parentIndex) => {
|
|
34
36
|
childrenRecord[parentIndex] = childIndices;
|
|
35
37
|
});
|
|
36
38
|
section.treeLayout = {
|
|
37
39
|
rootAccountRowIndices: section.accounts
|
|
38
|
-
.map((_, i) =>
|
|
40
|
+
.map((_, i) => (parentIndices[i] === -1 ? i : -1))
|
|
39
41
|
.filter((i) => i >= 0),
|
|
40
42
|
childrenRowIndicesByParent: childrenRecord,
|
|
41
43
|
};
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
44
|
+
const siblingsPerRow = buildSiblingIndicesPerRow(section.accounts, parentIndices);
|
|
45
|
+
const hasChildFlags = computeHorizontalHasChildFlags(parentIndices);
|
|
46
|
+
for (let accountIndex = 0; accountIndex < section.accounts.length; accountIndex++) {
|
|
47
|
+
const row = section.accounts[accountIndex];
|
|
48
|
+
// Same column order as `report.classColumns` (see `pivotSectionToHorizontal`).
|
|
49
|
+
const classRowAmounts = row.classBalances.map((classBalance) => classBalance.balance.amount);
|
|
48
50
|
row.accountReportData = {
|
|
49
51
|
accountId: row.accountId,
|
|
50
52
|
accountName: row.accountName,
|
|
51
53
|
accountType: row.accountType,
|
|
52
54
|
depth: row.depth,
|
|
53
55
|
balancesInTimeframe: buildHorizontalClassColumnBalancesSlice(report, classRowAmounts, row.rowTotal.amount),
|
|
54
|
-
horizontalTreeView:
|
|
56
|
+
horizontalTreeView: buildHorizontalTreeViewFromPrecomputed(section.accounts, accountIndex, parentIndices, siblingsPerRow, hasChildFlags),
|
|
55
57
|
};
|
|
56
58
|
}
|
|
57
59
|
}
|
|
@@ -69,23 +71,14 @@ function buildHorizontalClassColumnBalancesSlice(report, classAmounts, totalAmou
|
|
|
69
71
|
const currencyCode = firstHeader?.balance.currencyCode ?? fallback.currencyCode;
|
|
70
72
|
const currencySymbol = firstHeader?.balance.currencySymbol ?? fallback.currencySymbol;
|
|
71
73
|
const timeframe = firstHeader?.balanceTimeFrame ?? 'month';
|
|
72
|
-
const emptySlice =
|
|
73
|
-
balances: [],
|
|
74
|
-
additionalBalances: [],
|
|
75
|
-
filter: {
|
|
76
|
-
firstMonthOfFY: report.firstMonthOfFY,
|
|
77
|
-
timeframe,
|
|
78
|
-
balancesRange: { numberOfPeriods: 0, orderBy: 'ascending_date' },
|
|
79
|
-
additionalBalances: [],
|
|
80
|
-
},
|
|
81
|
-
};
|
|
74
|
+
const emptySlice = (0, horizontalSectionEmptyBalancesSlice_1.getEmptyHorizontalSectionBalancesSlice)(report.firstMonthOfFY, timeframe);
|
|
82
75
|
if (report.selectedPeriod == null) {
|
|
83
76
|
return emptySlice;
|
|
84
77
|
}
|
|
85
78
|
const { startDate, endDate } = report.selectedPeriod;
|
|
86
|
-
const balances = report.classColumns.map((col,
|
|
79
|
+
const balances = report.classColumns.map((col, columnIndex) => ({
|
|
87
80
|
balance: {
|
|
88
|
-
amount: classAmounts[
|
|
81
|
+
amount: classAmounts[columnIndex] ?? 0,
|
|
89
82
|
currencyCode,
|
|
90
83
|
currencySymbol,
|
|
91
84
|
},
|
|
@@ -123,64 +116,143 @@ function buildHorizontalClassColumnBalancesSlice(report, classAmounts, totalAmou
|
|
|
123
116
|
},
|
|
124
117
|
};
|
|
125
118
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
119
|
+
/**
|
|
120
|
+
* Parent index per DFS row — O(n). Matches the scan in the former
|
|
121
|
+
* `findHorizontalAccountParentIndex` (depth <= 2 rows have no parent).
|
|
122
|
+
*/
|
|
123
|
+
function computeHorizontalAccountParentIndices(rows) {
|
|
124
|
+
const accountRowCount = rows.length;
|
|
125
|
+
const parentIndex = new Array(accountRowCount).fill(-1);
|
|
126
|
+
const stack = [];
|
|
127
|
+
for (let rowIndex = 0; rowIndex < accountRowCount; rowIndex++) {
|
|
128
|
+
const accountRow = rows[rowIndex];
|
|
129
|
+
if (accountRow == null) {
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
const depth = accountRow.depth;
|
|
133
|
+
while (stack.length > 0) {
|
|
134
|
+
const topRow = rows[stack[stack.length - 1]];
|
|
135
|
+
if (topRow == null) {
|
|
136
|
+
stack.pop();
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
if (topRow.depth < depth) {
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
stack.pop();
|
|
143
|
+
}
|
|
144
|
+
if (depth > 2 && stack.length > 0) {
|
|
145
|
+
const peek = stack[stack.length - 1];
|
|
146
|
+
const parentRow = rows[peek];
|
|
147
|
+
if (parentRow != null && parentRow.depth === depth - 1) {
|
|
148
|
+
parentIndex[rowIndex] = peek;
|
|
136
149
|
}
|
|
137
|
-
return -1;
|
|
138
150
|
}
|
|
151
|
+
stack.push(rowIndex);
|
|
139
152
|
}
|
|
140
|
-
return
|
|
153
|
+
return parentIndex;
|
|
141
154
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
const
|
|
145
|
-
const
|
|
146
|
-
for (let
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
155
|
+
/** Sibling row indices (same parent + depth), in traversal order — O(n). */
|
|
156
|
+
function buildSiblingIndicesPerRow(rows, parentIndices) {
|
|
157
|
+
const accountRowCount = rows.length;
|
|
158
|
+
const keyToIndices = new Map();
|
|
159
|
+
for (let rowIndex = 0; rowIndex < accountRowCount; rowIndex++) {
|
|
160
|
+
const parentRowIndex = parentIndices[rowIndex];
|
|
161
|
+
const accountRow = rows[rowIndex];
|
|
162
|
+
if (parentRowIndex == null || accountRow == null) {
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
const key = `${parentRowIndex}:${accountRow.depth}`;
|
|
166
|
+
let siblingGroup = keyToIndices.get(key);
|
|
167
|
+
if (siblingGroup == null) {
|
|
168
|
+
siblingGroup = [];
|
|
169
|
+
keyToIndices.set(key, siblingGroup);
|
|
170
|
+
}
|
|
171
|
+
siblingGroup.push(rowIndex);
|
|
172
|
+
}
|
|
173
|
+
const siblingsPerRow = new Array(accountRowCount);
|
|
174
|
+
for (let rowIndex = 0; rowIndex < accountRowCount; rowIndex++) {
|
|
175
|
+
const parentRowIndex = parentIndices[rowIndex];
|
|
176
|
+
const accountRow = rows[rowIndex];
|
|
177
|
+
if (parentRowIndex == null || accountRow == null) {
|
|
178
|
+
siblingsPerRow[rowIndex] = [rowIndex];
|
|
179
|
+
continue;
|
|
151
180
|
}
|
|
181
|
+
const key = `${parentRowIndex}:${accountRow.depth}`;
|
|
182
|
+
const group = keyToIndices.get(key);
|
|
183
|
+
siblingsPerRow[rowIndex] = group != null ? group : [rowIndex];
|
|
152
184
|
}
|
|
153
|
-
return
|
|
185
|
+
return siblingsPerRow;
|
|
154
186
|
}
|
|
155
|
-
function
|
|
156
|
-
|
|
157
|
-
|
|
187
|
+
function computeHorizontalHasChildFlags(parentIndices) {
|
|
188
|
+
const rowCount = parentIndices.length;
|
|
189
|
+
const flags = new Array(rowCount).fill(false);
|
|
190
|
+
for (let rowIndex = 0; rowIndex < rowCount; rowIndex++) {
|
|
191
|
+
const parentRowIndex = parentIndices[rowIndex];
|
|
192
|
+
if (parentRowIndex != null &&
|
|
193
|
+
parentRowIndex >= 0 &&
|
|
194
|
+
parentRowIndex < rowCount) {
|
|
195
|
+
flags[parentRowIndex] = true;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return flags;
|
|
158
199
|
}
|
|
159
|
-
function
|
|
160
|
-
const
|
|
200
|
+
function buildHorizontalSubAccountAncestorMetadataFromPrecomputed(dfsOrderedAccountRows, currentRowIndex, parentIndices, siblingsPerRow, hasChildFlags) {
|
|
201
|
+
const chain = [];
|
|
161
202
|
let walkRowIndex = currentRowIndex;
|
|
162
203
|
while (walkRowIndex >= 0) {
|
|
163
|
-
|
|
164
|
-
|
|
204
|
+
chain.unshift(walkRowIndex);
|
|
205
|
+
const parentRowIndex = parentIndices[walkRowIndex];
|
|
206
|
+
if (parentRowIndex == null) {
|
|
207
|
+
break;
|
|
208
|
+
}
|
|
209
|
+
walkRowIndex = parentRowIndex;
|
|
165
210
|
}
|
|
166
|
-
return
|
|
167
|
-
const
|
|
211
|
+
return chain.map((rowIndex) => {
|
|
212
|
+
const accountRow = dfsOrderedAccountRows[rowIndex];
|
|
213
|
+
const siblingIndices = siblingsPerRow[rowIndex];
|
|
214
|
+
const hasChild = hasChildFlags[rowIndex];
|
|
215
|
+
if (accountRow == null || siblingIndices == null) {
|
|
216
|
+
return {
|
|
217
|
+
depth: 0,
|
|
218
|
+
hasSibling: false,
|
|
219
|
+
hasChildren: hasChild === true,
|
|
220
|
+
isLastNode: true,
|
|
221
|
+
name: '',
|
|
222
|
+
};
|
|
223
|
+
}
|
|
168
224
|
return {
|
|
169
|
-
depth:
|
|
225
|
+
depth: accountRow.depth - 2,
|
|
170
226
|
hasSibling: siblingIndices.length > 1,
|
|
171
|
-
hasChildren:
|
|
227
|
+
hasChildren: hasChild === true,
|
|
172
228
|
isLastNode: rowIndex === siblingIndices[siblingIndices.length - 1],
|
|
173
|
-
name:
|
|
229
|
+
name: accountRow.accountName,
|
|
174
230
|
};
|
|
175
231
|
});
|
|
176
232
|
}
|
|
177
|
-
function
|
|
233
|
+
function buildHorizontalTreeViewFromPrecomputed(dfsOrderedAccountRows, currentRowIndex, parentIndices, siblingsPerRow, hasChildFlags) {
|
|
234
|
+
const siblingIndices = siblingsPerRow[currentRowIndex];
|
|
235
|
+
const accountRow = dfsOrderedAccountRows[currentRowIndex];
|
|
236
|
+
const hasChildrenFlag = hasChildFlags[currentRowIndex];
|
|
237
|
+
const treeDepth = accountRow != null ? Math.max(0, accountRow.depth - 2) : 0;
|
|
238
|
+
const hasChildren = hasChildrenFlag === true;
|
|
239
|
+
const siblings = siblingIndices ?? [currentRowIndex];
|
|
240
|
+
return {
|
|
241
|
+
subAccountAncestorMetadata: buildHorizontalSubAccountAncestorMetadataFromPrecomputed(dfsOrderedAccountRows, currentRowIndex, parentIndices, siblingsPerRow, hasChildFlags),
|
|
242
|
+
treeDepth,
|
|
243
|
+
hasChildren,
|
|
244
|
+
hasSiblings: siblings.length > 1,
|
|
245
|
+
isLeaf: !hasChildren,
|
|
246
|
+
isLastNode: currentRowIndex === siblings[siblings.length - 1],
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
function buildHorizontalAccountChildIndicesByParent(parentIndices) {
|
|
178
250
|
const childIndicesByParentRowIndex = new Map();
|
|
179
|
-
for (let rowIndex = 0; rowIndex <
|
|
180
|
-
const parentRowIndex =
|
|
181
|
-
if (parentRowIndex >= 0) {
|
|
251
|
+
for (let rowIndex = 0; rowIndex < parentIndices.length; rowIndex++) {
|
|
252
|
+
const parentRowIndex = parentIndices[rowIndex];
|
|
253
|
+
if (parentRowIndex != null && parentRowIndex >= 0) {
|
|
182
254
|
let childRowIndices = childIndicesByParentRowIndex.get(parentRowIndex);
|
|
183
|
-
if (childRowIndices
|
|
255
|
+
if (childRowIndices == null) {
|
|
184
256
|
childRowIndices = [];
|
|
185
257
|
childIndicesByParentRowIndex.set(parentRowIndex, childRowIndices);
|
|
186
258
|
}
|
|
@@ -189,16 +261,3 @@ function buildHorizontalAccountChildIndicesByParent(dfsOrderedAccountRows) {
|
|
|
189
261
|
}
|
|
190
262
|
return childIndicesByParentRowIndex;
|
|
191
263
|
}
|
|
192
|
-
function buildHorizontalTreeView(dfsOrderedAccountRows, currentRowIndex) {
|
|
193
|
-
const treeDepth = Math.max(0, dfsOrderedAccountRows[currentRowIndex].depth - 2);
|
|
194
|
-
const siblingIndices = getHorizontalAccountSiblingIndices(dfsOrderedAccountRows, currentRowIndex);
|
|
195
|
-
const hasChildren = horizontalAccountHasChildRows(dfsOrderedAccountRows, currentRowIndex);
|
|
196
|
-
return {
|
|
197
|
-
subAccountAncestorMetadata: buildHorizontalSubAccountAncestorMetadata(dfsOrderedAccountRows, currentRowIndex),
|
|
198
|
-
treeDepth,
|
|
199
|
-
hasChildren,
|
|
200
|
-
hasSiblings: siblingIndices.length > 1,
|
|
201
|
-
isLeaf: !hasChildren,
|
|
202
|
-
isLastNode: currentRowIndex === siblingIndices[siblingIndices.length - 1],
|
|
203
|
-
};
|
|
204
|
-
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zeniai/client-epic-state",
|
|
3
|
-
"version": "4.20.
|
|
3
|
+
"version": "4.20.1-beta11ND",
|
|
4
4
|
"description": "Shared module between Web & Mobile containing required abstractions for state management, async network communication. ",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"module": "lib/esm/index.js",
|