@zeniai/client-epic-state 4.19.87-betaVR2 → 4.19.87
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/entity/snackbar/snackbarTypes.d.ts +1 -1
- package/lib/entity/snackbar/snackbarTypes.js +0 -1
- package/lib/epic.d.ts +1 -10
- package/lib/epic.js +1 -10
- package/lib/esm/entity/snackbar/snackbarTypes.js +0 -1
- package/lib/esm/epic.js +1 -10
- package/lib/esm/index.js +2 -8
- package/lib/esm/view/expenseAutomationView/payload/missingReceiptsPayload.js +1 -189
- package/lib/esm/view/expenseAutomationView/reducers/missingReceiptsViewReducer.js +2 -426
- package/lib/esm/view/expenseAutomationView/selectors/missingReceiptsSelector.js +1 -246
- package/lib/esm/view/expenseAutomationView/types/missingReceiptsViewState.js +0 -39
- package/lib/esm/zeniAPI.js +2 -0
- package/lib/index.d.ts +3 -6
- package/lib/index.js +32 -80
- package/lib/tsconfig.typecheck.tsbuildinfo +1 -0
- package/lib/view/expenseAutomationView/helpers/transactionCategorizationLocalDataHelper.d.ts +1 -1
- package/lib/view/expenseAutomationView/payload/missingReceiptsPayload.d.ts +0 -118
- package/lib/view/expenseAutomationView/payload/missingReceiptsPayload.js +0 -201
- package/lib/view/expenseAutomationView/reducers/missingReceiptsViewReducer.d.ts +3 -76
- package/lib/view/expenseAutomationView/reducers/missingReceiptsViewReducer.js +2 -427
- package/lib/view/expenseAutomationView/selectorTypes/missingReceiptsSelectorTypes.d.ts +1 -69
- package/lib/view/expenseAutomationView/selectors/missingReceiptsSelector.d.ts +1 -1
- package/lib/view/expenseAutomationView/selectors/missingReceiptsSelector.js +0 -245
- package/lib/view/expenseAutomationView/types/missingReceiptsViewState.d.ts +0 -142
- package/lib/view/expenseAutomationView/types/missingReceiptsViewState.js +1 -49
- package/lib/zeniAPI.js +2 -0
- package/package.json +2 -2
- package/lib/esm/view/expenseAutomationView/bulkUploadTiming.js +0 -10
- package/lib/esm/view/expenseAutomationView/epics/missingReceipts/bulkUploadReceiptsEpic.js +0 -29
- package/lib/esm/view/expenseAutomationView/epics/missingReceipts/confirmBulkUploadMatchEpic.js +0 -49
- package/lib/esm/view/expenseAutomationView/epics/missingReceipts/fetchBulkUploadBatchDetailsEpic.js +0 -40
- package/lib/esm/view/expenseAutomationView/epics/missingReceipts/fetchBulkUploadBatchesEpic.js +0 -47
- package/lib/esm/view/expenseAutomationView/epics/missingReceipts/fetchCompletedTransactionsEpic.js +0 -61
- package/lib/esm/view/expenseAutomationView/epics/missingReceipts/fetchMoreBatchDetailsEpic.js +0 -34
- package/lib/esm/view/expenseAutomationView/epics/missingReceipts/fetchMultipleBatchDetailsEpic.js +0 -57
- package/lib/esm/view/expenseAutomationView/epics/missingReceipts/searchTransactionsForManualMatchEpic.js +0 -78
- package/lib/esm/view/expenseAutomationView/epics/missingReceipts/watchBulkUploadBatchStatusEpic.js +0 -82
- package/lib/view/expenseAutomationView/bulkUploadTiming.d.ts +0 -10
- package/lib/view/expenseAutomationView/bulkUploadTiming.js +0 -13
- package/lib/view/expenseAutomationView/epics/missingReceipts/bulkUploadReceiptsEpic.d.ts +0 -18
- package/lib/view/expenseAutomationView/epics/missingReceipts/bulkUploadReceiptsEpic.js +0 -33
- package/lib/view/expenseAutomationView/epics/missingReceipts/confirmBulkUploadMatchEpic.d.ts +0 -7
- package/lib/view/expenseAutomationView/epics/missingReceipts/confirmBulkUploadMatchEpic.js +0 -53
- package/lib/view/expenseAutomationView/epics/missingReceipts/fetchBulkUploadBatchDetailsEpic.d.ts +0 -8
- package/lib/view/expenseAutomationView/epics/missingReceipts/fetchBulkUploadBatchDetailsEpic.js +0 -44
- package/lib/view/expenseAutomationView/epics/missingReceipts/fetchBulkUploadBatchesEpic.d.ts +0 -7
- package/lib/view/expenseAutomationView/epics/missingReceipts/fetchBulkUploadBatchesEpic.js +0 -51
- package/lib/view/expenseAutomationView/epics/missingReceipts/fetchCompletedTransactionsEpic.d.ts +0 -8
- package/lib/view/expenseAutomationView/epics/missingReceipts/fetchCompletedTransactionsEpic.js +0 -65
- package/lib/view/expenseAutomationView/epics/missingReceipts/fetchMoreBatchDetailsEpic.d.ts +0 -8
- package/lib/view/expenseAutomationView/epics/missingReceipts/fetchMoreBatchDetailsEpic.js +0 -38
- package/lib/view/expenseAutomationView/epics/missingReceipts/fetchMultipleBatchDetailsEpic.d.ts +0 -9
- package/lib/view/expenseAutomationView/epics/missingReceipts/fetchMultipleBatchDetailsEpic.js +0 -62
- package/lib/view/expenseAutomationView/epics/missingReceipts/searchTransactionsForManualMatchEpic.d.ts +0 -14
- package/lib/view/expenseAutomationView/epics/missingReceipts/searchTransactionsForManualMatchEpic.js +0 -82
- package/lib/view/expenseAutomationView/epics/missingReceipts/watchBulkUploadBatchStatusEpic.d.ts +0 -25
- package/lib/view/expenseAutomationView/epics/missingReceipts/watchBulkUploadBatchStatusEpic.js +0 -88
|
@@ -9,9 +9,6 @@ const timePeriod_1 = require("../../../commonStateTypes/timePeriod");
|
|
|
9
9
|
const tenantSelector_1 = require("../../../entity/tenant/tenantSelector");
|
|
10
10
|
const transactionSelector_1 = require("../../../entity/transaction/transactionSelector");
|
|
11
11
|
const expenseAutomationViewSelectorTypes_1 = require("../selectorTypes/expenseAutomationViewSelectorTypes");
|
|
12
|
-
const missingReceiptsPayload_1 = require("../payload/missingReceiptsPayload");
|
|
13
|
-
const missingReceiptsViewReducer_1 = require("../reducers/missingReceiptsViewReducer");
|
|
14
|
-
const missingReceiptsViewState_1 = require("../types/missingReceiptsViewState");
|
|
15
12
|
function getExpenseAutomationMissingReceiptsView(state) {
|
|
16
13
|
const { expenseAutomationMissingReceiptsViewState, expenseAutomationViewState, transactionState, monthEndCloseChecksState, } = state;
|
|
17
14
|
const currentTenant = (0, tenantSelector_1.getCurrentTenant)(state);
|
|
@@ -56,131 +53,6 @@ function getExpenseAutomationMissingReceiptsView(state) {
|
|
|
56
53
|
const completionStatus = missingReceiptsCompletionStatus != null
|
|
57
54
|
? missingReceiptsCompletionStatus
|
|
58
55
|
: 'incomplete';
|
|
59
|
-
const { bulkUpload, pendingMissingReceiptsTabNavigation, } = expenseAutomationMissingReceiptsViewState;
|
|
60
|
-
const currentBatchStatus = bulkUpload.currentBatchId != null
|
|
61
|
-
? bulkUpload.batchStatusById[bulkUpload.currentBatchId]
|
|
62
|
-
: undefined;
|
|
63
|
-
const batchList = monthYearPeriodId != null
|
|
64
|
-
? bulkUpload.batchListByPeriod[monthYearPeriodId] ?? []
|
|
65
|
-
: [];
|
|
66
|
-
/**
|
|
67
|
-
* Primary "Unmatched" batch = first completed batch in platform list order, preferring the
|
|
68
|
-
* first batch that still has possible_matches/no_match work when any completed batch has such work.
|
|
69
|
-
* Batches without details yet: wait if the list row still reports unmatched work; otherwise skip
|
|
70
|
-
* so we do not block the whole tab while other rows are not fetched yet.
|
|
71
|
-
*/
|
|
72
|
-
const primaryBatchIdFromDetailsOrder = getPrimaryBatchIdFromBatchListOrder(batchList, bulkUpload.batchDetailsById, bulkUpload.failedBatchDetailIds);
|
|
73
|
-
/**
|
|
74
|
-
* During upload/matching, prefer `currentBatchId` only when its batch details are loaded,
|
|
75
|
-
* completed, and still have unmatched-tab work. Otherwise keep `primaryBatchIdFromDetailsOrder`.
|
|
76
|
-
*
|
|
77
|
-
* If we pinned to `currentBatchId` as soon as it was absent from the batch list (in-flight
|
|
78
|
-
* upload), details are often not loaded yet — `unmatchedFiles` would empty while older batches
|
|
79
|
-
* moved to "past", so the primary "Unmatched Receipts" section vanished during auto-matching.
|
|
80
|
-
*/
|
|
81
|
-
const unmatchedSectionBatchId = (() => {
|
|
82
|
-
const phase = bulkUpload.phase;
|
|
83
|
-
if (phase !== 'matching' && phase !== 'uploading') {
|
|
84
|
-
return primaryBatchIdFromDetailsOrder;
|
|
85
|
-
}
|
|
86
|
-
const currentId = bulkUpload.currentBatchId;
|
|
87
|
-
if (currentId == null) {
|
|
88
|
-
return primaryBatchIdFromDetailsOrder;
|
|
89
|
-
}
|
|
90
|
-
const currentDetails = bulkUpload.batchDetailsById[currentId];
|
|
91
|
-
if (currentDetails != null &&
|
|
92
|
-
currentDetails.status === 'completed' &&
|
|
93
|
-
batchDetailsHasUnmatchedWork(currentDetails)) {
|
|
94
|
-
return currentId;
|
|
95
|
-
}
|
|
96
|
-
return primaryBatchIdFromDetailsOrder;
|
|
97
|
-
})();
|
|
98
|
-
const completedBatchesInApiOrder = batchList.filter((batchListItem) => (0, missingReceiptsPayload_1.isBatchListStatusCompleted)(batchListItem.status));
|
|
99
|
-
const currentBatchDetails = unmatchedSectionBatchId != null
|
|
100
|
-
? bulkUpload.batchDetailsById[unmatchedSectionBatchId]
|
|
101
|
-
: undefined;
|
|
102
|
-
const resolvedFiles = currentBatchDetails?.files.map((file) => resolveBatchFile(file, currentBatchDetails.batchId, transactionState));
|
|
103
|
-
const failedIds = new Set(bulkUpload.failedBatchDetailIds);
|
|
104
|
-
const hasMoreBatchDetails = completedBatchesInApiOrder.some((batchListItem) => bulkUpload.batchDetailsById[batchListItem.batchId] == null &&
|
|
105
|
-
!failedIds.has(batchListItem.batchId));
|
|
106
|
-
const getSortValue = (file) => {
|
|
107
|
-
const matchedTransaction = file.matchedTransaction;
|
|
108
|
-
if (matchedTransaction == null) {
|
|
109
|
-
return null;
|
|
110
|
-
}
|
|
111
|
-
switch (bulkUpload.sortKey) {
|
|
112
|
-
case 'vendor':
|
|
113
|
-
return (matchedTransaction.vendorName ?? matchedTransaction.description)?.toLowerCase() ?? null;
|
|
114
|
-
case 'category': {
|
|
115
|
-
const line = matchedTransaction.lines?.[0];
|
|
116
|
-
return line?.account?.accountName?.toLowerCase() ?? null;
|
|
117
|
-
}
|
|
118
|
-
case 'class': {
|
|
119
|
-
const line = matchedTransaction.lines?.[0];
|
|
120
|
-
return line?.class?.className?.toLowerCase() ?? null;
|
|
121
|
-
}
|
|
122
|
-
case 'date':
|
|
123
|
-
return matchedTransaction.date.valueOf();
|
|
124
|
-
case 'amount':
|
|
125
|
-
return matchedTransaction.amount.amount;
|
|
126
|
-
default:
|
|
127
|
-
return null;
|
|
128
|
-
}
|
|
129
|
-
};
|
|
130
|
-
const bulkUploadSortDirection = bulkUpload.sortOrder === 'ascending' ? 'asc' : 'desc';
|
|
131
|
-
const matchedFiles = (0, orderBy_1.default)(resolvedFiles?.filter((resolvedFile) => resolvedFile.status === 'exact_match') ?? [], getSortValue, bulkUploadSortDirection);
|
|
132
|
-
const unmatchedFiles = (0, orderBy_1.default)(resolvedFiles?.filter((resolvedFile) => (0, missingReceiptsViewState_1.isUnmatchedTabFileStatus)(resolvedFile.status)) ?? [], getSortValue, bulkUploadSortDirection);
|
|
133
|
-
const progress = currentBatchStatus != null
|
|
134
|
-
? {
|
|
135
|
-
...currentBatchStatus.progress,
|
|
136
|
-
percentage: currentBatchStatus.progress.total > 0
|
|
137
|
-
? Math.round((currentBatchStatus.progress.processed /
|
|
138
|
-
currentBatchStatus.progress.total) *
|
|
139
|
-
100)
|
|
140
|
-
: 0,
|
|
141
|
-
}
|
|
142
|
-
: { total: 0, processed: 0, percentage: 0 };
|
|
143
|
-
const batchListFetchState = bulkUpload.batchListFetchState;
|
|
144
|
-
/**
|
|
145
|
-
* When primary batch is not resolved yet, still aggregate unmatched files from every batch
|
|
146
|
-
* that has details loaded (otherwise past is empty and receipts vanish from the UI).
|
|
147
|
-
*/
|
|
148
|
-
const pastBatches = unmatchedSectionBatchId != null
|
|
149
|
-
? batchList.filter((batchListItem) => batchListItem.batchId !== unmatchedSectionBatchId)
|
|
150
|
-
: batchList;
|
|
151
|
-
const pastUnmatchedFiles = pastBatches.flatMap((pastBatch) => {
|
|
152
|
-
const details = bulkUpload.batchDetailsById[pastBatch.batchId];
|
|
153
|
-
if (details == null) {
|
|
154
|
-
return [];
|
|
155
|
-
}
|
|
156
|
-
return details.files
|
|
157
|
-
.filter((batchFile) => (0, missingReceiptsViewState_1.isUnmatchedTabFileStatus)(batchFile.status))
|
|
158
|
-
.map((batchFile) => resolveBatchFile(batchFile, pastBatch.batchId, transactionState));
|
|
159
|
-
});
|
|
160
|
-
const completedTransactionsCacheKey = monthYearPeriodId != null
|
|
161
|
-
? (0, missingReceiptsViewReducer_1.getCompletedTransactionsCacheKey)(monthYearPeriodId, bulkUpload.sortKey, bulkUpload.sortOrder, bulkUpload.completedSubTab)
|
|
162
|
-
: undefined;
|
|
163
|
-
const completedTransactionsForPeriod = completedTransactionsCacheKey != null
|
|
164
|
-
? bulkUpload.completedTransactionsByPeriod[completedTransactionsCacheKey]
|
|
165
|
-
: undefined;
|
|
166
|
-
const completedTransactionIds = completedTransactionsForPeriod?.transactionIds ?? [];
|
|
167
|
-
const resolvedCompletedTransactions = (0, transactionSelector_1.getSupportedTransactionsByIds)(transactionState, completedTransactionIds);
|
|
168
|
-
const resolvedBatchDetails = currentBatchDetails != null && resolvedFiles != null
|
|
169
|
-
? {
|
|
170
|
-
batchId: currentBatchDetails.batchId,
|
|
171
|
-
createdAt: currentBatchDetails.createdAt,
|
|
172
|
-
files: resolvedFiles,
|
|
173
|
-
status: currentBatchDetails.status,
|
|
174
|
-
summary: currentBatchDetails.summary,
|
|
175
|
-
}
|
|
176
|
-
: undefined;
|
|
177
|
-
const manualSearchResults = bulkUpload.manualSearch.results.map((result) => {
|
|
178
|
-
const transaction = (0, transactionSelector_1.getSupportedTransactionById)(transactionState, result.transactionId);
|
|
179
|
-
return {
|
|
180
|
-
...result,
|
|
181
|
-
vendorLogo: result.vendorLogo ?? transaction?.logo?.href,
|
|
182
|
-
};
|
|
183
|
-
});
|
|
184
56
|
return {
|
|
185
57
|
fetchState,
|
|
186
58
|
error,
|
|
@@ -189,122 +61,5 @@ function getExpenseAutomationMissingReceiptsView(state) {
|
|
|
189
61
|
refreshStatus,
|
|
190
62
|
transactions: sortedTransactions,
|
|
191
63
|
completionStatus,
|
|
192
|
-
pendingMissingReceiptsTabNavigation: pendingMissingReceiptsTabNavigation ?? null,
|
|
193
|
-
bulkUpload: {
|
|
194
|
-
phase: bulkUpload.phase,
|
|
195
|
-
uploadStatus: bulkUpload.uploadStatus,
|
|
196
|
-
batchStatus: currentBatchStatus,
|
|
197
|
-
batchDetails: resolvedBatchDetails,
|
|
198
|
-
completedSubTab: bulkUpload.completedSubTab,
|
|
199
|
-
completedTransactions: {
|
|
200
|
-
fetchState: completedTransactionsForPeriod?.fetchState ?? {
|
|
201
|
-
fetchState: 'Not-Started',
|
|
202
|
-
error: undefined,
|
|
203
|
-
},
|
|
204
|
-
nextPageToken: completedTransactionsForPeriod?.nextPageToken ?? null,
|
|
205
|
-
totalCount: completedTransactionsForPeriod?.totalCount ?? 0,
|
|
206
|
-
transactions: resolvedCompletedTransactions,
|
|
207
|
-
},
|
|
208
|
-
matchedFiles,
|
|
209
|
-
unmatchedFiles,
|
|
210
|
-
progress,
|
|
211
|
-
currentResultsTab: bulkUpload.currentResultsTab,
|
|
212
|
-
sortKey: bulkUpload.sortKey,
|
|
213
|
-
sortOrder: bulkUpload.sortOrder,
|
|
214
|
-
batchList,
|
|
215
|
-
batchListFetchState,
|
|
216
|
-
pastBatches,
|
|
217
|
-
pastUnmatchedFiles,
|
|
218
|
-
confirmMatchStatus: bulkUpload.confirmMatchStatus,
|
|
219
|
-
manualSearchUiResetKey: bulkUpload.manualSearchUiResetKey,
|
|
220
|
-
hasMoreBatchDetails,
|
|
221
|
-
batchDetailsPaginationState: bulkUpload.batchDetailsPaginationState,
|
|
222
|
-
manualSearch: {
|
|
223
|
-
...bulkUpload.manualSearch,
|
|
224
|
-
results: manualSearchResults,
|
|
225
|
-
},
|
|
226
|
-
uploadProgress: bulkUpload.uploadProgress,
|
|
227
|
-
},
|
|
228
|
-
};
|
|
229
|
-
}
|
|
230
|
-
function batchDetailsHasUnmatchedWork(details) {
|
|
231
|
-
return details.files.some((file) => (0, missingReceiptsViewState_1.isUnmatchedTabFileStatus)(file.status));
|
|
232
|
-
}
|
|
233
|
-
/** List row summary before batch details load — aligns with unmatched / possible-match work. */
|
|
234
|
-
function batchListItemHasUnmatchedWork(batch) {
|
|
235
|
-
return batch.noMatchCount > 0 || batch.possibleMatchesCount > 0;
|
|
236
|
-
}
|
|
237
|
-
/**
|
|
238
|
-
* `batchList` must match platform order (same sequence as the bulk-upload batches API). Picks the
|
|
239
|
-
* primary batch for the top "Unmatched" section. Skips failed detail fetches.
|
|
240
|
-
*
|
|
241
|
-
* If details are not loaded for an earlier list row but that row reports no unmatched work, we
|
|
242
|
-
* skip it so later rows with loaded details can be primary (avoids an empty tab when the first
|
|
243
|
-
* fetch page only loaded later batches). If the list reports unmatched work but details are still
|
|
244
|
-
* loading, we wait (undefined) so primary order stays correct once details arrive.
|
|
245
|
-
*/
|
|
246
|
-
function getPrimaryBatchIdFromBatchListOrder(batchListInPlatformOrder, batchDetailsById, failedBatchDetailIds) {
|
|
247
|
-
const failedIds = new Set(failedBatchDetailIds);
|
|
248
|
-
const anyCompletedBatchHasUnmatchedWork = batchListInPlatformOrder.some((batch) => {
|
|
249
|
-
if (failedIds.has(batch.batchId)) {
|
|
250
|
-
return false;
|
|
251
|
-
}
|
|
252
|
-
const details = batchDetailsById[batch.batchId];
|
|
253
|
-
if (details != null &&
|
|
254
|
-
details.status === 'completed' &&
|
|
255
|
-
batchDetailsHasUnmatchedWork(details)) {
|
|
256
|
-
return true;
|
|
257
|
-
}
|
|
258
|
-
return details == null && batchListItemHasUnmatchedWork(batch);
|
|
259
|
-
});
|
|
260
|
-
for (const batch of batchListInPlatformOrder) {
|
|
261
|
-
if (failedIds.has(batch.batchId)) {
|
|
262
|
-
continue;
|
|
263
|
-
}
|
|
264
|
-
const details = batchDetailsById[batch.batchId];
|
|
265
|
-
if (details == null) {
|
|
266
|
-
if (batchListItemHasUnmatchedWork(batch)) {
|
|
267
|
-
return undefined;
|
|
268
|
-
}
|
|
269
|
-
continue;
|
|
270
|
-
}
|
|
271
|
-
if (details.status !== 'completed') {
|
|
272
|
-
continue;
|
|
273
|
-
}
|
|
274
|
-
if (anyCompletedBatchHasUnmatchedWork) {
|
|
275
|
-
if (batchDetailsHasUnmatchedWork(details)) {
|
|
276
|
-
return batch.batchId;
|
|
277
|
-
}
|
|
278
|
-
continue;
|
|
279
|
-
}
|
|
280
|
-
return batch.batchId;
|
|
281
|
-
}
|
|
282
|
-
return undefined;
|
|
283
|
-
}
|
|
284
|
-
function resolveBatchFile(file, batchId, transactionState) {
|
|
285
|
-
const matchedTransaction = file.matchedTransactionId != null
|
|
286
|
-
? (0, transactionSelector_1.getSupportedTransactionById)(transactionState, file.matchedTransactionId)
|
|
287
|
-
: undefined;
|
|
288
|
-
const candidates = file.candidates
|
|
289
|
-
?.map((ref) => {
|
|
290
|
-
const transaction = (0, transactionSelector_1.getSupportedTransactionById)(transactionState, ref.transactionId);
|
|
291
|
-
return transaction != null
|
|
292
|
-
? { transaction, matchScore: ref.matchScore }
|
|
293
|
-
: undefined;
|
|
294
|
-
})
|
|
295
|
-
.filter((candidate) => candidate != null);
|
|
296
|
-
return {
|
|
297
|
-
attachmentId: file.attachmentId,
|
|
298
|
-
batchId,
|
|
299
|
-
fileDownloadUrl: file.fileDownloadUrl,
|
|
300
|
-
fileId: file.fileId,
|
|
301
|
-
filename: file.filename,
|
|
302
|
-
filePreviewUrl: file.filePreviewUrl,
|
|
303
|
-
status: file.status,
|
|
304
|
-
matchedTransaction,
|
|
305
|
-
candidates: candidates != null && candidates.length > 0
|
|
306
|
-
? candidates
|
|
307
|
-
: undefined,
|
|
308
|
-
matchSource: file.matchSource,
|
|
309
64
|
};
|
|
310
65
|
}
|
|
@@ -12,149 +12,7 @@ export interface MissingReceiptsViewUIState {
|
|
|
12
12
|
sortKey: MissingReceiptsSortKey;
|
|
13
13
|
sortOrder: SortOrder;
|
|
14
14
|
}
|
|
15
|
-
/**
|
|
16
|
-
* Minimum trimmed query length before manual-match search calls the expense-automation API.
|
|
17
|
-
* Kept in sync with `ManualSearchInput` in web-components (fires search only when length ≥ this).
|
|
18
|
-
*/
|
|
19
|
-
export declare const MIN_MANUAL_TRANSACTION_SEARCH_LENGTH = 2;
|
|
20
|
-
export declare const toBatchStatusValue: (v: string) => "pending" | "processing" | "completed";
|
|
21
|
-
export type BatchStatusValue = ReturnType<typeof toBatchStatusValue>;
|
|
22
|
-
/** Canonical batch file statuses (API ingress may use aliases; see `parseBatchFileStatus`). */
|
|
23
|
-
export declare const BATCH_FILE_STATUSES: readonly ["exact_match", "possible_matches", "no_match", "failed"];
|
|
24
|
-
export declare const toBatchFileStatus: (v: string) => "failed" | "exact_match" | "possible_matches" | "no_match";
|
|
25
|
-
export type BatchFileStatus = ReturnType<typeof toBatchFileStatus>;
|
|
26
|
-
/** True for files that belong in the Unmatched tab (possible matches or no match). */
|
|
27
|
-
export declare function isUnmatchedTabFileStatus(status: BatchFileStatus): boolean;
|
|
28
|
-
export declare const toBulkUploadPhase: (v: string) => "completed" | "idle" | "uploading" | "matching";
|
|
29
|
-
export type BulkUploadPhase = ReturnType<typeof toBulkUploadPhase>;
|
|
30
|
-
export declare const toBulkUploadResultsTab: (v: string) => "completed" | "unmatched";
|
|
31
|
-
export type BulkUploadResultsTab = ReturnType<typeof toBulkUploadResultsTab>;
|
|
32
|
-
export declare const toMissingReceiptsTab: (v: string) => "completed" | "unmatched" | "missing";
|
|
33
|
-
export type MissingReceiptsTab = ReturnType<typeof toMissingReceiptsTab>;
|
|
34
|
-
export declare const toCompletedSubTab: (v: string) => "all" | "manual" | "ai_accountant";
|
|
35
|
-
export type CompletedSubTab = ReturnType<typeof toCompletedSubTab>;
|
|
36
|
-
export declare const toMatchSource: (v: string) => "manual" | "ai";
|
|
37
|
-
export type MatchSource = ReturnType<typeof toMatchSource>;
|
|
38
|
-
export declare const toBulkUploadSortKey: (v: string) => "date" | "amount" | "vendor" | "category" | "class";
|
|
39
|
-
export type BulkUploadSortKey = ReturnType<typeof toBulkUploadSortKey>;
|
|
40
|
-
export interface CandidateRef {
|
|
41
|
-
matchScore: number;
|
|
42
|
-
transactionId: ID;
|
|
43
|
-
}
|
|
44
|
-
export interface MatchCandidate {
|
|
45
|
-
amount: number;
|
|
46
|
-
currencyCode: string;
|
|
47
|
-
matchScore: number;
|
|
48
|
-
transactionDate: string;
|
|
49
|
-
transactionId: ID;
|
|
50
|
-
transactionType: string;
|
|
51
|
-
vendorName: string;
|
|
52
|
-
vendorLogo?: string;
|
|
53
|
-
}
|
|
54
|
-
export interface BatchFile {
|
|
55
|
-
attachmentId: ID;
|
|
56
|
-
fileDownloadUrl: string | null;
|
|
57
|
-
fileId: ID;
|
|
58
|
-
filename: string;
|
|
59
|
-
filePreviewUrl: string | null;
|
|
60
|
-
status: BatchFileStatus;
|
|
61
|
-
candidates?: CandidateRef[];
|
|
62
|
-
matchedTransactionId?: ID;
|
|
63
|
-
matchSource?: MatchSource;
|
|
64
|
-
}
|
|
65
|
-
export interface BatchSummary {
|
|
66
|
-
failed: number;
|
|
67
|
-
matched: number;
|
|
68
|
-
noMatch: number;
|
|
69
|
-
possibleMatches: number;
|
|
70
|
-
total: number;
|
|
71
|
-
}
|
|
72
|
-
export interface BatchStatus {
|
|
73
|
-
batchId: ID;
|
|
74
|
-
progress: {
|
|
75
|
-
processed: number;
|
|
76
|
-
total: number;
|
|
77
|
-
};
|
|
78
|
-
status: BatchStatusValue;
|
|
79
|
-
}
|
|
80
|
-
export interface BatchDetails {
|
|
81
|
-
batchId: ID;
|
|
82
|
-
createdAt: string;
|
|
83
|
-
files: BatchFile[];
|
|
84
|
-
status: BatchStatusValue;
|
|
85
|
-
summary: BatchSummary;
|
|
86
|
-
}
|
|
87
|
-
export interface BatchListItem {
|
|
88
|
-
batchId: ID;
|
|
89
|
-
createdAt: string;
|
|
90
|
-
failedCount: number;
|
|
91
|
-
matchedCount: number;
|
|
92
|
-
noMatchCount: number;
|
|
93
|
-
possibleMatchesCount: number;
|
|
94
|
-
status: string;
|
|
95
|
-
totalFiles: number;
|
|
96
|
-
}
|
|
97
|
-
export interface ManualSearchResult {
|
|
98
|
-
amount: number;
|
|
99
|
-
currencyCode: string;
|
|
100
|
-
entityId: string;
|
|
101
|
-
memo: string;
|
|
102
|
-
transactionDate: string;
|
|
103
|
-
transactionId: ID;
|
|
104
|
-
transactionType: string;
|
|
105
|
-
vendorName: string;
|
|
106
|
-
/** Present when API/merged transaction state supplies a logo URL. */
|
|
107
|
-
vendorLogo?: string;
|
|
108
|
-
}
|
|
109
|
-
export interface ManualSearchState {
|
|
110
|
-
fetchState: FetchStateAndError;
|
|
111
|
-
/** True while appending the next page (initial search uses fetchState only). */
|
|
112
|
-
isLoadingMore: boolean;
|
|
113
|
-
nextPageToken: string | null;
|
|
114
|
-
pageSize: number;
|
|
115
|
-
results: ManualSearchResult[];
|
|
116
|
-
searchQuery: string;
|
|
117
|
-
totalCount: number;
|
|
118
|
-
}
|
|
119
|
-
/** Same union as {@link CompletedSubTab} — used for completed-transactions API `match_type`. */
|
|
120
|
-
export type CompletedTransactionsMatchType = CompletedSubTab;
|
|
121
|
-
export interface CompletedTransactionsState {
|
|
122
|
-
fetchState: FetchStateAndError;
|
|
123
|
-
nextPageToken: string | null;
|
|
124
|
-
totalCount: number;
|
|
125
|
-
transactionIds: ID[];
|
|
126
|
-
}
|
|
127
|
-
export interface BulkUploadState {
|
|
128
|
-
batchDetailsById: Record<ID, BatchDetails>;
|
|
129
|
-
batchDetailsPaginationState: FetchStateAndError;
|
|
130
|
-
batchListByPeriod: Record<MonthYearPeriodId, BatchListItem[]>;
|
|
131
|
-
batchListFetchState: FetchStateAndError;
|
|
132
|
-
batchStatusById: Record<ID, BatchStatus>;
|
|
133
|
-
completedSubTab: CompletedSubTab;
|
|
134
|
-
completedTransactionsByPeriod: Record<string, CompletedTransactionsState>;
|
|
135
|
-
confirmMatchStatus: FetchStateAndError;
|
|
136
|
-
currentResultsTab: BulkUploadResultsTab;
|
|
137
|
-
failedBatchDetailIds: ID[];
|
|
138
|
-
/**
|
|
139
|
-
* Incremented when manual search Redux state is cleared so Unmatched `ManualSearchInput`
|
|
140
|
-
* instances remount and drop local query text.
|
|
141
|
-
*/
|
|
142
|
-
manualSearch: ManualSearchState;
|
|
143
|
-
manualSearchUiResetKey: number;
|
|
144
|
-
phase: BulkUploadPhase;
|
|
145
|
-
sortKey: BulkUploadSortKey;
|
|
146
|
-
sortOrder: SortOrder;
|
|
147
|
-
uploadProgress: number;
|
|
148
|
-
uploadStatus: FetchStateAndError;
|
|
149
|
-
currentBatchId?: ID;
|
|
150
|
-
}
|
|
151
15
|
export interface MissingReceiptsViewState extends FetchedState {
|
|
152
|
-
bulkUpload: BulkUploadState;
|
|
153
|
-
/**
|
|
154
|
-
* When set, the app shell should switch the Missing Receipts sub-tab (e.g. to Unmatched)
|
|
155
|
-
* once, then clear via `clearMissingReceiptsTabNavigation`.
|
|
156
|
-
*/
|
|
157
|
-
pendingMissingReceiptsTabNavigation: BulkUploadResultsTab | null;
|
|
158
16
|
refreshStatus: FetchStateAndError;
|
|
159
17
|
transactionIdsBySelectedPeriod: Record<MonthYearPeriodId, ID[]>;
|
|
160
18
|
uiState: MissingReceiptsViewUIState;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
exports.isUnmatchedTabFileStatus = isUnmatchedTabFileStatus;
|
|
3
|
+
exports.toMissingReceiptsSortKey = void 0;
|
|
5
4
|
const stringToUnion_1 = require("../../../commonStateTypes/stringToUnion");
|
|
6
5
|
const MISSING_RECEIPTS_SORT_KEYS = [
|
|
7
6
|
'vendor',
|
|
@@ -12,50 +11,3 @@ const MISSING_RECEIPTS_SORT_KEYS = [
|
|
|
12
11
|
];
|
|
13
12
|
const toMissingReceiptsSortKey = (v) => (0, stringToUnion_1.stringToUnion)(v, MISSING_RECEIPTS_SORT_KEYS);
|
|
14
13
|
exports.toMissingReceiptsSortKey = toMissingReceiptsSortKey;
|
|
15
|
-
// -- Bulk Receipt Upload Types --
|
|
16
|
-
/**
|
|
17
|
-
* Minimum trimmed query length before manual-match search calls the expense-automation API.
|
|
18
|
-
* Kept in sync with `ManualSearchInput` in web-components (fires search only when length ≥ this).
|
|
19
|
-
*/
|
|
20
|
-
exports.MIN_MANUAL_TRANSACTION_SEARCH_LENGTH = 2;
|
|
21
|
-
const BATCH_STATUS_VALUES = ['pending', 'processing', 'completed'];
|
|
22
|
-
const toBatchStatusValue = (v) => (0, stringToUnion_1.stringToUnion)(v.trim().toLowerCase(), BATCH_STATUS_VALUES);
|
|
23
|
-
exports.toBatchStatusValue = toBatchStatusValue;
|
|
24
|
-
/** Canonical batch file statuses (API ingress may use aliases; see `parseBatchFileStatus`). */
|
|
25
|
-
exports.BATCH_FILE_STATUSES = [
|
|
26
|
-
'exact_match',
|
|
27
|
-
'possible_matches',
|
|
28
|
-
'no_match',
|
|
29
|
-
'failed',
|
|
30
|
-
];
|
|
31
|
-
const toBatchFileStatus = (v) => (0, stringToUnion_1.stringToUnion)(v.trim().toLowerCase().replace(/-/g, '_'), exports.BATCH_FILE_STATUSES);
|
|
32
|
-
exports.toBatchFileStatus = toBatchFileStatus;
|
|
33
|
-
const UNMATCHED_TAB_FILE_STATUSES = ['possible_matches', 'no_match'];
|
|
34
|
-
/** True for files that belong in the Unmatched tab (possible matches or no match). */
|
|
35
|
-
function isUnmatchedTabFileStatus(status) {
|
|
36
|
-
return UNMATCHED_TAB_FILE_STATUSES.includes(status);
|
|
37
|
-
}
|
|
38
|
-
const BULK_UPLOAD_PHASES = ['idle', 'uploading', 'matching', 'completed'];
|
|
39
|
-
const toBulkUploadPhase = (v) => (0, stringToUnion_1.stringToUnion)(v.trim().toLowerCase(), BULK_UPLOAD_PHASES);
|
|
40
|
-
exports.toBulkUploadPhase = toBulkUploadPhase;
|
|
41
|
-
const BULK_UPLOAD_RESULTS_TABS = ['completed', 'unmatched'];
|
|
42
|
-
const toBulkUploadResultsTab = (v) => (0, stringToUnion_1.stringToUnion)(v.trim().toLowerCase(), BULK_UPLOAD_RESULTS_TABS);
|
|
43
|
-
exports.toBulkUploadResultsTab = toBulkUploadResultsTab;
|
|
44
|
-
const MISSING_RECEIPTS_TABS = ['missing', 'completed', 'unmatched'];
|
|
45
|
-
const toMissingReceiptsTab = (v) => (0, stringToUnion_1.stringToUnion)(v.trim().toLowerCase(), MISSING_RECEIPTS_TABS);
|
|
46
|
-
exports.toMissingReceiptsTab = toMissingReceiptsTab;
|
|
47
|
-
const COMPLETED_SUB_TABS = ['all', 'ai_accountant', 'manual'];
|
|
48
|
-
const toCompletedSubTab = (v) => (0, stringToUnion_1.stringToUnion)(v.trim().toLowerCase(), COMPLETED_SUB_TABS);
|
|
49
|
-
exports.toCompletedSubTab = toCompletedSubTab;
|
|
50
|
-
const MATCH_SOURCES = ['ai', 'manual'];
|
|
51
|
-
const toMatchSource = (v) => (0, stringToUnion_1.stringToUnion)(v.trim().toLowerCase(), MATCH_SOURCES);
|
|
52
|
-
exports.toMatchSource = toMatchSource;
|
|
53
|
-
const BULK_UPLOAD_SORT_KEYS = [
|
|
54
|
-
'amount',
|
|
55
|
-
'category',
|
|
56
|
-
'class',
|
|
57
|
-
'date',
|
|
58
|
-
'vendor',
|
|
59
|
-
];
|
|
60
|
-
const toBulkUploadSortKey = (v) => (0, stringToUnion_1.stringToUnion)(v.trim().toLowerCase(), BULK_UPLOAD_SORT_KEYS);
|
|
61
|
-
exports.toBulkUploadSortKey = toBulkUploadSortKey;
|
package/lib/zeniAPI.js
CHANGED
|
@@ -82,6 +82,8 @@ class ZeniAPIClient {
|
|
|
82
82
|
return (0, rxjs_1.from)(promise);
|
|
83
83
|
}
|
|
84
84
|
postFormData(restEndPoint, formData, signal) {
|
|
85
|
+
// Use apiHeaders for auth headers, then remove Content-Type
|
|
86
|
+
// so the browser sets multipart/form-data with the correct boundary
|
|
85
87
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
86
88
|
const { 'Content-Type': _contentType, ...headers } = this.apiHeaders();
|
|
87
89
|
const promise = (0, cross_fetch_1.fetch)(restEndPoint, {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zeniai/client-epic-state",
|
|
3
|
-
"version": "4.19.87
|
|
3
|
+
"version": "4.19.87",
|
|
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",
|
|
@@ -111,7 +111,7 @@
|
|
|
111
111
|
"find-dead-code": "ts-prune | grep -v '(used in module)'",
|
|
112
112
|
"find-unused-exports": "ts-unused-exports ./tsconfig.json",
|
|
113
113
|
"circular-dependency": "npx madge --circular --extensions ts ./src",
|
|
114
|
-
"build": "concurrently --kill-others-on-fail --handle-input \"echo 'Running ESLint...' && eslint . --ext .js,.jsx,.ts,.tsx\" \"echo 'TypeScript build in progress...' && time tsc && echo 'ESM build...' && tsc -p tsconfig.esm.json\"",
|
|
114
|
+
"build": "concurrently --kill-others-on-fail --handle-input \"echo 'Running ESLint...' && eslint . --ext .js,.jsx,.ts,.tsx\" \"echo 'TypeScript build in progress...' && time tsc && echo 'ESM build...' && tsc -p tsconfig.esm.json\" \"echo 'Running tests and typecheck...' && pnpm test\" && rimraf \"lib/**/__mocks__\" \"lib/**/__testHelpers__\"",
|
|
115
115
|
"only-build": "eslint . --ext .js,.jsx,.ts,.tsx && time tsc && tsc -p tsconfig.esm.json && rimraf \"lib/**/__mocks__\" \"lib/**/__testHelpers__\"",
|
|
116
116
|
"only-build-dev": "eslint . --ext .js,.jsx,.ts,.tsx && time tsc -p tsconfig.dev.json && tsc -p tsconfig.esm.dev.json && rimraf \"lib/**/__mocks__\" \"lib/**/__testHelpers__\"",
|
|
117
117
|
"format": "prettier --write --ignore-unknown \"src/**/*\" && pnpm lint",
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* After upload API success, hold the bar at 100% this long before transitioning to matching,
|
|
3
|
-
* so the fill animation can complete without an abrupt swap to the auto-matching banner.
|
|
4
|
-
*/
|
|
5
|
-
export const BULK_UPLOAD_BAR_COMPLETE_HOLD_MS = 400;
|
|
6
|
-
/**
|
|
7
|
-
* Max wall-clock wait for Pusher + batch-details resolution after upload. If exceeded, we stop
|
|
8
|
-
* fallback polling, clear the automatching banner (`phase` → completed), and refresh batch list.
|
|
9
|
-
*/
|
|
10
|
-
export const BULK_UPLOAD_AUTOMATCHING_TIMEOUT_MS = 300000;
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { concat, of, timer } from 'rxjs';
|
|
2
|
-
import { catchError, filter, mergeMap } from 'rxjs/operators';
|
|
3
|
-
import { createZeniAPIStatus, isSuccessResponse, } from '../../../../responsePayload';
|
|
4
|
-
import { BULK_UPLOAD_BAR_COMPLETE_HOLD_MS } from '../../bulkUploadTiming';
|
|
5
|
-
import { bulkUploadReceipts, bulkUploadReceiptsFailure, bulkUploadReceiptsSuccess, updateBulkUploadProgress, } from '../../reducers/missingReceiptsViewReducer';
|
|
6
|
-
export const bulkUploadReceiptsEpic = (actions$, _state$, zeniAPI) => actions$.pipe(filter(bulkUploadReceipts.match), mergeMap((action) => {
|
|
7
|
-
const { files } = action.payload;
|
|
8
|
-
const formData = new FormData();
|
|
9
|
-
for (const file of files) {
|
|
10
|
-
formData.append('files', file, file.name);
|
|
11
|
-
}
|
|
12
|
-
return zeniAPI
|
|
13
|
-
.postFormData(`${zeniAPI.apiEndPoints.communicationAgentMicroServiceBaseUrl}/1.0/receipts/bulk-upload`, formData)
|
|
14
|
-
.pipe(mergeMap((response) => {
|
|
15
|
-
if (isSuccessResponse(response) && response.data != null) {
|
|
16
|
-
const batchId = String(response.data.batch_id ?? '').trim();
|
|
17
|
-
if (batchId === '') {
|
|
18
|
-
return of(bulkUploadReceiptsFailure({
|
|
19
|
-
error: createZeniAPIStatus('Unexpected Error', 'Bulk upload succeeded but batch_id was missing or empty.'),
|
|
20
|
-
}));
|
|
21
|
-
}
|
|
22
|
-
return concat(of(updateBulkUploadProgress(100)), timer(BULK_UPLOAD_BAR_COMPLETE_HOLD_MS).pipe(mergeMap(() => of(bulkUploadReceiptsSuccess({ batchId })))));
|
|
23
|
-
}
|
|
24
|
-
return of(bulkUploadReceiptsFailure({ error: response.status }));
|
|
25
|
-
}), catchError((error) => of(bulkUploadReceiptsFailure({
|
|
26
|
-
error: createZeniAPIStatus('Unexpected Error', 'Bulk upload receipts API call errored out: ' +
|
|
27
|
-
JSON.stringify(error)),
|
|
28
|
-
}))));
|
|
29
|
-
}));
|
package/lib/esm/view/expenseAutomationView/epics/missingReceipts/confirmBulkUploadMatchEpic.js
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import { from } from 'rxjs';
|
|
2
|
-
import { catchError, exhaustMap, filter, mergeMap } from 'rxjs/operators';
|
|
3
|
-
import { openSnackbar } from '../../../../entity/snackbar/snackbarReducer';
|
|
4
|
-
import { createZeniAPIStatus, isSuccessResponse, } from '../../../../responsePayload';
|
|
5
|
-
import { confirmBulkUploadMatch, confirmBulkUploadMatchFailure, confirmBulkUploadMatchSuccess, } from '../../reducers/missingReceiptsViewReducer';
|
|
6
|
-
export const confirmBulkUploadMatchEpic = (actions$, _state$, zeniAPI) => actions$.pipe(filter(confirmBulkUploadMatch.match), exhaustMap((action) => {
|
|
7
|
-
const { batchId, attachmentId, transactionId, transactionType, fileName } = action.payload;
|
|
8
|
-
return zeniAPI
|
|
9
|
-
.postAndGetJSON(`${zeniAPI.apiEndPoints.communicationAgentMicroServiceBaseUrl}/1.0/receipts/manual-match`, {
|
|
10
|
-
attachment_id: attachmentId,
|
|
11
|
-
transaction_id: transactionId,
|
|
12
|
-
transaction_type: transactionType,
|
|
13
|
-
file_name: fileName,
|
|
14
|
-
})
|
|
15
|
-
.pipe(mergeMap((response) => {
|
|
16
|
-
if (isSuccessResponse(response)) {
|
|
17
|
-
return from([
|
|
18
|
-
confirmBulkUploadMatchSuccess({
|
|
19
|
-
batchId,
|
|
20
|
-
attachmentId,
|
|
21
|
-
transactionId,
|
|
22
|
-
}),
|
|
23
|
-
openSnackbar({
|
|
24
|
-
messageSection: 'receipt_match',
|
|
25
|
-
messageText: 'success',
|
|
26
|
-
type: 'success',
|
|
27
|
-
}),
|
|
28
|
-
]);
|
|
29
|
-
}
|
|
30
|
-
return from([
|
|
31
|
-
confirmBulkUploadMatchFailure({ error: response.status }),
|
|
32
|
-
openSnackbar({
|
|
33
|
-
messageSection: 'receipt_match',
|
|
34
|
-
messageText: 'failed',
|
|
35
|
-
type: 'error',
|
|
36
|
-
}),
|
|
37
|
-
]);
|
|
38
|
-
}), catchError((error) => from([
|
|
39
|
-
confirmBulkUploadMatchFailure({
|
|
40
|
-
error: createZeniAPIStatus('Unexpected Error', 'Confirm bulk upload match errored out: ' +
|
|
41
|
-
JSON.stringify(error)),
|
|
42
|
-
}),
|
|
43
|
-
openSnackbar({
|
|
44
|
-
messageSection: 'receipt_match',
|
|
45
|
-
messageText: 'failed',
|
|
46
|
-
type: 'error',
|
|
47
|
-
}),
|
|
48
|
-
])));
|
|
49
|
-
}));
|
package/lib/esm/view/expenseAutomationView/epics/missingReceipts/fetchBulkUploadBatchDetailsEpic.js
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { EMPTY, from, of } from 'rxjs';
|
|
2
|
-
import { catchError, filter, mergeMap } from 'rxjs/operators';
|
|
3
|
-
import { updateTransactions } from '../../../../entity/transaction/transactionReducer';
|
|
4
|
-
import { createZeniAPIStatus, isSuccessResponse, } from '../../../../responsePayload';
|
|
5
|
-
import { extractTransactionPayloadsFromBatchFiles, isBatchDetailsApiStatusCompleted, shouldFetchBatchDetailsForBatchId, toBatchDetails, } from '../../payload/missingReceiptsPayload';
|
|
6
|
-
import { fetchBulkUploadBatchDetails, fetchBulkUploadBatchDetailsFailure, fetchBulkUploadBatchDetailsSuccess, fetchBulkUploadBatches, } from '../../reducers/missingReceiptsViewReducer';
|
|
7
|
-
export const fetchBulkUploadBatchDetailsEpic = (actions$, state$, zeniAPI) => actions$.pipe(filter(fetchBulkUploadBatchDetails.match), mergeMap(() => {
|
|
8
|
-
const bulkUpload = state$.value.expenseAutomationMissingReceiptsViewState.bulkUpload;
|
|
9
|
-
const { currentBatchId } = bulkUpload;
|
|
10
|
-
if (currentBatchId == null) {
|
|
11
|
-
return of(fetchBulkUploadBatchDetailsFailure({
|
|
12
|
-
error: createZeniAPIStatus('Unexpected Error', 'No current batch ID available'),
|
|
13
|
-
}));
|
|
14
|
-
}
|
|
15
|
-
if (!shouldFetchBatchDetailsForBatchId(bulkUpload.batchStatusById, bulkUpload.batchListByPeriod, currentBatchId)) {
|
|
16
|
-
return EMPTY;
|
|
17
|
-
}
|
|
18
|
-
return zeniAPI
|
|
19
|
-
.getJSON(`${zeniAPI.apiEndPoints.communicationAgentMicroServiceBaseUrl}/1.0/batches/${currentBatchId}`)
|
|
20
|
-
.pipe(mergeMap((response) => {
|
|
21
|
-
if (!isSuccessResponse(response) ||
|
|
22
|
-
response.data == null ||
|
|
23
|
-
!isBatchDetailsApiStatusCompleted(response.data.status)) {
|
|
24
|
-
if (isSuccessResponse(response) && response.data != null) {
|
|
25
|
-
return EMPTY;
|
|
26
|
-
}
|
|
27
|
-
return of(fetchBulkUploadBatchDetailsFailure({ error: response.status }));
|
|
28
|
-
}
|
|
29
|
-
const actions = [];
|
|
30
|
-
const transactionPayloads = extractTransactionPayloadsFromBatchFiles(response.data.files);
|
|
31
|
-
if (transactionPayloads.length > 0) {
|
|
32
|
-
actions.push(updateTransactions(transactionPayloads, (value) => value.transaction_id));
|
|
33
|
-
}
|
|
34
|
-
actions.push(fetchBulkUploadBatchDetailsSuccess(toBatchDetails(response.data, zeniAPI.apiEndPoints.fileMicroServiceBaseUrl)));
|
|
35
|
-
actions.push(fetchBulkUploadBatches({ cacheOverride: true }));
|
|
36
|
-
return from(actions);
|
|
37
|
-
}), catchError((error) => of(fetchBulkUploadBatchDetailsFailure({
|
|
38
|
-
error: createZeniAPIStatus('Unexpected Error', 'Fetch batch details errored out: ' + JSON.stringify(error)),
|
|
39
|
-
}))));
|
|
40
|
-
}));
|