@zeniai/client-epic-state 4.19.99-betaSS3 → 4.19.99-betaVR3
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/esm/view/expenseAutomationView/epics/missingReceipts/bulkUploadReceiptsEpic.js +2 -2
- package/lib/esm/view/expenseAutomationView/epics/missingReceipts/watchBulkUploadBatchStatusEpic.js +4 -3
- package/lib/esm/view/profitAndLossClassesView/profitAndLossClassesByClassHorizontalSelector.js +3 -8
- package/lib/esm/view/profitAndLossClassesView/profitAndLossHorizontalEnrichment.js +140 -59
- package/lib/tsconfig.typecheck.tsbuildinfo +1 -0
- package/lib/view/expenseAutomationView/epics/missingReceipts/bulkUploadReceiptsEpic.js +1 -1
- package/lib/view/expenseAutomationView/epics/missingReceipts/watchBulkUploadBatchStatusEpic.js +4 -3
- package/lib/view/profitAndLossClassesView/profitAndLossClassesByClassHorizontalSelector.js +3 -8
- package/lib/view/profitAndLossClassesView/profitAndLossHorizontalEnrichment.js +140 -59
- package/package.json +1 -1
|
@@ -6,7 +6,7 @@ const operators_1 = require("rxjs/operators");
|
|
|
6
6
|
const responsePayload_1 = require("../../../../responsePayload");
|
|
7
7
|
const bulkUploadTiming_1 = require("../../bulkUploadTiming");
|
|
8
8
|
const missingReceiptsViewReducer_1 = require("../../reducers/missingReceiptsViewReducer");
|
|
9
|
-
const bulkUploadReceiptsEpic = (actions$, _state$, zeniAPI) => actions$.pipe((0, operators_1.filter)(missingReceiptsViewReducer_1.bulkUploadReceipts.match), (0, operators_1.
|
|
9
|
+
const bulkUploadReceiptsEpic = (actions$, _state$, zeniAPI) => actions$.pipe((0, operators_1.filter)(missingReceiptsViewReducer_1.bulkUploadReceipts.match), (0, operators_1.switchMap)((action) => {
|
|
10
10
|
const { files } = action.payload;
|
|
11
11
|
const formData = new FormData();
|
|
12
12
|
for (const file of files) {
|
package/lib/view/expenseAutomationView/epics/missingReceipts/watchBulkUploadBatchStatusEpic.js
CHANGED
|
@@ -29,7 +29,7 @@ exports.pusherBatchStatusCompletionEpic = pusherBatchStatusCompletionEpic;
|
|
|
29
29
|
* force `phase` to `completed` (dismiss automatching banner) and refresh the batch list.
|
|
30
30
|
* Cancels when the batch completes normally, bulk state is cleared, or another upload supersedes.
|
|
31
31
|
*/
|
|
32
|
-
const bulkUploadAutomatchingTimeoutEpic = (actions$, state$) => actions$.pipe((0, operators_1.filter)(missingReceiptsViewReducer_1.bulkUploadReceiptsSuccess.match), (0, operators_1.
|
|
32
|
+
const bulkUploadAutomatchingTimeoutEpic = (actions$, state$) => actions$.pipe((0, operators_1.filter)(missingReceiptsViewReducer_1.bulkUploadReceiptsSuccess.match), (0, operators_1.switchMap)((action) => {
|
|
33
33
|
const { batchId } = action.payload;
|
|
34
34
|
return (0, rxjs_1.timer)(bulkUploadTiming_1.BULK_UPLOAD_AUTOMATCHING_TIMEOUT_MS).pipe((0, operators_1.mergeMap)(() => {
|
|
35
35
|
const bulk = state$.value.expenseAutomationMissingReceiptsViewState.bulkUpload;
|
|
@@ -49,10 +49,11 @@ exports.bulkUploadAutomatchingTimeoutEpic = bulkUploadAutomatchingTimeoutEpic;
|
|
|
49
49
|
* error snackbar per upload session (first failure only) so the user is not spammed every
|
|
50
50
|
* interval; Pusher or a later successful poll still completes the flow.
|
|
51
51
|
*/
|
|
52
|
-
const pollBulkUploadBatchStatusEpic = (actions$, _state$, zeniAPI) => actions$.pipe((0, operators_1.filter)(missingReceiptsViewReducer_1.bulkUploadReceiptsSuccess.match), (0, operators_1.
|
|
52
|
+
const pollBulkUploadBatchStatusEpic = (actions$, _state$, zeniAPI) => actions$.pipe((0, operators_1.filter)(missingReceiptsViewReducer_1.bulkUploadReceiptsSuccess.match), (0, operators_1.switchMap)((action) => {
|
|
53
53
|
const { batchId } = action.payload;
|
|
54
54
|
let pollErrorNotified = false;
|
|
55
|
-
return (0, rxjs_1.timer)(FALLBACK_FIRST_DELAY_MS, FALLBACK_POLL_INTERVAL_MS).pipe((0, operators_1.takeUntil)((0, rxjs_1.merge)(actions$.pipe((0, operators_1.filter)(missingReceiptsViewReducer_1.clearBulkUpload.match)), actions$.pipe((0, operators_1.filter)(missingReceiptsViewReducer_1.pusherBatchStatusUpdate.match), (0, operators_1.filter)((action) => action.payload.batchId === batchId
|
|
55
|
+
return (0, rxjs_1.timer)(FALLBACK_FIRST_DELAY_MS, FALLBACK_POLL_INTERVAL_MS).pipe((0, operators_1.takeUntil)((0, rxjs_1.merge)(actions$.pipe((0, operators_1.filter)(missingReceiptsViewReducer_1.clearBulkUpload.match)), actions$.pipe((0, operators_1.filter)(missingReceiptsViewReducer_1.pusherBatchStatusUpdate.match), (0, operators_1.filter)((action) => action.payload.batchId === batchId &&
|
|
56
|
+
action.payload.status === 'completed')), actions$.pipe((0, operators_1.filter)(missingReceiptsViewReducer_1.fetchBulkUploadBatchDetailsSuccess.match), (0, operators_1.filter)((action) => action.payload.batchId === batchId)), actions$.pipe((0, operators_1.filter)(missingReceiptsViewReducer_1.bulkUploadAutomatchingTimedOut.match), (0, operators_1.filter)((action) => action.payload.batchId === batchId)))), (0, operators_1.switchMap)(() => zeniAPI
|
|
56
57
|
.getJSON(`${zeniAPI.apiEndPoints.communicationAgentMicroServiceBaseUrl}/1.0/batches/${batchId}`)
|
|
57
58
|
.pipe((0, operators_1.mergeMap)((response) => {
|
|
58
59
|
/** Same batch-details URL; polls until `status` is completed (Pusher may win first). */
|
|
@@ -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
|
/**
|
|
@@ -28,30 +28,31 @@ function enrichHorizontalSection(report, section) {
|
|
|
28
28
|
return total?.balance.amount ?? 0;
|
|
29
29
|
});
|
|
30
30
|
section.sectionHeaderBalancesSlice = buildHorizontalClassColumnBalancesSlice(report, classAmounts, section.sectionTotal.amount);
|
|
31
|
-
const
|
|
31
|
+
const parentIndices = computeHorizontalAccountParentIndices(section.accounts);
|
|
32
|
+
const childrenRowIndicesByParent = buildHorizontalAccountChildIndicesByParent(parentIndices);
|
|
32
33
|
const childrenRecord = {};
|
|
33
34
|
childrenRowIndicesByParent.forEach((childIndices, parentIndex) => {
|
|
34
35
|
childrenRecord[parentIndex] = childIndices;
|
|
35
36
|
});
|
|
36
37
|
section.treeLayout = {
|
|
37
38
|
rootAccountRowIndices: section.accounts
|
|
38
|
-
.map((_, i) =>
|
|
39
|
+
.map((_, i) => (parentIndices[i] === -1 ? i : -1))
|
|
39
40
|
.filter((i) => i >= 0),
|
|
40
41
|
childrenRowIndicesByParent: childrenRecord,
|
|
41
42
|
};
|
|
43
|
+
const siblingsPerRow = buildSiblingIndicesPerRow(section.accounts, parentIndices);
|
|
44
|
+
const hasChildFlags = computeHorizontalHasChildFlags(parentIndices);
|
|
42
45
|
for (let i = 0; i < section.accounts.length; i++) {
|
|
43
46
|
const row = section.accounts[i];
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
return cb?.balance.amount ?? 0;
|
|
47
|
-
});
|
|
47
|
+
// Same column order as `report.classColumns` (see `pivotSectionToHorizontal`).
|
|
48
|
+
const classRowAmounts = row.classBalances.map((b) => b.balance.amount);
|
|
48
49
|
row.accountReportData = {
|
|
49
50
|
accountId: row.accountId,
|
|
50
51
|
accountName: row.accountName,
|
|
51
52
|
accountType: row.accountType,
|
|
52
53
|
depth: row.depth,
|
|
53
54
|
balancesInTimeframe: buildHorizontalClassColumnBalancesSlice(report, classRowAmounts, row.rowTotal.amount),
|
|
54
|
-
horizontalTreeView:
|
|
55
|
+
horizontalTreeView: buildHorizontalTreeViewFromPrecomputed(section.accounts, i, parentIndices, siblingsPerRow, hasChildFlags),
|
|
55
56
|
};
|
|
56
57
|
}
|
|
57
58
|
}
|
|
@@ -123,64 +124,157 @@ function buildHorizontalClassColumnBalancesSlice(report, classAmounts, totalAmou
|
|
|
123
124
|
},
|
|
124
125
|
};
|
|
125
126
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
127
|
+
/**
|
|
128
|
+
* Parent index per DFS row — O(n). Matches the scan in the former
|
|
129
|
+
* `findHorizontalAccountParentIndex` (depth <= 2 rows have no parent).
|
|
130
|
+
*/
|
|
131
|
+
function computeHorizontalAccountParentIndices(rows) {
|
|
132
|
+
const accountRowCount = rows.length;
|
|
133
|
+
const parentIndex = new Array(accountRowCount).fill(-1);
|
|
134
|
+
const stack = [];
|
|
135
|
+
for (let rowIndex = 0; rowIndex < accountRowCount; rowIndex++) {
|
|
136
|
+
const accountRow = rows[rowIndex];
|
|
137
|
+
if (accountRow == null) {
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
const d = accountRow.depth;
|
|
141
|
+
while (stack.length > 0) {
|
|
142
|
+
const topIdx = stack[stack.length - 1];
|
|
143
|
+
if (topIdx == null) {
|
|
144
|
+
break;
|
|
136
145
|
}
|
|
137
|
-
|
|
146
|
+
const topRow = rows[topIdx];
|
|
147
|
+
if (topRow == null) {
|
|
148
|
+
stack.pop();
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
if (topRow.depth < d) {
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
stack.pop();
|
|
155
|
+
}
|
|
156
|
+
if (d <= 2) {
|
|
157
|
+
parentIndex[rowIndex] = -1;
|
|
158
|
+
}
|
|
159
|
+
else if (stack.length === 0) {
|
|
160
|
+
parentIndex[rowIndex] = -1;
|
|
138
161
|
}
|
|
162
|
+
else {
|
|
163
|
+
const peek = stack[stack.length - 1];
|
|
164
|
+
if (peek == null) {
|
|
165
|
+
parentIndex[rowIndex] = -1;
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
const parentRow = rows[peek];
|
|
169
|
+
parentIndex[rowIndex] =
|
|
170
|
+
parentRow != null && parentRow.depth === d - 1 ? peek : -1;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
stack.push(rowIndex);
|
|
139
174
|
}
|
|
140
|
-
return
|
|
175
|
+
return parentIndex;
|
|
141
176
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
const
|
|
145
|
-
const
|
|
146
|
-
for (let
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
177
|
+
/** Sibling row indices (same parent + depth), in traversal order — O(n). */
|
|
178
|
+
function buildSiblingIndicesPerRow(rows, parentIndices) {
|
|
179
|
+
const accountRowCount = rows.length;
|
|
180
|
+
const keyToIndices = new Map();
|
|
181
|
+
for (let rowIndex = 0; rowIndex < accountRowCount; rowIndex++) {
|
|
182
|
+
const parentRowIndex = parentIndices[rowIndex];
|
|
183
|
+
const accountRow = rows[rowIndex];
|
|
184
|
+
if (parentRowIndex == null || accountRow == null) {
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
const key = `${parentRowIndex}:${accountRow.depth}`;
|
|
188
|
+
let g = keyToIndices.get(key);
|
|
189
|
+
if (g == null) {
|
|
190
|
+
g = [];
|
|
191
|
+
keyToIndices.set(key, g);
|
|
151
192
|
}
|
|
193
|
+
g.push(rowIndex);
|
|
152
194
|
}
|
|
153
|
-
|
|
195
|
+
const siblingsPerRow = new Array(accountRowCount);
|
|
196
|
+
for (let rowIndex = 0; rowIndex < accountRowCount; rowIndex++) {
|
|
197
|
+
const parentRowIndex = parentIndices[rowIndex];
|
|
198
|
+
const accountRow = rows[rowIndex];
|
|
199
|
+
if (parentRowIndex == null || accountRow == null) {
|
|
200
|
+
siblingsPerRow[rowIndex] = [rowIndex];
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
const key = `${parentRowIndex}:${accountRow.depth}`;
|
|
204
|
+
const group = keyToIndices.get(key);
|
|
205
|
+
siblingsPerRow[rowIndex] = group != null ? group : [rowIndex];
|
|
206
|
+
}
|
|
207
|
+
return siblingsPerRow;
|
|
154
208
|
}
|
|
155
|
-
function
|
|
156
|
-
|
|
157
|
-
|
|
209
|
+
function computeHorizontalHasChildFlags(parentIndices) {
|
|
210
|
+
const rowCount = parentIndices.length;
|
|
211
|
+
const flags = new Array(rowCount).fill(false);
|
|
212
|
+
for (let rowIndex = 0; rowIndex < rowCount; rowIndex++) {
|
|
213
|
+
const parentRowIndex = parentIndices[rowIndex];
|
|
214
|
+
if (parentRowIndex != null &&
|
|
215
|
+
parentRowIndex >= 0 &&
|
|
216
|
+
parentRowIndex < rowCount) {
|
|
217
|
+
flags[parentRowIndex] = true;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return flags;
|
|
158
221
|
}
|
|
159
|
-
function
|
|
160
|
-
const
|
|
222
|
+
function buildHorizontalSubAccountAncestorMetadataFromPrecomputed(dfsOrderedAccountRows, currentRowIndex, parentIndices, siblingsPerRow, hasChildFlags) {
|
|
223
|
+
const chain = [];
|
|
161
224
|
let walkRowIndex = currentRowIndex;
|
|
162
225
|
while (walkRowIndex >= 0) {
|
|
163
|
-
|
|
164
|
-
|
|
226
|
+
chain.unshift(walkRowIndex);
|
|
227
|
+
const parentRowIndex = parentIndices[walkRowIndex];
|
|
228
|
+
if (parentRowIndex == null) {
|
|
229
|
+
break;
|
|
230
|
+
}
|
|
231
|
+
walkRowIndex = parentRowIndex;
|
|
165
232
|
}
|
|
166
|
-
return
|
|
167
|
-
const
|
|
233
|
+
return chain.map((rowIndex) => {
|
|
234
|
+
const accountRow = dfsOrderedAccountRows[rowIndex];
|
|
235
|
+
const siblingIndices = siblingsPerRow[rowIndex];
|
|
236
|
+
const hasChild = hasChildFlags[rowIndex];
|
|
237
|
+
if (accountRow == null || siblingIndices == null) {
|
|
238
|
+
return {
|
|
239
|
+
depth: 0,
|
|
240
|
+
hasSibling: false,
|
|
241
|
+
hasChildren: hasChild === true,
|
|
242
|
+
isLastNode: true,
|
|
243
|
+
name: '',
|
|
244
|
+
};
|
|
245
|
+
}
|
|
168
246
|
return {
|
|
169
|
-
depth:
|
|
247
|
+
depth: accountRow.depth - 2,
|
|
170
248
|
hasSibling: siblingIndices.length > 1,
|
|
171
|
-
hasChildren:
|
|
249
|
+
hasChildren: hasChild === true,
|
|
172
250
|
isLastNode: rowIndex === siblingIndices[siblingIndices.length - 1],
|
|
173
|
-
name:
|
|
251
|
+
name: accountRow.accountName,
|
|
174
252
|
};
|
|
175
253
|
});
|
|
176
254
|
}
|
|
177
|
-
function
|
|
255
|
+
function buildHorizontalTreeViewFromPrecomputed(dfsOrderedAccountRows, currentRowIndex, parentIndices, siblingsPerRow, hasChildFlags) {
|
|
256
|
+
const siblingIndices = siblingsPerRow[currentRowIndex];
|
|
257
|
+
const accountRow = dfsOrderedAccountRows[currentRowIndex];
|
|
258
|
+
const hasChildrenFlag = hasChildFlags[currentRowIndex];
|
|
259
|
+
const treeDepth = accountRow != null ? Math.max(0, accountRow.depth - 2) : 0;
|
|
260
|
+
const hasChildren = hasChildrenFlag === true;
|
|
261
|
+
const sibs = siblingIndices ?? [currentRowIndex];
|
|
262
|
+
return {
|
|
263
|
+
subAccountAncestorMetadata: buildHorizontalSubAccountAncestorMetadataFromPrecomputed(dfsOrderedAccountRows, currentRowIndex, parentIndices, siblingsPerRow, hasChildFlags),
|
|
264
|
+
treeDepth,
|
|
265
|
+
hasChildren,
|
|
266
|
+
hasSiblings: sibs.length > 1,
|
|
267
|
+
isLeaf: !hasChildren,
|
|
268
|
+
isLastNode: currentRowIndex === sibs[sibs.length - 1],
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
function buildHorizontalAccountChildIndicesByParent(parentIndices) {
|
|
178
272
|
const childIndicesByParentRowIndex = new Map();
|
|
179
|
-
for (let rowIndex = 0; rowIndex <
|
|
180
|
-
const parentRowIndex =
|
|
181
|
-
if (parentRowIndex >= 0) {
|
|
273
|
+
for (let rowIndex = 0; rowIndex < parentIndices.length; rowIndex++) {
|
|
274
|
+
const parentRowIndex = parentIndices[rowIndex];
|
|
275
|
+
if (parentRowIndex != null && parentRowIndex >= 0) {
|
|
182
276
|
let childRowIndices = childIndicesByParentRowIndex.get(parentRowIndex);
|
|
183
|
-
if (childRowIndices
|
|
277
|
+
if (childRowIndices == null) {
|
|
184
278
|
childRowIndices = [];
|
|
185
279
|
childIndicesByParentRowIndex.set(parentRowIndex, childRowIndices);
|
|
186
280
|
}
|
|
@@ -189,16 +283,3 @@ function buildHorizontalAccountChildIndicesByParent(dfsOrderedAccountRows) {
|
|
|
189
283
|
}
|
|
190
284
|
return childIndicesByParentRowIndex;
|
|
191
285
|
}
|
|
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.19.99-
|
|
3
|
+
"version": "4.19.99-betaVR3",
|
|
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",
|