@zeniai/client-epic-state 5.0.80 → 5.0.81-betaAD01

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 (196) hide show
  1. package/lib/commonStateTypes/cacheOverrideFetchReducer.d.ts +17 -0
  2. package/lib/commonStateTypes/cacheOverrideFetchReducer.js +20 -0
  3. package/lib/commonStateTypes/filterPrimitives.d.ts +23 -0
  4. package/lib/commonStateTypes/filterPrimitives.js +21 -0
  5. package/lib/commonStateTypes/pusherActions.d.ts +11 -0
  6. package/lib/commonStateTypes/pusherActions.js +6 -0
  7. package/lib/coreEpics.js +1 -1
  8. package/lib/entity/account/accountSelector.d.ts +27 -1
  9. package/lib/entity/account/accountSelector.js +47 -1
  10. package/lib/entity/account/accountState.d.ts +6 -1
  11. package/lib/entity/account/accountState.js +11 -4
  12. package/lib/entity/account/subAccountSelector.d.ts +1 -1
  13. package/lib/entity/aiAccountantCustomer/aiAccountantCustomerPayload.js +1 -3
  14. package/lib/entity/aiAccountantCustomer/aiAccountantCustomerReducer.js +1 -2
  15. package/lib/entity/aiAccountantCustomer/aiAccountantCustomerSelector.d.ts +1 -1
  16. package/lib/entity/aiCfo/aiCfoPayload.d.ts +2 -0
  17. package/lib/entity/aiCfo/aiCfoReducer.d.ts +1 -1
  18. package/lib/entity/aiCfo/aiCfoReducer.js +33 -3
  19. package/lib/entity/aiCfo/aiCfoSelector.js +13 -1
  20. package/lib/entity/aiCfo/aiCfoState.d.ts +2 -0
  21. package/lib/entity/chargeCard/chargeCardReducer.d.ts +19 -2
  22. package/lib/entity/chargeCard/chargeCardReducer.js +35 -2
  23. package/lib/entity/chargeCardTransaction/chargeCardTransactionReducer.d.ts +14 -1
  24. package/lib/entity/chargeCardTransaction/chargeCardTransactionReducer.js +36 -2
  25. package/lib/entity/class/classSelector.d.ts +35 -0
  26. package/lib/entity/class/classSelector.js +50 -0
  27. package/lib/entity/sectionProjectView/sectionProjectViewReducer.d.ts +1 -1
  28. package/lib/entity/sectionProjectView/sectionProjectViewSelector.js +1 -1
  29. package/lib/entity/tenant/SessionManager.js +6 -3
  30. package/lib/entity/tenant/clearAllEpic.d.ts +2 -2
  31. package/lib/entity/tenant/clearAllEpic.js +2 -2
  32. package/lib/entity/tenant/epic/saveAPIKeyConnectionEpic.js +6 -2
  33. package/lib/entity/tenant/epic/saveOAuthConnectionEpic.js +6 -2
  34. package/lib/entity/tenant/tenantReducer.js +6 -1
  35. package/lib/entity/transaction/payloadTypes/transactionLinePayload.js +3 -1
  36. package/lib/epic.d.ts +12 -11
  37. package/lib/epic.js +13 -12
  38. package/lib/esm/commonStateTypes/cacheOverrideFetchReducer.js +16 -0
  39. package/lib/esm/commonStateTypes/filterPrimitives.js +20 -0
  40. package/lib/esm/commonStateTypes/pusherActions.js +3 -0
  41. package/lib/esm/coreEpics.js +1 -1
  42. package/lib/esm/entity/account/accountSelector.js +46 -1
  43. package/lib/esm/entity/account/accountState.js +7 -1
  44. package/lib/esm/entity/aiAccountantCustomer/aiAccountantCustomerPayload.js +1 -3
  45. package/lib/esm/entity/aiAccountantCustomer/aiAccountantCustomerReducer.js +1 -2
  46. package/lib/esm/entity/aiCfo/aiCfoReducer.js +32 -2
  47. package/lib/esm/entity/aiCfo/aiCfoSelector.js +13 -1
  48. package/lib/esm/entity/chargeCard/chargeCardReducer.js +34 -1
  49. package/lib/esm/entity/chargeCardTransaction/chargeCardTransactionReducer.js +35 -1
  50. package/lib/esm/entity/class/classSelector.js +48 -0
  51. package/lib/esm/entity/sectionProjectView/sectionProjectViewSelector.js +1 -1
  52. package/lib/esm/entity/tenant/SessionManager.js +4 -1
  53. package/lib/esm/entity/tenant/clearAllEpic.js +2 -2
  54. package/lib/esm/entity/tenant/epic/saveAPIKeyConnectionEpic.js +6 -2
  55. package/lib/esm/entity/tenant/epic/saveOAuthConnectionEpic.js +6 -2
  56. package/lib/esm/entity/tenant/tenantReducer.js +6 -1
  57. package/lib/esm/entity/transaction/payloadTypes/transactionLinePayload.js +3 -1
  58. package/lib/esm/epic.js +13 -12
  59. package/lib/esm/index.js +33 -23
  60. package/lib/esm/reducer.js +2 -2
  61. package/lib/esm/view/aiAccountantView/epics/fetchAiAccountantCustomersEpic.js +2 -5
  62. package/lib/esm/view/aiAccountantView/epics/fetchAiAccountantJobsEpic.js +2 -5
  63. package/lib/esm/view/aiAccountantView/epics/triggerAiAccountantJobEpic.js +4 -1
  64. package/lib/esm/view/aiCfoView/aiCfoViewReducer.js +11 -1
  65. package/lib/esm/view/aiCfoView/epics/updateChatSessionPinEpic.js +46 -0
  66. package/lib/esm/view/companyHealthMetricView/companyHealthMetricViewSelector.js +1 -1
  67. package/lib/esm/view/companyTaskManagerView/companyTaskManagerViewSelector.js +1 -1
  68. package/lib/esm/view/companyTaskManagerView/epics/fetchTaskManagerMetricsEpic.js +1 -1
  69. package/lib/esm/view/companyView/helpers/cockpitHelpers.js +1 -1
  70. package/lib/esm/view/companyView/selector/companyManagementViewSelector.js +1 -1
  71. package/lib/esm/view/companyView/selector/companyPortfolioViewSelector.js +1 -1
  72. package/lib/esm/view/creditAgentView/creditAgentViewReducer.js +1 -16
  73. package/lib/esm/view/expenseAutomationView/epics/missingReceipts/fetchBulkUploadBatchesEpic.js +2 -2
  74. package/lib/esm/view/expenseAutomationView/epics/missingReceipts/fetchCompletedTransactionsEpic.js +2 -1
  75. package/lib/esm/view/expenseAutomationView/epics/missingReceipts/restoreBulkUploadAutomatchingOnMountEpic.js +8 -6
  76. package/lib/esm/view/expenseAutomationView/epics/missingReceipts/searchTransactionsForManualMatchEpic.js +7 -1
  77. package/lib/esm/view/expenseAutomationView/epics/transactionCategorization/fetchTransactionCategorizationEpic.js +11 -1
  78. package/lib/esm/view/expenseAutomationView/helpers/transactionCategorizationLocalDataHelper.js +21 -1
  79. package/lib/esm/view/expenseAutomationView/reducers/missingReceiptsViewReducer.js +2 -1
  80. package/lib/esm/view/expenseAutomationView/reducers/transactionsViewReducer.js +253 -14
  81. package/lib/esm/view/expenseAutomationView/selectors/transactionCategorizationSelector.js +147 -13
  82. package/lib/esm/view/expenseAutomationView/transactionFilterHelpers.js +258 -0
  83. package/lib/esm/view/expenseAutomationView/types/completedSubTab.js +17 -0
  84. package/lib/esm/view/expenseAutomationView/types/missingReceiptsViewState.js +0 -2
  85. package/lib/esm/view/financeStatement/financeStatementSelector.js +1 -1
  86. package/lib/esm/view/recommendation/fetchEntityRecommendationsForLineUpdateEpic.js +87 -0
  87. package/lib/esm/view/recommendation/recommendationHelper.js +29 -0
  88. package/lib/esm/view/recommendation/recommendationReducer.js +26 -1
  89. package/lib/esm/view/spendManagement/chargeCards/cashbackDetail/cashbackDetailReducer.js +3 -2
  90. package/lib/esm/view/spendManagement/chargeCards/chargeCardConfig/chargeCardConfigReducer.js +3 -2
  91. package/lib/esm/view/spendManagement/chargeCards/chargeCardDetail/chargeCardDetailReducer.js +55 -18
  92. package/lib/esm/view/spendManagement/chargeCards/chargeCardDetail/fetchChargeCardDetailEpic.js +16 -2
  93. package/lib/esm/view/spendManagement/chargeCards/chargeCardDetail/fetchChargeCardDetailPageEpic.js +5 -4
  94. package/lib/esm/view/spendManagement/chargeCards/chargeCardList/chargeCardListReducer.js +26 -9
  95. package/lib/esm/view/spendManagement/chargeCards/chargeCardList/fetchChargeCardListPageEpic.js +9 -6
  96. package/lib/esm/view/spendManagement/chargeCards/chargeCardPaymentHistory/chargeCardPaymentHistoryReducer.js +3 -2
  97. package/lib/esm/view/spendManagement/chargeCards/chargeCardPaymentHistory/fetchChargeCardPaymentPageEpic.js +1 -1
  98. package/lib/esm/view/spendManagement/chargeCards/chargeCardRepaymentDetail/chargeCardRepaymentDetailReducer.js +7 -3
  99. package/lib/esm/view/spendManagement/spendManagementFilterHelpers.js +4 -2
  100. package/lib/esm/view/subscriptionView/subscriptionViewSelector.js +1 -1
  101. package/lib/esm/view/taskManager/cannedResponsesView/epics/fetchCannedResponsesEpic.js +1 -1
  102. package/lib/esm/view/taskManager/cannedResponsesView/epics/saveCannedResponseEpic.js +1 -1
  103. package/lib/esm/view/taskManager/taskDetailView/epics/unsnoozeTaskEpic.js +1 -1
  104. package/lib/esm/view/taskManager/taskListView/taskListReducer.js +1 -1
  105. package/lib/esm/view/transactionDetail/transactionDetailReducer.js +121 -6
  106. package/lib/esm/view/transactionDetail/transactionDetailSelector.js +29 -1
  107. package/lib/index.d.ts +33 -26
  108. package/lib/index.js +86 -60
  109. package/lib/reducer.d.ts +2 -2
  110. package/lib/reducer.js +2 -2
  111. package/lib/view/aiAccountantView/aiAccountantViewPayload.d.ts +2 -2
  112. package/lib/view/aiAccountantView/aiAccountantViewReducer.d.ts +1 -1
  113. package/lib/view/aiAccountantView/epics/fetchAiAccountantCustomersEpic.js +2 -5
  114. package/lib/view/aiAccountantView/epics/fetchAiAccountantJobsEpic.js +2 -5
  115. package/lib/view/aiAccountantView/epics/triggerAiAccountantJobEpic.js +4 -1
  116. package/lib/view/aiCfoView/aiCfoViewReducer.d.ts +4 -1
  117. package/lib/view/aiCfoView/aiCfoViewReducer.js +12 -2
  118. package/lib/view/aiCfoView/epics/updateChatSessionPinEpic.d.ts +8 -0
  119. package/lib/view/aiCfoView/epics/updateChatSessionPinEpic.js +50 -0
  120. package/lib/view/companyHealthMetricView/companyHealthMetricViewSelector.js +1 -1
  121. package/lib/view/companyTaskManagerView/companyTaskManagerViewSelector.d.ts +1 -1
  122. package/lib/view/companyTaskManagerView/companyTaskManagerViewSelector.js +1 -1
  123. package/lib/view/companyView/helpers/cockpitHelpers.js +1 -1
  124. package/lib/view/companyView/selector/companyManagementViewSelector.js +1 -1
  125. package/lib/view/companyView/selector/companyPortfolioViewSelector.js +1 -1
  126. package/lib/view/companyView/types/cockpitTypes.d.ts +1 -1
  127. package/lib/view/creditAgentView/creditAgentViewPayload.d.ts +0 -3
  128. package/lib/view/creditAgentView/creditAgentViewReducer.d.ts +1 -1
  129. package/lib/view/creditAgentView/creditAgentViewReducer.js +2 -17
  130. package/lib/view/creditAgentView/creditAgentViewState.d.ts +0 -5
  131. package/lib/view/expenseAutomationView/epics/missingReceipts/fetchBulkUploadBatchesEpic.js +2 -2
  132. package/lib/view/expenseAutomationView/epics/missingReceipts/fetchCompletedTransactionsEpic.js +2 -1
  133. package/lib/view/expenseAutomationView/epics/missingReceipts/restoreBulkUploadAutomatchingOnMountEpic.js +8 -6
  134. package/lib/view/expenseAutomationView/epics/missingReceipts/searchTransactionsForManualMatchEpic.js +7 -1
  135. package/lib/view/expenseAutomationView/epics/transactionCategorization/fetchTransactionCategorizationEpic.d.ts +2 -2
  136. package/lib/view/expenseAutomationView/epics/transactionCategorization/fetchTransactionCategorizationEpic.js +10 -0
  137. package/lib/view/expenseAutomationView/helpers/transactionCategorizationLocalDataHelper.d.ts +2 -1
  138. package/lib/view/expenseAutomationView/helpers/transactionCategorizationLocalDataHelper.js +23 -2
  139. package/lib/view/expenseAutomationView/payload/transactionCategorizationPayload.d.ts +21 -0
  140. package/lib/view/expenseAutomationView/reducers/missingReceiptsViewReducer.d.ts +2 -1
  141. package/lib/view/expenseAutomationView/reducers/missingReceiptsViewReducer.js +2 -1
  142. package/lib/view/expenseAutomationView/reducers/transactionsViewReducer.d.ts +28 -5
  143. package/lib/view/expenseAutomationView/reducers/transactionsViewReducer.js +253 -14
  144. package/lib/view/expenseAutomationView/selectorTypes/missingReceiptsSelectorTypes.d.ts +2 -1
  145. package/lib/view/expenseAutomationView/selectorTypes/transactionsViewSelectorTypes.d.ts +16 -0
  146. package/lib/view/expenseAutomationView/selectors/transactionCategorizationSelector.d.ts +7 -1
  147. package/lib/view/expenseAutomationView/selectors/transactionCategorizationSelector.js +150 -13
  148. package/lib/view/expenseAutomationView/transactionFilterHelpers.d.ts +66 -0
  149. package/lib/view/expenseAutomationView/transactionFilterHelpers.js +262 -0
  150. package/lib/view/expenseAutomationView/types/completedSubTab.d.ts +10 -0
  151. package/lib/view/expenseAutomationView/types/completedSubTab.js +21 -0
  152. package/lib/view/expenseAutomationView/types/missingReceiptsViewState.d.ts +1 -2
  153. package/lib/view/expenseAutomationView/types/missingReceiptsViewState.js +1 -4
  154. package/lib/view/expenseAutomationView/types/transactionsViewState.d.ts +44 -2
  155. package/lib/view/financeStatement/financeStatementSelector.js +1 -1
  156. package/lib/view/profitAndLossProjectView/profitAndLossProjectViewSelectorTypes.d.ts +1 -1
  157. package/lib/view/recommendation/fetchEntityRecommendationsForLineUpdateEpic.d.ts +9 -0
  158. package/lib/view/recommendation/fetchEntityRecommendationsForLineUpdateEpic.js +91 -0
  159. package/lib/view/recommendation/recommendationHelper.d.ts +9 -0
  160. package/lib/view/recommendation/recommendationHelper.js +31 -1
  161. package/lib/view/recommendation/recommendationReducer.d.ts +20 -1
  162. package/lib/view/recommendation/recommendationReducer.js +27 -2
  163. package/lib/view/spendManagement/billPay/billList/billListState.d.ts +1 -1
  164. package/lib/view/spendManagement/chargeCards/cashbackDetail/cashbackDetailReducer.d.ts +3 -1
  165. package/lib/view/spendManagement/chargeCards/cashbackDetail/cashbackDetailReducer.js +3 -2
  166. package/lib/view/spendManagement/chargeCards/chargeCardConfig/chargeCardConfigReducer.d.ts +3 -1
  167. package/lib/view/spendManagement/chargeCards/chargeCardConfig/chargeCardConfigReducer.js +3 -2
  168. package/lib/view/spendManagement/chargeCards/chargeCardDetail/chargeCardDetailReducer.js +55 -18
  169. package/lib/view/spendManagement/chargeCards/chargeCardDetail/fetchChargeCardDetailEpic.js +16 -2
  170. package/lib/view/spendManagement/chargeCards/chargeCardDetail/fetchChargeCardDetailPageEpic.js +5 -4
  171. package/lib/view/spendManagement/chargeCards/chargeCardList/chargeCardListReducer.d.ts +18 -3
  172. package/lib/view/spendManagement/chargeCards/chargeCardList/chargeCardListReducer.js +27 -10
  173. package/lib/view/spendManagement/chargeCards/chargeCardList/fetchChargeCardListPageEpic.js +9 -6
  174. package/lib/view/spendManagement/chargeCards/chargeCardPaymentHistory/chargeCardPaymentHistory.d.ts +1 -1
  175. package/lib/view/spendManagement/chargeCards/chargeCardPaymentHistory/chargeCardPaymentHistoryReducer.d.ts +3 -1
  176. package/lib/view/spendManagement/chargeCards/chargeCardPaymentHistory/chargeCardPaymentHistoryReducer.js +3 -2
  177. package/lib/view/spendManagement/chargeCards/chargeCardPaymentHistory/fetchChargeCardPaymentPageEpic.js +1 -1
  178. package/lib/view/spendManagement/chargeCards/chargeCardRepaymentDetail/chargeCardRepaymentDetailReducer.d.ts +3 -1
  179. package/lib/view/spendManagement/chargeCards/chargeCardRepaymentDetail/chargeCardRepaymentDetailReducer.js +7 -3
  180. package/lib/view/spendManagement/reimbursement/remiListView/remiListState.d.ts +1 -1
  181. package/lib/view/spendManagement/spendManagementFilterHelpers.d.ts +12 -3
  182. package/lib/view/spendManagement/spendManagementFilterHelpers.js +4 -2
  183. package/lib/view/subscriptionView/subscriptionViewSelector.js +1 -1
  184. package/lib/view/taskManager/taskListView/taskList.d.ts +1 -1
  185. package/lib/view/taskManager/taskListView/taskListReducer.js +1 -1
  186. package/lib/view/transactionDetail/transactionDetailReducer.d.ts +11 -1
  187. package/lib/view/transactionDetail/transactionDetailReducer.js +122 -7
  188. package/lib/view/transactionDetail/transactionDetailSelector.d.ts +2 -0
  189. package/lib/view/transactionDetail/transactionDetailSelector.js +31 -2
  190. package/lib/view/transactionDetail/transactionDetailTypes.d.ts +1 -0
  191. package/lib/view/zeniOAuthView/zeniOAuthSelector.d.ts +1 -1
  192. package/package.json +9 -4
  193. package/lib/esm/view/creditAgentView/epics/fetchCreditAgentAccessEpic.js +0 -13
  194. package/lib/tsconfig.typecheck.tsbuildinfo +0 -1
  195. package/lib/view/creditAgentView/epics/fetchCreditAgentAccessEpic.d.ts +0 -11
  196. package/lib/view/creditAgentView/epics/fetchCreditAgentAccessEpic.js +0 -17
