@zeniai/client-epic-state 4.19.84-betaVR1 → 4.19.84-betaVR11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/esm/index.js +4 -1
- package/lib/esm/view/expenseAutomationView/epics/missingReceipts/bulkUploadReceiptsEpic.js +7 -3
- package/lib/esm/view/expenseAutomationView/epics/missingReceipts/fetchBulkUploadBatchDetailsEpic.js +1 -1
- 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 +14 -4
- package/lib/esm/view/expenseAutomationView/epics/missingReceipts/watchBulkUploadBatchStatusEpic.js +20 -3
- package/lib/esm/view/expenseAutomationView/payload/missingReceiptsPayload.js +38 -9
- package/lib/esm/view/expenseAutomationView/reducers/missingReceiptsViewReducer.js +25 -14
- package/lib/esm/view/expenseAutomationView/selectors/missingReceiptsSelector.js +32 -31
- package/lib/esm/view/expenseAutomationView/types/missingReceiptsViewState.js +39 -0
- package/lib/index.d.ts +2 -2
- package/lib/index.js +44 -33
- package/lib/tsconfig.typecheck.tsbuildinfo +1 -0
- package/lib/view/expenseAutomationView/epics/missingReceipts/bulkUploadReceiptsEpic.js +7 -3
- package/lib/view/expenseAutomationView/epics/missingReceipts/fetchBulkUploadBatchDetailsEpic.js +1 -1
- 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 +13 -3
- package/lib/view/expenseAutomationView/epics/missingReceipts/watchBulkUploadBatchStatusEpic.d.ts +7 -1
- package/lib/view/expenseAutomationView/epics/missingReceipts/watchBulkUploadBatchStatusEpic.js +20 -3
- package/lib/view/expenseAutomationView/payload/missingReceiptsPayload.d.ts +12 -1
- package/lib/view/expenseAutomationView/payload/missingReceiptsPayload.js +39 -9
- package/lib/view/expenseAutomationView/reducers/missingReceiptsViewReducer.d.ts +3 -3
- package/lib/view/expenseAutomationView/reducers/missingReceiptsViewReducer.js +25 -14
- package/lib/view/expenseAutomationView/selectors/missingReceiptsSelector.js +32 -31
- package/lib/view/expenseAutomationView/types/missingReceiptsViewState.d.ts +27 -9
- package/lib/view/expenseAutomationView/types/missingReceiptsViewState.js +49 -1
- package/package.json +2 -2
|
@@ -15,9 +15,13 @@ const bulkUploadReceiptsEpic = (actions$, _state$, zeniAPI) => actions$.pipe((0,
|
|
|
15
15
|
.postFormData(`${zeniAPI.apiEndPoints.communicationAgentMicroServiceBaseUrl}/1.0/receipts/bulk-upload`, formData)
|
|
16
16
|
.pipe((0, operators_1.mergeMap)((response) => {
|
|
17
17
|
if ((0, responsePayload_1.isSuccessResponse)(response) && response.data != null) {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
const batchId = String(response.data.batch_id ?? '').trim();
|
|
19
|
+
if (batchId === '') {
|
|
20
|
+
return (0, rxjs_1.of)((0, missingReceiptsViewReducer_1.bulkUploadReceiptsFailure)({
|
|
21
|
+
error: (0, responsePayload_1.createZeniAPIStatus)('Unexpected Error', 'Bulk upload succeeded but batch_id was missing or empty.'),
|
|
22
|
+
}));
|
|
23
|
+
}
|
|
24
|
+
return (0, rxjs_1.of)((0, missingReceiptsViewReducer_1.bulkUploadReceiptsSuccess)({ batchId }));
|
|
21
25
|
}
|
|
22
26
|
return (0, rxjs_1.of)((0, missingReceiptsViewReducer_1.bulkUploadReceiptsFailure)({ error: response.status }));
|
|
23
27
|
}), (0, operators_1.catchError)((error) => (0, rxjs_1.of)((0, missingReceiptsViewReducer_1.bulkUploadReceiptsFailure)({
|
package/lib/view/expenseAutomationView/epics/missingReceipts/fetchBulkUploadBatchDetailsEpic.js
CHANGED
|
@@ -35,7 +35,7 @@ const fetchBulkUploadBatchDetailsEpic = (actions$, state$, zeniAPI) => actions$.
|
|
|
35
35
|
actions.push((0, transactionReducer_1.updateTransactions)(transactionPayloads, (value) => value.transaction_id));
|
|
36
36
|
}
|
|
37
37
|
actions.push((0, missingReceiptsViewReducer_1.fetchBulkUploadBatchDetailsSuccess)((0, missingReceiptsPayload_1.toBatchDetails)(response.data, zeniAPI.apiEndPoints.fileMicroServiceBaseUrl)));
|
|
38
|
-
actions.push((0, missingReceiptsViewReducer_1.fetchBulkUploadBatches)(true));
|
|
38
|
+
actions.push((0, missingReceiptsViewReducer_1.fetchBulkUploadBatches)({ cacheOverride: true }));
|
|
39
39
|
return (0, rxjs_1.from)(actions);
|
|
40
40
|
}), (0, operators_1.catchError)((error) => (0, rxjs_1.of)((0, missingReceiptsViewReducer_1.fetchBulkUploadBatchDetailsFailure)({
|
|
41
41
|
error: (0, responsePayload_1.createZeniAPIStatus)('Unexpected Error', 'Fetch batch details errored out: ' + JSON.stringify(error)),
|
|
@@ -9,7 +9,7 @@ const responsePayload_1 = require("../../../../responsePayload");
|
|
|
9
9
|
const missingReceiptsPayload_1 = require("../../payload/missingReceiptsPayload");
|
|
10
10
|
const missingReceiptsViewReducer_1 = require("../../reducers/missingReceiptsViewReducer");
|
|
11
11
|
const fetchMultipleBatchDetailsEpic_1 = require("./fetchMultipleBatchDetailsEpic");
|
|
12
|
-
const MORE_BATCH_PAGE_SIZE =
|
|
12
|
+
const MORE_BATCH_PAGE_SIZE = 5;
|
|
13
13
|
const fetchMoreBatchDetailsEpic = (actions$, state$, zeniAPI) => actions$.pipe((0, operators_1.filter)(missingReceiptsViewReducer_1.fetchMoreBatchDetails.match),
|
|
14
14
|
/** One page at a time — avoids duplicate loads if IO + scroll fallback both fire. */
|
|
15
15
|
(0, operators_1.exhaustMap)(() => {
|
package/lib/view/expenseAutomationView/epics/missingReceipts/fetchMultipleBatchDetailsEpic.js
CHANGED
|
@@ -8,7 +8,7 @@ const transactionReducer_1 = require("../../../../entity/transaction/transaction
|
|
|
8
8
|
const responsePayload_1 = require("../../../../responsePayload");
|
|
9
9
|
const missingReceiptsPayload_1 = require("../../payload/missingReceiptsPayload");
|
|
10
10
|
const missingReceiptsViewReducer_1 = require("../../reducers/missingReceiptsViewReducer");
|
|
11
|
-
const INITIAL_BATCH_PAGE_SIZE =
|
|
11
|
+
const INITIAL_BATCH_PAGE_SIZE = 5;
|
|
12
12
|
function fetchBatchDetailsByIds(batchIds, zeniAPI) {
|
|
13
13
|
if (batchIds.length === 0) {
|
|
14
14
|
return rxjs_1.EMPTY;
|
package/lib/view/expenseAutomationView/epics/missingReceipts/searchTransactionsForManualMatchEpic.js
CHANGED
|
@@ -18,10 +18,20 @@ const transactionsViewState_1 = require("../../types/transactionsViewState");
|
|
|
18
18
|
exports.MANUAL_TRANSACTION_SEARCH_AUTO_CATEGORIZED = false;
|
|
19
19
|
const searchTransactionsForManualMatchEpic = (actions$, state$, zeniAPI) => {
|
|
20
20
|
const searchActions$ = actions$.pipe((0, operators_1.filter)(missingReceiptsViewReducer_1.searchTransactionsForManualMatch.match));
|
|
21
|
-
const pagination$ = searchActions$.pipe((0, operators_1.filter)((
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
const pagination$ = searchActions$.pipe((0, operators_1.filter)((action) => action.payload.pageToken != null));
|
|
22
|
+
/**
|
|
23
|
+
* No API for empty or 1-char queries; immediate merge so switchMap cancels any in-flight HTTP.
|
|
24
|
+
*/
|
|
25
|
+
const newSearchTooShortOrClear$ = searchActions$.pipe((0, operators_1.filter)((action) => action.payload.pageToken == null &&
|
|
26
|
+
action.payload.query.trim().length < 2));
|
|
27
|
+
const newSearchDebounced$ = searchActions$.pipe((0, operators_1.filter)((action) => action.payload.pageToken == null &&
|
|
28
|
+
action.payload.query.trim().length >= 2), (0, operators_1.debounceTime)(300));
|
|
29
|
+
return (0, rxjs_1.merge)(pagination$, newSearchTooShortOrClear$, newSearchDebounced$).pipe((0, operators_1.switchMap)((action) => {
|
|
24
30
|
const { query, pageToken } = action.payload;
|
|
31
|
+
if (pageToken == null &&
|
|
32
|
+
query.trim().length < 2) {
|
|
33
|
+
return rxjs_1.EMPTY;
|
|
34
|
+
}
|
|
25
35
|
const state = state$.value;
|
|
26
36
|
const { expenseAutomationViewState: { selectedPeriodByTenantId }, expenseAutomationMissingReceiptsViewState: { bulkUpload: { manualSearch, sortKey, sortOrder }, }, } = state;
|
|
27
37
|
const currentTenant = (0, tenantSelector_1.getCurrentTenant)(state);
|
package/lib/view/expenseAutomationView/epics/missingReceipts/watchBulkUploadBatchStatusEpic.d.ts
CHANGED
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
import { ActionsObservable, StateObservable } from 'redux-observable';
|
|
2
2
|
import { Observable } from 'rxjs';
|
|
3
|
+
import { openSnackbar } from '../../../../entity/snackbar/snackbarReducer';
|
|
3
4
|
import { updateTransactions } from '../../../../entity/transaction/transactionReducer';
|
|
4
5
|
import { RootState } from '../../../../reducer';
|
|
5
6
|
import { ZeniAPI } from '../../../../zeniAPI';
|
|
6
7
|
import { bulkUploadReceiptsSuccess, clearBulkUpload, fetchBulkUploadBatchDetailsSuccess, fetchBulkUploadBatches, pusherBatchStatusUpdate, requestMissingReceiptsTabNavigation } from '../../reducers/missingReceiptsViewReducer';
|
|
7
|
-
export type ActionType = ReturnType<typeof bulkUploadReceiptsSuccess> | ReturnType<typeof pusherBatchStatusUpdate> | ReturnType<typeof fetchBulkUploadBatchDetailsSuccess> | ReturnType<typeof fetchBulkUploadBatches> | ReturnType<typeof requestMissingReceiptsTabNavigation> | ReturnType<typeof updateTransactions> | ReturnType<typeof clearBulkUpload>;
|
|
8
|
+
export type ActionType = ReturnType<typeof bulkUploadReceiptsSuccess> | ReturnType<typeof pusherBatchStatusUpdate> | ReturnType<typeof fetchBulkUploadBatchDetailsSuccess> | ReturnType<typeof fetchBulkUploadBatches> | ReturnType<typeof requestMissingReceiptsTabNavigation> | ReturnType<typeof updateTransactions> | ReturnType<typeof clearBulkUpload> | ReturnType<typeof openSnackbar>;
|
|
8
9
|
/**
|
|
9
10
|
* On Pusher batch completion: refresh batch list (then fetchMultipleBatchDetailsEpic runs),
|
|
10
11
|
* and request switching to the Unmatched tab. Debounced to reduce duplicate refreshes.
|
|
11
12
|
*/
|
|
12
13
|
export declare const pusherBatchStatusCompletionEpic: (actions$: ActionsObservable<ActionType>) => Observable<ActionType>;
|
|
14
|
+
/**
|
|
15
|
+
* Fallback polling when Pusher is slow or unavailable. Network errors on a poll tick show one
|
|
16
|
+
* error snackbar per upload session (first failure only) so the user is not spammed every
|
|
17
|
+
* interval; Pusher or a later successful poll still completes the flow.
|
|
18
|
+
*/
|
|
13
19
|
export declare const pollBulkUploadBatchStatusEpic: (actions$: ActionsObservable<ActionType>, _state$: StateObservable<RootState>, zeniAPI: ZeniAPI) => Observable<ActionType>;
|
package/lib/view/expenseAutomationView/epics/missingReceipts/watchBulkUploadBatchStatusEpic.js
CHANGED
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.pollBulkUploadBatchStatusEpic = exports.pusherBatchStatusCompletionEpic = void 0;
|
|
4
4
|
const rxjs_1 = require("rxjs");
|
|
5
5
|
const operators_1 = require("rxjs/operators");
|
|
6
|
+
const snackbarReducer_1 = require("../../../../entity/snackbar/snackbarReducer");
|
|
6
7
|
const transactionReducer_1 = require("../../../../entity/transaction/transactionReducer");
|
|
7
8
|
const responsePayload_1 = require("../../../../responsePayload");
|
|
8
9
|
const missingReceiptsPayload_1 = require("../../payload/missingReceiptsPayload");
|
|
@@ -22,9 +23,15 @@ const pusherBatchStatusCompletionEpic = (actions$) => actions$.pipe((0, operator
|
|
|
22
23
|
(0, missingReceiptsViewReducer_1.requestMissingReceiptsTabNavigation)({ tab: 'unmatched' }),
|
|
23
24
|
])));
|
|
24
25
|
exports.pusherBatchStatusCompletionEpic = pusherBatchStatusCompletionEpic;
|
|
26
|
+
/**
|
|
27
|
+
* Fallback polling when Pusher is slow or unavailable. Network errors on a poll tick show one
|
|
28
|
+
* error snackbar per upload session (first failure only) so the user is not spammed every
|
|
29
|
+
* interval; Pusher or a later successful poll still completes the flow.
|
|
30
|
+
*/
|
|
25
31
|
const pollBulkUploadBatchStatusEpic = (actions$, _state$, zeniAPI) => actions$.pipe((0, operators_1.filter)(missingReceiptsViewReducer_1.bulkUploadReceiptsSuccess.match), (0, operators_1.mergeMap)((action) => {
|
|
26
32
|
const { batchId } = action.payload;
|
|
27
|
-
|
|
33
|
+
let pollErrorNotified = false;
|
|
34
|
+
return (0, rxjs_1.timer)(FALLBACK_FIRST_DELAY_MS, FALLBACK_POLL_INTERVAL_MS).pipe((0, operators_1.takeUntil)((0, rxjs_1.merge)(actions$.pipe((0, operators_1.filter)(missingReceiptsViewReducer_1.clearBulkUpload.match)), actions$.pipe((0, operators_1.filter)(missingReceiptsViewReducer_1.pusherBatchStatusUpdate.match), (0, operators_1.filter)((action) => action.payload.batchId === batchId)), actions$.pipe((0, operators_1.filter)(missingReceiptsViewReducer_1.fetchBulkUploadBatchDetailsSuccess.match), (0, operators_1.filter)((action) => action.payload.batchId === batchId)))), (0, operators_1.switchMap)(() => zeniAPI
|
|
28
35
|
.getJSON(`${zeniAPI.apiEndPoints.communicationAgentMicroServiceBaseUrl}/1.0/batches/${batchId}`)
|
|
29
36
|
.pipe((0, operators_1.mergeMap)((response) => {
|
|
30
37
|
/** Same batch-details URL; polls until `status` is completed (Pusher may win first). */
|
|
@@ -36,12 +43,22 @@ const pollBulkUploadBatchStatusEpic = (actions$, _state$, zeniAPI) => actions$.p
|
|
|
36
43
|
actions.push((0, transactionReducer_1.updateTransactions)(transactionPayloads, (value) => value.transaction_id));
|
|
37
44
|
}
|
|
38
45
|
actions.push((0, missingReceiptsViewReducer_1.fetchBulkUploadBatchDetailsSuccess)((0, missingReceiptsPayload_1.toBatchDetails)(response.data, zeniAPI.apiEndPoints.fileMicroServiceBaseUrl)));
|
|
39
|
-
actions.push((0, missingReceiptsViewReducer_1.fetchBulkUploadBatches)(true));
|
|
46
|
+
actions.push((0, missingReceiptsViewReducer_1.fetchBulkUploadBatches)({ cacheOverride: true }));
|
|
40
47
|
actions.push((0, missingReceiptsViewReducer_1.requestMissingReceiptsTabNavigation)({ tab: 'unmatched' }));
|
|
41
48
|
return (0, rxjs_1.from)(actions);
|
|
42
49
|
}
|
|
43
50
|
}
|
|
44
51
|
return rxjs_1.EMPTY;
|
|
45
|
-
}), (0, operators_1.catchError)(() =>
|
|
52
|
+
}), (0, operators_1.catchError)(() => {
|
|
53
|
+
if (!pollErrorNotified) {
|
|
54
|
+
pollErrorNotified = true;
|
|
55
|
+
return (0, rxjs_1.of)((0, snackbarReducer_1.openSnackbar)({
|
|
56
|
+
messageSection: 'receipts_upload',
|
|
57
|
+
messageText: 'failed',
|
|
58
|
+
type: 'error',
|
|
59
|
+
}));
|
|
60
|
+
}
|
|
61
|
+
return rxjs_1.EMPTY;
|
|
62
|
+
}))));
|
|
46
63
|
}));
|
|
47
64
|
exports.pollBulkUploadBatchStatusEpic = pollBulkUploadBatchStatusEpic;
|
|
@@ -3,7 +3,7 @@ import { TransactionPayload } from '../../../entity/transaction/payloadTypes/tra
|
|
|
3
3
|
import { SupportedTransactionPayload } from '../../../entity/transaction/transactionState';
|
|
4
4
|
import { ZeniAPIResponse } from '../../../responsePayload';
|
|
5
5
|
import type { MonthYearPeriodId } from '../../../commonStateTypes/timePeriod';
|
|
6
|
-
import type
|
|
6
|
+
import { type BatchDetails, type BatchFile, type BatchFileStatus, type BatchListItem, type BatchStatus, type BatchSummary, type ManualSearchResult } from '../types/missingReceiptsViewState';
|
|
7
7
|
export interface MissingReceiptsQueryPayload {
|
|
8
8
|
end_date: string;
|
|
9
9
|
is_attachment_missing_only: boolean;
|
|
@@ -54,6 +54,11 @@ export interface MatchedTransactionPayload {
|
|
|
54
54
|
export interface MatchCandidatePayload extends MatchedTransactionPayload {
|
|
55
55
|
match_score: number;
|
|
56
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* Wire payload for a batch file row. `status` may use legacy aliases (`matched`, `un_matched`,
|
|
59
|
+
* `possible_match`, `possible_matches`, etc.) until the backend is fully migrated; canonical
|
|
60
|
+
* values are applied in {@link parseBatchFileStatus} via {@link toBatchFile}.
|
|
61
|
+
*/
|
|
57
62
|
export interface BatchFilePayload {
|
|
58
63
|
attachment_id: string;
|
|
59
64
|
file_id: string;
|
|
@@ -78,6 +83,12 @@ export interface BatchDetailsResponsePayload {
|
|
|
78
83
|
};
|
|
79
84
|
}
|
|
80
85
|
export type BatchDetailsResponse = ZeniAPIResponse<BatchDetailsResponsePayload>;
|
|
86
|
+
/**
|
|
87
|
+
* Maps batch-details API `files[].status` strings to canonical {@link BatchFileStatus}.
|
|
88
|
+
* Legacy aliases are supported during rollout; unknown strings map to `failed` so Redux never
|
|
89
|
+
* holds untyped status values.
|
|
90
|
+
*/
|
|
91
|
+
export declare function parseBatchFileStatus(raw: string): BatchFileStatus;
|
|
81
92
|
export declare function toBatchFile(payload: BatchFilePayload, filesEndPoint?: string): BatchFile;
|
|
82
93
|
export declare function extractTransactionPayloadsFromBatchFiles(files: BatchFilePayload[]): SupportedTransactionPayload[];
|
|
83
94
|
export declare function toBatchSummary(payload: BatchDetailsResponsePayload['summary']): BatchSummary;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseBatchFileStatus = parseBatchFileStatus;
|
|
3
4
|
exports.toBatchFile = toBatchFile;
|
|
4
5
|
exports.extractTransactionPayloadsFromBatchFiles = extractTransactionPayloadsFromBatchFiles;
|
|
5
6
|
exports.toBatchSummary = toBatchSummary;
|
|
@@ -13,13 +14,40 @@ exports.toBatchListItem = toBatchListItem;
|
|
|
13
14
|
exports.supportedTransactionPayloadToManualSearchResult = supportedTransactionPayloadToManualSearchResult;
|
|
14
15
|
const urlPayload_1 = require("../../../commonPayloadTypes/urlPayload");
|
|
15
16
|
const transactionPayload_1 = require("../../../entity/transaction/payloadTypes/transactionPayload");
|
|
17
|
+
const missingReceiptsViewState_1 = require("../types/missingReceiptsViewState");
|
|
16
18
|
// -- Converter functions --
|
|
19
|
+
/**
|
|
20
|
+
* Maps batch-details API `files[].status` strings to canonical {@link BatchFileStatus}.
|
|
21
|
+
* Legacy aliases are supported during rollout; unknown strings map to `failed` so Redux never
|
|
22
|
+
* holds untyped status values.
|
|
23
|
+
*/
|
|
24
|
+
function parseBatchFileStatus(raw) {
|
|
25
|
+
const normalizedStatus = raw.trim().toLowerCase().replace(/-/g, '_');
|
|
26
|
+
switch (normalizedStatus) {
|
|
27
|
+
case 'exact_match':
|
|
28
|
+
case 'matched':
|
|
29
|
+
return 'exact_match';
|
|
30
|
+
case 'possible_matches':
|
|
31
|
+
case 'possible_match':
|
|
32
|
+
case 'possiblematch':
|
|
33
|
+
case 'un_matched':
|
|
34
|
+
case 'unmatched':
|
|
35
|
+
return 'possible_matches';
|
|
36
|
+
case 'no_match':
|
|
37
|
+
case 'nomatch':
|
|
38
|
+
return 'no_match';
|
|
39
|
+
case 'failed':
|
|
40
|
+
return 'failed';
|
|
41
|
+
default:
|
|
42
|
+
return 'failed';
|
|
43
|
+
}
|
|
44
|
+
}
|
|
17
45
|
function toBatchFile(payload, filesEndPoint) {
|
|
18
46
|
return {
|
|
19
47
|
attachmentId: payload.attachment_id,
|
|
20
|
-
candidates: payload.candidates?.map((
|
|
21
|
-
transactionId:
|
|
22
|
-
matchScore:
|
|
48
|
+
candidates: payload.candidates?.map((candidate) => ({
|
|
49
|
+
transactionId: candidate.transaction_id,
|
|
50
|
+
matchScore: candidate.match_score,
|
|
23
51
|
})),
|
|
24
52
|
fileDownloadUrl: payload.file_id != null && filesEndPoint != null
|
|
25
53
|
? `${filesEndPoint}/1.0/files/${payload.file_id}?query=${encodeURIComponent(JSON.stringify({ download_file: true }))}`
|
|
@@ -27,9 +55,11 @@ function toBatchFile(payload, filesEndPoint) {
|
|
|
27
55
|
fileId: payload.file_id,
|
|
28
56
|
filePreviewUrl: payload.file_preview_url,
|
|
29
57
|
filename: payload.filename,
|
|
30
|
-
matchSource: payload.match_source
|
|
58
|
+
matchSource: payload.match_source != null
|
|
59
|
+
? (0, missingReceiptsViewState_1.toMatchSource)(payload.match_source)
|
|
60
|
+
: undefined,
|
|
31
61
|
matchedTransactionId: payload.matched_transaction?.transaction_id,
|
|
32
|
-
status: payload.status,
|
|
62
|
+
status: parseBatchFileStatus(payload.status),
|
|
33
63
|
};
|
|
34
64
|
}
|
|
35
65
|
function extractTransactionPayloadsFromBatchFiles(files) {
|
|
@@ -79,8 +109,8 @@ function toBatchDetails(payload, filesEndPoint) {
|
|
|
79
109
|
return {
|
|
80
110
|
batchId: payload.batch_id,
|
|
81
111
|
createdAt: payload.created_at,
|
|
82
|
-
files: payload.files.map((
|
|
83
|
-
status: payload.status,
|
|
112
|
+
files: payload.files.map((file) => toBatchFile(file, filesEndPoint)),
|
|
113
|
+
status: (0, missingReceiptsViewState_1.toBatchStatusValue)(payload.status),
|
|
84
114
|
summary: toBatchSummary(payload.summary),
|
|
85
115
|
};
|
|
86
116
|
}
|
|
@@ -94,7 +124,7 @@ function isBatchListStatusCompleted(status) {
|
|
|
94
124
|
* statuses for UI; only `completed` rows should trigger batch-details requests.
|
|
95
125
|
*/
|
|
96
126
|
function filterBatchListItemsForBatchDetailsFetch(batches) {
|
|
97
|
-
return batches.filter((
|
|
127
|
+
return batches.filter((batch) => isBatchListStatusCompleted(batch.status));
|
|
98
128
|
}
|
|
99
129
|
/**
|
|
100
130
|
* Batch details API response `status` field — only completed payloads should be stored from
|
|
@@ -105,7 +135,7 @@ function isBatchDetailsApiStatusCompleted(status) {
|
|
|
105
135
|
}
|
|
106
136
|
function findBatchListItemForBatchId(batchListByPeriod, batchId) {
|
|
107
137
|
for (const list of Object.values(batchListByPeriod)) {
|
|
108
|
-
const found = list.find((
|
|
138
|
+
const found = list.find((batch) => batch.batchId === batchId);
|
|
109
139
|
if (found != null) {
|
|
110
140
|
return found;
|
|
111
141
|
}
|
|
@@ -5,7 +5,7 @@ import { MonthYearPeriod, TimePeriod } from '../../../commonStateTypes/timePerio
|
|
|
5
5
|
import { TransactionPayload } from '../../../entity/transaction/payloadTypes/transactionPayload';
|
|
6
6
|
import { SupportedTransactionPayload } from '../../../entity/transaction/transactionState';
|
|
7
7
|
import { ZeniAPIStatus } from '../../../responsePayload';
|
|
8
|
-
import type
|
|
8
|
+
import { type BatchDetails, type BatchListItem, type BatchStatus, type BulkUploadResultsTab, type BulkUploadSortKey, type BulkUploadState, type CompletedSubTab, type ManualSearchResult, type MissingReceiptsViewState, type MissingReceiptsViewUIState } from '../types/missingReceiptsViewState';
|
|
9
9
|
import type { MonthYearPeriodId } from '../../../commonStateTypes/timePeriod';
|
|
10
10
|
export declare const getCompletedTransactionsCacheKey: (periodId: MonthYearPeriodId, sortKey: BulkUploadSortKey, sortOrder: SortOrder, subTab: CompletedSubTab) => string;
|
|
11
11
|
export declare const initialBulkUploadState: BulkUploadState;
|
|
@@ -44,7 +44,7 @@ export declare const fetchMissingReceipts: import("@reduxjs/toolkit").ActionCrea
|
|
|
44
44
|
batchId: ID;
|
|
45
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
46
|
error: ZeniAPIStatus;
|
|
47
|
-
}, "expenseAutomationMissingReceiptsView/fetchMoreBatchDetailsFailure">, fetchBulkUploadBatches: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[input?:
|
|
47
|
+
}, "expenseAutomationMissingReceiptsView/fetchMoreBatchDetailsFailure">, fetchBulkUploadBatches: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[input?: {
|
|
48
48
|
cacheOverride?: boolean;
|
|
49
49
|
invalidateBatchDetailsCache?: boolean;
|
|
50
50
|
} | undefined], {
|
|
@@ -68,7 +68,7 @@ export declare const fetchMissingReceipts: import("@reduxjs/toolkit").ActionCrea
|
|
|
68
68
|
transactionId: ID;
|
|
69
69
|
}, "expenseAutomationMissingReceiptsView/confirmBulkUploadMatchSuccess">, confirmBulkUploadMatchFailure: import("@reduxjs/toolkit").ActionCreatorWithPayload<{
|
|
70
70
|
error: ZeniAPIStatus;
|
|
71
|
-
}, "expenseAutomationMissingReceiptsView/confirmBulkUploadMatchFailure">, setBulkUploadResultsTab: import("@reduxjs/toolkit").ActionCreatorWithPayload<
|
|
71
|
+
}, "expenseAutomationMissingReceiptsView/confirmBulkUploadMatchFailure">, setBulkUploadResultsTab: import("@reduxjs/toolkit").ActionCreatorWithPayload<"completed" | "unmatched", "expenseAutomationMissingReceiptsView/setBulkUploadResultsTab">, setBulkUploadCompletedSubTab: import("@reduxjs/toolkit").ActionCreatorWithPayload<"all" | "manual" | "ai_accountant", "expenseAutomationMissingReceiptsView/setBulkUploadCompletedSubTab">, setBulkUploadSortConfig: import("@reduxjs/toolkit").ActionCreatorWithPayload<{
|
|
72
72
|
sortKey: BulkUploadSortKey;
|
|
73
73
|
sortOrder: SortOrder;
|
|
74
74
|
}, "expenseAutomationMissingReceiptsView/setBulkUploadSortConfig">, clearBulkUpload: import("@reduxjs/toolkit").ActionCreatorWithoutPayload<"expenseAutomationMissingReceiptsView/clearBulkUpload">, searchTransactionsForManualMatch: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[query: string, pageToken?: string | null | undefined], {
|
|
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
4
4
|
exports.fetchCompletedTransactionsFailure = exports.fetchCompletedTransactionsSuccess = exports.fetchCompletedTransactions = exports.acknowledgeBulkUploadConfirmMatchComplete = 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 missingReceiptsViewState_1 = require("../types/missingReceiptsViewState");
|
|
7
8
|
const getCompletedTransactionsCacheKey = (periodId, sortKey, sortOrder, subTab) => `completed-${subTab}-${sortKey}-${sortOrder === 'ascending' ? 'asc' : 'desc'}-${periodId}`;
|
|
8
9
|
exports.getCompletedTransactionsCacheKey = getCompletedTransactionsCacheKey;
|
|
9
10
|
exports.initialBulkUploadState = {
|
|
@@ -290,19 +291,11 @@ const expenseAutomationMissingReceiptsView = (0, toolkit_1.createSlice)({
|
|
|
290
291
|
};
|
|
291
292
|
},
|
|
292
293
|
prepare(input) {
|
|
293
|
-
|
|
294
|
-
return {
|
|
295
|
-
payload: {
|
|
296
|
-
cacheOverride: input === true,
|
|
297
|
-
invalidateBatchDetailsCache: false,
|
|
298
|
-
},
|
|
299
|
-
};
|
|
300
|
-
}
|
|
301
|
-
const o = input ?? {};
|
|
294
|
+
const fetchOptions = input ?? {};
|
|
302
295
|
return {
|
|
303
296
|
payload: {
|
|
304
|
-
cacheOverride:
|
|
305
|
-
invalidateBatchDetailsCache:
|
|
297
|
+
cacheOverride: fetchOptions.cacheOverride === true,
|
|
298
|
+
invalidateBatchDetailsCache: fetchOptions.invalidateBatchDetailsCache === true,
|
|
306
299
|
},
|
|
307
300
|
};
|
|
308
301
|
},
|
|
@@ -355,17 +348,17 @@ const expenseAutomationMissingReceiptsView = (0, toolkit_1.createSlice)({
|
|
|
355
348
|
};
|
|
356
349
|
const details = draft.bulkUpload.batchDetailsById[batchId];
|
|
357
350
|
if (details != null) {
|
|
358
|
-
const file = details.files.find((
|
|
351
|
+
const file = details.files.find((batchFile) => batchFile.attachmentId === attachmentId);
|
|
359
352
|
if (file != null) {
|
|
360
353
|
const originalStatus = file.status;
|
|
361
|
-
file.status = '
|
|
354
|
+
file.status = 'exact_match';
|
|
362
355
|
file.matchedTransactionId = transactionId;
|
|
363
356
|
file.candidates = undefined;
|
|
364
357
|
details.summary.matched += 1;
|
|
365
358
|
if (originalStatus === 'no_match') {
|
|
366
359
|
details.summary.noMatch = Math.max(0, details.summary.noMatch - 1);
|
|
367
360
|
}
|
|
368
|
-
else {
|
|
361
|
+
else if (originalStatus === 'possible_matches') {
|
|
369
362
|
details.summary.possibleMatches = Math.max(0, details.summary.possibleMatches - 1);
|
|
370
363
|
}
|
|
371
364
|
}
|
|
@@ -397,6 +390,24 @@ const expenseAutomationMissingReceiptsView = (0, toolkit_1.createSlice)({
|
|
|
397
390
|
reducer(draft, action) {
|
|
398
391
|
const { query, pageToken } = action.payload;
|
|
399
392
|
const isLoadMore = pageToken != null;
|
|
393
|
+
const trimmed = query.trim();
|
|
394
|
+
/**
|
|
395
|
+
* Cleared or too-short query: no API (see epic). Reset fully (no In-Progress).
|
|
396
|
+
* Only bump `manualSearchUiResetKey` when clearing a real search — not when the UI
|
|
397
|
+
* dispatches `""` on every keystroke while length is below MIN (web-components always sends
|
|
398
|
+
* `onSearch("")` in that case), or ReceiptCard remounts and the input loses focus.
|
|
399
|
+
*/
|
|
400
|
+
if (!isLoadMore &&
|
|
401
|
+
trimmed.length < missingReceiptsViewState_1.MIN_MANUAL_TRANSACTION_SEARCH_LENGTH) {
|
|
402
|
+
const hadActiveSearch = draft.bulkUpload.manualSearch.searchQuery.trim().length >=
|
|
403
|
+
missingReceiptsViewState_1.MIN_MANUAL_TRANSACTION_SEARCH_LENGTH ||
|
|
404
|
+
draft.bulkUpload.manualSearch.results.length > 0;
|
|
405
|
+
draft.bulkUpload.manualSearch = exports.initialBulkUploadState.manualSearch;
|
|
406
|
+
if (hadActiveSearch) {
|
|
407
|
+
draft.bulkUpload.manualSearchUiResetKey += 1;
|
|
408
|
+
}
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
400
411
|
draft.bulkUpload.manualSearch.searchQuery = query;
|
|
401
412
|
if (isLoadMore) {
|
|
402
413
|
draft.bulkUpload.manualSearch.isLoadingMore = true;
|
|
@@ -11,6 +11,7 @@ const transactionSelector_1 = require("../../../entity/transaction/transactionSe
|
|
|
11
11
|
const expenseAutomationViewSelectorTypes_1 = require("../selectorTypes/expenseAutomationViewSelectorTypes");
|
|
12
12
|
const missingReceiptsPayload_1 = require("../payload/missingReceiptsPayload");
|
|
13
13
|
const missingReceiptsViewReducer_1 = require("../reducers/missingReceiptsViewReducer");
|
|
14
|
+
const missingReceiptsViewState_1 = require("../types/missingReceiptsViewState");
|
|
14
15
|
function getExpenseAutomationMissingReceiptsView(state) {
|
|
15
16
|
const { expenseAutomationMissingReceiptsViewState, expenseAutomationViewState, transactionState, monthEndCloseChecksState, } = state;
|
|
16
17
|
const currentTenant = (0, tenantSelector_1.getCurrentTenant)(state);
|
|
@@ -64,7 +65,7 @@ function getExpenseAutomationMissingReceiptsView(state) {
|
|
|
64
65
|
: [];
|
|
65
66
|
/**
|
|
66
67
|
* Primary "Unmatched" batch = first completed batch in platform list order, preferring the
|
|
67
|
-
* first batch that still has
|
|
68
|
+
* first batch that still has possible_matches/no_match work when any completed batch has such work.
|
|
68
69
|
* Batches without details yet: wait if the list row still reports unmatched work; otherwise skip
|
|
69
70
|
* so we do not block the whole tab while other rows are not fetched yet.
|
|
70
71
|
*/
|
|
@@ -73,41 +74,41 @@ function getExpenseAutomationMissingReceiptsView(state) {
|
|
|
73
74
|
const unmatchedSectionBatchId = bulkUpload.phase === 'matching' || bulkUpload.phase === 'uploading'
|
|
74
75
|
? bulkUpload.currentBatchId ?? primaryBatchIdFromDetailsOrder
|
|
75
76
|
: primaryBatchIdFromDetailsOrder;
|
|
76
|
-
const completedBatchesInApiOrder = batchList.filter((
|
|
77
|
+
const completedBatchesInApiOrder = batchList.filter((batchListItem) => (0, missingReceiptsPayload_1.isBatchListStatusCompleted)(batchListItem.status));
|
|
77
78
|
const currentBatchDetails = unmatchedSectionBatchId != null
|
|
78
79
|
? bulkUpload.batchDetailsById[unmatchedSectionBatchId]
|
|
79
80
|
: undefined;
|
|
80
81
|
const resolvedFiles = currentBatchDetails?.files.map((file) => resolveBatchFile(file, currentBatchDetails.batchId, transactionState));
|
|
81
82
|
const failedIds = new Set(bulkUpload.failedBatchDetailIds);
|
|
82
|
-
const hasMoreBatchDetails = completedBatchesInApiOrder.some((
|
|
83
|
-
!failedIds.has(
|
|
83
|
+
const hasMoreBatchDetails = completedBatchesInApiOrder.some((batchListItem) => bulkUpload.batchDetailsById[batchListItem.batchId] == null &&
|
|
84
|
+
!failedIds.has(batchListItem.batchId));
|
|
84
85
|
const getSortValue = (file) => {
|
|
85
|
-
const
|
|
86
|
-
if (
|
|
86
|
+
const matchedTransaction = file.matchedTransaction;
|
|
87
|
+
if (matchedTransaction == null) {
|
|
87
88
|
return null;
|
|
88
89
|
}
|
|
89
90
|
switch (bulkUpload.sortKey) {
|
|
90
91
|
case 'vendor':
|
|
91
|
-
return (
|
|
92
|
+
return (matchedTransaction.vendorName ?? matchedTransaction.description)?.toLowerCase() ?? null;
|
|
92
93
|
case 'category': {
|
|
93
|
-
const line =
|
|
94
|
+
const line = matchedTransaction.lines?.[0];
|
|
94
95
|
return line?.account?.accountName?.toLowerCase() ?? null;
|
|
95
96
|
}
|
|
96
97
|
case 'class': {
|
|
97
|
-
const line =
|
|
98
|
+
const line = matchedTransaction.lines?.[0];
|
|
98
99
|
return line?.class?.className?.toLowerCase() ?? null;
|
|
99
100
|
}
|
|
100
101
|
case 'date':
|
|
101
|
-
return
|
|
102
|
+
return matchedTransaction.date.valueOf();
|
|
102
103
|
case 'amount':
|
|
103
|
-
return
|
|
104
|
+
return matchedTransaction.amount.amount;
|
|
104
105
|
default:
|
|
105
106
|
return null;
|
|
106
107
|
}
|
|
107
108
|
};
|
|
108
109
|
const bulkUploadSortDirection = bulkUpload.sortOrder === 'ascending' ? 'asc' : 'desc';
|
|
109
|
-
const matchedFiles = (0, orderBy_1.default)(resolvedFiles?.filter((
|
|
110
|
-
const unmatchedFiles = (0, orderBy_1.default)(resolvedFiles?.filter((
|
|
110
|
+
const matchedFiles = (0, orderBy_1.default)(resolvedFiles?.filter((resolvedFile) => resolvedFile.status === 'exact_match') ?? [], getSortValue, bulkUploadSortDirection);
|
|
111
|
+
const unmatchedFiles = (0, orderBy_1.default)(resolvedFiles?.filter((resolvedFile) => (0, missingReceiptsViewState_1.isUnmatchedTabFileStatus)(resolvedFile.status)) ?? [], getSortValue, bulkUploadSortDirection);
|
|
111
112
|
const progress = currentBatchStatus != null
|
|
112
113
|
? {
|
|
113
114
|
...currentBatchStatus.progress,
|
|
@@ -124,16 +125,16 @@ function getExpenseAutomationMissingReceiptsView(state) {
|
|
|
124
125
|
* that has details loaded (otherwise past is empty and receipts vanish from the UI).
|
|
125
126
|
*/
|
|
126
127
|
const pastBatches = unmatchedSectionBatchId != null
|
|
127
|
-
? batchList.filter((
|
|
128
|
+
? batchList.filter((batchListItem) => batchListItem.batchId !== unmatchedSectionBatchId)
|
|
128
129
|
: batchList;
|
|
129
|
-
const pastUnmatchedFiles = pastBatches.flatMap((
|
|
130
|
-
const details = bulkUpload.batchDetailsById[
|
|
130
|
+
const pastUnmatchedFiles = pastBatches.flatMap((pastBatch) => {
|
|
131
|
+
const details = bulkUpload.batchDetailsById[pastBatch.batchId];
|
|
131
132
|
if (details == null) {
|
|
132
133
|
return [];
|
|
133
134
|
}
|
|
134
135
|
return details.files
|
|
135
|
-
.filter((
|
|
136
|
-
.map((
|
|
136
|
+
.filter((batchFile) => (0, missingReceiptsViewState_1.isUnmatchedTabFileStatus)(batchFile.status))
|
|
137
|
+
.map((batchFile) => resolveBatchFile(batchFile, pastBatch.batchId, transactionState));
|
|
137
138
|
});
|
|
138
139
|
const completedTransactionsCacheKey = monthYearPeriodId != null
|
|
139
140
|
? (0, missingReceiptsViewReducer_1.getCompletedTransactionsCacheKey)(monthYearPeriodId, bulkUpload.sortKey, bulkUpload.sortOrder, bulkUpload.completedSubTab)
|
|
@@ -152,11 +153,11 @@ function getExpenseAutomationMissingReceiptsView(state) {
|
|
|
152
153
|
summary: currentBatchDetails.summary,
|
|
153
154
|
}
|
|
154
155
|
: undefined;
|
|
155
|
-
const manualSearchResults = bulkUpload.manualSearch.results.map((
|
|
156
|
-
const
|
|
156
|
+
const manualSearchResults = bulkUpload.manualSearch.results.map((result) => {
|
|
157
|
+
const transaction = (0, transactionSelector_1.getSupportedTransactionById)(transactionState, result.transactionId);
|
|
157
158
|
return {
|
|
158
|
-
...
|
|
159
|
-
vendorLogo:
|
|
159
|
+
...result,
|
|
160
|
+
vendorLogo: result.vendorLogo ?? transaction?.logo?.href,
|
|
160
161
|
};
|
|
161
162
|
});
|
|
162
163
|
return {
|
|
@@ -206,7 +207,7 @@ function getExpenseAutomationMissingReceiptsView(state) {
|
|
|
206
207
|
};
|
|
207
208
|
}
|
|
208
209
|
function batchDetailsHasUnmatchedWork(details) {
|
|
209
|
-
return details.files.some((
|
|
210
|
+
return details.files.some((file) => (0, missingReceiptsViewState_1.isUnmatchedTabFileStatus)(file.status));
|
|
210
211
|
}
|
|
211
212
|
/** List row summary before batch details load — aligns with unmatched / possible-match work. */
|
|
212
213
|
function batchListItemHasUnmatchedWork(batch) {
|
|
@@ -223,17 +224,17 @@ function batchListItemHasUnmatchedWork(batch) {
|
|
|
223
224
|
*/
|
|
224
225
|
function getPrimaryBatchIdFromBatchListOrder(batchListInPlatformOrder, batchDetailsById, failedBatchDetailIds) {
|
|
225
226
|
const failedIds = new Set(failedBatchDetailIds);
|
|
226
|
-
const anyCompletedBatchHasUnmatchedWork = batchListInPlatformOrder.some((
|
|
227
|
-
if (failedIds.has(
|
|
227
|
+
const anyCompletedBatchHasUnmatchedWork = batchListInPlatformOrder.some((batch) => {
|
|
228
|
+
if (failedIds.has(batch.batchId)) {
|
|
228
229
|
return false;
|
|
229
230
|
}
|
|
230
|
-
const
|
|
231
|
-
if (
|
|
232
|
-
|
|
233
|
-
batchDetailsHasUnmatchedWork(
|
|
231
|
+
const details = batchDetailsById[batch.batchId];
|
|
232
|
+
if (details != null &&
|
|
233
|
+
details.status === 'completed' &&
|
|
234
|
+
batchDetailsHasUnmatchedWork(details)) {
|
|
234
235
|
return true;
|
|
235
236
|
}
|
|
236
|
-
return
|
|
237
|
+
return details == null && batchListItemHasUnmatchedWork(batch);
|
|
237
238
|
});
|
|
238
239
|
for (const batch of batchListInPlatformOrder) {
|
|
239
240
|
if (failedIds.has(batch.batchId)) {
|
|
@@ -270,7 +271,7 @@ function resolveBatchFile(file, batchId, transactionState) {
|
|
|
270
271
|
? { transaction, matchScore: ref.matchScore }
|
|
271
272
|
: undefined;
|
|
272
273
|
})
|
|
273
|
-
.filter((
|
|
274
|
+
.filter((candidate) => candidate != null);
|
|
274
275
|
return {
|
|
275
276
|
attachmentId: file.attachmentId,
|
|
276
277
|
batchId,
|
|
@@ -3,7 +3,6 @@ import { SortOrder } from '../../../commonStateTypes/selectorTypes/sortOrderType
|
|
|
3
3
|
import { MonthYearPeriodId } from '../../../commonStateTypes/timePeriod';
|
|
4
4
|
export declare const toMissingReceiptsSortKey: (v: string) => "date" | "amount" | "vendor" | "category" | "class";
|
|
5
5
|
export type MissingReceiptsSortKey = ReturnType<typeof toMissingReceiptsSortKey>;
|
|
6
|
-
export type BulkUploadSortKey = 'amount' | 'category' | 'class' | 'date' | 'vendor';
|
|
7
6
|
export interface MissingReceiptsViewUIState {
|
|
8
7
|
scrollPosition: {
|
|
9
8
|
scrollTop: number;
|
|
@@ -13,13 +12,31 @@ export interface MissingReceiptsViewUIState {
|
|
|
13
12
|
sortKey: MissingReceiptsSortKey;
|
|
14
13
|
sortOrder: SortOrder;
|
|
15
14
|
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
export
|
|
21
|
-
export
|
|
22
|
-
export type
|
|
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>;
|
|
23
40
|
export interface CandidateRef {
|
|
24
41
|
matchScore: number;
|
|
25
42
|
transactionId: ID;
|
|
@@ -99,7 +116,8 @@ export interface ManualSearchState {
|
|
|
99
116
|
searchQuery: string;
|
|
100
117
|
totalCount: number;
|
|
101
118
|
}
|
|
102
|
-
|
|
119
|
+
/** Same union as {@link CompletedSubTab} — used for completed-transactions API `match_type`. */
|
|
120
|
+
export type CompletedTransactionsMatchType = CompletedSubTab;
|
|
103
121
|
export interface CompletedTransactionsState {
|
|
104
122
|
fetchState: FetchStateAndError;
|
|
105
123
|
nextPageToken: string | null;
|