@zeniai/client-epic-state 4.19.84-betaVR1 → 4.19.84-betaVR10

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.
Files changed (29) hide show
  1. package/lib/esm/index.js +2 -1
  2. package/lib/esm/view/expenseAutomationView/epics/missingReceipts/bulkUploadReceiptsEpic.js +7 -3
  3. package/lib/esm/view/expenseAutomationView/epics/missingReceipts/fetchBulkUploadBatchDetailsEpic.js +1 -1
  4. package/lib/esm/view/expenseAutomationView/epics/missingReceipts/fetchMoreBatchDetailsEpic.js +1 -1
  5. package/lib/esm/view/expenseAutomationView/epics/missingReceipts/fetchMultipleBatchDetailsEpic.js +1 -1
  6. package/lib/esm/view/expenseAutomationView/epics/missingReceipts/searchTransactionsForManualMatchEpic.js +13 -3
  7. package/lib/esm/view/expenseAutomationView/epics/missingReceipts/watchBulkUploadBatchStatusEpic.js +19 -2
  8. package/lib/esm/view/expenseAutomationView/payload/missingReceiptsPayload.js +27 -1
  9. package/lib/esm/view/expenseAutomationView/reducers/missingReceiptsViewReducer.js +21 -10
  10. package/lib/esm/view/expenseAutomationView/selectors/missingReceiptsSelector.js +6 -5
  11. package/lib/esm/view/expenseAutomationView/types/missingReceiptsViewState.js +10 -0
  12. package/lib/index.d.ts +2 -2
  13. package/lib/index.js +35 -33
  14. package/lib/tsconfig.typecheck.tsbuildinfo +1 -0
  15. package/lib/view/expenseAutomationView/epics/missingReceipts/bulkUploadReceiptsEpic.js +7 -3
  16. package/lib/view/expenseAutomationView/epics/missingReceipts/fetchBulkUploadBatchDetailsEpic.js +1 -1
  17. package/lib/view/expenseAutomationView/epics/missingReceipts/fetchMoreBatchDetailsEpic.js +1 -1
  18. package/lib/view/expenseAutomationView/epics/missingReceipts/fetchMultipleBatchDetailsEpic.js +1 -1
  19. package/lib/view/expenseAutomationView/epics/missingReceipts/searchTransactionsForManualMatchEpic.js +12 -2
  20. package/lib/view/expenseAutomationView/epics/missingReceipts/watchBulkUploadBatchStatusEpic.d.ts +7 -1
  21. package/lib/view/expenseAutomationView/epics/missingReceipts/watchBulkUploadBatchStatusEpic.js +19 -2
  22. package/lib/view/expenseAutomationView/payload/missingReceiptsPayload.d.ts +12 -1
  23. package/lib/view/expenseAutomationView/payload/missingReceiptsPayload.js +28 -1
  24. package/lib/view/expenseAutomationView/reducers/missingReceiptsViewReducer.d.ts +2 -2
  25. package/lib/view/expenseAutomationView/reducers/missingReceiptsViewReducer.js +21 -10
  26. package/lib/view/expenseAutomationView/selectors/missingReceiptsSelector.js +6 -5
  27. package/lib/view/expenseAutomationView/types/missingReceiptsViewState.d.ts +8 -1
  28. package/lib/view/expenseAutomationView/types/missingReceiptsViewState.js +12 -1
  29. package/package.json +2 -2