@@ -1,12 +1,12 @@
1
1
  import { EMPTY, concat, from, of } from 'rxjs';
2
2
  import { catchError, concatMap, filter, mergeMap, take } from 'rxjs/operators';
3
+ import { toMonthYearPeriodId } from '../../../../commonStateTypes/timePeriod';
4
+ import { getCurrentTenant } from '../../../../entity/tenant/tenantSelector';
3
5
  import { isSuccessResponse } from '../../../../responsePayload';
4
- import { isBatchDetailsStatusPendingOrProcessing, toBatchListItem, } from '../../payload/missingReceiptsPayload';
5
- import { fetchBulkUploadBatchesSuccess, restoreBulkUploadAutomatchingOnMount, restoreBulkUploadMatchingState, } from '../../reducers/missingReceiptsViewReducer';
6
6
  import { BULK_UPLOAD_BATCH_LIST_DATE_RANGE_DAYS } from '../../helpers/bulkUploadTiming';
7
7
  import { rollingCalendarDateRangeInclusive } from '../../helpers/rollingCalendarDateRangeInclusive';
8
- import { toMonthYearPeriodId } from '../../../../commonStateTypes/timePeriod';
9
- import { getCurrentTenant } from '../../../../entity/tenant/tenantSelector';
8
+ import { isBatchDetailsStatusPendingOrProcessing, toBatchListItem, } from '../../payload/missingReceiptsPayload';
9
+ import { fetchBulkUploadBatchesSuccess, restoreBulkUploadAutomatchingOnMount, restoreBulkUploadMatchingState, } from '../../reducers/missingReceiptsViewReducer';
10
10
  /**
11
11
  * On missing-receipts entry / period switch, hydrate the automatching banner for the logged-in
12
12
  * user without relying on UI-driven list fetches (the Missing tab does not fetch the batch list
@@ -49,10 +49,12 @@ export const restoreBulkUploadAutomatchingOnMountEpic = (actions$, state$, zeniA
49
49
  bulkUpload.batchListFetchState.fetchState === 'Completed';
50
50
  const listSource = alreadyCached
51
51
  ? of(null)
52
- : zeniAPI.getJSON(listUrl).pipe(catchError(() => of(null)));
52
+ : zeniAPI
53
+ .getJSON(listUrl)
54
+ .pipe(catchError(() => of(null)));
53
55
  return listSource.pipe(mergeMap((listResponse) => {
54
56
  const batchList = alreadyCached
55
- ? bulkUpload.batchListByPeriod[periodId] ?? []
57
+ ? (bulkUpload.batchListByPeriod[periodId] ?? [])
56
58
  : listResponse != null &&
57
59
  isSuccessResponse(listResponse) &&
58
60
  listResponse.data != null
@@ -2,10 +2,11 @@ import { EMPTY, from, merge, of } from 'rxjs';
2
2
  import { catchError, debounceTime, filter, mergeMap, switchMap, } from 'rxjs/operators';
3
3
  import { updateTransactions } from '../../../../entity/transaction/transactionReducer';
4
4
  import { createZeniAPIStatus, isSuccessResponse, } from '../../../../responsePayload';
5
+ import { rollingCalendarDateRangeInclusive } from '../../helpers/rollingCalendarDateRangeInclusive';
5
6
  import { supportedTransactionPayloadToManualSearchResult } from '../../payload/missingReceiptsPayload';
6
7
  import { searchTransactionsForManualMatch, searchTransactionsForManualMatchFailure, searchTransactionsForManualMatchSuccess, } from '../../reducers/missingReceiptsViewReducer';
8
+ import { DEFAULT_COMPLETED_SUB_TAB } from '../../types/completedSubTab';
7
9
  import { toTransactionsSortKey } from '../../types/transactionsViewState';
8
- import { rollingCalendarDateRangeInclusive } from '../../helpers/rollingCalendarDateRangeInclusive';
9
10
  /**
10
11
  * `auto_categorized` for manual transaction search (Unmatched).
11
12
  * Backend contract TBD — align with `fetchTransactionCategorizationEpic` (`selectedTab === 'autoCategorized'`)
@@ -46,6 +47,11 @@ export const searchTransactionsForManualMatchEpic = (actions$, state$, zeniAPI)
46
47
  page_token: pageToken ?? null,
47
48
  page_size: manualSearch.pageSize,
48
49
  search_text: query,
50
+ // Manual search ignores the Completed sub-tab axis — always span the
51
+ // default sentinel so the receipt-matching UX returns results
52
+ // regardless of where the transaction would otherwise be bucketed on
53
+ // the Completed tab.
54
+ sub_tab: DEFAULT_COMPLETED_SUB_TAB,
49
55
  };
50
56
  return zeniAPI
51
57
  .getJSON(`${zeniAPI.apiEndPoints.accountMicroServiceBaseUrl}/1.0/expense-automation/transactions?query=${encodeURIComponent(JSON.stringify(queryParam))}`)
@@ -5,7 +5,8 @@ import { getCurrentTenant } from '../../../../entity/tenant/tenantSelector';
5
5
  import { updateTransactions } from '../../../../entity/transaction/transactionReducer';
6
6
  import { createZeniAPIStatus, isSuccessResponse, } from '../../../../responsePayload';
7
7
  import { getLineItemsByTransactionsIdsFromResponse } from '../../helpers/transactionCategorizationLocalDataHelper';
8
- import { fetchTransactionCategorization, fetchTransactionCategorizationFailure, fetchTransactionCategorizationSuccess, initializeTransactionCategorizationViewLocalData, resetOtherTabsFetchState, updateTotalCountForTransactionCategorization, updateTransactionCategorizationUIState, } from '../../reducers/transactionsViewReducer';
8
+ import { fetchTransactionCategorization, fetchTransactionCategorizationFailure, fetchTransactionCategorizationSuccess, initializeTransactionCategorizationViewLocalData, resetOtherTabsFetchState, updateParentTotalCountForTab, updateTotalCountForTransactionCategorization, updateTransactionCategorizationUIState, } from '../../reducers/transactionsViewReducer';
9
+ import { DEFAULT_COMPLETED_SUB_TAB } from '../../types/completedSubTab';
9
10
  import { TRANSACTIONS_TABS, toTransactionsSortKey, } from '../../types/transactionsViewState';
10
11
  export const fetchTransactionCategorizationEpic = (actions$, state$, zeniAPI) => actions$.pipe(filter(fetchTransactionCategorization.match), switchMap((action) => {
11
12
  const { expenseAutomationViewState: { selectedPeriodByTenantId }, expenseAutomationTransactionsViewState, } = state$.value;
@@ -23,6 +24,9 @@ export const fetchTransactionCategorizationEpic = (actions$, state$, zeniAPI) =>
23
24
  page_token: pageToken,
24
25
  page_size: 25,
25
26
  search_text: uiState.searchString,
27
+ sub_tab: selectedTab === 'autoCategorized'
28
+ ? expenseAutomationTransactionsViewState.selectedTransactionCategorizationCompletedSubTab
29
+ : DEFAULT_COMPLETED_SUB_TAB,
26
30
  };
27
31
  return zeniAPI
28
32
  .getJSON(`${zeniAPI.apiEndPoints.accountMicroServiceBaseUrl}/1.0/expense-automation/transactions?query=${encodeURIComponent(JSON.stringify(queryParam))}`)
@@ -45,6 +49,12 @@ export const fetchTransactionCategorizationEpic = (actions$, state$, zeniAPI) =>
45
49
  selectedTab,
46
50
  totalCount: response.data.total_count,
47
51
  }));
52
+ updateActions.push(updateParentTotalCountForTab({
53
+ selectedPeriod,
54
+ selectedTab,
55
+ parentTotalCount: response.data.parent_tab_total_count ??
56
+ response.data.total_count,
57
+ }));
48
58
  updateActions.push(resetOtherTabsFetchState({
49
59
  refreshViewInBackground,
50
60
  tabs: TRANSACTIONS_TABS.filter((tab) => selectedTab !== tab),
@@ -59,6 +59,22 @@ export const filterAutoTabLineItems = (tabView, transactionId, updatedTransactio
59
59
  }
60
60
  };
61
61
  export const MAX_SELECTION_LIMIT = 5;
62
+ // Returns true when the transaction should be auto-added to the
63
+ // selectedCheckBoxTransactionIds list after a categorization-related
64
+ // state mutation: all line items are filled, the selection limit has
65
+ // not been hit, and the id isn't already in the list. Consolidates the
66
+ // triplet that was inlined at four sites (updateSelectedCustomer/Vendor,
67
+ // setAllItemsToCategoryClassInLocalDataForCategorization, and
68
+ // setEntityRecommendationForLineIdInLocalData below).
69
+ export const shouldAutoSelectAndAdd = (transaction, localData, selectedCheckBoxTransactionIds, uncategorizedAccounts, isUncategorizedExpenseCategoryEnabled, isAccountingClassesEnabled) => {
70
+ if (selectedCheckBoxTransactionIds.includes(transaction.id)) {
71
+ return false;
72
+ }
73
+ if (selectedCheckBoxTransactionIds.length >= MAX_SELECTION_LIMIT) {
74
+ return false;
75
+ }
76
+ return checkIfAllLineItemsAreCategoryClassFilled(transaction, localData, uncategorizedAccounts, isUncategorizedExpenseCategoryEnabled, isAccountingClassesEnabled);
77
+ };
62
78
  export const toTransactionDetailLocalData = (transaction, selectedTab, lineItemsByTransactionIds, uncategorizedIncomeExpense, isUncategorizedExpenseCategoryEnabled) => {
63
79
  const transactionLocal = {
64
80
  ...initialSupportedTransactionCategorization.transactionReviewLocalData,
@@ -544,10 +560,14 @@ export const setEntityRecommendationForLineIdInLocalData = (draft, transaction,
544
560
  selectedEntity: transactionDetailLocalData.selectedEntity,
545
561
  tabSpecificLineItems: transactionDetailLocalData.tabSpecificLineItems,
546
562
  };
563
+ // Auto-uncheck if not all line items are filled (the row's
564
+ // category/class became stale after the entity update). Otherwise
565
+ // route through shouldAutoSelectAndAdd so the MAX_SELECTION_LIMIT
566
+ // gate is consistently applied across all auto-add sites.
547
567
  if (!checkIfAllLineItemsAreCategoryClassFilled(transaction, newLocalData, uncategorizedAccounts, isUncategorizedExpenseCategoryEnabled, isAccountingClassesEnabled)) {
548
568
  selectedCheckBoxTransactionIds = selectedCheckBoxTransactionIds.filter((id) => id !== transaction.id);
549
569
  }
550
- else if (selectedCheckBoxTransactionIds.includes(transaction.id) === false) {
570
+ else if (shouldAutoSelectAndAdd(transaction, newLocalData, selectedCheckBoxTransactionIds, uncategorizedAccounts, isUncategorizedExpenseCategoryEnabled, isAccountingClassesEnabled)) {
551
571
  selectedCheckBoxTransactionIds.push(transaction.id);
552
572
  }
553
573
  draft.selectedCheckBoxTransactionIds = selectedCheckBoxTransactionIds;
@@ -1,5 +1,6 @@
1
1
  import { createAction, createSlice } from '@reduxjs/toolkit';
2
2
  import { toMonthYearPeriodId } from '../../../commonStateTypes/timePeriod';
3
+ import { DEFAULT_COMPLETED_SUB_TAB, } from '../types/completedSubTab';
3
4
  import { MIN_MANUAL_TRANSACTION_SEARCH_LENGTH, } from '../types/missingReceiptsViewState';
4
5
  export const getCompletedTransactionsCacheKey = (periodId, sortKey, sortOrder, subTab) => {
5
6
  const sortKeyPart = sortKey ?? 'none';
@@ -13,7 +14,7 @@ export const initialBulkUploadState = {
13
14
  batchListByPeriod: {},
14
15
  batchListFetchState: { fetchState: 'Not-Started', error: undefined },
15
16
  batchStatusById: {},
16
- completedSubTab: 'all',
17
+ completedSubTab: DEFAULT_COMPLETED_SUB_TAB,
17
18
  completedTransactionsByPeriod: {},
18
19
  confirmMatchStatus: { fetchState: 'Not-Started', error: undefined },
19
20
  currentBatchId: undefined,
@@ -3,7 +3,8 @@ import recordGet from 'lodash/get';
3
3
  import uniq from 'lodash/uniq';
4
4
  import { toMonthYearPeriodId, } from '../../../commonStateTypes/timePeriod';
5
5
  import { isVendorTransaction } from '../../../entity/transaction/stateTypes/vendorTransaction';
6
- import { MAX_SELECTION_LIMIT, checkIfAllLineItemsAreCategoryClassFilled, filterAutoTabLineItems, mergeTabSpecificLineItems, removeTransactionFromTabView, setEntityRecommendationForLineIdInLocalData, toSetAllLineItemsToCategoryClass, toTransactionDetailLocalData, } from '../helpers/transactionCategorizationLocalDataHelper';
6
+ import { filterAutoTabLineItems, mergeTabSpecificLineItems, removeTransactionFromTabView, setEntityRecommendationForLineIdInLocalData, shouldAutoSelectAndAdd, toSetAllLineItemsToCategoryClass, toTransactionDetailLocalData, } from '../helpers/transactionCategorizationLocalDataHelper';
7
+ import { DEFAULT_COMPLETED_SUB_TAB, } from '../types/completedSubTab';
7
8
  export const initialTransactionTabViewState = {
8
9
  saveStatus: {
9
10
  fetchState: 'Not-Started',
@@ -27,6 +28,10 @@ export const initialTransactionTabViewState = {
27
28
  totalCount: {},
28
29
  limit: 10,
29
30
  },
31
+ filters: {
32
+ categoryCombinationOperator: 'AND',
33
+ categories: [],
34
+ },
30
35
  fetchState: 'Not-Started',
31
36
  error: undefined,
32
37
  hasValidState() {
@@ -41,12 +46,74 @@ export const initialTransactionTabViewState = {
41
46
  selectedTransactionLineId: undefined,
42
47
  };
43
48
  export const initialState = {
49
+ autoCategorizedSubTabCache: {},
50
+ parentTotalCountByTab: {
51
+ review: {},
52
+ autoCategorized: {},
53
+ },
44
54
  selectedTransactionCategorizationTab: 'review',
55
+ selectedTransactionCategorizationCompletedSubTab: DEFAULT_COMPLETED_SUB_TAB,
45
56
  transactionCategorizationView: {
46
57
  review: { ...initialTransactionTabViewState },
47
58
  autoCategorized: { ...initialTransactionTabViewState },
48
59
  },
49
60
  };
61
+ /**
62
+ * Capture the autoCategorized tab's current paginated dataset for a sub-tab.
63
+ * The mid-flight `'In-Progress'` state is normalised to `'Completed'` because
64
+ * the in-flight request is cancelled by `switchMap` in the fetch epic when the
65
+ * sub-tab changes — restoring `'In-Progress'` later would leave the UI stuck
66
+ * showing a phantom loading state with no fetch actually running. Length-zero
67
+ * datasets degrade to `'Not-Started'` so the next visit triggers a fresh
68
+ * fetch from page 1 instead of short-circuiting on stale empty caches.
69
+ *
70
+ * Snapshot scope (deliberately narrow):
71
+ * - Captured: `pageToken`, `totalCount`, `transactionIdsBySelectedPeriod`,
72
+ * `fetchState`, `error`, `scrollPosition`. The first five are the keys the
73
+ * fetch epic's cache check reads to decide whether to short-circuit a
74
+ * sub-tab revisit; `scrollPosition` is captured per sub-tab so revisiting
75
+ * restores the user's last offset and switching to a never-visited sub-tab
76
+ * starts at the top instead of inheriting the outgoing sub-tab's offset
77
+ * (which would land the user at a meaningless row index against a
78
+ * completely different list).
79
+ * - Intentionally NOT captured (left in place on `autoCat` and inherited by
80
+ * the incoming sub-tab):
81
+ * - `uiState.searchString`, `uiState.sortKey`, `uiState.sortOrder`,
82
+ * `uiState.nodeCollapseState`, `uiState.limit` — search and sort are
83
+ * user-applied preferences that persist across sub-tabs by design (a
84
+ * search query stays active when the user toggles between Manual / AI
85
+ * Accountant).
86
+ * - `selectedCheckBoxTransactionIds` — selection state is intentionally
87
+ * cleared at the action source (the fetch epic / save flow), not
88
+ * by the snapshot path. Carrying selection across sub-tabs is safe
89
+ * because every sub-tab change goes through a fetch round-trip that
90
+ * reconciles selection against the new row IDs.
91
+ * - `transactionReviewLocalDataById`, `transactionIdsWithUnsavedData`,
92
+ * `uploadReceiptStatusById`, `saveStatus`, `refreshStatus`,
93
+ * `markAsNotMiscategorizedStatus` — these are per-row / per-flight
94
+ * caches that are write-through global state, not sub-tab-scoped.
95
+ *
96
+ * If the contract changes (e.g. selection should reset on every sub-tab
97
+ * switch), update both this comment and the snapshot return shape together.
98
+ */
99
+ function snapshotAutoCategorizedTab(autoCat) {
100
+ const transactionIdsBySelectedPeriod = {
101
+ ...autoCat.transactionIdsBySelectedPeriod,
102
+ };
103
+ const hasAnyLoadedRows = Object.values(transactionIdsBySelectedPeriod).some((ids) => ids.length > 0);
104
+ let normalisedFetchState = autoCat.fetchState;
105
+ if (normalisedFetchState === 'In-Progress') {
106
+ normalisedFetchState = hasAnyLoadedRows ? 'Completed' : 'Not-Started';
107
+ }
108
+ return {
109
+ error: autoCat.error,
110
+ fetchState: normalisedFetchState,
111
+ pageToken: autoCat.uiState.pageToken,
112
+ scrollPosition: autoCat.uiState.scrollPosition,
113
+ totalCount: { ...autoCat.uiState.totalCount },
114
+ transactionIdsBySelectedPeriod,
115
+ };
116
+ }
50
117
  const expenseAutomationTransactionsView = createSlice({
51
118
  name: 'expenseAutomationTransactionsView',
52
119
  initialState,
@@ -236,6 +303,12 @@ const expenseAutomationTransactionsView = createSlice({
236
303
  uiState.totalCount;
237
304
  }
238
305
  },
306
+ updateTransactionFilters(draft, action) {
307
+ const { selectedTab, filters } = action.payload;
308
+ // `filters` is non-nullable per the payload type, so no runtime
309
+ // null-check is needed — dropping it removes a dead defensive branch.
310
+ draft.transactionCategorizationView[selectedTab].filters = filters;
311
+ },
239
312
  saveTransactionCategorization: {
240
313
  prepare(selectedTab, transactionIds, cotTrackingByTransactionId, isUncategorizedExpenseCategoryEnabled) {
241
314
  return {
@@ -260,6 +333,55 @@ const expenseAutomationTransactionsView = createSlice({
260
333
  return { payload: { newSelectedTab } };
261
334
  },
262
335
  },
336
+ updateTransactionCategorizationCompletedSubTab: {
337
+ reducer(draft, action) {
338
+ const { newSubTab } = action.payload;
339
+ const previousSubTab = draft.selectedTransactionCategorizationCompletedSubTab;
340
+ if (previousSubTab === newSubTab) {
341
+ return;
342
+ }
343
+ const autoCat = draft.transactionCategorizationView.autoCategorized;
344
+ // Snapshot the outgoing sub-tab so a future revisit can short-circuit
345
+ // the fetch epic (mirrors the parent-tab "don't refetch what's
346
+ // already cached" behaviour). Period-scoped data is preserved as-is
347
+ // because the snapshot itself is keyed only by sub-tab; the active
348
+ // tab's per-period maps remain the source of truth while live.
349
+ draft.autoCategorizedSubTabCache[previousSubTab] =
350
+ snapshotAutoCategorizedTab(autoCat);
351
+ const cached = draft.autoCategorizedSubTabCache[newSubTab];
352
+ if (cached != null) {
353
+ autoCat.uiState.pageToken = cached.pageToken;
354
+ autoCat.uiState.totalCount = { ...cached.totalCount };
355
+ autoCat.uiState.scrollPosition = cached.scrollPosition;
356
+ autoCat.transactionIdsBySelectedPeriod = {
357
+ ...cached.transactionIdsBySelectedPeriod,
358
+ };
359
+ autoCat.fetchState = cached.fetchState;
360
+ autoCat.error = cached.error;
361
+ }
362
+ else {
363
+ // Sub-tab has never been visited — clear the active slot so the
364
+ // view-epic's `fetchState === 'Not-Started' || transactionIds.length
365
+ // === 0` cache check falls through and triggers a fresh page-1
366
+ // fetch for the new dataset. `scrollPosition` is reset to the top
367
+ // so the incoming list isn't anchored to the outgoing sub-tab's
368
+ // offset (different rows → previous offset is meaningless).
369
+ autoCat.uiState.pageToken = null;
370
+ autoCat.uiState.totalCount = {};
371
+ autoCat.uiState.scrollPosition = {
372
+ scrollTop: 0,
373
+ scrollLeft: undefined,
374
+ };
375
+ autoCat.transactionIdsBySelectedPeriod = {};
376
+ autoCat.fetchState = 'Not-Started';
377
+ autoCat.error = undefined;
378
+ }
379
+ draft.selectedTransactionCategorizationCompletedSubTab = newSubTab;
380
+ },
381
+ prepare(newSubTab) {
382
+ return { payload: { newSubTab } };
383
+ },
384
+ },
263
385
  updateTransactionCategorization: {
264
386
  reducer(draft, action) {
265
387
  const selectedTab = action.payload.selectedTab;
@@ -308,12 +430,9 @@ const expenseAutomationTransactionsView = createSlice({
308
430
  const selectedCheckBoxTransactionIds = draft.transactionCategorizationView[selectedTab]
309
431
  .selectedCheckBoxTransactionIds;
310
432
  if (selectedTransaction != null &&
311
- checkIfAllLineItemsAreCategoryClassFilled(selectedTransaction, newTransactionLocalData, uncategorizedAccounts, isUncategorizedExpenseCategoryEnabled, isAccountingClassesEnabled) === true && // check if all line items are filled
312
- selectedCheckBoxTransactionIds.length < MAX_SELECTION_LIMIT &&
313
- selectedCheckBoxTransactionIds.includes(transactionId) === false) {
433
+ shouldAutoSelectAndAdd(selectedTransaction, newTransactionLocalData, selectedCheckBoxTransactionIds, uncategorizedAccounts, isUncategorizedExpenseCategoryEnabled, isAccountingClassesEnabled)) {
314
434
  draft.transactionCategorizationView[selectedTab].selectedCheckBoxTransactionIds = [
315
- ...draft.transactionCategorizationView[selectedTab]
316
- .selectedCheckBoxTransactionIds,
435
+ ...selectedCheckBoxTransactionIds,
317
436
  transactionId,
318
437
  ];
319
438
  }
@@ -396,9 +515,26 @@ const expenseAutomationTransactionsView = createSlice({
396
515
  const transactionLocalData = recordGet(draft.transactionCategorizationView[selectedTab]
397
516
  .transactionReviewLocalDataById, transactionId.id, undefined);
398
517
  if (transactionLocalData != null) {
518
+ const newLocalData = setEntityRecommendationForLineIdInLocalData(draft.transactionCategorizationView[selectedTab], transaction, transactionLocalData.transactionReviewLocalData, entity, lineIds, uncategorizedAccounts, action.payload.recommendationWithCOTByLineId, isUncategorizedExpenseCategoryEnabled, isAccountingClassesEnabled);
519
+ // Release the per-line fetch flag for every line this fetch
520
+ // covered. Done in-place on the freshly returned local data
521
+ // (the helper produces a new mutable object), before we write
522
+ // it back to the draft.
523
+ lineIds.forEach((lineId) => {
524
+ const lineItem = newLocalData.lineItemById[lineId];
525
+ if (lineItem != null) {
526
+ newLocalData.lineItemById[lineId] = {
527
+ ...lineItem,
528
+ categoryClassRecommendationsFetchState: {
529
+ fetchState: 'Completed',
530
+ error: undefined,
531
+ },
532
+ };
533
+ }
534
+ });
399
535
  draft.transactionCategorizationView[selectedTab].transactionReviewLocalDataById[transaction.id] = {
400
536
  transactionId: transaction.id,
401
- transactionReviewLocalData: setEntityRecommendationForLineIdInLocalData(draft.transactionCategorizationView[selectedTab], transaction, transactionLocalData.transactionReviewLocalData, entity, lineIds, uncategorizedAccounts, action.payload.recommendationWithCOTByLineId, isUncategorizedExpenseCategoryEnabled, isAccountingClassesEnabled),
537
+ transactionReviewLocalData: newLocalData,
402
538
  };
403
539
  // Mark transaction as dirty (autofill happened)
404
540
  if (!draft.transactionCategorizationView[selectedTab].transactionIdsWithUnsavedData.includes(transaction.id)) {
@@ -411,6 +547,99 @@ const expenseAutomationTransactionsView = createSlice({
411
547
  }
412
548
  },
413
549
  },
550
+ // Start arm of the line-update recommendations fetch. Flips covered
551
+ // lines to In-Progress AND removes the transaction from
552
+ // selectedCheckBoxTransactionIds — the row is unchecked + disabled
553
+ // until either the success arm (recheck if limit allows) or the
554
+ // failure arm releases the per-line flag.
555
+ markCategoryClassRecommendationsInProgressForCategorization: {
556
+ reducer(draft, action) {
557
+ const { selectedTab, transactionId, lineIds } = action.payload;
558
+ const tab = draft.transactionCategorizationView[selectedTab];
559
+ const transactionLocalData = recordGet(tab.transactionReviewLocalDataById, transactionId.id, undefined);
560
+ if (transactionLocalData != null) {
561
+ lineIds.forEach((lineId) => {
562
+ const lineItem = transactionLocalData.transactionReviewLocalData.lineItemById[lineId];
563
+ if (lineItem != null) {
564
+ transactionLocalData.transactionReviewLocalData.lineItemById[lineId] = {
565
+ ...lineItem,
566
+ categoryClassRecommendationsFetchState: {
567
+ fetchState: 'In-Progress',
568
+ error: undefined,
569
+ },
570
+ };
571
+ }
572
+ });
573
+ }
574
+ // Uncheck on fetch start: the row's existing category/class are
575
+ // stale after a payee change, so we drop the auto-selection until
576
+ // fresh recommendations land.
577
+ if (tab.selectedCheckBoxTransactionIds.includes(transactionId.id)) {
578
+ tab.selectedCheckBoxTransactionIds =
579
+ tab.selectedCheckBoxTransactionIds.filter((id) => id !== transactionId.id);
580
+ }
581
+ },
582
+ prepare(selectedTab, transactionId, lineIds) {
583
+ return { payload: { selectedTab, transactionId, lineIds } };
584
+ },
585
+ },
586
+ // Completed arm. Used by the epic when the HTTP call succeeds but the
587
+ // server returned no per-line recommendations — we still need to
588
+ // release the In-Progress flag so the skeleton resolves. Successful
589
+ // responses with recommendations go through
590
+ // setEntityRecommendationForLineIdsForCategorization instead (which
591
+ // also flips the lines to Completed).
592
+ markCategoryClassRecommendationsCompletedForCategorization: {
593
+ reducer(draft, action) {
594
+ const { selectedTab, transactionId, lineIds } = action.payload;
595
+ const transactionLocalData = recordGet(draft.transactionCategorizationView[selectedTab]
596
+ .transactionReviewLocalDataById, transactionId.id, undefined);
597
+ if (transactionLocalData != null) {
598
+ lineIds.forEach((lineId) => {
599
+ const lineItem = transactionLocalData.transactionReviewLocalData.lineItemById[lineId];
600
+ if (lineItem != null) {
601
+ transactionLocalData.transactionReviewLocalData.lineItemById[lineId] = {
602
+ ...lineItem,
603
+ categoryClassRecommendationsFetchState: {
604
+ fetchState: 'Completed',
605
+ error: undefined,
606
+ },
607
+ };
608
+ }
609
+ });
610
+ }
611
+ },
612
+ prepare(selectedTab, transactionId, lineIds) {
613
+ return { payload: { selectedTab, transactionId, lineIds } };
614
+ },
615
+ },
616
+ // Failure arm. Flips covered lines to Error so the skeleton resolves
617
+ // and downstream UI can react to the failure. The transaction stays
618
+ // unchecked from the start-arm action.
619
+ markCategoryClassRecommendationsFailureForCategorization: {
620
+ reducer(draft, action) {
621
+ const { selectedTab, transactionId, lineIds, error } = action.payload;
622
+ const transactionLocalData = recordGet(draft.transactionCategorizationView[selectedTab]
623
+ .transactionReviewLocalDataById, transactionId.id, undefined);
624
+ if (transactionLocalData != null) {
625
+ lineIds.forEach((lineId) => {
626
+ const lineItem = transactionLocalData.transactionReviewLocalData.lineItemById[lineId];
627
+ if (lineItem != null) {
628
+ transactionLocalData.transactionReviewLocalData.lineItemById[lineId] = {
629
+ ...lineItem,
630
+ categoryClassRecommendationsFetchState: {
631
+ fetchState: 'Error',
632
+ error,
633
+ },
634
+ };
635
+ }
636
+ });
637
+ }
638
+ },
639
+ prepare(selectedTab, transactionId, lineIds, error) {
640
+ return { payload: { selectedTab, transactionId, lineIds, error } };
641
+ },
642
+ },
414
643
  fetchTransactionCategorizationSuccess(draft, action) {
415
644
  const { selectedTab, refreshViewInBackground } = action.payload;
416
645
  if (draft.transactionCategorizationView[selectedTab].fetchState ===
@@ -506,9 +735,7 @@ const expenseAutomationTransactionsView = createSlice({
506
735
  .selectedCheckBoxTransactionIds;
507
736
  if (newTransactionLocalData.customer != null &&
508
737
  newTransactionLocalData.customer.id != null &&
509
- checkIfAllLineItemsAreCategoryClassFilled(selectedTransaction, newTransactionLocalData, uncategorizedAccounts, isUncategorizedExpenseCategoryEnabled, isAccountingClassesEnabled) === true && // check if all line items are filled
510
- selectedCheckBoxTransactionIds.length < MAX_SELECTION_LIMIT &&
511
- selectedCheckBoxTransactionIds.includes(transactionId) === false) {
738
+ shouldAutoSelectAndAdd(selectedTransaction, newTransactionLocalData, selectedCheckBoxTransactionIds, uncategorizedAccounts, isUncategorizedExpenseCategoryEnabled, isAccountingClassesEnabled)) {
512
739
  draft.transactionCategorizationView[selectedTab].selectedCheckBoxTransactionIds = [
513
740
  ...selectedCheckBoxTransactionIds,
514
741
  transactionId,
@@ -582,9 +809,7 @@ const expenseAutomationTransactionsView = createSlice({
582
809
  .selectedCheckBoxTransactionIds;
583
810
  if (newTransactionLocalData.vendor != null &&
584
811
  newTransactionLocalData.vendor.id != null &&
585
- checkIfAllLineItemsAreCategoryClassFilled(selectedTransaction, newTransactionLocalData, uncategorizedAccounts, isUncategorizedExpenseCategoryEnabled, isAccountingClassesEnabled) === true && // check if all line items are filled
586
- selectedCheckBoxTransactionIds.length < MAX_SELECTION_LIMIT &&
587
- selectedCheckBoxTransactionIds.includes(transactionId) === false) {
812
+ shouldAutoSelectAndAdd(selectedTransaction, newTransactionLocalData, selectedCheckBoxTransactionIds, uncategorizedAccounts, isUncategorizedExpenseCategoryEnabled, isAccountingClassesEnabled)) {
588
813
  draft.transactionCategorizationView[selectedTab].selectedCheckBoxTransactionIds = [
589
814
  ...selectedCheckBoxTransactionIds,
590
815
  transactionId,
@@ -697,6 +922,10 @@ const expenseAutomationTransactionsView = createSlice({
697
922
  const { selectedTab, selectedPeriod, totalCount } = action.payload;
698
923
  draft.transactionCategorizationView[selectedTab].uiState.totalCount[toMonthYearPeriodId(selectedPeriod)] = totalCount;
699
924
  },
925
+ updateParentTotalCountForTab(draft, action) {
926
+ const { selectedTab, selectedPeriod, parentTotalCount } = action.payload;
927
+ draft.parentTotalCountByTab[selectedTab][toMonthYearPeriodId(selectedPeriod)] = parentTotalCount;
928
+ },
700
929
  updateSelectedCheckboxTransactionIds(draft, action) {
701
930
  const selectedTab = action.payload.selectedTab;
702
931
  const transactionIds = action.payload.transactionIds;
@@ -707,6 +936,16 @@ const expenseAutomationTransactionsView = createSlice({
707
936
  draft.transactionCategorizationView[selectedTab] = {
708
937
  ...initialTransactionTabViewState,
709
938
  };
939
+ draft.parentTotalCountByTab[selectedTab] = {};
940
+ // The per-sub-tab snapshot cache lives at the slice root and is
941
+ // exclusively populated by the autoCategorized tab's sub-tab switching
942
+ // (see updateTransactionCategorizationCompletedSubTab). When that tab
943
+ // is reset, leaving stale snapshots behind would short-circuit the
944
+ // next sub-tab visit with phantom row IDs / page tokens that no longer
945
+ // correspond to anything in state — drop them in lockstep.
946
+ if (selectedTab === 'autoCategorized') {
947
+ draft.autoCategorizedSubTabCache = {};
948
+ }
710
949
  },
711
950
  clearExpenseAutomationTransactionsView(draft) {
712
951
  Object.assign(draft, initialState);
@@ -803,5 +1042,5 @@ const expenseAutomationTransactionsView = createSlice({
803
1042
  },
804
1043
  },
805
1044
  });
806
- export const { fetchTransactionCategorization, updateTransactionCategorizationUIState, initializeTransactionCategorizationViewLocalData, saveTransactionCategorizationLocalData, fetchTransactionCategorizationFailure, saveTransactionCategorization, updateTransactionCategorization, updateCurrentSelectedTransactionCategorizationTab, updateTransactionCategorizationSaveStatus, markTransactionAsNotMiscategorized, updateStatusForTransactionNotMiscategorizedUpdateForCategorization, updateSelectedVendorForTransaction, updateSelectedCustomerForTransaction, setAllItemsToCategoryClassInLocalDataForCategorization, updateTotalCountForTransactionCategorization, fetchTransactionCategorizationSuccess, resetOtherTabsFetchState, fetchTransactionCategorizationView, updateSelectedCheckboxTransactionIds, clearExpenseAutomationTransactionsViewPerTabView, clearExpenseAutomationTransactionsView, setEntityRecommendationForLineIdsForCategorization, updateSelectedTransactionId, syncTransactionCategorizationFromDetailSave, backgroundRefetchReviewTab, updateTransactionCategorizationUploadReceiptState, uploadTransactionCategorizationReceiptSuccess, } = expenseAutomationTransactionsView.actions;
1045
+ export const { fetchTransactionCategorization, updateTransactionCategorizationUIState, updateTransactionFilters, initializeTransactionCategorizationViewLocalData, saveTransactionCategorizationLocalData, fetchTransactionCategorizationFailure, saveTransactionCategorization, updateTransactionCategorization, updateCurrentSelectedTransactionCategorizationTab, updateTransactionCategorizationCompletedSubTab, updateTransactionCategorizationSaveStatus, markTransactionAsNotMiscategorized, updateStatusForTransactionNotMiscategorizedUpdateForCategorization, updateSelectedVendorForTransaction, updateSelectedCustomerForTransaction, setAllItemsToCategoryClassInLocalDataForCategorization, updateTotalCountForTransactionCategorization, updateParentTotalCountForTab, fetchTransactionCategorizationSuccess, resetOtherTabsFetchState, fetchTransactionCategorizationView, updateSelectedCheckboxTransactionIds, clearExpenseAutomationTransactionsViewPerTabView, clearExpenseAutomationTransactionsView, setEntityRecommendationForLineIdsForCategorization, markCategoryClassRecommendationsInProgressForCategorization, markCategoryClassRecommendationsCompletedForCategorization, markCategoryClassRecommendationsFailureForCategorization, updateSelectedTransactionId, syncTransactionCategorizationFromDetailSave, backgroundRefetchReviewTab, updateTransactionCategorizationUploadReceiptState, uploadTransactionCategorizationReceiptSuccess, } = expenseAutomationTransactionsView.actions;
807
1046
  export default expenseAutomationTransactionsView.reducer;