@zeniai/client-epic-state 4.19.84-betaVR16 → 4.19.84-betaVR2
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/epic.js +1 -1
- package/lib/esm/epic.js +2 -2
- package/lib/esm/index.js +3 -7
- package/lib/esm/view/expenseAutomationView/epics/missingReceipts/bulkUploadReceiptsEpic.js +5 -10
- package/lib/esm/view/expenseAutomationView/epics/missingReceipts/fetchMoreBatchDetailsEpic.js +1 -1
- package/lib/esm/view/expenseAutomationView/epics/missingReceipts/fetchMultipleBatchDetailsEpic.js +1 -1
- package/lib/esm/view/expenseAutomationView/epics/missingReceipts/searchTransactionsForManualMatchEpic.js +4 -14
- package/lib/esm/view/expenseAutomationView/epics/missingReceipts/watchBulkUploadBatchStatusEpic.js +3 -43
- package/lib/esm/view/expenseAutomationView/payload/missingReceiptsPayload.js +10 -52
- package/lib/esm/view/expenseAutomationView/reducers/missingReceiptsViewReducer.js +22 -54
- package/lib/esm/view/expenseAutomationView/selectors/missingReceiptsSelector.js +35 -57
- package/lib/esm/view/expenseAutomationView/types/missingReceiptsViewState.js +0 -39
- package/lib/index.d.ts +3 -4
- package/lib/index.js +33 -48
- package/lib/view/expenseAutomationView/epics/missingReceipts/bulkUploadReceiptsEpic.d.ts +0 -3
- package/lib/view/expenseAutomationView/epics/missingReceipts/bulkUploadReceiptsEpic.js +3 -8
- package/lib/view/expenseAutomationView/epics/missingReceipts/fetchMoreBatchDetailsEpic.js +1 -1
- package/lib/view/expenseAutomationView/epics/missingReceipts/fetchMultipleBatchDetailsEpic.js +1 -1
- package/lib/view/expenseAutomationView/epics/missingReceipts/searchTransactionsForManualMatchEpic.js +3 -13
- package/lib/view/expenseAutomationView/epics/missingReceipts/watchBulkUploadBatchStatusEpic.d.ts +2 -14
- package/lib/view/expenseAutomationView/epics/missingReceipts/watchBulkUploadBatchStatusEpic.js +3 -44
- package/lib/view/expenseAutomationView/payload/missingReceiptsPayload.d.ts +1 -12
- package/lib/view/expenseAutomationView/payload/missingReceiptsPayload.js +10 -53
- package/lib/view/expenseAutomationView/reducers/missingReceiptsViewReducer.d.ts +5 -11
- package/lib/view/expenseAutomationView/reducers/missingReceiptsViewReducer.js +22 -54
- package/lib/view/expenseAutomationView/selectors/missingReceiptsSelector.js +35 -57
- package/lib/view/expenseAutomationView/types/missingReceiptsViewState.d.ts +9 -27
- package/lib/view/expenseAutomationView/types/missingReceiptsViewState.js +1 -49
- package/package.json +1 -1
- package/lib/esm/view/expenseAutomationView/bulkUploadTiming.js +0 -10
- package/lib/view/expenseAutomationView/bulkUploadTiming.d.ts +0 -10
- package/lib/view/expenseAutomationView/bulkUploadTiming.js +0 -13
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createSlice } from '@reduxjs/toolkit';
|
|
2
2
|
import { toMonthYearPeriodId } from '../../../commonStateTypes/timePeriod';
|
|
3
|
-
import { MIN_MANUAL_TRANSACTION_SEARCH_LENGTH, } from '../types/missingReceiptsViewState';
|
|
4
3
|
export const getCompletedTransactionsCacheKey = (periodId, sortKey, sortOrder, subTab) => `completed-${subTab}-${sortKey}-${sortOrder === 'ascending' ? 'asc' : 'desc'}-${periodId}`;
|
|
5
4
|
export const initialBulkUploadState = {
|
|
6
5
|
batchDetailsById: {},
|
|
@@ -51,8 +50,6 @@ export const initialState = {
|
|
|
51
50
|
return this.fetchState == 'Completed';
|
|
52
51
|
},
|
|
53
52
|
};
|
|
54
|
-
/** Epic trigger only; batch-details fetch is handled in epics. */
|
|
55
|
-
export const fetchBulkUploadBatchDetails = createAction('expenseAutomationMissingReceiptsView/fetchBulkUploadBatchDetails');
|
|
56
53
|
const expenseAutomationMissingReceiptsView = createSlice({
|
|
57
54
|
name: 'expenseAutomationMissingReceiptsView',
|
|
58
55
|
initialState,
|
|
@@ -156,7 +153,8 @@ const expenseAutomationMissingReceiptsView = createSlice({
|
|
|
156
153
|
},
|
|
157
154
|
// -- Bulk Upload Actions --
|
|
158
155
|
bulkUploadReceipts: {
|
|
159
|
-
|
|
156
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
157
|
+
reducer(draft, _action) {
|
|
160
158
|
draft.bulkUpload.phase = 'uploading';
|
|
161
159
|
draft.bulkUpload.uploadProgress = 0;
|
|
162
160
|
draft.bulkUpload.uploadStatus = {
|
|
@@ -173,7 +171,6 @@ const expenseAutomationMissingReceiptsView = createSlice({
|
|
|
173
171
|
},
|
|
174
172
|
bulkUploadReceiptsSuccess(draft, action) {
|
|
175
173
|
draft.bulkUpload.currentBatchId = action.payload.batchId;
|
|
176
|
-
draft.bulkUpload.uploadProgress = 100;
|
|
177
174
|
draft.bulkUpload.uploadStatus = {
|
|
178
175
|
fetchState: 'Completed',
|
|
179
176
|
error: undefined,
|
|
@@ -187,18 +184,6 @@ const expenseAutomationMissingReceiptsView = createSlice({
|
|
|
187
184
|
};
|
|
188
185
|
draft.bulkUpload.phase = 'idle';
|
|
189
186
|
},
|
|
190
|
-
bulkUploadAutomatchingTimedOut: {
|
|
191
|
-
reducer(draft, action) {
|
|
192
|
-
if (draft.bulkUpload.phase !== 'matching' ||
|
|
193
|
-
draft.bulkUpload.currentBatchId !== action.payload.batchId) {
|
|
194
|
-
return;
|
|
195
|
-
}
|
|
196
|
-
draft.bulkUpload.phase = 'completed';
|
|
197
|
-
},
|
|
198
|
-
prepare(batchId) {
|
|
199
|
-
return { payload: { batchId } };
|
|
200
|
-
},
|
|
201
|
-
},
|
|
202
187
|
pusherBatchStatusUpdate(draft, action) {
|
|
203
188
|
const batchStatus = action.payload;
|
|
204
189
|
draft.bulkUpload.batchStatusById[batchStatus.batchId] = batchStatus;
|
|
@@ -219,18 +204,18 @@ const expenseAutomationMissingReceiptsView = createSlice({
|
|
|
219
204
|
clearMissingReceiptsTabNavigation(draft) {
|
|
220
205
|
draft.pendingMissingReceiptsTabNavigation = null;
|
|
221
206
|
},
|
|
207
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
208
|
+
fetchBulkUploadBatchDetails(_draft) {
|
|
209
|
+
// trigger epic, no state change needed
|
|
210
|
+
},
|
|
222
211
|
fetchBulkUploadBatchDetailsSuccess(draft, action) {
|
|
223
212
|
const details = action.payload;
|
|
224
213
|
draft.bulkUpload.batchDetailsById[details.batchId] = details;
|
|
225
214
|
draft.bulkUpload.phase = 'completed';
|
|
226
215
|
},
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
},
|
|
231
|
-
prepare(payload) {
|
|
232
|
-
return { payload };
|
|
233
|
-
},
|
|
216
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
217
|
+
fetchBulkUploadBatchDetailsFailure(draft, _action) {
|
|
218
|
+
draft.bulkUpload.phase = 'completed';
|
|
234
219
|
},
|
|
235
220
|
storeBatchDetails(draft, action) {
|
|
236
221
|
const details = action.payload;
|
|
@@ -261,8 +246,8 @@ const expenseAutomationMissingReceiptsView = createSlice({
|
|
|
261
246
|
error: undefined,
|
|
262
247
|
};
|
|
263
248
|
},
|
|
264
|
-
fetchMoreBatchDetails(
|
|
265
|
-
|
|
249
|
+
fetchMoreBatchDetails(_draft) {
|
|
250
|
+
_draft.bulkUpload.batchDetailsPaginationState = {
|
|
266
251
|
fetchState: 'In-Progress',
|
|
267
252
|
error: undefined,
|
|
268
253
|
};
|
|
@@ -300,11 +285,11 @@ const expenseAutomationMissingReceiptsView = createSlice({
|
|
|
300
285
|
};
|
|
301
286
|
},
|
|
302
287
|
prepare(input) {
|
|
303
|
-
const
|
|
288
|
+
const o = input ?? {};
|
|
304
289
|
return {
|
|
305
290
|
payload: {
|
|
306
|
-
cacheOverride:
|
|
307
|
-
invalidateBatchDetailsCache:
|
|
291
|
+
cacheOverride: o.cacheOverride === true,
|
|
292
|
+
invalidateBatchDetailsCache: o.invalidateBatchDetailsCache === true,
|
|
308
293
|
},
|
|
309
294
|
};
|
|
310
295
|
},
|
|
@@ -338,7 +323,8 @@ const expenseAutomationMissingReceiptsView = createSlice({
|
|
|
338
323
|
};
|
|
339
324
|
},
|
|
340
325
|
confirmBulkUploadMatch: {
|
|
341
|
-
|
|
326
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
327
|
+
reducer(draft, _action) {
|
|
342
328
|
draft.bulkUpload.confirmMatchStatus = {
|
|
343
329
|
fetchState: 'In-Progress',
|
|
344
330
|
error: undefined,
|
|
@@ -356,17 +342,17 @@ const expenseAutomationMissingReceiptsView = createSlice({
|
|
|
356
342
|
};
|
|
357
343
|
const details = draft.bulkUpload.batchDetailsById[batchId];
|
|
358
344
|
if (details != null) {
|
|
359
|
-
const file = details.files.find((
|
|
345
|
+
const file = details.files.find((f) => f.attachmentId === attachmentId);
|
|
360
346
|
if (file != null) {
|
|
361
347
|
const originalStatus = file.status;
|
|
362
|
-
file.status = '
|
|
348
|
+
file.status = 'matched';
|
|
363
349
|
file.matchedTransactionId = transactionId;
|
|
364
350
|
file.candidates = undefined;
|
|
365
351
|
details.summary.matched += 1;
|
|
366
352
|
if (originalStatus === 'no_match') {
|
|
367
353
|
details.summary.noMatch = Math.max(0, details.summary.noMatch - 1);
|
|
368
354
|
}
|
|
369
|
-
else
|
|
355
|
+
else {
|
|
370
356
|
details.summary.possibleMatches = Math.max(0, details.summary.possibleMatches - 1);
|
|
371
357
|
}
|
|
372
358
|
}
|
|
@@ -398,24 +384,6 @@ const expenseAutomationMissingReceiptsView = createSlice({
|
|
|
398
384
|
reducer(draft, action) {
|
|
399
385
|
const { query, pageToken } = action.payload;
|
|
400
386
|
const isLoadMore = pageToken != null;
|
|
401
|
-
const trimmed = query.trim();
|
|
402
|
-
/**
|
|
403
|
-
* Cleared or too-short query: no API (see epic). Reset fully (no In-Progress).
|
|
404
|
-
* Only bump `manualSearchUiResetKey` when clearing a real search — not when the UI
|
|
405
|
-
* dispatches `""` on every keystroke while length is below MIN (web-components always sends
|
|
406
|
-
* `onSearch("")` in that case), or ReceiptCard remounts and the input loses focus.
|
|
407
|
-
*/
|
|
408
|
-
if (!isLoadMore &&
|
|
409
|
-
trimmed.length < MIN_MANUAL_TRANSACTION_SEARCH_LENGTH) {
|
|
410
|
-
const hadActiveSearch = draft.bulkUpload.manualSearch.searchQuery.trim().length >=
|
|
411
|
-
MIN_MANUAL_TRANSACTION_SEARCH_LENGTH ||
|
|
412
|
-
draft.bulkUpload.manualSearch.results.length > 0;
|
|
413
|
-
draft.bulkUpload.manualSearch = initialBulkUploadState.manualSearch;
|
|
414
|
-
if (hadActiveSearch) {
|
|
415
|
-
draft.bulkUpload.manualSearchUiResetKey += 1;
|
|
416
|
-
}
|
|
417
|
-
return;
|
|
418
|
-
}
|
|
419
387
|
draft.bulkUpload.manualSearch.searchQuery = query;
|
|
420
388
|
if (isLoadMore) {
|
|
421
389
|
draft.bulkUpload.manualSearch.isLoadingMore = true;
|
|
@@ -439,7 +407,7 @@ const expenseAutomationMissingReceiptsView = createSlice({
|
|
|
439
407
|
const { append, nextPageToken, results, totalCount } = action.payload;
|
|
440
408
|
if (append) {
|
|
441
409
|
const merged = [...draft.bulkUpload.manualSearch.results, ...results];
|
|
442
|
-
const byId = new Map(merged.map((
|
|
410
|
+
const byId = new Map(merged.map((r) => [r.transactionId, r]));
|
|
443
411
|
draft.bulkUpload.manualSearch.results = Array.from(byId.values());
|
|
444
412
|
}
|
|
445
413
|
else {
|
|
@@ -544,7 +512,7 @@ const expenseAutomationMissingReceiptsView = createSlice({
|
|
|
544
512
|
});
|
|
545
513
|
export const { fetchMissingReceipts, fetchMissingReceiptsSuccess, fetchMissingReceiptsFailure, uploadMissingReceiptSuccess, updateMissingReceiptUploadState, updateMissingReceiptsUIState, markMissingReceiptAsDone, clearExpenseAutomationMissingReceiptsView,
|
|
546
514
|
// Bulk Upload
|
|
547
|
-
bulkUploadReceipts, updateBulkUploadProgress, bulkUploadReceiptsSuccess, bulkUploadReceiptsFailure,
|
|
515
|
+
bulkUploadReceipts, updateBulkUploadProgress, bulkUploadReceiptsSuccess, bulkUploadReceiptsFailure, pusherBatchStatusUpdate, requestMissingReceiptsTabNavigation, clearMissingReceiptsTabNavigation, fetchBulkUploadBatchDetails, fetchBulkUploadBatchDetailsSuccess, fetchBulkUploadBatchDetailsFailure, storeBatchDetails, batchDetailFetchFailed, setInitialBatchDetailsLoading, fetchMoreBatchDetails, fetchMoreBatchDetailsComplete, fetchMoreBatchDetailsFailure, fetchBulkUploadBatches, fetchBulkUploadBatchesSuccess, fetchBulkUploadBatchesFailure, confirmBulkUploadMatch, confirmBulkUploadMatchSuccess, confirmBulkUploadMatchFailure, setBulkUploadResultsTab, setBulkUploadCompletedSubTab, setBulkUploadSortConfig, clearBulkUpload, searchTransactionsForManualMatch, searchTransactionsForManualMatchSuccess, searchTransactionsForManualMatchFailure, clearManualSearchResults, acknowledgeBulkUploadConfirmMatchComplete,
|
|
548
516
|
// Completed Transactions
|
|
549
517
|
fetchCompletedTransactions, fetchCompletedTransactionsSuccess, fetchCompletedTransactionsFailure, } = expenseAutomationMissingReceiptsView.actions;
|
|
550
518
|
export default expenseAutomationMissingReceiptsView.reducer;
|
|
@@ -5,7 +5,6 @@ import { getSupportedTransactionById, getSupportedTransactionsByIds, } from '../
|
|
|
5
5
|
import { getAllSteps } from '../selectorTypes/expenseAutomationViewSelectorTypes';
|
|
6
6
|
import { isBatchListStatusCompleted } from '../payload/missingReceiptsPayload';
|
|
7
7
|
import { getCompletedTransactionsCacheKey } from '../reducers/missingReceiptsViewReducer';
|
|
8
|
-
import { isUnmatchedTabFileStatus, } from '../types/missingReceiptsViewState';
|
|
9
8
|
export function getExpenseAutomationMissingReceiptsView(state) {
|
|
10
9
|
const { expenseAutomationMissingReceiptsViewState, expenseAutomationViewState, transactionState, monthEndCloseChecksState, } = state;
|
|
11
10
|
const currentTenant = getCurrentTenant(state);
|
|
@@ -59,71 +58,50 @@ export function getExpenseAutomationMissingReceiptsView(state) {
|
|
|
59
58
|
: [];
|
|
60
59
|
/**
|
|
61
60
|
* Primary "Unmatched" batch = first completed batch in platform list order, preferring the
|
|
62
|
-
* first batch that still has
|
|
61
|
+
* first batch that still has un_matched/no_match work when any completed batch has such work.
|
|
63
62
|
* Batches without details yet: wait if the list row still reports unmatched work; otherwise skip
|
|
64
63
|
* so we do not block the whole tab while other rows are not fetched yet.
|
|
65
64
|
*/
|
|
66
65
|
const primaryBatchIdFromDetailsOrder = getPrimaryBatchIdFromBatchListOrder(batchList, bulkUpload.batchDetailsById, bulkUpload.failedBatchDetailIds);
|
|
67
|
-
/**
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
* upload), details are often not loaded yet — `unmatchedFiles` would empty while older batches
|
|
73
|
-
* moved to "past", so the primary "Unmatched Receipts" section vanished during auto-matching.
|
|
74
|
-
*/
|
|
75
|
-
const unmatchedSectionBatchId = (() => {
|
|
76
|
-
const phase = bulkUpload.phase;
|
|
77
|
-
if (phase !== 'matching' && phase !== 'uploading') {
|
|
78
|
-
return primaryBatchIdFromDetailsOrder;
|
|
79
|
-
}
|
|
80
|
-
const currentId = bulkUpload.currentBatchId;
|
|
81
|
-
if (currentId == null) {
|
|
82
|
-
return primaryBatchIdFromDetailsOrder;
|
|
83
|
-
}
|
|
84
|
-
const currentDetails = bulkUpload.batchDetailsById[currentId];
|
|
85
|
-
if (currentDetails != null &&
|
|
86
|
-
currentDetails.status === 'completed' &&
|
|
87
|
-
batchDetailsHasUnmatchedWork(currentDetails)) {
|
|
88
|
-
return currentId;
|
|
89
|
-
}
|
|
90
|
-
return primaryBatchIdFromDetailsOrder;
|
|
91
|
-
})();
|
|
92
|
-
const completedBatchesInApiOrder = batchList.filter((batchListItem) => isBatchListStatusCompleted(batchListItem.status));
|
|
66
|
+
/** During upload/matching, the in-flight batch may not appear in batchList yet — prefer currentBatchId. */
|
|
67
|
+
const unmatchedSectionBatchId = bulkUpload.phase === 'matching' || bulkUpload.phase === 'uploading'
|
|
68
|
+
? bulkUpload.currentBatchId ?? primaryBatchIdFromDetailsOrder
|
|
69
|
+
: primaryBatchIdFromDetailsOrder;
|
|
70
|
+
const completedBatchesInApiOrder = batchList.filter((b) => isBatchListStatusCompleted(b.status));
|
|
93
71
|
const currentBatchDetails = unmatchedSectionBatchId != null
|
|
94
72
|
? bulkUpload.batchDetailsById[unmatchedSectionBatchId]
|
|
95
73
|
: undefined;
|
|
96
74
|
const resolvedFiles = currentBatchDetails?.files.map((file) => resolveBatchFile(file, currentBatchDetails.batchId, transactionState));
|
|
97
75
|
const failedIds = new Set(bulkUpload.failedBatchDetailIds);
|
|
98
|
-
const hasMoreBatchDetails = completedBatchesInApiOrder.some((
|
|
99
|
-
!failedIds.has(
|
|
76
|
+
const hasMoreBatchDetails = completedBatchesInApiOrder.some((b) => bulkUpload.batchDetailsById[b.batchId] == null &&
|
|
77
|
+
!failedIds.has(b.batchId));
|
|
100
78
|
const getSortValue = (file) => {
|
|
101
|
-
const
|
|
102
|
-
if (
|
|
79
|
+
const t = file.matchedTransaction;
|
|
80
|
+
if (t == null) {
|
|
103
81
|
return null;
|
|
104
82
|
}
|
|
105
83
|
switch (bulkUpload.sortKey) {
|
|
106
84
|
case 'vendor':
|
|
107
|
-
return (
|
|
85
|
+
return (t.vendorName ?? t.description)?.toLowerCase() ?? null;
|
|
108
86
|
case 'category': {
|
|
109
|
-
const line =
|
|
87
|
+
const line = t.lines?.[0];
|
|
110
88
|
return line?.account?.accountName?.toLowerCase() ?? null;
|
|
111
89
|
}
|
|
112
90
|
case 'class': {
|
|
113
|
-
const line =
|
|
91
|
+
const line = t.lines?.[0];
|
|
114
92
|
return line?.class?.className?.toLowerCase() ?? null;
|
|
115
93
|
}
|
|
116
94
|
case 'date':
|
|
117
|
-
return
|
|
95
|
+
return t.date.valueOf();
|
|
118
96
|
case 'amount':
|
|
119
|
-
return
|
|
97
|
+
return t.amount.amount;
|
|
120
98
|
default:
|
|
121
99
|
return null;
|
|
122
100
|
}
|
|
123
101
|
};
|
|
124
102
|
const bulkUploadSortDirection = bulkUpload.sortOrder === 'ascending' ? 'asc' : 'desc';
|
|
125
|
-
const matchedFiles = orderBy(resolvedFiles?.filter((
|
|
126
|
-
const unmatchedFiles = orderBy(resolvedFiles?.filter((
|
|
103
|
+
const matchedFiles = orderBy(resolvedFiles?.filter((f) => f.status === 'matched') ?? [], getSortValue, bulkUploadSortDirection);
|
|
104
|
+
const unmatchedFiles = orderBy(resolvedFiles?.filter((f) => f.status === 'un_matched' || f.status === 'no_match') ?? [], getSortValue, bulkUploadSortDirection);
|
|
127
105
|
const progress = currentBatchStatus != null
|
|
128
106
|
? {
|
|
129
107
|
...currentBatchStatus.progress,
|
|
@@ -140,16 +118,16 @@ export function getExpenseAutomationMissingReceiptsView(state) {
|
|
|
140
118
|
* that has details loaded (otherwise past is empty and receipts vanish from the UI).
|
|
141
119
|
*/
|
|
142
120
|
const pastBatches = unmatchedSectionBatchId != null
|
|
143
|
-
? batchList.filter((
|
|
121
|
+
? batchList.filter((b) => b.batchId !== unmatchedSectionBatchId)
|
|
144
122
|
: batchList;
|
|
145
|
-
const pastUnmatchedFiles = pastBatches.flatMap((
|
|
146
|
-
const details = bulkUpload.batchDetailsById[
|
|
123
|
+
const pastUnmatchedFiles = pastBatches.flatMap((b) => {
|
|
124
|
+
const details = bulkUpload.batchDetailsById[b.batchId];
|
|
147
125
|
if (details == null) {
|
|
148
126
|
return [];
|
|
149
127
|
}
|
|
150
128
|
return details.files
|
|
151
|
-
.filter((
|
|
152
|
-
.map((
|
|
129
|
+
.filter((f) => f.status === 'un_matched' || f.status === 'no_match')
|
|
130
|
+
.map((f) => resolveBatchFile(f, b.batchId, transactionState));
|
|
153
131
|
});
|
|
154
132
|
const completedTransactionsCacheKey = monthYearPeriodId != null
|
|
155
133
|
? getCompletedTransactionsCacheKey(monthYearPeriodId, bulkUpload.sortKey, bulkUpload.sortOrder, bulkUpload.completedSubTab)
|
|
@@ -168,11 +146,11 @@ export function getExpenseAutomationMissingReceiptsView(state) {
|
|
|
168
146
|
summary: currentBatchDetails.summary,
|
|
169
147
|
}
|
|
170
148
|
: undefined;
|
|
171
|
-
const manualSearchResults = bulkUpload.manualSearch.results.map((
|
|
172
|
-
const
|
|
149
|
+
const manualSearchResults = bulkUpload.manualSearch.results.map((r) => {
|
|
150
|
+
const t = getSupportedTransactionById(transactionState, r.transactionId);
|
|
173
151
|
return {
|
|
174
|
-
...
|
|
175
|
-
vendorLogo:
|
|
152
|
+
...r,
|
|
153
|
+
vendorLogo: r.vendorLogo ?? t?.logo?.href,
|
|
176
154
|
};
|
|
177
155
|
});
|
|
178
156
|
return {
|
|
@@ -222,7 +200,7 @@ export function getExpenseAutomationMissingReceiptsView(state) {
|
|
|
222
200
|
};
|
|
223
201
|
}
|
|
224
202
|
function batchDetailsHasUnmatchedWork(details) {
|
|
225
|
-
return details.files.some((
|
|
203
|
+
return details.files.some((f) => f.status === 'un_matched' || f.status === 'no_match');
|
|
226
204
|
}
|
|
227
205
|
/** List row summary before batch details load — aligns with unmatched / possible-match work. */
|
|
228
206
|
function batchListItemHasUnmatchedWork(batch) {
|
|
@@ -239,17 +217,17 @@ function batchListItemHasUnmatchedWork(batch) {
|
|
|
239
217
|
*/
|
|
240
218
|
function getPrimaryBatchIdFromBatchListOrder(batchListInPlatformOrder, batchDetailsById, failedBatchDetailIds) {
|
|
241
219
|
const failedIds = new Set(failedBatchDetailIds);
|
|
242
|
-
const anyCompletedBatchHasUnmatchedWork = batchListInPlatformOrder.some((
|
|
243
|
-
if (failedIds.has(
|
|
220
|
+
const anyCompletedBatchHasUnmatchedWork = batchListInPlatformOrder.some((b) => {
|
|
221
|
+
if (failedIds.has(b.batchId)) {
|
|
244
222
|
return false;
|
|
245
223
|
}
|
|
246
|
-
const
|
|
247
|
-
if (
|
|
248
|
-
|
|
249
|
-
batchDetailsHasUnmatchedWork(
|
|
224
|
+
const d = batchDetailsById[b.batchId];
|
|
225
|
+
if (d != null &&
|
|
226
|
+
d.status === 'completed' &&
|
|
227
|
+
batchDetailsHasUnmatchedWork(d)) {
|
|
250
228
|
return true;
|
|
251
229
|
}
|
|
252
|
-
return
|
|
230
|
+
return d == null && batchListItemHasUnmatchedWork(b);
|
|
253
231
|
});
|
|
254
232
|
for (const batch of batchListInPlatformOrder) {
|
|
255
233
|
if (failedIds.has(batch.batchId)) {
|
|
@@ -286,7 +264,7 @@ function resolveBatchFile(file, batchId, transactionState) {
|
|
|
286
264
|
? { transaction, matchScore: ref.matchScore }
|
|
287
265
|
: undefined;
|
|
288
266
|
})
|
|
289
|
-
.filter((
|
|
267
|
+
.filter((c) => c != null);
|
|
290
268
|
return {
|
|
291
269
|
attachmentId: file.attachmentId,
|
|
292
270
|
batchId,
|
|
@@ -7,42 +7,3 @@ const MISSING_RECEIPTS_SORT_KEYS = [
|
|
|
7
7
|
'amount',
|
|
8
8
|
];
|
|
9
9
|
export const toMissingReceiptsSortKey = (v) => stringToUnion(v, MISSING_RECEIPTS_SORT_KEYS);
|
|
10
|
-
// -- Bulk Receipt Upload Types --
|
|
11
|
-
/**
|
|
12
|
-
* Minimum trimmed query length before manual-match search calls the expense-automation API.
|
|
13
|
-
* Kept in sync with `ManualSearchInput` in web-components (fires search only when length ≥ this).
|
|
14
|
-
*/
|
|
15
|
-
export const MIN_MANUAL_TRANSACTION_SEARCH_LENGTH = 2;
|
|
16
|
-
const BATCH_STATUS_VALUES = ['pending', 'processing', 'completed'];
|
|
17
|
-
export const toBatchStatusValue = (v) => stringToUnion(v.trim().toLowerCase(), BATCH_STATUS_VALUES);
|
|
18
|
-
/** Canonical batch file statuses (API ingress may use aliases; see `parseBatchFileStatus`). */
|
|
19
|
-
export const BATCH_FILE_STATUSES = [
|
|
20
|
-
'exact_match',
|
|
21
|
-
'possible_matches',
|
|
22
|
-
'no_match',
|
|
23
|
-
'failed',
|
|
24
|
-
];
|
|
25
|
-
export const toBatchFileStatus = (v) => stringToUnion(v.trim().toLowerCase().replace(/-/g, '_'), BATCH_FILE_STATUSES);
|
|
26
|
-
const UNMATCHED_TAB_FILE_STATUSES = ['possible_matches', 'no_match'];
|
|
27
|
-
/** True for files that belong in the Unmatched tab (possible matches or no match). */
|
|
28
|
-
export function isUnmatchedTabFileStatus(status) {
|
|
29
|
-
return UNMATCHED_TAB_FILE_STATUSES.includes(status);
|
|
30
|
-
}
|
|
31
|
-
const BULK_UPLOAD_PHASES = ['idle', 'uploading', 'matching', 'completed'];
|
|
32
|
-
export const toBulkUploadPhase = (v) => stringToUnion(v.trim().toLowerCase(), BULK_UPLOAD_PHASES);
|
|
33
|
-
const BULK_UPLOAD_RESULTS_TABS = ['completed', 'unmatched'];
|
|
34
|
-
export const toBulkUploadResultsTab = (v) => stringToUnion(v.trim().toLowerCase(), BULK_UPLOAD_RESULTS_TABS);
|
|
35
|
-
const MISSING_RECEIPTS_TABS = ['missing', 'completed', 'unmatched'];
|
|
36
|
-
export const toMissingReceiptsTab = (v) => stringToUnion(v.trim().toLowerCase(), MISSING_RECEIPTS_TABS);
|
|
37
|
-
const COMPLETED_SUB_TABS = ['all', 'ai_accountant', 'manual'];
|
|
38
|
-
export const toCompletedSubTab = (v) => stringToUnion(v.trim().toLowerCase(), COMPLETED_SUB_TABS);
|
|
39
|
-
const MATCH_SOURCES = ['ai', 'manual'];
|
|
40
|
-
export const toMatchSource = (v) => stringToUnion(v.trim().toLowerCase(), MATCH_SOURCES);
|
|
41
|
-
const BULK_UPLOAD_SORT_KEYS = [
|
|
42
|
-
'amount',
|
|
43
|
-
'category',
|
|
44
|
-
'class',
|
|
45
|
-
'date',
|
|
46
|
-
'vendor',
|
|
47
|
-
];
|
|
48
|
-
export const toBulkUploadSortKey = (v) => stringToUnion(v.trim().toLowerCase(), BULK_UPLOAD_SORT_KEYS);
|
package/lib/index.d.ts
CHANGED
|
@@ -265,7 +265,7 @@ import { MAX_SELECTION_LIMIT, checkIfAllLineItemsAreCategoryClassFilled, getLine
|
|
|
265
265
|
import { UploadStatementDocumentAIPayload, UploadStatementDocumentAIResponse } from './view/expenseAutomationView/payload/reconciliationPayload';
|
|
266
266
|
import { clearExpenseAutomationFluxAnalysisView, fetchFluxAnalysisView, reviewFluxAnalysisView, updateFluxAnalysisViewPageMetaData, updateFluxAnalysisViewUIState, updateOperatingExpensesIdsForReview, updateSelectedSectionIdsForReview } from './view/expenseAutomationView/reducers/fluxAnalysisViewReducer';
|
|
267
267
|
import { clearJeScheduleLocalData as clearExpenseAutomationJEScheduleLocalData, clearExpenseAutomationJESchedulesView, fetchJeSchedulesPage as fetchExpenseAutomationJESchedulesPage, ignoreRecommendedJeSchedule as ignoreExpenseAutomationJESchedule, initializeAccountSettingsView as initializeJeAccountSettingsView, initializeJeScheduleLocalData, removeJeScheduleTransactionKey, retryJeSchedule as retryExpenseAutomationJESchedule, saveAccountSettings as saveJeAccountSettings, saveAccountSettingsLocalData as saveJeAccountSettingsLocalData, updateJeScheduleLocalDataById, updateJeScheduleTransactionKeys } from './view/expenseAutomationView/reducers/jeSchedulesViewReducer';
|
|
268
|
-
import { bulkUploadReceipts,
|
|
268
|
+
import { bulkUploadReceipts, bulkUploadReceiptsFailure, bulkUploadReceiptsSuccess, clearBulkUpload, confirmBulkUploadMatch, confirmBulkUploadMatchFailure, confirmBulkUploadMatchSuccess, fetchBulkUploadBatchDetails, fetchBulkUploadBatchDetailsFailure, fetchBulkUploadBatchDetailsSuccess, storeBatchDetails, fetchMoreBatchDetails, fetchMoreBatchDetailsComplete, fetchMoreBatchDetailsFailure, fetchBulkUploadBatches, fetchBulkUploadBatchesFailure, fetchBulkUploadBatchesSuccess, fetchMissingReceipts as fetchExpenseAutomationMissingReceipts, markMissingReceiptAsDone as markExpenseAutomationMissingReceiptAsDone, pusherBatchStatusUpdate, requestMissingReceiptsTabNavigation, clearMissingReceiptsTabNavigation, setBulkUploadResultsTab, setBulkUploadCompletedSubTab, setBulkUploadSortConfig, searchTransactionsForManualMatch, searchTransactionsForManualMatchSuccess, searchTransactionsForManualMatchFailure, clearManualSearchResults, acknowledgeBulkUploadConfirmMatchComplete, fetchCompletedTransactions, fetchCompletedTransactionsSuccess, fetchCompletedTransactionsFailure, updateBulkUploadProgress, updateMissingReceiptUploadState as updateExpenseAutomationMissingReceiptUploadState, updateMissingReceiptsUIState as updateExpenseAutomationMissingReceiptsUIState, uploadMissingReceiptSuccess as uploadExpenseAutomationMissingReceiptSuccess } from './view/expenseAutomationView/reducers/missingReceiptsViewReducer';
|
|
269
269
|
import { deleteAccountStatement, fetchReconciliation as fetchReconciliationView, saveReconciliationDetail as saveExpenseAutomationReconciliationDetail, saveReconciliationReview as saveExpenseAutomationReconciliationReview, setConnectionInProgressForAccount as setConnectionInProgressForAccountReconciliation, setStatementParseInProgress, updateAccountReconciliationLocalData as updateExpenseAutomationAccountReconciliationLocalData, updateSelectedAccountId as updateExpenseAutomationAccountReconciliationSelectedAccountId, updateSelectedTab as updateExpenseAutomationAccountReconciliationSelectedTab, updateReconListScrollPosition as updateExpenseAutomationReconListScrollPosition, updateReviewTabSortState as updateExpenseAutomationReconReviewTabListSortState, updateReviewTabLocalData as updateExpenseAutomationReconReviewTabLocalData, updateReconcileTabListScrollState as updateExpenseAutomationReconcileTabListScrollState, updateReconcileTabListSortState as updateExpenseAutomationReconcileTabListSortState, updateReconcileTabLocalData as updateExpenseAutomationReconcileTabLocalData, updateSelectedDrawerAccountId as updateExpenseAutomationSelectedDrawerAccountId, updateStatementUploadChosen, uploadAccountStatement } from './view/expenseAutomationView/reducers/reconciliationViewReducer';
|
|
270
270
|
import { backgroundRefetchReviewTab, clearExpenseAutomationTransactionsView, fetchTransactionCategorization, fetchTransactionCategorizationFailure, fetchTransactionCategorizationView, initializeTransactionCategorizationViewLocalData, markTransactionAsNotMiscategorized, saveTransactionCategorization, saveTransactionCategorizationLocalData, setAllItemsToCategoryClassInLocalDataForCategorization, setEntityRecommendationForLineIdsForCategorization, syncTransactionCategorizationFromDetailSave, updateCurrentSelectedTransactionCategorizationTab, updateSelectedCheckboxTransactionIds, updateSelectedCustomerForTransaction, updateSelectedTransactionId, updateSelectedVendorForTransaction, updateTransactionCategorization, updateTransactionCategorizationSaveStatus, updateTransactionCategorizationUIState, updateTransactionCategorizationUploadReceiptState, uploadTransactionCategorizationReceiptSuccess } from './view/expenseAutomationView/reducers/transactionsViewReducer';
|
|
271
271
|
import { ExpenseAutomationStepDetails, ExpenseAutomationViewSelector } from './view/expenseAutomationView/selectorTypes/expenseAutomationViewSelectorTypes';
|
|
@@ -281,7 +281,7 @@ import { getExpenseAutomationReconciliationView, isAccountReconReport } from './
|
|
|
281
281
|
import { getExpenseAutomationTransactionView } from './view/expenseAutomationView/selectors/transactionCategorizationSelector';
|
|
282
282
|
import { FluxAnalysisActionType, FluxAnalysisReviewStatus, FluxAnalysisSortKey, FluxAnalysisViewUIState, FluxBalancesByMonth } from './view/expenseAutomationView/types/fluxAnalysisViewState';
|
|
283
283
|
import { AccountSettingsLocalData, JEScheduleLocalData } from './view/expenseAutomationView/types/jeSchedulesViewState';
|
|
284
|
-
import {
|
|
284
|
+
import { BatchDetails, BatchFile, BatchFileStatus, BatchListItem, BatchStatus, BatchStatusValue, BatchSummary, BulkUploadPhase, BulkUploadResultsTab, BulkUploadSortKey, BulkUploadState, CandidateRef, CompletedSubTab, MatchCandidate, MatchSource, MissingReceiptsTab } from './view/expenseAutomationView/types/missingReceiptsViewState';
|
|
285
285
|
import { CompletedTransactionsSelectorData, ResolvedBatchFile, ResolvedCandidate, ResolvedBatchDetails } from './view/expenseAutomationView/selectorTypes/missingReceiptsSelectorTypes';
|
|
286
286
|
import { MissingReceiptsSortKey as ExpenseAutomationMissingReceiptsSortKey, MissingReceiptsViewState as ExpenseAutomationMissingReceiptsViewState, MissingReceiptsViewUIState as ExpenseAutomationMissingReceiptsViewUIState, toMissingReceiptsSortKey as toExpenseAutomationMissingReceiptsSortKey } from './view/expenseAutomationView/types/missingReceiptsViewState';
|
|
287
287
|
import { AccountReconciliationLocalData, ReconciliationViewTabType as ExpenseAutomationReconciliationViewTab, ReconReconcileSortKey, ReconReviewSortKey, ReconciliationReconcileTabLocalData, ReconciliationReviewTabLocalData, SaveReconcileDetailActionPayload as SaveExpenseAutomationReconciliationActionType, toReconciliationTabsType } from './view/expenseAutomationView/types/reconciliationViewState';
|
|
@@ -623,7 +623,7 @@ export { TransactionsOrder, COABalancesSliceOrder, EntityOrder, Section, Section
|
|
|
623
623
|
export { ClassesViewSelectorReportV2 };
|
|
624
624
|
export { BalancesTimeseries, TrendTimeseries, BalanceKind };
|
|
625
625
|
export { fetchMonthEndCloseChecks, fetchMonthClosePerformanceTrend, MonthClosePerformanceTrend, MonthEndCloseCheck, getMonthEndCloseChecksViewByTenantId, MonthEndCloseChecksView, MonthEndCloseCheckFrequency, MonthCloseCheckMetrics, ALL_MONTH_END_CLOSE_CHECKS_FREQUENCY, MonthEndAuditSummary, };
|
|
626
|
-
export { ExpenseAutomationViewSelector, ExpenseAutomationStepDetails, ExpenseAutomationViewType, ExpenseAutomationMissingReceiptsViewSelector, BulkUploadSelectorData, ExpenseAutomationFluxAnalysisViewSelector, FluxAnalysisVendorView, FluxAnalysisViewSectionReport, FluxVendorAccountsAndClassesView, ExpenseAutomationMissingReceiptsViewUIState, ExpenseAutomationMissingReceiptsViewState, ExpenseAutomationViewState, ExpenseAutomationTransactionsTab, ExpenseAutomationMissingReceiptsSortKey,
|
|
626
|
+
export { ExpenseAutomationViewSelector, ExpenseAutomationStepDetails, ExpenseAutomationViewType, ExpenseAutomationMissingReceiptsViewSelector, BulkUploadSelectorData, ExpenseAutomationFluxAnalysisViewSelector, FluxAnalysisVendorView, FluxAnalysisViewSectionReport, FluxVendorAccountsAndClassesView, ExpenseAutomationMissingReceiptsViewUIState, ExpenseAutomationMissingReceiptsViewState, ExpenseAutomationViewState, ExpenseAutomationTransactionsTab, ExpenseAutomationMissingReceiptsSortKey, BatchDetails, BatchFile, BatchFileStatus, BatchListItem, BatchStatus, BatchStatusValue, BatchSummary, BulkUploadPhase, BulkUploadResultsTab, BulkUploadSortKey, BulkUploadState, CandidateRef, CompletedSubTab, CompletedTransactionsSelectorData, MatchCandidate, MatchSource, MissingReceiptsTab, ResolvedBatchFile, ResolvedBatchDetails, ResolvedCandidate, getExpenseAutomationView, toExpenseAutomationMissingReceiptsSortKey, toExpenseAutomationTransactionsTabKey, toExpenseAutomationViewType, uploadExpenseAutomationMissingReceiptSuccess, markExpenseAutomationMissingReceiptAsDone, fetchAllExpenseAutomationTabs, refreshExpenseAutomationCurrentTab, updateCurrentSelectedTransactionCategorizationTab, fetchExpenseAutomationMissingReceipts, bulkUploadReceipts, bulkUploadReceiptsSuccess, bulkUploadReceiptsFailure, updateBulkUploadProgress, pusherBatchStatusUpdate, requestMissingReceiptsTabNavigation, clearMissingReceiptsTabNavigation, fetchBulkUploadBatchDetails, fetchBulkUploadBatchDetailsSuccess, fetchBulkUploadBatchDetailsFailure, storeBatchDetails, fetchMoreBatchDetails, fetchMoreBatchDetailsComplete, fetchMoreBatchDetailsFailure, fetchBulkUploadBatches, fetchBulkUploadBatchesSuccess, fetchBulkUploadBatchesFailure, confirmBulkUploadMatch, confirmBulkUploadMatchSuccess, confirmBulkUploadMatchFailure, setBulkUploadResultsTab, setBulkUploadCompletedSubTab, setBulkUploadSortConfig, clearBulkUpload, searchTransactionsForManualMatch, searchTransactionsForManualMatchSuccess, searchTransactionsForManualMatchFailure, clearManualSearchResults, acknowledgeBulkUploadConfirmMatchComplete, fetchCompletedTransactions, fetchCompletedTransactionsSuccess, fetchCompletedTransactionsFailure, fetchFluxAnalysisView, clearExpenseAutomationFluxAnalysisView, updateOperatingExpensesIdsForReview as updateFluxOperatingExpensesIdsForReview, updateSelectedSectionIdsForReview as updateFluxAnalysisSelectedSectionIdsForReview, reviewFluxAnalysisView, updateExpenseAutomationMissingReceiptUploadState, updateExpenseAutomationMissingReceiptsUIState, updateTransactionCategorizationUploadReceiptState, uploadTransactionCategorizationReceiptSuccess, FluxAnalysisOperatingExpenseView, FluxAnalysisSectionType, getExpenseAutomationFluxAnalysisView, FluxAnalysisSortKey, FluxAnalysisActionType, FluxBalancesByMonth, updateCurrentSelectedView, updateCurrentSelectedPeriod, getExpenseAutomationTransactionView, ReconReconcileSortKey, ReconciliationReconcileTabLocalData, FluxAnalysisReviewStatus, updateFluxAnalysisViewUIState, FluxAnalysisViewUIState, updateFluxAnalysisViewPageMetaData, MAX_SELECTION_LIMIT, checkIfAllLineItemsAreCategoryClassFilled, getLineItemsByTransactionIdsFromLocalData, isAnyItemWithUncategorizedExpenseAccount, SaveExpenseAutomationReconciliationActionType, saveExpenseAutomationReconciliationDetail, updateExpenseAutomationReconcileTabListScrollState, updateExpenseAutomationReconReviewTabListSortState, updateExpenseAutomationReconcileTabListSortState, updateExpenseAutomationReconcileTabLocalData, updateExpenseAutomationAccountReconciliationSelectedTab, updateExpenseAutomationAccountReconciliationSelectedAccountId, ExpenseAutomationReconciliationViewSelector, getExpenseAutomationReconciliationView, AccountReconciliationBySection, fetchReconciliationView, uploadAccountStatementIntoDocumentAI, UploadStatementDocumentAIResponse, updateExpenseAutomationReconListScrollPosition, setConnectionInProgressForAccountReconciliation, AccountReconciliationByAccount, AccountReconciliationEntity, getAccountReconByAccountIdAndSelectedPeriod, ExpenseAutomationReconciliationViewTab, toReconciliationTabsType, isAccountReconReport, ReconReviewSortKey, AccountReconSectionID, ReconciliationReviewTabLocalData, TransactionsToReview, RecommendedActionCodeType, ReconciliationStatusCodeType, BalanceDataStatusCodeType, updateExpenseAutomationReconReviewTabLocalData, updateExpenseAutomationSelectedDrawerAccountId, saveExpenseAutomationReconciliationReview, updateExpenseAutomationAccountReconciliationLocalData, BankStatusCodeType, ReconciliationAccountSourceType, toReconciliationAccountSource, StatementStatusCodeType, AccountReconciliationLocalData, StatementDataStatusCodeType, deleteAccountStatement, uploadAccountStatement, UploadStatementDocumentAIPayload, updateStatementUploadChosen, isReviewTransactionBankTransferType, isReviewTransactionBillPaymentType, isReviewTransactionCreditCardPaymentType, isReviewTransactionDepositType, isReviewTransactionExpenseType, isReviewTransactionCreditCardCreditType, setStatementParseInProgress, };
|
|
627
627
|
export { JEScheduleLocalData };
|
|
628
628
|
export { ExpenseAutomationJESchedulesViewSelector, JEAccountSettingsView, JEScheduledTransactionWithFailedEntries, };
|
|
629
629
|
export { fetchTransactionCategorization, fetchTransactionCategorizationView, updateTransactionCategorizationUIState, updateSelectedCheckboxTransactionIds, setEntityRecommendationForLineIdsForCategorization, initializeTransactionCategorizationViewLocalData, setAllItemsToCategoryClassInLocalDataForCategorization, saveTransactionCategorizationLocalData, fetchTransactionCategorizationFailure, saveTransactionCategorization, updateTransactionCategorization, updateTransactionCategorizationSaveStatus, markTransactionAsNotMiscategorized, updateSelectedVendorForTransaction, updateSelectedCustomerForTransaction, updateSelectedTransactionId, syncTransactionCategorizationFromDetailSave, backgroundRefetchReviewTab, clearExpenseAutomationTransactionsView, TransactionsSortKey, toTransactionsSortKey, TransactionsTab, TransactionCategorizationLineItemData, TransactionReviewLocalData, SupportedTransactionCategorization, ExpenseAutomationTransactionsViewState, ExpenseAutomationTransactionsViewUIState, ExpenseAutomationTransactionViewSelector, TransactionReviewLocalDataSelectorView, };
|
|
@@ -870,6 +870,5 @@ export { fetchTreasuryTaxLetterList } from './view/spendManagement/treasury/trea
|
|
|
870
870
|
export { TreasuryTaxLettersSelectorView, getTreasuryTaxLetters, } from './view/spendManagement/treasury/treasuryTaxLetterList/treasuryTaxLetterListSelector';
|
|
871
871
|
export { TreasuryTransactionStatus } from './view/spendManagement/treasury/treasuryList/treasuryDetailPayload';
|
|
872
872
|
export type { TransactionActivityLog } from './entity/transactionActivityLog/transactionActivityLogState';
|
|
873
|
-
export { BULK_UPLOAD_AUTOMATCHING_TIMEOUT_MS, BULK_UPLOAD_BAR_COMPLETE_HOLD_MS, } from './view/expenseAutomationView/bulkUploadTiming';
|
|
874
873
|
export { fetchTransactionActivityLog } from './view/transactionActivityLogView/transactionActivityLogViewReducer';
|
|
875
874
|
export { TransactionActivityLogViewSelectorView, getTransactionActivityLogView, } from './view/transactionActivityLogView/transactionActivityLogViewSelector';
|