package/lib/esm/index.js CHANGED
@@ -169,6 +169,7 @@ import { backgroundRefetchReviewTab, clearExpenseAutomationTransactionsView, fet
169
169
  import { getExpenseAutomationFluxAnalysisView } from './view/expenseAutomationView/selectors/fluxAnalysisViewSelector';
170
170
  import { getExpenseAutomationReconciliationView, isAccountReconReport, } from './view/expenseAutomationView/selectors/reconciliationViewSelector';
171
171
  import { getExpenseAutomationTransactionView } from './view/expenseAutomationView/selectors/transactionCategorizationSelector';
172
+ import { isUnmatchedTabFileStatus, } from './view/expenseAutomationView/types/missingReceiptsViewState';
172
173
  import { toMissingReceiptsSortKey as toExpenseAutomationMissingReceiptsSortKey, } from './view/expenseAutomationView/types/missingReceiptsViewState';
173
174
  import { toReconciliationTabsType, } from './view/expenseAutomationView/types/reconciliationViewState';
174
175
  import { toTransactionsTabKey as toExpenseAutomationTransactionsTabKey, toTransactionsSortKey, } from './view/expenseAutomationView/types/transactionsViewState';
@@ -415,7 +416,7 @@ export { newBalancesFilterClassesView };
415
416
  export { toCOABalanceTypeStrict, toCOABalanceType as toCOABlanaceType, isCalculatedBalanceType, };
416
417
  export { stringToUnion, stringToUnionStrict };
417
418
  export { fetchMonthEndCloseChecks, fetchMonthClosePerformanceTrend, getMonthEndCloseChecksViewByTenantId, ALL_MONTH_END_CLOSE_CHECKS_FREQUENCY, };
418
- export { getExpenseAutomationView, toExpenseAutomationMissingReceiptsSortKey, toExpenseAutomationTransactionsTabKey, toExpenseAutomationViewType, uploadExpenseAutomationMissingReceiptSuccess, markExpenseAutomationMissingReceiptAsDone, fetchAllExpenseAutomationTabs, refreshExpenseAutomationCurrentTab, updateCurrentSelectedTransactionCategorizationTab, fetchExpenseAutomationMissingReceipts,
419
+ export { isUnmatchedTabFileStatus, getExpenseAutomationView, toExpenseAutomationMissingReceiptsSortKey, toExpenseAutomationTransactionsTabKey, toExpenseAutomationViewType, uploadExpenseAutomationMissingReceiptSuccess, markExpenseAutomationMissingReceiptAsDone, fetchAllExpenseAutomationTabs, refreshExpenseAutomationCurrentTab, updateCurrentSelectedTransactionCategorizationTab, fetchExpenseAutomationMissingReceipts,
419
420
  // Bulk Upload Actions
420
421
  bulkUploadReceipts, bulkUploadReceiptsSuccess, bulkUploadReceiptsFailure, updateBulkUploadProgress, pusherBatchStatusUpdate, requestMissingReceiptsTabNavigation, clearMissingReceiptsTabNavigation, fetchBulkUploadBatchDetails, fetchBulkUploadBatchDetailsSuccess, fetchBulkUploadBatchDetailsFailure, storeBatchDetails, fetchMoreBatchDetails, fetchMoreBatchDetailsComplete, fetchMoreBatchDetailsFailure, fetchBulkUploadBatches, fetchBulkUploadBatchesSuccess, fetchBulkUploadBatchesFailure, confirmBulkUploadMatch, confirmBulkUploadMatchSuccess, confirmBulkUploadMatchFailure, setBulkUploadResultsTab, setBulkUploadCompletedSubTab, setBulkUploadSortConfig, clearBulkUpload, searchTransactionsForManualMatch, searchTransactionsForManualMatchSuccess, searchTransactionsForManualMatchFailure, clearManualSearchResults, acknowledgeBulkUploadConfirmMatchComplete, fetchCompletedTransactions, fetchCompletedTransactionsSuccess, fetchCompletedTransactionsFailure, fetchFluxAnalysisView, clearExpenseAutomationFluxAnalysisView, updateOperatingExpensesIdsForReview as updateFluxOperatingExpensesIdsForReview, updateSelectedSectionIdsForReview as updateFluxAnalysisSelectedSectionIdsForReview, reviewFluxAnalysisView, updateExpenseAutomationMissingReceiptUploadState, updateExpenseAutomationMissingReceiptsUIState, updateTransactionCategorizationUploadReceiptState, uploadTransactionCategorizationReceiptSuccess, getExpenseAutomationFluxAnalysisView, updateCurrentSelectedView, updateCurrentSelectedPeriod, getExpenseAutomationTransactionView, updateFluxAnalysisViewUIState, updateFluxAnalysisViewPageMetaData, MAX_SELECTION_LIMIT, checkIfAllLineItemsAreCategoryClassFilled, getLineItemsByTransactionIdsFromLocalData, isAnyItemWithUncategorizedExpenseAccount, saveExpenseAutomationReconciliationDetail, updateExpenseAutomationReconcileTabListScrollState, updateExpenseAutomationReconReviewTabListSortState, updateExpenseAutomationReconcileTabListSortState, updateExpenseAutomationReconcileTabLocalData, updateExpenseAutomationAccountReconciliationSelectedTab, updateExpenseAutomationAccountReconciliationSelectedAccountId, getExpenseAutomationReconciliationView, fetchReconciliationView, uploadAccountStatementIntoDocumentAI, updateExpenseAutomationReconListScrollPosition, setConnectionInProgressForAccountReconciliation, getAccountReconByAccountIdAndSelectedPeriod, toReconciliationTabsType, isAccountReconReport, updateExpenseAutomationReconReviewTabLocalData, updateExpenseAutomationSelectedDrawerAccountId, saveExpenseAutomationReconciliationReview, updateExpenseAutomationAccountReconciliationLocalData, toReconciliationAccountSource, deleteAccountStatement, uploadAccountStatement, updateStatementUploadChosen, isReviewTransactionBankTransferType, isReviewTransactionBillPaymentType, isReviewTransactionCreditCardPaymentType, isReviewTransactionDepositType, isReviewTransactionExpenseType, isReviewTransactionCreditCardCreditType, setStatementParseInProgress, };
421
422
  export { fetchTransactionCategorization, fetchTransactionCategorizationView, updateTransactionCategorizationUIState, updateSelectedCheckboxTransactionIds, setEntityRecommendationForLineIdsForCategorization, initializeTransactionCategorizationViewLocalData, setAllItemsToCategoryClassInLocalDataForCategorization, saveTransactionCategorizationLocalData, fetchTransactionCategorizationFailure, saveTransactionCategorization, updateTransactionCategorization, updateTransactionCategorizationSaveStatus, markTransactionAsNotMiscategorized, updateSelectedVendorForTransaction, updateSelectedCustomerForTransaction, updateSelectedTransactionId, syncTransactionCategorizationFromDetailSave, backgroundRefetchReviewTab, clearExpenseAutomationTransactionsView, toTransactionsSortKey, };
@@ -12,9 +12,13 @@ export const bulkUploadReceiptsEpic = (actions$, _state$, zeniAPI) => actions$.p
12
12
  .postFormData(`${zeniAPI.apiEndPoints.communicationAgentMicroServiceBaseUrl}/1.0/receipts/bulk-upload`, formData)
13
13
  .pipe(mergeMap((response) => {
14
14
  if (isSuccessResponse(response) && response.data != null) {
15
- return of(bulkUploadReceiptsSuccess({
16
- batchId: response.data.batch_id,
17
- }));
15
+ const batchId = String(response.data.batch_id ?? '').trim();
16
+ if (batchId === '') {
17
+ return of(bulkUploadReceiptsFailure({
18
+ error: createZeniAPIStatus('Unexpected Error', 'Bulk upload succeeded but batch_id was missing or empty.'),
19
+ }));
20
+ }
21
+ return of(bulkUploadReceiptsSuccess({ batchId }));
18
22
  }
19
23
  return of(bulkUploadReceiptsFailure({ error: response.status }));
20
24
  }), catchError((error) => of(bulkUploadReceiptsFailure({
@@ -32,7 +32,7 @@ export const fetchBulkUploadBatchDetailsEpic = (actions$, state$, zeniAPI) => ac
32
32
  actions.push(updateTransactions(transactionPayloads, (value) => value.transaction_id));
33
33
  }
34
34
  actions.push(fetchBulkUploadBatchDetailsSuccess(toBatchDetails(response.data, zeniAPI.apiEndPoints.fileMicroServiceBaseUrl)));
35
- actions.push(fetchBulkUploadBatches(true));
35
+ actions.push(fetchBulkUploadBatches({ cacheOverride: true }));
36
36
  return from(actions);
37
37
  }), catchError((error) => of(fetchBulkUploadBatchDetailsFailure({
38
38
  error: createZeniAPIStatus('Unexpected Error', 'Fetch batch details errored out: ' + JSON.stringify(error)),
@@ -6,7 +6,7 @@ import { createZeniAPIStatus } from '../../../../responsePayload';
6
6
  import { filterBatchListItemsForBatchDetailsFetch } from '../../payload/missingReceiptsPayload';
7
7
  import { fetchMoreBatchDetails, fetchMoreBatchDetailsComplete, fetchMoreBatchDetailsFailure, } from '../../reducers/missingReceiptsViewReducer';
8
8
  import { fetchBatchDetailsByIds } from './fetchMultipleBatchDetailsEpic';
9
- const MORE_BATCH_PAGE_SIZE = 1;
9
+ const MORE_BATCH_PAGE_SIZE = 5;
10
10
  export const fetchMoreBatchDetailsEpic = (actions$, state$, zeniAPI) => actions$.pipe(filter(fetchMoreBatchDetails.match),
11
11
  /** One page at a time — avoids duplicate loads if IO + scroll fallback both fire. */
12
12
  exhaustMap(() => {
@@ -4,7 +4,7 @@ import { updateTransactions } from '../../../../entity/transaction/transactionRe
4
4
  import { createZeniAPIStatus, isSuccessResponse, } from '../../../../responsePayload';
5
5
  import { extractTransactionPayloadsFromBatchFiles, filterBatchListItemsForBatchDetailsFetch, isBatchDetailsApiStatusCompleted, shouldFetchBatchDetailsForBatchId, toBatchDetails, } from '../../payload/missingReceiptsPayload';
6
6
  import { batchDetailFetchFailed, fetchBulkUploadBatchDetails, fetchBulkUploadBatchesSuccess, fetchMoreBatchDetailsComplete, setInitialBatchDetailsLoading, storeBatchDetails, } from '../../reducers/missingReceiptsViewReducer';
7
- const INITIAL_BATCH_PAGE_SIZE = 1;
7
+ const INITIAL_BATCH_PAGE_SIZE = 5;
8
8
  export function fetchBatchDetailsByIds(batchIds, zeniAPI) {
9
9
  if (batchIds.length === 0) {
10
10
  return EMPTY;
@@ -1,4 +1,4 @@
1
- import { from, merge, of } from 'rxjs';
1
+ import { EMPTY, from, merge, of } from 'rxjs';
2
2
  import { catchError, debounceTime, filter, mergeMap, switchMap } from 'rxjs/operators';
3
3
  import { convertToPeriod } from '../../../../commonStateTypes/timePeriod';
4
4
  import { getCurrentTenant } from '../../../../entity/tenant/tenantSelector';
@@ -16,9 +16,19 @@ export const MANUAL_TRANSACTION_SEARCH_AUTO_CATEGORIZED = false;
16
16
  export const searchTransactionsForManualMatchEpic = (actions$, state$, zeniAPI) => {
17
17
  const searchActions$ = actions$.pipe(filter(searchTransactionsForManualMatch.match));
18
18
  const pagination$ = searchActions$.pipe(filter((a) => a.payload.pageToken != null));
19
- const newSearch$ = searchActions$.pipe(filter((a) => a.payload.pageToken == null), debounceTime(300));
20
- return merge(pagination$, newSearch$).pipe(switchMap((action) => {
19
+ /**
20
+ * No API for empty or 1-char queries; immediate merge so switchMap cancels any in-flight HTTP.
21
+ */
22
+ const newSearchTooShortOrClear$ = searchActions$.pipe(filter((a) => a.payload.pageToken == null &&
23
+ a.payload.query.trim().length < 2));
24
+ const newSearchDebounced$ = searchActions$.pipe(filter((a) => a.payload.pageToken == null &&
25
+ a.payload.query.trim().length >= 2), debounceTime(300));
26
+ return merge(pagination$, newSearchTooShortOrClear$, newSearchDebounced$).pipe(switchMap((action) => {
21
27
  const { query, pageToken } = action.payload;
28
+ if (pageToken == null &&
29
+ query.trim().length < 2) {
30
+ return EMPTY;
31
+ }
22
32
  const state = state$.value;
23
33
  const { expenseAutomationViewState: { selectedPeriodByTenantId }, expenseAutomationMissingReceiptsViewState: { bulkUpload: { manualSearch, sortKey, sortOrder }, }, } = state;
24
34
  const currentTenant = getCurrentTenant(state);
@@ -1,5 +1,6 @@
1
1
  import { EMPTY, from, merge, of, timer } from 'rxjs';
2
2
  import { catchError, debounceTime, filter, mergeMap, switchMap, takeUntil, } from 'rxjs/operators';
3
+ import { openSnackbar } from '../../../../entity/snackbar/snackbarReducer';
3
4
  import { updateTransactions } from '../../../../entity/transaction/transactionReducer';
4
5
  import { isSuccessResponse } from '../../../../responsePayload';
5
6
  import { extractTransactionPayloadsFromBatchFiles, isBatchDetailsApiStatusCompleted, toBatchDetails, } from '../../payload/missingReceiptsPayload';
@@ -18,8 +19,14 @@ export const pusherBatchStatusCompletionEpic = (actions$) => actions$.pipe(filte
18
19
  }),
19
20
  requestMissingReceiptsTabNavigation({ tab: 'unmatched' }),
20
21
  ])));
22
+ /**
23
+ * Fallback polling when Pusher is slow or unavailable. Network errors on a poll tick show one
24
+ * error snackbar per upload session (first failure only) so the user is not spammed every
25
+ * interval; Pusher or a later successful poll still completes the flow.
26
+ */
21
27
  export const pollBulkUploadBatchStatusEpic = (actions$, _state$, zeniAPI) => actions$.pipe(filter(bulkUploadReceiptsSuccess.match), mergeMap((action) => {
22
28
  const { batchId } = action.payload;
29
+ let pollErrorNotified = false;
23
30
  return timer(FALLBACK_FIRST_DELAY_MS, FALLBACK_POLL_INTERVAL_MS).pipe(takeUntil(merge(actions$.pipe(filter(clearBulkUpload.match)), actions$.pipe(filter(pusherBatchStatusUpdate.match), filter((a) => a.payload.batchId === batchId)), actions$.pipe(filter(fetchBulkUploadBatchDetailsSuccess.match), filter((a) => a.payload.batchId === batchId)))), switchMap(() => zeniAPI
24
31
  .getJSON(`${zeniAPI.apiEndPoints.communicationAgentMicroServiceBaseUrl}/1.0/batches/${batchId}`)
25
32
  .pipe(mergeMap((response) => {
@@ -32,11 +39,21 @@ export const pollBulkUploadBatchStatusEpic = (actions$, _state$, zeniAPI) => act
32
39
  actions.push(updateTransactions(transactionPayloads, (value) => value.transaction_id));
33
40
  }
34
41
  actions.push(fetchBulkUploadBatchDetailsSuccess(toBatchDetails(response.data, zeniAPI.apiEndPoints.fileMicroServiceBaseUrl)));
35
- actions.push(fetchBulkUploadBatches(true));
42
+ actions.push(fetchBulkUploadBatches({ cacheOverride: true }));
36
43
  actions.push(requestMissingReceiptsTabNavigation({ tab: 'unmatched' }));
37
44
  return from(actions);
38
45
  }
39
46
  }
40
47
  return EMPTY;
41
- }), catchError(() => of()))));
48
+ }), catchError(() => {
49
+ if (!pollErrorNotified) {
50
+ pollErrorNotified = true;
51
+ return of(openSnackbar({
52
+ messageSection: 'receipts_upload',
53
+ messageText: 'failed',
54
+ type: 'error',
55
+ }));
56
+ }
57
+ return EMPTY;
58
+ }))));
42
59
  }));
@@ -1,6 +1,32 @@
1
1
  import { toURL } from '../../../commonPayloadTypes/urlPayload';
2
2
  import { getTransactionPayloadAmount, } from '../../../entity/transaction/payloadTypes/transactionPayload';
3
3
  // -- Converter functions --
4
+ /**
5
+ * Maps batch-details API `files[].status` strings to canonical {@link BatchFileStatus}.
6
+ * Legacy aliases are supported during rollout; unknown strings map to `failed` so Redux never
7
+ * holds untyped status values.
8
+ */
9
+ export function parseBatchFileStatus(raw) {
10
+ const s = raw.trim().toLowerCase().replace(/-/g, '_');
11
+ switch (s) {
12
+ case 'exact_match':
13
+ case 'matched':
14
+ return 'exact_match';
15
+ case 'possible_matches':
16
+ case 'possible_match':
17
+ case 'possiblematch':
18
+ case 'un_matched':
19
+ case 'unmatched':
20
+ return 'possible_matches';
21
+ case 'no_match':
22
+ case 'nomatch':
23
+ return 'no_match';
24
+ case 'failed':
25
+ return 'failed';
26
+ default:
27
+ return 'failed';
28
+ }
29
+ }
4
30
  export function toBatchFile(payload, filesEndPoint) {
5
31
  return {
6
32
  attachmentId: payload.attachment_id,
@@ -16,7 +42,7 @@ export function toBatchFile(payload, filesEndPoint) {
16
42
  filename: payload.filename,
17
43
  matchSource: payload.match_source,
18
44
  matchedTransactionId: payload.matched_transaction?.transaction_id,
19
- status: payload.status,
45
+ status: parseBatchFileStatus(payload.status),
20
46
  };
21
47
  }
22
48
  export function extractTransactionPayloadsFromBatchFiles(files) {
@@ -1,5 +1,6 @@
1
1
  import { createSlice } from '@reduxjs/toolkit';
2
2
  import { toMonthYearPeriodId } from '../../../commonStateTypes/timePeriod';
3
+ import { MIN_MANUAL_TRANSACTION_SEARCH_LENGTH, } from '../types/missingReceiptsViewState';
3
4
  export const getCompletedTransactionsCacheKey = (periodId, sortKey, sortOrder, subTab) => `completed-${subTab}-${sortKey}-${sortOrder === 'ascending' ? 'asc' : 'desc'}-${periodId}`;
4
5
  export const initialBulkUploadState = {
5
6
  batchDetailsById: {},
@@ -285,14 +286,6 @@ const expenseAutomationMissingReceiptsView = createSlice({
285
286
  };
286
287
  },
287
288
  prepare(input) {
288
- if (input === true || input === false) {
289
- return {
290
- payload: {
291
- cacheOverride: input === true,
292
- invalidateBatchDetailsCache: false,
293
- },
294
- };
295
- }
296
289
  const o = input ?? {};
297
290
  return {
298
291
  payload: {
@@ -353,14 +346,14 @@ const expenseAutomationMissingReceiptsView = createSlice({
353
346
  const file = details.files.find((f) => f.attachmentId === attachmentId);
354
347
  if (file != null) {
355
348
  const originalStatus = file.status;
356
- file.status = 'matched';
349
+ file.status = 'exact_match';
357
350
  file.matchedTransactionId = transactionId;
358
351
  file.candidates = undefined;
359
352
  details.summary.matched += 1;
360
353
  if (originalStatus === 'no_match') {
361
354
  details.summary.noMatch = Math.max(0, details.summary.noMatch - 1);
362
355
  }
363
- else {
356
+ else if (originalStatus === 'possible_matches') {
364
357
  details.summary.possibleMatches = Math.max(0, details.summary.possibleMatches - 1);
365
358
  }
366
359
  }
@@ -392,6 +385,24 @@ const expenseAutomationMissingReceiptsView = createSlice({
392
385
  reducer(draft, action) {
393
386
  const { query, pageToken } = action.payload;
394
387
  const isLoadMore = pageToken != null;
388
+ const trimmed = query.trim();
389
+ /**
390
+ * Cleared or too-short query: no API (see epic). Reset fully (no In-Progress).
391
+ * Only bump `manualSearchUiResetKey` when clearing a real search — not when the UI
392
+ * dispatches `""` on every keystroke while length is below MIN (web-components always sends
393
+ * `onSearch("")` in that case), or ReceiptCard remounts and the input loses focus.
394
+ */
395
+ if (!isLoadMore &&
396
+ trimmed.length < MIN_MANUAL_TRANSACTION_SEARCH_LENGTH) {
397
+ const hadActiveSearch = draft.bulkUpload.manualSearch.searchQuery.trim().length >=
398
+ MIN_MANUAL_TRANSACTION_SEARCH_LENGTH ||
399
+ draft.bulkUpload.manualSearch.results.length > 0;
400
+ draft.bulkUpload.manualSearch = initialBulkUploadState.manualSearch;
401
+ if (hadActiveSearch) {
402
+ draft.bulkUpload.manualSearchUiResetKey += 1;
403
+ }
404
+ return;
405
+ }
395
406
  draft.bulkUpload.manualSearch.searchQuery = query;
396
407
  if (isLoadMore) {
397
408
  draft.bulkUpload.manualSearch.isLoadingMore = true;
@@ -5,6 +5,7 @@ import { getSupportedTransactionById, getSupportedTransactionsByIds, } from '../
5
5
  import { getAllSteps } from '../selectorTypes/expenseAutomationViewSelectorTypes';
6
6
  import { isBatchListStatusCompleted } from '../payload/missingReceiptsPayload';
7
7
  import { getCompletedTransactionsCacheKey } from '../reducers/missingReceiptsViewReducer';
8
+ import { isUnmatchedTabFileStatus, } from '../types/missingReceiptsViewState';
8
9
  export function getExpenseAutomationMissingReceiptsView(state) {
9
10
  const { expenseAutomationMissingReceiptsViewState, expenseAutomationViewState, transactionState, monthEndCloseChecksState, } = state;
10
11
  const currentTenant = getCurrentTenant(state);
@@ -58,7 +59,7 @@ export function getExpenseAutomationMissingReceiptsView(state) {
58
59
  : [];
59
60
  /**
60
61
  * Primary "Unmatched" batch = first completed batch in platform list order, preferring the
61
- * first batch that still has un_matched/no_match work when any completed batch has such work.
62
+ * first batch that still has possible_matches/no_match work when any completed batch has such work.
62
63
  * Batches without details yet: wait if the list row still reports unmatched work; otherwise skip
63
64
  * so we do not block the whole tab while other rows are not fetched yet.
64
65
  */
@@ -100,8 +101,8 @@ export function getExpenseAutomationMissingReceiptsView(state) {
100
101
  }
101
102
  };
102
103
  const bulkUploadSortDirection = bulkUpload.sortOrder === 'ascending' ? 'asc' : 'desc';
103
- const matchedFiles = orderBy(resolvedFiles?.filter((f) => f.status === 'matched') ?? [], getSortValue, bulkUploadSortDirection);
104
- const unmatchedFiles = orderBy(resolvedFiles?.filter((f) => f.status === 'un_matched' || f.status === 'no_match') ?? [], getSortValue, bulkUploadSortDirection);
104
+ const matchedFiles = orderBy(resolvedFiles?.filter((f) => f.status === 'exact_match') ?? [], getSortValue, bulkUploadSortDirection);
105
+ const unmatchedFiles = orderBy(resolvedFiles?.filter((f) => isUnmatchedTabFileStatus(f.status)) ?? [], getSortValue, bulkUploadSortDirection);
105
106
  const progress = currentBatchStatus != null
106
107
  ? {
107
108
  ...currentBatchStatus.progress,
@@ -126,7 +127,7 @@ export function getExpenseAutomationMissingReceiptsView(state) {
126
127
  return [];
127
128
  }
128
129
  return details.files
129
- .filter((f) => f.status === 'un_matched' || f.status === 'no_match')
130
+ .filter((f) => isUnmatchedTabFileStatus(f.status))
130
131
  .map((f) => resolveBatchFile(f, b.batchId, transactionState));
131
132
  });
132
133
  const completedTransactionsCacheKey = monthYearPeriodId != null
@@ -200,7 +201,7 @@ export function getExpenseAutomationMissingReceiptsView(state) {
200
201
  };
201
202
  }
202
203
  function batchDetailsHasUnmatchedWork(details) {
203
- return details.files.some((f) => f.status === 'un_matched' || f.status === 'no_match');
204
+ return details.files.some((f) => isUnmatchedTabFileStatus(f.status));
204
205
  }
205
206
  /** List row summary before batch details load — aligns with unmatched / possible-match work. */
206
207
  function batchListItemHasUnmatchedWork(batch) {
@@ -7,3 +7,13 @@ const MISSING_RECEIPTS_SORT_KEYS = [
7
7
  'amount',
8
8
  ];
9
9
  export const toMissingReceiptsSortKey = (v) => stringToUnion(v, MISSING_RECEIPTS_SORT_KEYS);
10
+ // -- Bulk Receipt Upload Types --
11
+ /**
12
+ * Minimum trimmed query length before manual-match search calls the expense-automation API.
13
+ * Kept in sync with `ManualSearchInput` in web-components (fires search only when length ≥ this).
14
+ */
15
+ export const MIN_MANUAL_TRANSACTION_SEARCH_LENGTH = 2;
16
+ /** True for files that belong in the Unmatched tab (possible matches or no match). */
17
+ export function isUnmatchedTabFileStatus(status) {
18
+ return status === 'possible_matches' || status === 'no_match';
19
+ }
package/lib/index.d.ts CHANGED
@@ -281,7 +281,7 @@ import { getExpenseAutomationReconciliationView, isAccountReconReport } from './
281
281
  import { getExpenseAutomationTransactionView } from './view/expenseAutomationView/selectors/transactionCategorizationSelector';
282
282
  import { FluxAnalysisActionType, FluxAnalysisReviewStatus, FluxAnalysisSortKey, FluxAnalysisViewUIState, FluxBalancesByMonth } from './view/expenseAutomationView/types/fluxAnalysisViewState';
283
283
  import { AccountSettingsLocalData, JEScheduleLocalData } from './view/expenseAutomationView/types/jeSchedulesViewState';
284
- import { BatchDetails, BatchFile, BatchFileStatus, BatchListItem, BatchStatus, BatchStatusValue, BatchSummary, BulkUploadPhase, BulkUploadResultsTab, BulkUploadSortKey, BulkUploadState, CandidateRef, CompletedSubTab, MatchCandidate, MatchSource, MissingReceiptsTab } from './view/expenseAutomationView/types/missingReceiptsViewState';
284
+ import { BatchDetails, BatchFile, BatchFileStatus, BatchListItem, BatchStatus, BatchStatusValue, BatchSummary, BulkUploadPhase, BulkUploadResultsTab, BulkUploadSortKey, BulkUploadState, CandidateRef, CompletedSubTab, isUnmatchedTabFileStatus, MatchCandidate, MatchSource, MissingReceiptsTab } from './view/expenseAutomationView/types/missingReceiptsViewState';
285
285
  import { CompletedTransactionsSelectorData, ResolvedBatchFile, ResolvedCandidate, ResolvedBatchDetails } from './view/expenseAutomationView/selectorTypes/missingReceiptsSelectorTypes';
286
286
  import { MissingReceiptsSortKey as ExpenseAutomationMissingReceiptsSortKey, MissingReceiptsViewState as ExpenseAutomationMissingReceiptsViewState, MissingReceiptsViewUIState as ExpenseAutomationMissingReceiptsViewUIState, toMissingReceiptsSortKey as toExpenseAutomationMissingReceiptsSortKey } from './view/expenseAutomationView/types/missingReceiptsViewState';
287
287
  import { AccountReconciliationLocalData, ReconciliationViewTabType as ExpenseAutomationReconciliationViewTab, ReconReconcileSortKey, ReconReviewSortKey, ReconciliationReconcileTabLocalData, ReconciliationReviewTabLocalData, SaveReconcileDetailActionPayload as SaveExpenseAutomationReconciliationActionType, toReconciliationTabsType } from './view/expenseAutomationView/types/reconciliationViewState';
@@ -623,7 +623,7 @@ export { TransactionsOrder, COABalancesSliceOrder, EntityOrder, Section, Section
623
623
  export { ClassesViewSelectorReportV2 };
624
624
  export { BalancesTimeseries, TrendTimeseries, BalanceKind };
625
625
  export { fetchMonthEndCloseChecks, fetchMonthClosePerformanceTrend, MonthClosePerformanceTrend, MonthEndCloseCheck, getMonthEndCloseChecksViewByTenantId, MonthEndCloseChecksView, MonthEndCloseCheckFrequency, MonthCloseCheckMetrics, ALL_MONTH_END_CLOSE_CHECKS_FREQUENCY, MonthEndAuditSummary, };
626
- export { ExpenseAutomationViewSelector, ExpenseAutomationStepDetails, ExpenseAutomationViewType, ExpenseAutomationMissingReceiptsViewSelector, BulkUploadSelectorData, ExpenseAutomationFluxAnalysisViewSelector, FluxAnalysisVendorView, FluxAnalysisViewSectionReport, FluxVendorAccountsAndClassesView, ExpenseAutomationMissingReceiptsViewUIState, ExpenseAutomationMissingReceiptsViewState, ExpenseAutomationViewState, ExpenseAutomationTransactionsTab, ExpenseAutomationMissingReceiptsSortKey, BatchDetails, BatchFile, BatchFileStatus, BatchListItem, BatchStatus, BatchStatusValue, BatchSummary, BulkUploadPhase, BulkUploadResultsTab, BulkUploadSortKey, BulkUploadState, CandidateRef, CompletedSubTab, CompletedTransactionsSelectorData, MatchCandidate, MatchSource, MissingReceiptsTab, ResolvedBatchFile, ResolvedBatchDetails, ResolvedCandidate, getExpenseAutomationView, toExpenseAutomationMissingReceiptsSortKey, toExpenseAutomationTransactionsTabKey, toExpenseAutomationViewType, uploadExpenseAutomationMissingReceiptSuccess, markExpenseAutomationMissingReceiptAsDone, fetchAllExpenseAutomationTabs, refreshExpenseAutomationCurrentTab, updateCurrentSelectedTransactionCategorizationTab, fetchExpenseAutomationMissingReceipts, bulkUploadReceipts, bulkUploadReceiptsSuccess, bulkUploadReceiptsFailure, updateBulkUploadProgress, pusherBatchStatusUpdate, requestMissingReceiptsTabNavigation, clearMissingReceiptsTabNavigation, fetchBulkUploadBatchDetails, fetchBulkUploadBatchDetailsSuccess, fetchBulkUploadBatchDetailsFailure, storeBatchDetails, fetchMoreBatchDetails, fetchMoreBatchDetailsComplete, fetchMoreBatchDetailsFailure, fetchBulkUploadBatches, fetchBulkUploadBatchesSuccess, fetchBulkUploadBatchesFailure, confirmBulkUploadMatch, confirmBulkUploadMatchSuccess, confirmBulkUploadMatchFailure, setBulkUploadResultsTab, setBulkUploadCompletedSubTab, setBulkUploadSortConfig, clearBulkUpload, searchTransactionsForManualMatch, searchTransactionsForManualMatchSuccess, searchTransactionsForManualMatchFailure, clearManualSearchResults, acknowledgeBulkUploadConfirmMatchComplete, fetchCompletedTransactions, fetchCompletedTransactionsSuccess, fetchCompletedTransactionsFailure, fetchFluxAnalysisView, clearExpenseAutomationFluxAnalysisView, updateOperatingExpensesIdsForReview as updateFluxOperatingExpensesIdsForReview, updateSelectedSectionIdsForReview as updateFluxAnalysisSelectedSectionIdsForReview, reviewFluxAnalysisView, updateExpenseAutomationMissingReceiptUploadState, updateExpenseAutomationMissingReceiptsUIState, updateTransactionCategorizationUploadReceiptState, uploadTransactionCategorizationReceiptSuccess, FluxAnalysisOperatingExpenseView, FluxAnalysisSectionType, getExpenseAutomationFluxAnalysisView, FluxAnalysisSortKey, FluxAnalysisActionType, FluxBalancesByMonth, updateCurrentSelectedView, updateCurrentSelectedPeriod, getExpenseAutomationTransactionView, ReconReconcileSortKey, ReconciliationReconcileTabLocalData, FluxAnalysisReviewStatus, updateFluxAnalysisViewUIState, FluxAnalysisViewUIState, updateFluxAnalysisViewPageMetaData, MAX_SELECTION_LIMIT, checkIfAllLineItemsAreCategoryClassFilled, getLineItemsByTransactionIdsFromLocalData, isAnyItemWithUncategorizedExpenseAccount, SaveExpenseAutomationReconciliationActionType, saveExpenseAutomationReconciliationDetail, updateExpenseAutomationReconcileTabListScrollState, updateExpenseAutomationReconReviewTabListSortState, updateExpenseAutomationReconcileTabListSortState, updateExpenseAutomationReconcileTabLocalData, updateExpenseAutomationAccountReconciliationSelectedTab, updateExpenseAutomationAccountReconciliationSelectedAccountId, ExpenseAutomationReconciliationViewSelector, getExpenseAutomationReconciliationView, AccountReconciliationBySection, fetchReconciliationView, uploadAccountStatementIntoDocumentAI, UploadStatementDocumentAIResponse, updateExpenseAutomationReconListScrollPosition, setConnectionInProgressForAccountReconciliation, AccountReconciliationByAccount, AccountReconciliationEntity, getAccountReconByAccountIdAndSelectedPeriod, ExpenseAutomationReconciliationViewTab, toReconciliationTabsType, isAccountReconReport, ReconReviewSortKey, AccountReconSectionID, ReconciliationReviewTabLocalData, TransactionsToReview, RecommendedActionCodeType, ReconciliationStatusCodeType, BalanceDataStatusCodeType, updateExpenseAutomationReconReviewTabLocalData, updateExpenseAutomationSelectedDrawerAccountId, saveExpenseAutomationReconciliationReview, updateExpenseAutomationAccountReconciliationLocalData, BankStatusCodeType, ReconciliationAccountSourceType, toReconciliationAccountSource, StatementStatusCodeType, AccountReconciliationLocalData, StatementDataStatusCodeType, deleteAccountStatement, uploadAccountStatement, UploadStatementDocumentAIPayload, updateStatementUploadChosen, isReviewTransactionBankTransferType, isReviewTransactionBillPaymentType, isReviewTransactionCreditCardPaymentType, isReviewTransactionDepositType, isReviewTransactionExpenseType, isReviewTransactionCreditCardCreditType, setStatementParseInProgress, };
626
+ export { ExpenseAutomationViewSelector, ExpenseAutomationStepDetails, ExpenseAutomationViewType, ExpenseAutomationMissingReceiptsViewSelector, BulkUploadSelectorData, ExpenseAutomationFluxAnalysisViewSelector, FluxAnalysisVendorView, FluxAnalysisViewSectionReport, FluxVendorAccountsAndClassesView, ExpenseAutomationMissingReceiptsViewUIState, ExpenseAutomationMissingReceiptsViewState, ExpenseAutomationViewState, ExpenseAutomationTransactionsTab, ExpenseAutomationMissingReceiptsSortKey, BatchDetails, BatchFile, BatchFileStatus, BatchListItem, isUnmatchedTabFileStatus, BatchStatus, BatchStatusValue, BatchSummary, BulkUploadPhase, BulkUploadResultsTab, BulkUploadSortKey, BulkUploadState, CandidateRef, CompletedSubTab, CompletedTransactionsSelectorData, MatchCandidate, MatchSource, MissingReceiptsTab, ResolvedBatchFile, ResolvedBatchDetails, ResolvedCandidate, getExpenseAutomationView, toExpenseAutomationMissingReceiptsSortKey, toExpenseAutomationTransactionsTabKey, toExpenseAutomationViewType, uploadExpenseAutomationMissingReceiptSuccess, markExpenseAutomationMissingReceiptAsDone, fetchAllExpenseAutomationTabs, refreshExpenseAutomationCurrentTab, updateCurrentSelectedTransactionCategorizationTab, fetchExpenseAutomationMissingReceipts, bulkUploadReceipts, bulkUploadReceiptsSuccess, bulkUploadReceiptsFailure, updateBulkUploadProgress, pusherBatchStatusUpdate, requestMissingReceiptsTabNavigation, clearMissingReceiptsTabNavigation, fetchBulkUploadBatchDetails, fetchBulkUploadBatchDetailsSuccess, fetchBulkUploadBatchDetailsFailure, storeBatchDetails, fetchMoreBatchDetails, fetchMoreBatchDetailsComplete, fetchMoreBatchDetailsFailure, fetchBulkUploadBatches, fetchBulkUploadBatchesSuccess, fetchBulkUploadBatchesFailure, confirmBulkUploadMatch, confirmBulkUploadMatchSuccess, confirmBulkUploadMatchFailure, setBulkUploadResultsTab, setBulkUploadCompletedSubTab, setBulkUploadSortConfig, clearBulkUpload, searchTransactionsForManualMatch, searchTransactionsForManualMatchSuccess, searchTransactionsForManualMatchFailure, clearManualSearchResults, acknowledgeBulkUploadConfirmMatchComplete, fetchCompletedTransactions, fetchCompletedTransactionsSuccess, fetchCompletedTransactionsFailure, fetchFluxAnalysisView, clearExpenseAutomationFluxAnalysisView, updateOperatingExpensesIdsForReview as updateFluxOperatingExpensesIdsForReview, updateSelectedSectionIdsForReview as updateFluxAnalysisSelectedSectionIdsForReview, reviewFluxAnalysisView, updateExpenseAutomationMissingReceiptUploadState, updateExpenseAutomationMissingReceiptsUIState, updateTransactionCategorizationUploadReceiptState, uploadTransactionCategorizationReceiptSuccess, FluxAnalysisOperatingExpenseView, FluxAnalysisSectionType, getExpenseAutomationFluxAnalysisView, FluxAnalysisSortKey, FluxAnalysisActionType, FluxBalancesByMonth, updateCurrentSelectedView, updateCurrentSelectedPeriod, getExpenseAutomationTransactionView, ReconReconcileSortKey, ReconciliationReconcileTabLocalData, FluxAnalysisReviewStatus, updateFluxAnalysisViewUIState, FluxAnalysisViewUIState, updateFluxAnalysisViewPageMetaData, MAX_SELECTION_LIMIT, checkIfAllLineItemsAreCategoryClassFilled, getLineItemsByTransactionIdsFromLocalData, isAnyItemWithUncategorizedExpenseAccount, SaveExpenseAutomationReconciliationActionType, saveExpenseAutomationReconciliationDetail, updateExpenseAutomationReconcileTabListScrollState, updateExpenseAutomationReconReviewTabListSortState, updateExpenseAutomationReconcileTabListSortState, updateExpenseAutomationReconcileTabLocalData, updateExpenseAutomationAccountReconciliationSelectedTab, updateExpenseAutomationAccountReconciliationSelectedAccountId, ExpenseAutomationReconciliationViewSelector, getExpenseAutomationReconciliationView, AccountReconciliationBySection, fetchReconciliationView, uploadAccountStatementIntoDocumentAI, UploadStatementDocumentAIResponse, updateExpenseAutomationReconListScrollPosition, setConnectionInProgressForAccountReconciliation, AccountReconciliationByAccount, AccountReconciliationEntity, getAccountReconByAccountIdAndSelectedPeriod, ExpenseAutomationReconciliationViewTab, toReconciliationTabsType, isAccountReconReport, ReconReviewSortKey, AccountReconSectionID, ReconciliationReviewTabLocalData, TransactionsToReview, RecommendedActionCodeType, ReconciliationStatusCodeType, BalanceDataStatusCodeType, updateExpenseAutomationReconReviewTabLocalData, updateExpenseAutomationSelectedDrawerAccountId, saveExpenseAutomationReconciliationReview, updateExpenseAutomationAccountReconciliationLocalData, BankStatusCodeType, ReconciliationAccountSourceType, toReconciliationAccountSource, StatementStatusCodeType, AccountReconciliationLocalData, StatementDataStatusCodeType, deleteAccountStatement, uploadAccountStatement, UploadStatementDocumentAIPayload, updateStatementUploadChosen, isReviewTransactionBankTransferType, isReviewTransactionBillPaymentType, isReviewTransactionCreditCardPaymentType, isReviewTransactionDepositType, isReviewTransactionExpenseType, isReviewTransactionCreditCardCreditType, setStatementParseInProgress, };
627
627
  export { JEScheduleLocalData };
628
628
  export { ExpenseAutomationJESchedulesViewSelector, JEAccountSettingsView, JEScheduledTransactionWithFailedEntries, };
629
629
  export { fetchTransactionCategorization, fetchTransactionCategorizationView, updateTransactionCategorizationUIState, updateSelectedCheckboxTransactionIds, setEntityRecommendationForLineIdsForCategorization, initializeTransactionCategorizationViewLocalData, setAllItemsToCategoryClassInLocalDataForCategorization, saveTransactionCategorizationLocalData, fetchTransactionCategorizationFailure, saveTransactionCategorization, updateTransactionCategorization, updateTransactionCategorizationSaveStatus, markTransactionAsNotMiscategorized, updateSelectedVendorForTransaction, updateSelectedCustomerForTransaction, updateSelectedTransactionId, syncTransactionCategorizationFromDetailSave, backgroundRefetchReviewTab, clearExpenseAutomationTransactionsView, TransactionsSortKey, toTransactionsSortKey, TransactionsTab, TransactionCategorizationLineItemData, TransactionReviewLocalData, SupportedTransactionCategorization, ExpenseAutomationTransactionsViewState, ExpenseAutomationTransactionsViewUIState, ExpenseAutomationTransactionViewSelector, TransactionReviewLocalDataSelectorView, };