@zeniai/client-epic-state 4.19.37-betaVR2 → 4.19.37-betaVR21
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.d.ts +2 -1
- package/lib/epic.js +2 -1
- package/lib/esm/epic.js +2 -1
- package/lib/esm/index.js +2 -2
- package/lib/esm/view/expenseAutomationView/epics/missingReceipts/bulkUploadReceiptsEpic.js +4 -8
- package/lib/esm/view/expenseAutomationView/epics/missingReceipts/fetchBulkUploadBatchDetailsEpic.js +2 -2
- package/lib/esm/view/expenseAutomationView/epics/missingReceipts/fetchBulkUploadBatchesEpic.js +6 -1
- package/lib/esm/view/expenseAutomationView/epics/missingReceipts/fetchCompletedTransactionsEpic.js +7 -3
- package/lib/esm/view/expenseAutomationView/epics/missingReceipts/fetchMoreBatchDetailsEpic.js +36 -0
- package/lib/esm/view/expenseAutomationView/epics/missingReceipts/fetchMultipleBatchDetailsEpic.js +24 -15
- package/lib/esm/view/expenseAutomationView/epics/missingReceipts/searchTransactionsForManualMatchEpic.js +65 -24
- package/lib/esm/view/expenseAutomationView/epics/missingReceipts/watchBulkUploadBatchStatusEpic.js +32 -32
- package/lib/esm/view/expenseAutomationView/payload/missingReceiptsPayload.js +25 -18
- package/lib/esm/view/expenseAutomationView/reducers/missingReceiptsViewReducer.js +108 -22
- package/lib/esm/view/expenseAutomationView/selectors/missingReceiptsSelector.js +110 -13
- package/lib/index.d.ts +2 -2
- package/lib/index.js +36 -32
- package/lib/view/expenseAutomationView/epics/missingReceipts/bulkUploadReceiptsEpic.d.ts +2 -5
- package/lib/view/expenseAutomationView/epics/missingReceipts/bulkUploadReceiptsEpic.js +3 -7
- package/lib/view/expenseAutomationView/epics/missingReceipts/fetchBulkUploadBatchDetailsEpic.js +2 -2
- package/lib/view/expenseAutomationView/epics/missingReceipts/fetchBulkUploadBatchesEpic.js +6 -1
- package/lib/view/expenseAutomationView/epics/missingReceipts/fetchCompletedTransactionsEpic.js +6 -2
- package/lib/view/expenseAutomationView/epics/missingReceipts/fetchMoreBatchDetailsEpic.d.ts +8 -0
- package/lib/view/expenseAutomationView/epics/missingReceipts/fetchMoreBatchDetailsEpic.js +43 -0
- package/lib/view/expenseAutomationView/epics/missingReceipts/fetchMultipleBatchDetailsEpic.d.ts +3 -2
- package/lib/view/expenseAutomationView/epics/missingReceipts/fetchMultipleBatchDetailsEpic.js +25 -12
- package/lib/view/expenseAutomationView/epics/missingReceipts/searchTransactionsForManualMatchEpic.d.ts +11 -3
- package/lib/view/expenseAutomationView/epics/missingReceipts/searchTransactionsForManualMatchEpic.js +63 -22
- package/lib/view/expenseAutomationView/epics/missingReceipts/watchBulkUploadBatchStatusEpic.d.ts +5 -7
- package/lib/view/expenseAutomationView/epics/missingReceipts/watchBulkUploadBatchStatusEpic.js +28 -28
- package/lib/view/expenseAutomationView/payload/missingReceiptsPayload.d.ts +17 -35
- package/lib/view/expenseAutomationView/payload/missingReceiptsPayload.js +27 -20
- package/lib/view/expenseAutomationView/reducers/missingReceiptsViewReducer.d.ts +20 -5
- package/lib/view/expenseAutomationView/reducers/missingReceiptsViewReducer.js +110 -23
- package/lib/view/expenseAutomationView/selectorTypes/missingReceiptsSelectorTypes.d.ts +7 -1
- package/lib/view/expenseAutomationView/selectors/missingReceiptsSelector.js +110 -13
- package/lib/view/expenseAutomationView/types/missingReceiptsViewState.d.ts +17 -2
- package/package.json +2 -2
- package/lib/tsconfig.typecheck.tsbuildinfo +0 -1
|
@@ -6,6 +6,8 @@ import { TransactionPayload } from '../../../entity/transaction/payloadTypes/tra
|
|
|
6
6
|
import { SupportedTransactionPayload } from '../../../entity/transaction/transactionState';
|
|
7
7
|
import { ZeniAPIStatus } from '../../../responsePayload';
|
|
8
8
|
import type { BatchDetails, BatchListItem, BatchStatus, BulkUploadResultsTab, BulkUploadSortKey, BulkUploadState, CompletedSubTab, ManualSearchResult, MissingReceiptsViewState, MissingReceiptsViewUIState } from '../types/missingReceiptsViewState';
|
|
9
|
+
import type { MonthYearPeriodId } from '../../../commonStateTypes/timePeriod';
|
|
10
|
+
export declare const getCompletedTransactionsCacheKey: (periodId: MonthYearPeriodId, sortKey: BulkUploadSortKey, sortOrder: SortOrder, subTab: CompletedSubTab) => string;
|
|
9
11
|
export declare const initialBulkUploadState: BulkUploadState;
|
|
10
12
|
export declare const initialState: MissingReceiptsViewState;
|
|
11
13
|
export declare const fetchMissingReceipts: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[period: TimePeriod, minimumTransactionAmount: Amount, cacheOverride?: any, refreshViewInBackground?: any], {
|
|
@@ -34,9 +36,15 @@ export declare const fetchMissingReceipts: import("@reduxjs/toolkit").ActionCrea
|
|
|
34
36
|
batchId: ID;
|
|
35
37
|
}, "expenseAutomationMissingReceiptsView/bulkUploadReceiptsSuccess">, bulkUploadReceiptsFailure: import("@reduxjs/toolkit").ActionCreatorWithPayload<{
|
|
36
38
|
error: ZeniAPIStatus;
|
|
37
|
-
}, "expenseAutomationMissingReceiptsView/bulkUploadReceiptsFailure">,
|
|
39
|
+
}, "expenseAutomationMissingReceiptsView/bulkUploadReceiptsFailure">, pusherBatchStatusUpdate: import("@reduxjs/toolkit").ActionCreatorWithPayload<BatchStatus, "expenseAutomationMissingReceiptsView/pusherBatchStatusUpdate">, requestMissingReceiptsTabNavigation: import("@reduxjs/toolkit").ActionCreatorWithPayload<{
|
|
40
|
+
tab: BulkUploadResultsTab;
|
|
41
|
+
}, "expenseAutomationMissingReceiptsView/requestMissingReceiptsTabNavigation">, clearMissingReceiptsTabNavigation: import("@reduxjs/toolkit").ActionCreatorWithoutPayload<"expenseAutomationMissingReceiptsView/clearMissingReceiptsTabNavigation">, fetchBulkUploadBatchDetails: import("@reduxjs/toolkit").ActionCreatorWithoutPayload<"expenseAutomationMissingReceiptsView/fetchBulkUploadBatchDetails">, fetchBulkUploadBatchDetailsSuccess: import("@reduxjs/toolkit").ActionCreatorWithPayload<BatchDetails, "expenseAutomationMissingReceiptsView/fetchBulkUploadBatchDetailsSuccess">, fetchBulkUploadBatchDetailsFailure: import("@reduxjs/toolkit").ActionCreatorWithPayload<{
|
|
38
42
|
error: ZeniAPIStatus;
|
|
39
|
-
}, "expenseAutomationMissingReceiptsView/fetchBulkUploadBatchDetailsFailure">, storeBatchDetails: import("@reduxjs/toolkit").ActionCreatorWithPayload<BatchDetails, "expenseAutomationMissingReceiptsView/storeBatchDetails">,
|
|
43
|
+
}, "expenseAutomationMissingReceiptsView/fetchBulkUploadBatchDetailsFailure">, storeBatchDetails: import("@reduxjs/toolkit").ActionCreatorWithPayload<BatchDetails, "expenseAutomationMissingReceiptsView/storeBatchDetails">, batchDetailFetchFailed: import("@reduxjs/toolkit").ActionCreatorWithPayload<{
|
|
44
|
+
batchId: ID;
|
|
45
|
+
}, "expenseAutomationMissingReceiptsView/batchDetailFetchFailed">, setInitialBatchDetailsLoading: import("@reduxjs/toolkit").ActionCreatorWithoutPayload<"expenseAutomationMissingReceiptsView/setInitialBatchDetailsLoading">, fetchMoreBatchDetails: import("@reduxjs/toolkit").ActionCreatorWithoutPayload<"expenseAutomationMissingReceiptsView/fetchMoreBatchDetails">, fetchMoreBatchDetailsComplete: import("@reduxjs/toolkit").ActionCreatorWithoutPayload<"expenseAutomationMissingReceiptsView/fetchMoreBatchDetailsComplete">, fetchMoreBatchDetailsFailure: import("@reduxjs/toolkit").ActionCreatorWithPayload<{
|
|
46
|
+
error: ZeniAPIStatus;
|
|
47
|
+
}, "expenseAutomationMissingReceiptsView/fetchMoreBatchDetailsFailure">, fetchBulkUploadBatches: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[cacheOverride?: any], {
|
|
40
48
|
cacheOverride: any;
|
|
41
49
|
}, "expenseAutomationMissingReceiptsView/fetchBulkUploadBatches", never, never>, fetchBulkUploadBatchesSuccess: import("@reduxjs/toolkit").ActionCreatorWithPayload<{
|
|
42
50
|
batchList: BatchListItem[];
|
|
@@ -59,12 +67,19 @@ export declare const fetchMissingReceipts: import("@reduxjs/toolkit").ActionCrea
|
|
|
59
67
|
}, "expenseAutomationMissingReceiptsView/confirmBulkUploadMatchFailure">, setBulkUploadResultsTab: import("@reduxjs/toolkit").ActionCreatorWithPayload<BulkUploadResultsTab, "expenseAutomationMissingReceiptsView/setBulkUploadResultsTab">, setBulkUploadCompletedSubTab: import("@reduxjs/toolkit").ActionCreatorWithPayload<CompletedSubTab, "expenseAutomationMissingReceiptsView/setBulkUploadCompletedSubTab">, setBulkUploadSortConfig: import("@reduxjs/toolkit").ActionCreatorWithPayload<{
|
|
60
68
|
sortKey: BulkUploadSortKey;
|
|
61
69
|
sortOrder: SortOrder;
|
|
62
|
-
}, "expenseAutomationMissingReceiptsView/setBulkUploadSortConfig">, clearBulkUpload: import("@reduxjs/toolkit").ActionCreatorWithoutPayload<"expenseAutomationMissingReceiptsView/clearBulkUpload">, searchTransactionsForManualMatch: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[query: string], {
|
|
70
|
+
}, "expenseAutomationMissingReceiptsView/setBulkUploadSortConfig">, clearBulkUpload: import("@reduxjs/toolkit").ActionCreatorWithoutPayload<"expenseAutomationMissingReceiptsView/clearBulkUpload">, searchTransactionsForManualMatch: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[query: string, pageToken?: string | null | undefined], {
|
|
63
71
|
query: string;
|
|
64
|
-
|
|
72
|
+
pageToken: string | undefined;
|
|
73
|
+
}, "expenseAutomationMissingReceiptsView/searchTransactionsForManualMatch", never, never>, searchTransactionsForManualMatchSuccess: import("@reduxjs/toolkit").ActionCreatorWithPayload<{
|
|
74
|
+
append: boolean;
|
|
75
|
+
nextPageToken: string | null;
|
|
76
|
+
results: ManualSearchResult[];
|
|
77
|
+
totalCount: number;
|
|
78
|
+
}, "expenseAutomationMissingReceiptsView/searchTransactionsForManualMatchSuccess">, searchTransactionsForManualMatchFailure: import("@reduxjs/toolkit").ActionCreatorWithPayload<{
|
|
65
79
|
error: ZeniAPIStatus;
|
|
66
|
-
}, "expenseAutomationMissingReceiptsView/searchTransactionsForManualMatchFailure">, clearManualSearchResults: import("@reduxjs/toolkit").ActionCreatorWithoutPayload<"expenseAutomationMissingReceiptsView/clearManualSearchResults">, fetchCompletedTransactions: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[pageToken?: string | undefined], {
|
|
80
|
+
}, "expenseAutomationMissingReceiptsView/searchTransactionsForManualMatchFailure">, clearManualSearchResults: import("@reduxjs/toolkit").ActionCreatorWithoutPayload<"expenseAutomationMissingReceiptsView/clearManualSearchResults">, fetchCompletedTransactions: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[pageToken?: string | undefined, cacheOverride?: boolean | undefined], {
|
|
67
81
|
pageToken: string | undefined;
|
|
82
|
+
cacheOverride: boolean | undefined;
|
|
68
83
|
}, "expenseAutomationMissingReceiptsView/fetchCompletedTransactions", never, never>, fetchCompletedTransactionsSuccess: import("@reduxjs/toolkit").ActionCreatorWithPayload<{
|
|
69
84
|
isInitialLoad: boolean;
|
|
70
85
|
nextPageToken: string | null;
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
var _a;
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
exports.fetchCompletedTransactionsFailure = exports.fetchCompletedTransactionsSuccess = exports.fetchCompletedTransactions = exports.clearManualSearchResults = exports.searchTransactionsForManualMatchFailure = exports.searchTransactionsForManualMatchSuccess = exports.searchTransactionsForManualMatch = exports.clearBulkUpload = exports.setBulkUploadSortConfig = exports.setBulkUploadCompletedSubTab = exports.setBulkUploadResultsTab = exports.confirmBulkUploadMatchFailure = exports.confirmBulkUploadMatchSuccess = exports.confirmBulkUploadMatch = exports.fetchBulkUploadBatchesFailure = exports.fetchBulkUploadBatchesSuccess = exports.fetchBulkUploadBatches = exports.storeBatchDetails = exports.fetchBulkUploadBatchDetailsFailure = exports.fetchBulkUploadBatchDetailsSuccess = exports.fetchBulkUploadBatchDetails = exports.
|
|
4
|
+
exports.fetchCompletedTransactionsFailure = exports.fetchCompletedTransactionsSuccess = exports.fetchCompletedTransactions = exports.clearManualSearchResults = exports.searchTransactionsForManualMatchFailure = exports.searchTransactionsForManualMatchSuccess = exports.searchTransactionsForManualMatch = exports.clearBulkUpload = exports.setBulkUploadSortConfig = exports.setBulkUploadCompletedSubTab = exports.setBulkUploadResultsTab = exports.confirmBulkUploadMatchFailure = exports.confirmBulkUploadMatchSuccess = exports.confirmBulkUploadMatch = exports.fetchBulkUploadBatchesFailure = exports.fetchBulkUploadBatchesSuccess = exports.fetchBulkUploadBatches = exports.fetchMoreBatchDetailsFailure = exports.fetchMoreBatchDetailsComplete = exports.fetchMoreBatchDetails = exports.setInitialBatchDetailsLoading = exports.batchDetailFetchFailed = exports.storeBatchDetails = exports.fetchBulkUploadBatchDetailsFailure = exports.fetchBulkUploadBatchDetailsSuccess = exports.fetchBulkUploadBatchDetails = exports.clearMissingReceiptsTabNavigation = exports.requestMissingReceiptsTabNavigation = exports.pusherBatchStatusUpdate = exports.bulkUploadReceiptsFailure = exports.bulkUploadReceiptsSuccess = exports.updateBulkUploadProgress = exports.bulkUploadReceipts = exports.clearExpenseAutomationMissingReceiptsView = exports.markMissingReceiptAsDone = exports.updateMissingReceiptsUIState = exports.updateMissingReceiptUploadState = exports.uploadMissingReceiptSuccess = exports.fetchMissingReceiptsFailure = exports.fetchMissingReceiptsSuccess = exports.fetchMissingReceipts = exports.initialState = exports.initialBulkUploadState = exports.getCompletedTransactionsCacheKey = void 0;
|
|
5
5
|
const toolkit_1 = require("@reduxjs/toolkit");
|
|
6
6
|
const timePeriod_1 = require("../../../commonStateTypes/timePeriod");
|
|
7
|
+
const getCompletedTransactionsCacheKey = (periodId, sortKey, sortOrder, subTab) => `completed-${subTab}-${sortKey}-${sortOrder === 'ascending' ? 'asc' : 'desc'}-${periodId}`;
|
|
8
|
+
exports.getCompletedTransactionsCacheKey = getCompletedTransactionsCacheKey;
|
|
7
9
|
exports.initialBulkUploadState = {
|
|
8
10
|
batchDetailsById: {},
|
|
11
|
+
batchDetailsPaginationState: { fetchState: 'Not-Started', error: undefined },
|
|
9
12
|
batchListByPeriod: {},
|
|
10
13
|
batchListFetchState: { fetchState: 'Not-Started', error: undefined },
|
|
11
14
|
batchStatusById: {},
|
|
@@ -14,10 +17,15 @@ exports.initialBulkUploadState = {
|
|
|
14
17
|
confirmMatchStatus: { fetchState: 'Not-Started', error: undefined },
|
|
15
18
|
currentBatchId: undefined,
|
|
16
19
|
currentResultsTab: 'unmatched',
|
|
20
|
+
failedBatchDetailIds: [],
|
|
17
21
|
manualSearch: {
|
|
18
22
|
fetchState: { fetchState: 'Not-Started', error: undefined },
|
|
23
|
+
isLoadingMore: false,
|
|
24
|
+
nextPageToken: null,
|
|
25
|
+
pageSize: 25,
|
|
19
26
|
results: [],
|
|
20
27
|
searchQuery: '',
|
|
28
|
+
totalCount: 0,
|
|
21
29
|
},
|
|
22
30
|
phase: 'idle',
|
|
23
31
|
sortKey: 'date',
|
|
@@ -27,6 +35,7 @@ exports.initialBulkUploadState = {
|
|
|
27
35
|
};
|
|
28
36
|
exports.initialState = {
|
|
29
37
|
bulkUpload: exports.initialBulkUploadState,
|
|
38
|
+
pendingMissingReceiptsTabNavigation: null,
|
|
30
39
|
uploadReceiptStatusById: {},
|
|
31
40
|
refreshStatus: {
|
|
32
41
|
fetchState: 'Not-Started',
|
|
@@ -179,14 +188,16 @@ const expenseAutomationMissingReceiptsView = (0, toolkit_1.createSlice)({
|
|
|
179
188
|
};
|
|
180
189
|
draft.bulkUpload.phase = 'idle';
|
|
181
190
|
},
|
|
182
|
-
updateBulkUploadBatchStatus(draft, action) {
|
|
183
|
-
const batchStatus = action.payload;
|
|
184
|
-
draft.bulkUpload.batchStatusById[batchStatus.batchId] = batchStatus;
|
|
185
|
-
},
|
|
186
191
|
pusherBatchStatusUpdate(draft, action) {
|
|
187
192
|
const batchStatus = action.payload;
|
|
188
193
|
draft.bulkUpload.batchStatusById[batchStatus.batchId] = batchStatus;
|
|
189
194
|
},
|
|
195
|
+
requestMissingReceiptsTabNavigation(draft, action) {
|
|
196
|
+
draft.pendingMissingReceiptsTabNavigation = action.payload.tab;
|
|
197
|
+
},
|
|
198
|
+
clearMissingReceiptsTabNavigation(draft) {
|
|
199
|
+
draft.pendingMissingReceiptsTabNavigation = null;
|
|
200
|
+
},
|
|
190
201
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
191
202
|
fetchBulkUploadBatchDetails(_draft) {
|
|
192
203
|
// trigger epic, no state change needed
|
|
@@ -203,6 +214,58 @@ const expenseAutomationMissingReceiptsView = (0, toolkit_1.createSlice)({
|
|
|
203
214
|
storeBatchDetails(draft, action) {
|
|
204
215
|
const details = action.payload;
|
|
205
216
|
draft.bulkUpload.batchDetailsById[details.batchId] = details;
|
|
217
|
+
/**
|
|
218
|
+
* Multi-batch fetch paths use `storeBatchDetails` (not `fetchBulkUploadBatchDetailsSuccess`).
|
|
219
|
+
* Without this, `phase` stays `matching` after upload forever and the Unmatched tab keeps
|
|
220
|
+
* showing the skeleton (`bulkUploadPhase === 'matching'` in web-components).
|
|
221
|
+
*/
|
|
222
|
+
if (draft.bulkUpload.phase === 'matching' &&
|
|
223
|
+
draft.bulkUpload.currentBatchId === details.batchId) {
|
|
224
|
+
draft.bulkUpload.phase = 'completed';
|
|
225
|
+
}
|
|
226
|
+
},
|
|
227
|
+
batchDetailFetchFailed(draft, action) {
|
|
228
|
+
const { batchId } = action.payload;
|
|
229
|
+
if (!draft.bulkUpload.failedBatchDetailIds.includes(batchId)) {
|
|
230
|
+
draft.bulkUpload.failedBatchDetailIds.push(batchId);
|
|
231
|
+
}
|
|
232
|
+
if (draft.bulkUpload.phase === 'matching' &&
|
|
233
|
+
draft.bulkUpload.currentBatchId === batchId) {
|
|
234
|
+
draft.bulkUpload.phase = 'completed';
|
|
235
|
+
}
|
|
236
|
+
},
|
|
237
|
+
setInitialBatchDetailsLoading(draft) {
|
|
238
|
+
draft.bulkUpload.batchDetailsPaginationState = {
|
|
239
|
+
fetchState: 'In-Progress',
|
|
240
|
+
error: undefined,
|
|
241
|
+
};
|
|
242
|
+
},
|
|
243
|
+
fetchMoreBatchDetails(_draft) {
|
|
244
|
+
_draft.bulkUpload.batchDetailsPaginationState = {
|
|
245
|
+
fetchState: 'In-Progress',
|
|
246
|
+
error: undefined,
|
|
247
|
+
};
|
|
248
|
+
},
|
|
249
|
+
fetchMoreBatchDetailsComplete(draft) {
|
|
250
|
+
draft.bulkUpload.batchDetailsPaginationState = {
|
|
251
|
+
fetchState: 'Completed',
|
|
252
|
+
error: undefined,
|
|
253
|
+
};
|
|
254
|
+
const currentId = draft.bulkUpload.currentBatchId;
|
|
255
|
+
const currentDetails = currentId != null
|
|
256
|
+
? draft.bulkUpload.batchDetailsById[currentId]
|
|
257
|
+
: undefined;
|
|
258
|
+
if (draft.bulkUpload.phase === 'matching' &&
|
|
259
|
+
currentId != null &&
|
|
260
|
+
currentDetails?.status === 'completed') {
|
|
261
|
+
draft.bulkUpload.phase = 'completed';
|
|
262
|
+
}
|
|
263
|
+
},
|
|
264
|
+
fetchMoreBatchDetailsFailure(draft, action) {
|
|
265
|
+
draft.bulkUpload.batchDetailsPaginationState = {
|
|
266
|
+
fetchState: 'Error',
|
|
267
|
+
error: action.payload.error,
|
|
268
|
+
};
|
|
206
269
|
},
|
|
207
270
|
fetchBulkUploadBatches: {
|
|
208
271
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
@@ -277,7 +340,6 @@ const expenseAutomationMissingReceiptsView = (0, toolkit_1.createSlice)({
|
|
|
277
340
|
},
|
|
278
341
|
setBulkUploadCompletedSubTab(draft, action) {
|
|
279
342
|
draft.bulkUpload.completedSubTab = action.payload;
|
|
280
|
-
draft.bulkUpload.completedTransactionsByPeriod = {};
|
|
281
343
|
},
|
|
282
344
|
setBulkUploadSortConfig(draft, action) {
|
|
283
345
|
draft.bulkUpload.sortKey = action.payload.sortKey;
|
|
@@ -285,28 +347,51 @@ const expenseAutomationMissingReceiptsView = (0, toolkit_1.createSlice)({
|
|
|
285
347
|
},
|
|
286
348
|
clearBulkUpload(draft) {
|
|
287
349
|
Object.assign(draft.bulkUpload, exports.initialBulkUploadState);
|
|
350
|
+
draft.pendingMissingReceiptsTabNavigation = null;
|
|
288
351
|
},
|
|
289
352
|
searchTransactionsForManualMatch: {
|
|
290
353
|
reducer(draft, action) {
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
354
|
+
const { query, pageToken } = action.payload;
|
|
355
|
+
const isLoadMore = pageToken != null;
|
|
356
|
+
draft.bulkUpload.manualSearch.searchQuery = query;
|
|
357
|
+
if (isLoadMore) {
|
|
358
|
+
draft.bulkUpload.manualSearch.isLoadingMore = true;
|
|
359
|
+
}
|
|
360
|
+
else {
|
|
361
|
+
draft.bulkUpload.manualSearch.isLoadingMore = false;
|
|
362
|
+
draft.bulkUpload.manualSearch.results = [];
|
|
363
|
+
draft.bulkUpload.manualSearch.nextPageToken = null;
|
|
364
|
+
draft.bulkUpload.manualSearch.totalCount = 0;
|
|
365
|
+
draft.bulkUpload.manualSearch.fetchState = {
|
|
366
|
+
fetchState: 'In-Progress',
|
|
367
|
+
error: undefined,
|
|
368
|
+
};
|
|
369
|
+
}
|
|
297
370
|
},
|
|
298
|
-
prepare(query) {
|
|
299
|
-
return { payload: { query } };
|
|
371
|
+
prepare(query, pageToken) {
|
|
372
|
+
return { payload: { query, pageToken: pageToken ?? undefined } };
|
|
300
373
|
},
|
|
301
374
|
},
|
|
302
375
|
searchTransactionsForManualMatchSuccess(draft, action) {
|
|
303
|
-
|
|
376
|
+
const { append, nextPageToken, results, totalCount } = action.payload;
|
|
377
|
+
if (append) {
|
|
378
|
+
const merged = [...draft.bulkUpload.manualSearch.results, ...results];
|
|
379
|
+
const byId = new Map(merged.map((r) => [r.transactionId, r]));
|
|
380
|
+
draft.bulkUpload.manualSearch.results = Array.from(byId.values());
|
|
381
|
+
}
|
|
382
|
+
else {
|
|
383
|
+
draft.bulkUpload.manualSearch.results = results;
|
|
384
|
+
}
|
|
385
|
+
draft.bulkUpload.manualSearch.nextPageToken = nextPageToken;
|
|
386
|
+
draft.bulkUpload.manualSearch.totalCount = totalCount;
|
|
387
|
+
draft.bulkUpload.manualSearch.isLoadingMore = false;
|
|
304
388
|
draft.bulkUpload.manualSearch.fetchState = {
|
|
305
389
|
fetchState: 'Completed',
|
|
306
390
|
error: undefined,
|
|
307
391
|
};
|
|
308
392
|
},
|
|
309
393
|
searchTransactionsForManualMatchFailure(draft, action) {
|
|
394
|
+
draft.bulkUpload.manualSearch.isLoadingMore = false;
|
|
310
395
|
draft.bulkUpload.manualSearch.fetchState = {
|
|
311
396
|
fetchState: 'Error',
|
|
312
397
|
error: action.payload.error,
|
|
@@ -332,15 +417,16 @@ const expenseAutomationMissingReceiptsView = (0, toolkit_1.createSlice)({
|
|
|
332
417
|
}
|
|
333
418
|
}
|
|
334
419
|
},
|
|
335
|
-
prepare(pageToken) {
|
|
336
|
-
return { payload: { pageToken } };
|
|
420
|
+
prepare(pageToken, cacheOverride) {
|
|
421
|
+
return { payload: { pageToken, cacheOverride } };
|
|
337
422
|
},
|
|
338
423
|
},
|
|
339
424
|
fetchCompletedTransactionsSuccess(draft, action) {
|
|
340
425
|
const { transactionIds, nextPageToken, totalCount, selectedPeriod, isInitialLoad } = action.payload;
|
|
341
426
|
const periodId = (0, timePeriod_1.toMonthYearPeriodId)(selectedPeriod);
|
|
427
|
+
const cacheKey = (0, exports.getCompletedTransactionsCacheKey)(periodId, draft.bulkUpload.sortKey, draft.bulkUpload.sortOrder, draft.bulkUpload.completedSubTab);
|
|
342
428
|
if (isInitialLoad) {
|
|
343
|
-
draft.bulkUpload.completedTransactionsByPeriod[
|
|
429
|
+
draft.bulkUpload.completedTransactionsByPeriod[cacheKey] = {
|
|
344
430
|
transactionIds,
|
|
345
431
|
nextPageToken,
|
|
346
432
|
totalCount,
|
|
@@ -348,7 +434,7 @@ const expenseAutomationMissingReceiptsView = (0, toolkit_1.createSlice)({
|
|
|
348
434
|
};
|
|
349
435
|
}
|
|
350
436
|
else {
|
|
351
|
-
const existing = draft.bulkUpload.completedTransactionsByPeriod[
|
|
437
|
+
const existing = draft.bulkUpload.completedTransactionsByPeriod[cacheKey];
|
|
352
438
|
if (existing != null) {
|
|
353
439
|
existing.transactionIds.push(...transactionIds);
|
|
354
440
|
existing.nextPageToken = nextPageToken;
|
|
@@ -356,7 +442,7 @@ const expenseAutomationMissingReceiptsView = (0, toolkit_1.createSlice)({
|
|
|
356
442
|
existing.fetchState = { fetchState: 'Completed', error: undefined };
|
|
357
443
|
}
|
|
358
444
|
else {
|
|
359
|
-
draft.bulkUpload.completedTransactionsByPeriod[
|
|
445
|
+
draft.bulkUpload.completedTransactionsByPeriod[cacheKey] = {
|
|
360
446
|
transactionIds,
|
|
361
447
|
nextPageToken,
|
|
362
448
|
totalCount,
|
|
@@ -367,7 +453,8 @@ const expenseAutomationMissingReceiptsView = (0, toolkit_1.createSlice)({
|
|
|
367
453
|
},
|
|
368
454
|
fetchCompletedTransactionsFailure(draft, action) {
|
|
369
455
|
const periodId = (0, timePeriod_1.toMonthYearPeriodId)(action.payload.selectedPeriod);
|
|
370
|
-
const
|
|
456
|
+
const cacheKey = (0, exports.getCompletedTransactionsCacheKey)(periodId, draft.bulkUpload.sortKey, draft.bulkUpload.sortOrder, draft.bulkUpload.completedSubTab);
|
|
457
|
+
const existing = draft.bulkUpload.completedTransactionsByPeriod[cacheKey];
|
|
371
458
|
if (existing != null) {
|
|
372
459
|
existing.fetchState = {
|
|
373
460
|
fetchState: 'Error',
|
|
@@ -375,7 +462,7 @@ const expenseAutomationMissingReceiptsView = (0, toolkit_1.createSlice)({
|
|
|
375
462
|
};
|
|
376
463
|
}
|
|
377
464
|
else {
|
|
378
|
-
draft.bulkUpload.completedTransactionsByPeriod[
|
|
465
|
+
draft.bulkUpload.completedTransactionsByPeriod[cacheKey] = {
|
|
379
466
|
fetchState: { fetchState: 'Error', error: action.payload.error },
|
|
380
467
|
nextPageToken: null,
|
|
381
468
|
totalCount: 0,
|
|
@@ -387,7 +474,7 @@ const expenseAutomationMissingReceiptsView = (0, toolkit_1.createSlice)({
|
|
|
387
474
|
});
|
|
388
475
|
_a = expenseAutomationMissingReceiptsView.actions, exports.fetchMissingReceipts = _a.fetchMissingReceipts, exports.fetchMissingReceiptsSuccess = _a.fetchMissingReceiptsSuccess, exports.fetchMissingReceiptsFailure = _a.fetchMissingReceiptsFailure, exports.uploadMissingReceiptSuccess = _a.uploadMissingReceiptSuccess, exports.updateMissingReceiptUploadState = _a.updateMissingReceiptUploadState, exports.updateMissingReceiptsUIState = _a.updateMissingReceiptsUIState, exports.markMissingReceiptAsDone = _a.markMissingReceiptAsDone, exports.clearExpenseAutomationMissingReceiptsView = _a.clearExpenseAutomationMissingReceiptsView,
|
|
389
476
|
// Bulk Upload
|
|
390
|
-
exports.bulkUploadReceipts = _a.bulkUploadReceipts, exports.updateBulkUploadProgress = _a.updateBulkUploadProgress, exports.bulkUploadReceiptsSuccess = _a.bulkUploadReceiptsSuccess, exports.bulkUploadReceiptsFailure = _a.bulkUploadReceiptsFailure, exports.
|
|
477
|
+
exports.bulkUploadReceipts = _a.bulkUploadReceipts, exports.updateBulkUploadProgress = _a.updateBulkUploadProgress, exports.bulkUploadReceiptsSuccess = _a.bulkUploadReceiptsSuccess, exports.bulkUploadReceiptsFailure = _a.bulkUploadReceiptsFailure, exports.pusherBatchStatusUpdate = _a.pusherBatchStatusUpdate, exports.requestMissingReceiptsTabNavigation = _a.requestMissingReceiptsTabNavigation, exports.clearMissingReceiptsTabNavigation = _a.clearMissingReceiptsTabNavigation, exports.fetchBulkUploadBatchDetails = _a.fetchBulkUploadBatchDetails, exports.fetchBulkUploadBatchDetailsSuccess = _a.fetchBulkUploadBatchDetailsSuccess, exports.fetchBulkUploadBatchDetailsFailure = _a.fetchBulkUploadBatchDetailsFailure, exports.storeBatchDetails = _a.storeBatchDetails, exports.batchDetailFetchFailed = _a.batchDetailFetchFailed, exports.setInitialBatchDetailsLoading = _a.setInitialBatchDetailsLoading, exports.fetchMoreBatchDetails = _a.fetchMoreBatchDetails, exports.fetchMoreBatchDetailsComplete = _a.fetchMoreBatchDetailsComplete, exports.fetchMoreBatchDetailsFailure = _a.fetchMoreBatchDetailsFailure, exports.fetchBulkUploadBatches = _a.fetchBulkUploadBatches, exports.fetchBulkUploadBatchesSuccess = _a.fetchBulkUploadBatchesSuccess, exports.fetchBulkUploadBatchesFailure = _a.fetchBulkUploadBatchesFailure, exports.confirmBulkUploadMatch = _a.confirmBulkUploadMatch, exports.confirmBulkUploadMatchSuccess = _a.confirmBulkUploadMatchSuccess, exports.confirmBulkUploadMatchFailure = _a.confirmBulkUploadMatchFailure, exports.setBulkUploadResultsTab = _a.setBulkUploadResultsTab, exports.setBulkUploadCompletedSubTab = _a.setBulkUploadCompletedSubTab, exports.setBulkUploadSortConfig = _a.setBulkUploadSortConfig, exports.clearBulkUpload = _a.clearBulkUpload, exports.searchTransactionsForManualMatch = _a.searchTransactionsForManualMatch, exports.searchTransactionsForManualMatchSuccess = _a.searchTransactionsForManualMatchSuccess, exports.searchTransactionsForManualMatchFailure = _a.searchTransactionsForManualMatchFailure, exports.clearManualSearchResults = _a.clearManualSearchResults,
|
|
391
478
|
// Completed Transactions
|
|
392
479
|
exports.fetchCompletedTransactions = _a.fetchCompletedTransactions, exports.fetchCompletedTransactionsSuccess = _a.fetchCompletedTransactionsSuccess, exports.fetchCompletedTransactionsFailure = _a.fetchCompletedTransactionsFailure;
|
|
393
480
|
exports.default = expenseAutomationMissingReceiptsView.reducer;
|
|
@@ -15,9 +15,11 @@ export interface ResolvedCandidate {
|
|
|
15
15
|
}
|
|
16
16
|
export interface ResolvedBatchFile {
|
|
17
17
|
attachmentId: ID;
|
|
18
|
+
batchId: ID;
|
|
19
|
+
fileDownloadUrl: string | null;
|
|
18
20
|
fileId: ID;
|
|
19
21
|
filename: string;
|
|
20
|
-
filePreviewUrl: string;
|
|
22
|
+
filePreviewUrl: string | null;
|
|
21
23
|
status: BatchFileStatus;
|
|
22
24
|
candidates?: ResolvedCandidate[];
|
|
23
25
|
matchedTransaction?: SupportedTransaction;
|
|
@@ -25,6 +27,7 @@ export interface ResolvedBatchFile {
|
|
|
25
27
|
}
|
|
26
28
|
export interface BulkUploadSelectorData {
|
|
27
29
|
batchDetails: ResolvedBatchDetails | undefined;
|
|
30
|
+
batchDetailsPaginationState: FetchStateAndError;
|
|
28
31
|
batchList: BatchListItem[];
|
|
29
32
|
batchListFetchState: FetchStateAndError;
|
|
30
33
|
batchStatus: BatchStatus | undefined;
|
|
@@ -32,6 +35,7 @@ export interface BulkUploadSelectorData {
|
|
|
32
35
|
completedTransactions: CompletedTransactionsSelectorData;
|
|
33
36
|
confirmMatchStatus: FetchStateAndError;
|
|
34
37
|
currentResultsTab: BulkUploadResultsTab;
|
|
38
|
+
hasMoreBatchDetails: boolean;
|
|
35
39
|
manualSearch: ManualSearchState;
|
|
36
40
|
matchedFiles: ResolvedBatchFile[];
|
|
37
41
|
pastBatches: BatchListItem[];
|
|
@@ -64,6 +68,8 @@ export interface ResolvedBatchDetails {
|
|
|
64
68
|
export interface ExpenseAutomationMissingReceiptsViewSelector extends SelectorView {
|
|
65
69
|
bulkUpload: BulkUploadSelectorData;
|
|
66
70
|
completionStatus: CompletionStatusType;
|
|
71
|
+
/** When set, UI should switch bulk-upload sub-tab once (e.g. Unmatched on batch completion). */
|
|
72
|
+
pendingMissingReceiptsTabNavigation: BulkUploadResultsTab | null;
|
|
67
73
|
refreshStatus: FetchStateAndError;
|
|
68
74
|
transactions: SupportedTransaction[];
|
|
69
75
|
uiState: MissingReceiptsViewUIState;
|
|
@@ -9,6 +9,8 @@ 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");
|
|
12
14
|
function getExpenseAutomationMissingReceiptsView(state) {
|
|
13
15
|
const { expenseAutomationMissingReceiptsViewState, expenseAutomationViewState, transactionState, monthEndCloseChecksState, } = state;
|
|
14
16
|
const currentTenant = (0, tenantSelector_1.getCurrentTenant)(state);
|
|
@@ -53,14 +55,34 @@ function getExpenseAutomationMissingReceiptsView(state) {
|
|
|
53
55
|
const completionStatus = missingReceiptsCompletionStatus != null
|
|
54
56
|
? missingReceiptsCompletionStatus
|
|
55
57
|
: 'incomplete';
|
|
56
|
-
const { bulkUpload } = expenseAutomationMissingReceiptsViewState;
|
|
58
|
+
const { bulkUpload, pendingMissingReceiptsTabNavigation, } = expenseAutomationMissingReceiptsViewState;
|
|
57
59
|
const currentBatchStatus = bulkUpload.currentBatchId != null
|
|
58
60
|
? bulkUpload.batchStatusById[bulkUpload.currentBatchId]
|
|
59
61
|
: undefined;
|
|
60
|
-
const
|
|
61
|
-
? bulkUpload.
|
|
62
|
+
const batchList = monthYearPeriodId != null
|
|
63
|
+
? bulkUpload.batchListByPeriod[monthYearPeriodId] ?? []
|
|
64
|
+
: [];
|
|
65
|
+
/** API returns newest-first (`sort_order: desc`); primary batch uses oldest-first order. */
|
|
66
|
+
const batchListChronological = (0, orderBy_1.default)(batchList, (b) => new Date(b.createdAt).valueOf(), 'asc');
|
|
67
|
+
/**
|
|
68
|
+
* Primary "Unmatched" batch = first completed batch in chronological order, preferring the
|
|
69
|
+
* first batch that still has un_matched/no_match work when any completed batch has such work.
|
|
70
|
+
* Batches without details yet: wait if the list row still reports unmatched work; otherwise skip
|
|
71
|
+
* so we do not block the whole tab while older rows are not fetched yet.
|
|
72
|
+
*/
|
|
73
|
+
const primaryBatchIdFromDetailsOrder = getPrimaryBatchIdFromBatchListOrder(batchListChronological, bulkUpload.batchDetailsById, bulkUpload.failedBatchDetailIds);
|
|
74
|
+
/** During upload/matching, the in-flight batch may not appear in batchList yet — prefer currentBatchId. */
|
|
75
|
+
const unmatchedSectionBatchId = bulkUpload.phase === 'matching' || bulkUpload.phase === 'uploading'
|
|
76
|
+
? bulkUpload.currentBatchId ?? primaryBatchIdFromDetailsOrder
|
|
77
|
+
: primaryBatchIdFromDetailsOrder;
|
|
78
|
+
const completedBatchesInApiOrder = batchList.filter((b) => (0, missingReceiptsPayload_1.isBatchListStatusCompleted)(b.status));
|
|
79
|
+
const currentBatchDetails = unmatchedSectionBatchId != null
|
|
80
|
+
? bulkUpload.batchDetailsById[unmatchedSectionBatchId]
|
|
62
81
|
: undefined;
|
|
63
|
-
const resolvedFiles = currentBatchDetails?.files.map((file) => resolveBatchFile(file, transactionState));
|
|
82
|
+
const resolvedFiles = currentBatchDetails?.files.map((file) => resolveBatchFile(file, currentBatchDetails.batchId, transactionState));
|
|
83
|
+
const failedIds = new Set(bulkUpload.failedBatchDetailIds);
|
|
84
|
+
const hasMoreBatchDetails = completedBatchesInApiOrder.some((b) => bulkUpload.batchDetailsById[b.batchId] == null &&
|
|
85
|
+
!failedIds.has(b.batchId));
|
|
64
86
|
const getSortValue = (file) => {
|
|
65
87
|
const t = file.matchedTransaction;
|
|
66
88
|
if (t == null) {
|
|
@@ -98,11 +120,14 @@ function getExpenseAutomationMissingReceiptsView(state) {
|
|
|
98
120
|
: 0,
|
|
99
121
|
}
|
|
100
122
|
: { total: 0, processed: 0, percentage: 0 };
|
|
101
|
-
const batchList = monthYearPeriodId != null
|
|
102
|
-
? bulkUpload.batchListByPeriod[monthYearPeriodId] ?? []
|
|
103
|
-
: [];
|
|
104
123
|
const batchListFetchState = bulkUpload.batchListFetchState;
|
|
105
|
-
|
|
124
|
+
/**
|
|
125
|
+
* When primary batch is not resolved yet, still aggregate unmatched files from every batch
|
|
126
|
+
* that has details loaded (otherwise past is empty and receipts vanish from the UI).
|
|
127
|
+
*/
|
|
128
|
+
const pastBatches = unmatchedSectionBatchId != null
|
|
129
|
+
? batchList.filter((b) => b.batchId !== unmatchedSectionBatchId)
|
|
130
|
+
: batchList;
|
|
106
131
|
const pastUnmatchedFiles = pastBatches.flatMap((b) => {
|
|
107
132
|
const details = bulkUpload.batchDetailsById[b.batchId];
|
|
108
133
|
if (details == null) {
|
|
@@ -110,10 +135,13 @@ function getExpenseAutomationMissingReceiptsView(state) {
|
|
|
110
135
|
}
|
|
111
136
|
return details.files
|
|
112
137
|
.filter((f) => f.status === 'un_matched' || f.status === 'no_match')
|
|
113
|
-
.map((f) => resolveBatchFile(f, transactionState));
|
|
138
|
+
.map((f) => resolveBatchFile(f, b.batchId, transactionState));
|
|
114
139
|
});
|
|
115
|
-
const
|
|
116
|
-
? bulkUpload.
|
|
140
|
+
const completedTransactionsCacheKey = monthYearPeriodId != null
|
|
141
|
+
? (0, missingReceiptsViewReducer_1.getCompletedTransactionsCacheKey)(monthYearPeriodId, bulkUpload.sortKey, bulkUpload.sortOrder, bulkUpload.completedSubTab)
|
|
142
|
+
: undefined;
|
|
143
|
+
const completedTransactionsForPeriod = completedTransactionsCacheKey != null
|
|
144
|
+
? bulkUpload.completedTransactionsByPeriod[completedTransactionsCacheKey]
|
|
117
145
|
: undefined;
|
|
118
146
|
const completedTransactionIds = completedTransactionsForPeriod?.transactionIds ?? [];
|
|
119
147
|
const resolvedCompletedTransactions = (0, transactionSelector_1.getSupportedTransactionsByIds)(transactionState, completedTransactionIds);
|
|
@@ -126,6 +154,13 @@ function getExpenseAutomationMissingReceiptsView(state) {
|
|
|
126
154
|
summary: currentBatchDetails.summary,
|
|
127
155
|
}
|
|
128
156
|
: undefined;
|
|
157
|
+
const manualSearchResults = bulkUpload.manualSearch.results.map((r) => {
|
|
158
|
+
const t = (0, transactionSelector_1.getSupportedTransactionById)(transactionState, r.transactionId);
|
|
159
|
+
return {
|
|
160
|
+
...r,
|
|
161
|
+
vendorLogo: r.vendorLogo ?? t?.logo?.href,
|
|
162
|
+
};
|
|
163
|
+
});
|
|
129
164
|
return {
|
|
130
165
|
fetchState,
|
|
131
166
|
error,
|
|
@@ -134,6 +169,7 @@ function getExpenseAutomationMissingReceiptsView(state) {
|
|
|
134
169
|
refreshStatus,
|
|
135
170
|
transactions: sortedTransactions,
|
|
136
171
|
completionStatus,
|
|
172
|
+
pendingMissingReceiptsTabNavigation: pendingMissingReceiptsTabNavigation ?? null,
|
|
137
173
|
bulkUpload: {
|
|
138
174
|
phase: bulkUpload.phase,
|
|
139
175
|
uploadStatus: bulkUpload.uploadStatus,
|
|
@@ -160,12 +196,71 @@ function getExpenseAutomationMissingReceiptsView(state) {
|
|
|
160
196
|
pastBatches,
|
|
161
197
|
pastUnmatchedFiles,
|
|
162
198
|
confirmMatchStatus: bulkUpload.confirmMatchStatus,
|
|
163
|
-
|
|
199
|
+
hasMoreBatchDetails,
|
|
200
|
+
batchDetailsPaginationState: bulkUpload.batchDetailsPaginationState,
|
|
201
|
+
manualSearch: {
|
|
202
|
+
...bulkUpload.manualSearch,
|
|
203
|
+
results: manualSearchResults,
|
|
204
|
+
},
|
|
164
205
|
uploadProgress: bulkUpload.uploadProgress,
|
|
165
206
|
},
|
|
166
207
|
};
|
|
167
208
|
}
|
|
168
|
-
function
|
|
209
|
+
function batchDetailsHasUnmatchedWork(details) {
|
|
210
|
+
return details.files.some((f) => f.status === 'un_matched' || f.status === 'no_match');
|
|
211
|
+
}
|
|
212
|
+
/** List row summary before batch details load — aligns with unmatched / possible-match work. */
|
|
213
|
+
function batchListItemHasUnmatchedWork(batch) {
|
|
214
|
+
return batch.noMatchCount > 0 || batch.possibleMatchesCount > 0;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* `batchList` must be in chronological order (oldest first). Picks the primary batch for the
|
|
218
|
+
* top "Unmatched" section. Skips failed detail fetches.
|
|
219
|
+
*
|
|
220
|
+
* If details are not loaded for an older batch but the list row reports no unmatched work, we
|
|
221
|
+
* skip that batch so newer batches with loaded details can be primary (avoids an empty tab when
|
|
222
|
+
* the first fetch page only loaded newer batches). If the list reports unmatched work but details
|
|
223
|
+
* are still loading, we wait (undefined) so primary order stays correct once details arrive.
|
|
224
|
+
*/
|
|
225
|
+
function getPrimaryBatchIdFromBatchListOrder(batchListChronological, batchDetailsById, failedBatchDetailIds) {
|
|
226
|
+
const failedIds = new Set(failedBatchDetailIds);
|
|
227
|
+
const anyCompletedBatchHasUnmatchedWork = batchListChronological.some((b) => {
|
|
228
|
+
if (failedIds.has(b.batchId)) {
|
|
229
|
+
return false;
|
|
230
|
+
}
|
|
231
|
+
const d = batchDetailsById[b.batchId];
|
|
232
|
+
if (d != null &&
|
|
233
|
+
d.status === 'completed' &&
|
|
234
|
+
batchDetailsHasUnmatchedWork(d)) {
|
|
235
|
+
return true;
|
|
236
|
+
}
|
|
237
|
+
return d == null && batchListItemHasUnmatchedWork(b);
|
|
238
|
+
});
|
|
239
|
+
for (const batch of batchListChronological) {
|
|
240
|
+
if (failedIds.has(batch.batchId)) {
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
const details = batchDetailsById[batch.batchId];
|
|
244
|
+
if (details == null) {
|
|
245
|
+
if (batchListItemHasUnmatchedWork(batch)) {
|
|
246
|
+
return undefined;
|
|
247
|
+
}
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
if (details.status !== 'completed') {
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
if (anyCompletedBatchHasUnmatchedWork) {
|
|
254
|
+
if (batchDetailsHasUnmatchedWork(details)) {
|
|
255
|
+
return batch.batchId;
|
|
256
|
+
}
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
return batch.batchId;
|
|
260
|
+
}
|
|
261
|
+
return undefined;
|
|
262
|
+
}
|
|
263
|
+
function resolveBatchFile(file, batchId, transactionState) {
|
|
169
264
|
const matchedTransaction = file.matchedTransactionId != null
|
|
170
265
|
? (0, transactionSelector_1.getSupportedTransactionById)(transactionState, file.matchedTransactionId)
|
|
171
266
|
: undefined;
|
|
@@ -179,6 +274,8 @@ function resolveBatchFile(file, transactionState) {
|
|
|
179
274
|
.filter((c) => c != null);
|
|
180
275
|
return {
|
|
181
276
|
attachmentId: file.attachmentId,
|
|
277
|
+
batchId,
|
|
278
|
+
fileDownloadUrl: file.fileDownloadUrl,
|
|
182
279
|
fileId: file.fileId,
|
|
183
280
|
filename: file.filename,
|
|
184
281
|
filePreviewUrl: file.filePreviewUrl,
|
|
@@ -36,9 +36,10 @@ export interface MatchCandidate {
|
|
|
36
36
|
}
|
|
37
37
|
export interface BatchFile {
|
|
38
38
|
attachmentId: ID;
|
|
39
|
+
fileDownloadUrl: string | null;
|
|
39
40
|
fileId: ID;
|
|
40
41
|
filename: string;
|
|
41
|
-
filePreviewUrl: string;
|
|
42
|
+
filePreviewUrl: string | null;
|
|
42
43
|
status: BatchFileStatus;
|
|
43
44
|
candidates?: CandidateRef[];
|
|
44
45
|
matchedTransactionId?: ID;
|
|
@@ -85,11 +86,18 @@ export interface ManualSearchResult {
|
|
|
85
86
|
transactionId: ID;
|
|
86
87
|
transactionType: string;
|
|
87
88
|
vendorName: string;
|
|
89
|
+
/** Present when API/merged transaction state supplies a logo URL. */
|
|
90
|
+
vendorLogo?: string;
|
|
88
91
|
}
|
|
89
92
|
export interface ManualSearchState {
|
|
90
93
|
fetchState: FetchStateAndError;
|
|
94
|
+
/** True while appending the next page (initial search uses fetchState only). */
|
|
95
|
+
isLoadingMore: boolean;
|
|
96
|
+
nextPageToken: string | null;
|
|
97
|
+
pageSize: number;
|
|
91
98
|
results: ManualSearchResult[];
|
|
92
99
|
searchQuery: string;
|
|
100
|
+
totalCount: number;
|
|
93
101
|
}
|
|
94
102
|
export type CompletedTransactionsMatchType = 'all' | 'ai_accountant' | 'manual';
|
|
95
103
|
export interface CompletedTransactionsState {
|
|
@@ -100,13 +108,15 @@ export interface CompletedTransactionsState {
|
|
|
100
108
|
}
|
|
101
109
|
export interface BulkUploadState {
|
|
102
110
|
batchDetailsById: Record<ID, BatchDetails>;
|
|
111
|
+
batchDetailsPaginationState: FetchStateAndError;
|
|
103
112
|
batchListByPeriod: Record<MonthYearPeriodId, BatchListItem[]>;
|
|
104
113
|
batchListFetchState: FetchStateAndError;
|
|
105
114
|
batchStatusById: Record<ID, BatchStatus>;
|
|
106
115
|
completedSubTab: CompletedSubTab;
|
|
107
|
-
completedTransactionsByPeriod: Record<
|
|
116
|
+
completedTransactionsByPeriod: Record<string, CompletedTransactionsState>;
|
|
108
117
|
confirmMatchStatus: FetchStateAndError;
|
|
109
118
|
currentResultsTab: BulkUploadResultsTab;
|
|
119
|
+
failedBatchDetailIds: ID[];
|
|
110
120
|
manualSearch: ManualSearchState;
|
|
111
121
|
phase: BulkUploadPhase;
|
|
112
122
|
sortKey: BulkUploadSortKey;
|
|
@@ -117,6 +127,11 @@ export interface BulkUploadState {
|
|
|
117
127
|
}
|
|
118
128
|
export interface MissingReceiptsViewState extends FetchedState {
|
|
119
129
|
bulkUpload: BulkUploadState;
|
|
130
|
+
/**
|
|
131
|
+
* When set, the app shell should switch the Missing Receipts sub-tab (e.g. to Unmatched)
|
|
132
|
+
* once, then clear via `clearMissingReceiptsTabNavigation`.
|
|
133
|
+
*/
|
|
134
|
+
pendingMissingReceiptsTabNavigation: BulkUploadResultsTab | null;
|
|
120
135
|
refreshStatus: FetchStateAndError;
|
|
121
136
|
transactionIdsBySelectedPeriod: Record<MonthYearPeriodId, ID[]>;
|
|
122
137
|
uiState: MissingReceiptsViewUIState;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zeniai/client-epic-state",
|
|
3
|
-
"version": "4.19.37-
|
|
3
|
+
"version": "4.19.37-betaVR21",
|
|
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\"",
|
|
115
115
|
"only-build": "eslint . --ext .js,.jsx,.ts,.tsx && time tsc && tsc -p tsconfig.esm.json && rimraf \"lib/**/__mocks__\" \"lib/**/__testHelpers__\"",
|
|
116
116
|
"format": "prettier --write --ignore-unknown \"src/**/*\" && pnpm lint",
|
|
117
117
|
"format-watch": "(git diff --name-only --diff-filter=ACM && git ls-files --others --exclude-standard) | grep -E '\\.(ts|tsx)$' | xargs prettier --write",
|