@zeniai/client-epic-state 5.2.35-beta0MM → 5.2.35-beta11MM

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 (89) hide show
  1. package/lib/entity/actualMatching/actualMatchingPayload.d.ts +7 -1
  2. package/lib/entity/actualMatching/actualMatchingPayload.js +19 -12
  3. package/lib/entity/actualMatching/actualMatchingReducer.d.ts +3 -2
  4. package/lib/entity/actualMatching/actualMatchingReducer.js +3 -3
  5. package/lib/entity/actualMatching/actualMatchingState.d.ts +3 -2
  6. package/lib/entity/aiCfo/aiCfoReducer.js +78 -4
  7. package/lib/entity/aiCfo/aiCfoState.d.ts +16 -0
  8. package/lib/entity/jeSchedules/jeSchedulesPayload.d.ts +2 -0
  9. package/lib/entity/jeSchedules/jeSchedulesPayload.js +4 -0
  10. package/lib/entity/jeSchedules/jeSchedulesState.d.ts +2 -0
  11. package/lib/entity/snackbar/snackbarTypes.d.ts +1 -1
  12. package/lib/entity/snackbar/snackbarTypes.js +2 -0
  13. package/lib/esm/entity/actualMatching/actualMatchingPayload.js +18 -12
  14. package/lib/esm/entity/actualMatching/actualMatchingReducer.js +3 -3
  15. package/lib/esm/entity/aiCfo/aiCfoReducer.js +79 -5
  16. package/lib/esm/entity/jeSchedules/jeSchedulesPayload.js +4 -0
  17. package/lib/esm/entity/snackbar/snackbarTypes.js +2 -0
  18. package/lib/esm/index.js +6 -6
  19. package/lib/esm/view/expenseAutomationView/epics/actualMatching/fetchActualMatchingEpic.js +3 -5
  20. package/lib/esm/view/expenseAutomationView/epics/actualMatching/fetchActualMatchingMatchEpic.js +3 -2
  21. package/lib/esm/view/expenseAutomationView/epics/actualMatching/matchAndReverseAccrualsEpic.js +80 -25
  22. package/lib/esm/view/expenseAutomationView/epics/actualMatching/refreshActualMatchingList.js +16 -0
  23. package/lib/esm/view/expenseAutomationView/epics/actualMatching/searchMatchAccrualsEpic.js +16 -6
  24. package/lib/esm/view/expenseAutomationView/epics/actualMatching/searchMatchTransactionsEpic.js +12 -11
  25. package/lib/esm/view/expenseAutomationView/epics/actualMatching/undoReversalAccrualsEpic.js +50 -26
  26. package/lib/esm/view/expenseAutomationView/epics/common/fetchAllExpenseAutomationTabsEpic.js +5 -9
  27. package/lib/esm/view/expenseAutomationView/epics/common/refreshExpenseAutomationCurrentTabEpic.js +1 -9
  28. package/lib/esm/view/expenseAutomationView/epics/jeSchedule/fetchJeSchedulePageEpic.js +13 -3
  29. package/lib/esm/view/expenseAutomationView/epics/jeSchedule/prefetchOtherJeSchedulesTabEpic.js +34 -16
  30. package/lib/esm/view/expenseAutomationView/epics/jeSchedule/switchJESchedulesTabEpic.js +8 -8
  31. package/lib/esm/view/expenseAutomationView/expenseAutomationViewReducer.js +2 -1
  32. package/lib/esm/view/expenseAutomationView/expenseAutomationViewSelector.js +5 -2
  33. package/lib/esm/view/expenseAutomationView/expenseAutomationViewState.js +4 -2
  34. package/lib/esm/view/expenseAutomationView/payload/actualMatchingPayload.js +10 -10
  35. package/lib/esm/view/expenseAutomationView/reducers/actualMatchingViewReducer.js +107 -20
  36. package/lib/esm/view/expenseAutomationView/reducers/jeSchedulesViewReducer.js +2 -1
  37. package/lib/esm/view/expenseAutomationView/selectors/actualMatchingViewSelector.js +206 -61
  38. package/lib/esm/view/expenseAutomationView/selectors/jeSchedulesViewSelector.js +1 -0
  39. package/lib/esm/view/expenseAutomationView/types/actualMatchingViewState.js +3 -0
  40. package/lib/esm/view/expenseAutomationView/types/jeSchedulesViewState.js +3 -0
  41. package/lib/esm/view/financeStatement/financeStatementReducer.js +17 -27
  42. package/lib/esm/view/spendManagement/billPay/billList/billListSelector.js +3 -3
  43. package/lib/index.d.ts +7 -6
  44. package/lib/index.js +52 -48
  45. package/lib/view/expenseAutomationView/epics/actualMatching/fetchActualMatchingEpic.js +3 -5
  46. package/lib/view/expenseAutomationView/epics/actualMatching/fetchActualMatchingMatchEpic.d.ts +1 -1
  47. package/lib/view/expenseAutomationView/epics/actualMatching/fetchActualMatchingMatchEpic.js +3 -2
  48. package/lib/view/expenseAutomationView/epics/actualMatching/matchAndReverseAccrualsEpic.d.ts +2 -1
  49. package/lib/view/expenseAutomationView/epics/actualMatching/matchAndReverseAccrualsEpic.js +77 -22
  50. package/lib/view/expenseAutomationView/epics/actualMatching/refreshActualMatchingList.d.ts +3 -0
  51. package/lib/view/expenseAutomationView/epics/actualMatching/refreshActualMatchingList.js +19 -0
  52. package/lib/view/expenseAutomationView/epics/actualMatching/searchMatchAccrualsEpic.d.ts +3 -2
  53. package/lib/view/expenseAutomationView/epics/actualMatching/searchMatchAccrualsEpic.js +15 -5
  54. package/lib/view/expenseAutomationView/epics/actualMatching/searchMatchTransactionsEpic.js +10 -9
  55. package/lib/view/expenseAutomationView/epics/actualMatching/undoReversalAccrualsEpic.d.ts +2 -1
  56. package/lib/view/expenseAutomationView/epics/actualMatching/undoReversalAccrualsEpic.js +48 -24
  57. package/lib/view/expenseAutomationView/epics/common/fetchAllExpenseAutomationTabsEpic.d.ts +1 -2
  58. package/lib/view/expenseAutomationView/epics/common/fetchAllExpenseAutomationTabsEpic.js +5 -9
  59. package/lib/view/expenseAutomationView/epics/common/refreshExpenseAutomationCurrentTabEpic.d.ts +1 -2
  60. package/lib/view/expenseAutomationView/epics/common/refreshExpenseAutomationCurrentTabEpic.js +1 -9
  61. package/lib/view/expenseAutomationView/epics/jeSchedule/fetchJeSchedulePageEpic.d.ts +2 -1
  62. package/lib/view/expenseAutomationView/epics/jeSchedule/fetchJeSchedulePageEpic.js +13 -3
  63. package/lib/view/expenseAutomationView/epics/jeSchedule/prefetchOtherJeSchedulesTabEpic.d.ts +5 -1
  64. package/lib/view/expenseAutomationView/epics/jeSchedule/prefetchOtherJeSchedulesTabEpic.js +33 -15
  65. package/lib/view/expenseAutomationView/epics/jeSchedule/switchJESchedulesTabEpic.d.ts +1 -2
  66. package/lib/view/expenseAutomationView/epics/jeSchedule/switchJESchedulesTabEpic.js +8 -8
  67. package/lib/view/expenseAutomationView/expenseAutomationViewReducer.d.ts +2 -1
  68. package/lib/view/expenseAutomationView/expenseAutomationViewReducer.js +2 -1
  69. package/lib/view/expenseAutomationView/expenseAutomationViewSelector.js +5 -2
  70. package/lib/view/expenseAutomationView/expenseAutomationViewState.d.ts +2 -1
  71. package/lib/view/expenseAutomationView/expenseAutomationViewState.js +6 -3
  72. package/lib/view/expenseAutomationView/helpers/transactionCategorizationLocalDataHelper.d.ts +1 -1
  73. package/lib/view/expenseAutomationView/payload/actualMatchingPayload.d.ts +3 -3
  74. package/lib/view/expenseAutomationView/payload/actualMatchingPayload.js +10 -10
  75. package/lib/view/expenseAutomationView/reducers/actualMatchingViewReducer.d.ts +21 -16
  76. package/lib/view/expenseAutomationView/reducers/actualMatchingViewReducer.js +107 -20
  77. package/lib/view/expenseAutomationView/reducers/jeSchedulesViewReducer.d.ts +2 -1
  78. package/lib/view/expenseAutomationView/reducers/jeSchedulesViewReducer.js +2 -1
  79. package/lib/view/expenseAutomationView/selectorTypes/actualMatchingViewSelectorTypes.d.ts +13 -9
  80. package/lib/view/expenseAutomationView/selectorTypes/expenseAutomationViewSelectorTypes.d.ts +9 -0
  81. package/lib/view/expenseAutomationView/selectors/actualMatchingViewSelector.d.ts +11 -2
  82. package/lib/view/expenseAutomationView/selectors/actualMatchingViewSelector.js +209 -60
  83. package/lib/view/expenseAutomationView/selectors/jeSchedulesViewSelector.js +1 -0
  84. package/lib/view/expenseAutomationView/types/actualMatchingViewState.d.ts +8 -2
  85. package/lib/view/expenseAutomationView/types/actualMatchingViewState.js +4 -0
  86. package/lib/view/expenseAutomationView/types/jeSchedulesViewState.js +3 -0
  87. package/lib/view/financeStatement/financeStatementReducer.js +17 -27
  88. package/lib/view/spendManagement/billPay/billList/billListSelector.js +2 -2
  89. package/package.json +1 -1
package/lib/esm/index.js CHANGED
@@ -184,11 +184,11 @@ import { updateDashboardLayout } from './view/dashboardLayout/dashboardLayoutRed
184
184
  import uploadAccountStatementIntoDocumentAI from './view/expenseAutomationView/epics/accountRecon/uploadAccountStatementDocumentAIHelper';
185
185
  import { fetchAllExpenseAutomationTabs, refreshExpenseAutomationCurrentTab, updateCurrentSelectedPeriod, updateCurrentSelectedView, } from './view/expenseAutomationView/expenseAutomationViewReducer';
186
186
  import { getExpenseAutomationView } from './view/expenseAutomationView/expenseAutomationViewSelector';
187
- import { toExpenseAutomationViewType, } from './view/expenseAutomationView/expenseAutomationViewState';
187
+ import { toExpenseAutomationViewType, toExpenseAutomationViewTypeStrict, } from './view/expenseAutomationView/expenseAutomationViewState';
188
188
  import { computeNewScheduleJeDetails } from './view/expenseAutomationView/helpers/newScheduleLocalDataHelper';
189
189
  import { isReviewTransactionBankTransferType, isReviewTransactionBillPaymentType, isReviewTransactionCreditCardCreditType, isReviewTransactionCreditCardPaymentType, isReviewTransactionDepositType, isReviewTransactionExpenseType, } from './view/expenseAutomationView/helpers/reconciliationHelpers';
190
190
  import { MAX_SELECTION_LIMIT, checkIfAllLineItemsAreCategoryClassFilled, getLineItemsByTransactionIdsFromLocalData, isAnyItemWithUncategorizedExpenseAccount, } from './view/expenseAutomationView/helpers/transactionCategorizationLocalDataHelper';
191
- import { clearActualMatchingView as clearExpenseAutomationActualMatchingView, clearMatchNewModal, closeMatchNewModal, fetchActualMatching as fetchExpenseAutomationActualMatching, fetchActualMatchingMatch, fetchActualMatchingPage as fetchExpenseAutomationActualMatchingPage, matchAndReverseAccruals, openMatchNewModal, searchMatchAccruals, searchMatchTransactions, undoReversalAccruals, updateActualMatchingUIState as updateExpenseAutomationActualMatchingUIState, } from './view/expenseAutomationView/reducers/actualMatchingViewReducer';
191
+ import { ackCloseMatchAfterUndo, clearActualMatchingView as clearExpenseAutomationActualMatchingView, clearMatchNewModal, closeMatchNewModal, fetchActualMatching as fetchExpenseAutomationActualMatching, fetchActualMatchingMatch, fetchActualMatchingPage as fetchExpenseAutomationActualMatchingPage, matchAndReverseAccruals, openMatchNewModal, searchMatchAccruals, searchMatchTransactions, undoReversalAccruals, updateActualMatchingUIState as updateExpenseAutomationActualMatchingUIState, } from './view/expenseAutomationView/reducers/actualMatchingViewReducer';
192
192
  import { clearExpenseAutomationFluxAnalysisView, fetchFluxAnalysisView, reviewFluxAnalysisView, updateFluxAnalysisViewPageMetaData, updateFluxAnalysisViewUIState, updateOperatingExpensesIdsForReview, updateSelectedSectionIdsForReview, } from './view/expenseAutomationView/reducers/fluxAnalysisViewReducer';
193
193
  import { clearJeScheduleLocalData as clearExpenseAutomationJEScheduleLocalData, clearExpenseAutomationJESchedulesView, clearSearchTransactionsForNewSchedule as clearJeSearchTransactionsForNewSchedule, createOneTimeAccrual, createOneTimeAccrualFailure, createOneTimeAccrualSuccess, fetchJeSchedulesPage as fetchExpenseAutomationJESchedulesPage, fetchOneTimeAccrual, ignoreRecommendedJeSchedule as ignoreExpenseAutomationJESchedule, ignoreOneTimeAccrual, initializeAccountSettingsView as initializeJeAccountSettingsView, initializeNewSchedule as initializeJeNewSchedule, initializeJeScheduleLocalData, promoteOneTimeAccrual, removeJeOneTimeAccrualLocalData, removeJeScheduleTransactionKey, resetNewScheduleLocalData as resetJeNewScheduleLocalData, retryJeSchedule as retryExpenseAutomationJESchedule, revertJeSchedule, revertOneTimeAccrual, saveAccountSettings as saveJeAccountSettings, saveAccountSettingsLocalData as saveJeAccountSettingsLocalData, saveNewSchedule as saveJeNewSchedule, saveNewScheduleFailure as saveJeNewScheduleFailure, saveNewScheduleSuccess as saveJeNewScheduleSuccess, searchTransactionsForNewSchedule as searchJeTransactionsForNewSchedule, searchTransactionsForNewScheduleFailure as searchJeTransactionsForNewScheduleFailure, searchTransactionsForNewScheduleSuccess as searchJeTransactionsForNewScheduleSuccess, toggleJECreatedGroupCollapsed, toggleJEPendingGroupCollapsed, updateJESchedulesUIState as updateExpenseAutomationJESchedulesUIState, updateJEScheduleTypeFilter, updateNewScheduleLocalData as updateJeNewScheduleLocalData, updateJeOneTimeAccrualLocalData, updateJeScheduleLocalDataById, updateJeScheduleTransactionKeys, } from './view/expenseAutomationView/reducers/jeSchedulesViewReducer';
194
194
  import { acknowledgeBulkUploadConfirmMatchComplete, bulkUploadAutomatchingTimedOut, bulkUploadReceipts, bulkUploadReceiptsFailure, bulkUploadReceiptsSuccess, clearBulkUpload, clearBulkUploadBatchDetailsForScopeChange, clearManualSearchResults, clearMissingReceiptsTabNavigation, confirmBulkUploadMatch, confirmBulkUploadMatchFailure, confirmBulkUploadMatchSuccess, fetchBulkUploadBatchDetails, fetchBulkUploadBatchDetailsFailure, fetchBulkUploadBatchDetailsSuccess, fetchBulkUploadBatches, fetchBulkUploadBatchesFailure, fetchBulkUploadBatchesSuccess, fetchCompletedTransactions, fetchCompletedTransactionsFailure, fetchCompletedTransactionsSuccess, fetchMissingReceipts as fetchExpenseAutomationMissingReceipts, fetchMoreBatchDetails, fetchMoreBatchDetailsComplete, fetchMoreBatchDetailsFailure, markBatchDetailRefreshAttempted, markMissingReceiptAsDone as markExpenseAutomationMissingReceiptAsDone, pusherBatchStatusUpdate, refreshBatchDetailsForBatchId, requestMissingReceiptsTabNavigation, restoreBulkUploadAutomatchingOnMount, restoreBulkUploadMatchingState, searchTransactionsForManualMatch, searchTransactionsForManualMatchFailure, searchTransactionsForManualMatchSuccess, setBulkUploadCompletedSubTab, setBulkUploadResultsTab, setBulkUploadSortConfig, setBulkUploadUploadedFileCount, storeBatchDetails, updateBulkUploadProgress, updateMissingReceiptUploadState as updateExpenseAutomationMissingReceiptUploadState, updateMissingReceiptsUIState as updateExpenseAutomationMissingReceiptsUIState, uploadMissingReceiptSuccess as uploadExpenseAutomationMissingReceiptSuccess, } from './view/expenseAutomationView/reducers/missingReceiptsViewReducer';
@@ -200,7 +200,7 @@ import { getExpenseAutomationReconciliationView, getLedgerAccountIdsWithStatemen
200
200
  import { getExpenseAutomationTransactionView, getLastTransferEntryReplacement, } from './view/expenseAutomationView/selectors/transactionCategorizationSelector';
201
201
  import { applyTransactionFilters } from './view/expenseAutomationView/transactionFilterHelpers';
202
202
  import { TRANSACTION_FILTER_CATEGORIES, } from './view/expenseAutomationView/transactionFilterTypes';
203
- import { MATCH_ACCRUAL_SEARCH_LIMIT, MATCH_SEARCH_DEBOUNCE_MS, MATCH_TRANSACTION_SEARCH_PAGE_SIZE, toActualMatchingSortKey as toExpenseAutomationActualMatchingSortKey, toMatchAndReverseResultStatus, toUndoReversalResultStatus, } from './view/expenseAutomationView/types/actualMatchingViewState';
203
+ import { MATCH_ACCRUAL_SEARCH_LIMIT, MATCH_SEARCH_DEBOUNCE_MS, MATCH_TRANSACTION_SEARCH_PAGE_SIZE, actualMatchingTransactionLineKey, toActualMatchingSortKey as toExpenseAutomationActualMatchingSortKey, toMatchAndReverseResultStatus, toUndoReversalResultStatus, } from './view/expenseAutomationView/types/actualMatchingViewState';
204
204
  import { DEFAULT_COMPLETED_SUB_TAB, toCompletedSubTab, } from './view/expenseAutomationView/types/completedSubTab';
205
205
  import { ALL_JE_PAGE_TABS, toJEScheduleSortKey as toExpenseAutomationJEScheduleSortKey, toJEPageTab, } from './view/expenseAutomationView/types/jeSchedulesViewState';
206
206
  import { BATCH_FILE_STATUSES, isUnmatchedTabFileStatus, toBatchFileStatus, toBatchStatusValue, toBulkUploadPhase, toBulkUploadResultsTab, toBulkUploadSortKey, toMatchSource, toMissingReceiptsTab, } from './view/expenseAutomationView/types/missingReceiptsViewState';
@@ -491,7 +491,7 @@ export { stringToUnion, stringToUnionStrict };
491
491
  export { fetchMonthEndCloseChecks, fetchMonthClosePerformanceTrend, getMonthEndCloseChecksViewByTenantId, ALL_MONTH_END_CLOSE_CHECKS_FREQUENCY, };
492
492
  export {
493
493
  // Bulk Upload Types
494
- BATCH_FILE_STATUSES, isUnmatchedTabFileStatus, DEFAULT_COMPLETED_SUB_TAB, DEFAULT_TRANSACTION_GROUP, TRANSACTION_GROUPS, toTransactionGroup, toBatchFileStatus, toBatchStatusValue, toBulkUploadPhase, toBulkUploadResultsTab, toBulkUploadSortKey, toCompletedSubTab, toMatchSource, toMissingReceiptsTab, ALL_JE_PAGE_TABS, MATCH_ACCRUAL_SEARCH_LIMIT, MATCH_SEARCH_DEBOUNCE_MS, MATCH_TRANSACTION_SEARCH_PAGE_SIZE, toExpenseAutomationJEScheduleSortKey, toExpenseAutomationActualMatchingSortKey, toJEPageTab, toMatchAndReverseResultStatus, toUndoReversalResultStatus, toggleJECreatedGroupCollapsed, toggleJEPendingGroupCollapsed, updateJEScheduleTypeFilter, getExpenseAutomationView, toExpenseAutomationMissingReceiptsSortKey, toExpenseAutomationTransactionsTabKey, toExpenseAutomationViewType, uploadExpenseAutomationMissingReceiptSuccess, markExpenseAutomationMissingReceiptAsDone, fetchAllExpenseAutomationTabs, refreshExpenseAutomationCurrentTab, updateCurrentSelectedTransactionCategorizationTab, updateTransactionCategorizationCompletedSubTab, updateTransactionGroup, fetchExpenseAutomationMissingReceipts,
494
+ BATCH_FILE_STATUSES, isUnmatchedTabFileStatus, DEFAULT_COMPLETED_SUB_TAB, DEFAULT_TRANSACTION_GROUP, TRANSACTION_GROUPS, toTransactionGroup, toBatchFileStatus, toBatchStatusValue, toBulkUploadPhase, toBulkUploadResultsTab, toBulkUploadSortKey, toCompletedSubTab, toMatchSource, toMissingReceiptsTab, actualMatchingTransactionLineKey, ALL_JE_PAGE_TABS, MATCH_ACCRUAL_SEARCH_LIMIT, MATCH_SEARCH_DEBOUNCE_MS, MATCH_TRANSACTION_SEARCH_PAGE_SIZE, toExpenseAutomationJEScheduleSortKey, toExpenseAutomationActualMatchingSortKey, toJEPageTab, toMatchAndReverseResultStatus, toUndoReversalResultStatus, toggleJECreatedGroupCollapsed, toggleJEPendingGroupCollapsed, updateJEScheduleTypeFilter, getExpenseAutomationView, toExpenseAutomationMissingReceiptsSortKey, toExpenseAutomationTransactionsTabKey, toExpenseAutomationViewType, toExpenseAutomationViewTypeStrict, uploadExpenseAutomationMissingReceiptSuccess, markExpenseAutomationMissingReceiptAsDone, fetchAllExpenseAutomationTabs, refreshExpenseAutomationCurrentTab, updateCurrentSelectedTransactionCategorizationTab, updateTransactionCategorizationCompletedSubTab, updateTransactionGroup, fetchExpenseAutomationMissingReceipts,
495
495
  // Bulk Upload Actions
496
496
  bulkUploadReceipts, bulkUploadAutomatchingTimedOut, bulkUploadReceiptsSuccess, bulkUploadReceiptsFailure, restoreBulkUploadAutomatchingOnMount, restoreBulkUploadMatchingState, updateBulkUploadProgress, pusherBatchStatusUpdate, requestMissingReceiptsTabNavigation, clearMissingReceiptsTabNavigation, fetchBulkUploadBatchDetails, fetchBulkUploadBatchDetailsSuccess, fetchBulkUploadBatchDetailsFailure, storeBatchDetails, fetchMoreBatchDetails, fetchMoreBatchDetailsComplete, fetchMoreBatchDetailsFailure, fetchBulkUploadBatches, fetchBulkUploadBatchesSuccess, fetchBulkUploadBatchesFailure, clearBulkUploadBatchDetailsForScopeChange, markBatchDetailRefreshAttempted, refreshBatchDetailsForBatchId, confirmBulkUploadMatch, confirmBulkUploadMatchSuccess, confirmBulkUploadMatchFailure, setBulkUploadResultsTab, setBulkUploadCompletedSubTab, setBulkUploadSortConfig, setBulkUploadUploadedFileCount, 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, getLastTransferEntryReplacement, updateFluxAnalysisViewUIState, updateFluxAnalysisViewPageMetaData, MAX_SELECTION_LIMIT, checkIfAllLineItemsAreCategoryClassFilled, getLineItemsByTransactionIdsFromLocalData, isAnyItemWithUncategorizedExpenseAccount, excludeAccountFromReconciliation, includeAccountInReconciliation, saveExpenseAutomationReconciliationDetail, updateExpenseAutomationReconcileTabListScrollState, updateExpenseAutomationReconReviewTabListSortState, updateExpenseAutomationReconcileTabListSortState, updateExpenseAutomationReconcileTabLocalData, updateExpenseAutomationAccountReconciliationSelectedTab, updateExpenseAutomationAccountReconciliationSelectedAccountId, getExpenseAutomationReconciliationView, getLedgerAccountIdsWithStatementOverrideWarning, getLedgerAccountsForCombinedStatementMapping, getReparseStatementStatusByAccountId, getUnavailableLedgerAccountIds, fetchReconciliationView, uploadAccountStatementIntoDocumentAI, updateExpenseAutomationReconListScrollPosition, setConnectionInProgressForAccountReconciliation, getAccountReconByAccountIdAndSelectedPeriod, toReconciliationTabsType, isAccountReconReport, updateExpenseAutomationReconReviewTabLocalData, updateExpenseAutomationSelectedDrawerAccountId, updateStatementProcessingFailed, saveExpenseAutomationReconciliationReview, updateExpenseAutomationAccountReconciliationLocalData, toReconciliationAccountSource, deleteAccountStatement, deleteCombinedStatement, uploadAccountStatement, parseCombinedStatement, parseStatement, parseStatementSuccess, parseStatementFailure, reparseStatement, reparseStatementSuccess, reparseStatementFailure, resetReparseStatementStatus, updateParsedStatementData, updateNodeCollapseState, updateStatementUploadChosen, submitCombinedStatementUpdate, submitStatementUpdate, clearStatementDateConflict, confirmStatementMapping, updateStatementUpdateLocalData, isReviewTransactionBankTransferType, isReviewTransactionBillPaymentType, isReviewTransactionCreditCardPaymentType, isReviewTransactionDepositType, isReviewTransactionExpenseType, isReviewTransactionCreditCardCreditType, setStatementParseInProgress, setStatementProcessingDismissedToBackground, updateStatementProcessingBackgroundCompletedSteps, };
497
497
  export { computeNewScheduleJeDetails, createOneTimeAccrual, createOneTimeAccrualFailure, createOneTimeAccrualSuccess, fetchOneTimeAccrual, getJeNewAccrualAccountsView, ignoreOneTimeAccrual, promoteOneTimeAccrual, revertJeSchedule, revertOneTimeAccrual, getJeNewScheduleSearchTransactionsView, getJeNewScheduleView, };
@@ -662,7 +662,7 @@ export { TIME_SERIES_DURATIONS, convertToTimeSeriesSelectionRange, toTimeSeriesD
662
662
  export { fetchApAging, getApAgingReport, updateApAgingUIState, };
663
663
  export { fetchApAgingDetail, getApAgingDetailForVendor, updateApAgingDetailUIState, };
664
664
  export { clearExpenseAutomationJEScheduleLocalData, clearExpenseAutomationJESchedulesView, clearJeSearchTransactionsForNewSchedule, fetchExpenseAutomationJESchedulesPage, ignoreExpenseAutomationJESchedule, initializeJeAccountSettingsView, initializeJeNewSchedule, initializeJeScheduleLocalData, removeJeScheduleTransactionKey, resetJeNewScheduleLocalData, retryExpenseAutomationJESchedule, saveJeNewSchedule, saveJeNewScheduleFailure, saveJeNewScheduleSuccess, searchJeTransactionsForNewSchedule, searchJeTransactionsForNewScheduleFailure, searchJeTransactionsForNewScheduleSuccess, updateExpenseAutomationJESchedulesUIState, updateJeNewScheduleLocalData, removeJeOneTimeAccrualLocalData, updateJeOneTimeAccrualLocalData, updateJeScheduleLocalDataById, updateJeScheduleTransactionKeys, };
665
- export { clearExpenseAutomationActualMatchingView, closeMatchNewModal, clearMatchNewModal, fetchExpenseAutomationActualMatching, fetchActualMatchingMatch, fetchExpenseAutomationActualMatchingPage, matchAndReverseAccruals, openMatchNewModal, searchMatchAccruals, searchMatchTransactions, undoReversalAccruals, updateExpenseAutomationActualMatchingUIState, };
665
+ export { ackCloseMatchAfterUndo, clearExpenseAutomationActualMatchingView, closeMatchNewModal, clearMatchNewModal, fetchExpenseAutomationActualMatching, fetchActualMatchingMatch, fetchExpenseAutomationActualMatchingPage, matchAndReverseAccruals, openMatchNewModal, searchMatchAccruals, searchMatchTransactions, undoReversalAccruals, updateExpenseAutomationActualMatchingUIState, };
666
666
  export { createNewSchedulesAccrued, deleteScheduleAccruedDetail, cancelScheduleAccruedJournalEntry, fetchScheduleAccruedDetails, fetchScheduleAccruedDetailsPage, resetJEAccruedLinkInLocalData, saveScheduleAccruedDetails, updateAmountsInScheduleAccruedDetail, updatedJEAccruedLinkWithRecommendedLocalData, updatedJELinkInLocalDataAccruedExpenses, fetchRecommendedTransactionRowIndex, clearSelectedJELinkRowIndex, updateLinkBillExpenseLocalData, updateScheduleAccruedDetailsLocalData, updateSelectedJEAccruedScheduleKey, resetSelectedJEAccruedScheduleKey, resetAccruedDetailNewScheduleState, };
667
667
  export { getJEScheduleTransactionKey, ALL_JE_SCHEDULE_IGNORE_REASONS, ALL_JE_SCHEDULE_REVERT_REASONS, ALL_ONE_TIME_ACCRUAL_IGNORE_REASONS, ALL_ONE_TIME_ACCRUAL_REVERT_REASONS, ALL_SCHEDULES_TYPES, getScheduleListReport, getAccruedScheduleListReport, fetchScheduleList, fetchAccruedScheduleList, fetchDownloadSchedules, fetchSchedulesAccount, updateScheduleListLocalData, getFetchStateForScheduleAccountList, toScheduleTypesType, toScheduleTypesTypeStrict, toScheduleListTabsFileTypeStrict, toScheduleSubTabType, updateScheduleListSubTab, updateScheduleListSearchText, updateScheduleListScrollState, updateScheduleListSortState, updateSelectedJEScheduleKey, fetchScheduleDetails, getScheduleDetailsView, getAccruedScheduleDetailsView, fetchScheduleDetailsPage, saveScheduleDetails, deleteScheduleDetail, createNewSchedules, updateScheduleDetailsLocalData, updateScheduleListDownloadState, updateAccruedJEScheduleAccruedByListKey, updatedSelectedJELinkRowIndex, getQBOUrlForLink, getThirdPartyIDFromQBOURL, updatedJELinkInLocalData, updateAmountsInScheduleDetail, resetJELinkInLocalData, updatedJELinkWithRecommendedLocalData, getFetchStateForScheduleListByType, getDefaultSelectedTimeframeForScheduleType, markAsCompleteScheduleDetail, resetMarkAsCompleteStatus, fetchVendorTabView, updateVendorTabViewTab, getVendorTabView, };
668
668
  export { toVendorFirstReviewViewColumnKeyType, getGlobalMerchantAutoCompleteResults, getVendorFirstReviewView, getVendorFirstReviewAttachmentView, fetchVendorFirstReviewView, fetchVendorFirstReviewAttachments, updateVendorFirstReviewViewScrollYOffset, resetVendorFirstReviewLocalData, updateVendorFirstReviewViewPageToken, clearRecentlySavedErroredVendorData, saveVendorFirstReviewView, updateVendorFirstReviewViewLocalData, updateVendorFirstReviewSortUiState, fetchGlobalMerchantAutoCompleteView, clearGlobalMerchantAutoCompleteResults, updateReviewVendorDetailLocalData, saveVendorDetailsView, getVendorDetailSelectorView, };
@@ -866,4 +866,4 @@ export { getInvoicingOnboardingGateView } from './view/invoicing/invoicingConfig
866
866
  // Accrued JE schedules are not a discriminated union member, so consumers need this guard to
867
867
  // narrow a JEScheduleRow before reading baseTransaction / recurringAmount.
868
868
  export { getExpenseAutomationJEOneTimeAccrualDetailView, getJEScheduleRowType, isAccruedScheduleRow, isAiAccountantSchedule, isExistingTabRow, isOneTimeAccrualRow, isRenderableOneTimeAccrual, isPendingReviewRow, } from './view/expenseAutomationView/selectors/jeSchedulesViewSelector';
869
- export { getActualMatchingAmount, getActualMatchingReversalDate, parseActualMatchingMatchId, getExpenseAutomationActualMatchingMatchView, getExpenseAutomationActualMatchingView, isAutoReversedAccrual, isManuallyReversedAccrual, isPendingReviewAccrual, } from './view/expenseAutomationView/selectors/actualMatchingViewSelector';
869
+ export { getActualMatchingAmount, getActualMatchingReversalDate, getCloseMatchAfterUndo, parseActualMatchingMatchId, getExpenseAutomationActualMatchingMatchView, getExpenseAutomationActualMatchingView, isAutoReversedAccrual, isManuallyReversedAccrual, isPendingReviewAccrual, } from './view/expenseAutomationView/selectors/actualMatchingViewSelector';
@@ -1,17 +1,15 @@
1
1
  import { from, of } from 'rxjs';
2
2
  import { catchError, filter, mergeMap, switchMap, takeUntil, } from 'rxjs/operators';
3
3
  import { updateActualMatchingMatches } from '../../../../entity/actualMatching/actualMatchingReducer';
4
- import { getCurrentTenant } from '../../../../entity/tenant/tenantSelector';
5
4
  import { createZeniAPIStatus, isSuccessResponse, } from '../../../../responsePayload';
6
5
  import { clearAll } from '../../../../rootActions';
7
6
  import { toServiceMonthFromPeriod, } from '../../payload/actualMatchingPayload';
8
7
  import { fetchActualMatching, fetchActualMatchingFailure, fetchActualMatchingSuccess, } from '../../reducers/actualMatchingViewReducer';
8
+ import { getSelectedMonthYearForCurrentTenant, getTenantDisplayCurrency, } from '../../selectors/actualMatchingViewSelector';
9
9
  export const fetchActualMatchingEpic = (actions$, state$, zeniAPI) => actions$.pipe(filter(fetchActualMatching.match), switchMap((action) => {
10
10
  const state = state$.value;
11
11
  const { period, refreshViewInBackground } = action.payload;
12
- const currentTenant = getCurrentTenant(state);
13
- const selectedPeriod = state.expenseAutomationViewState
14
- .selectedPeriodByTenantId[currentTenant?.tenantId ?? ''] ?? {
12
+ const selectedPeriod = getSelectedMonthYearForCurrentTenant(state) ?? {
15
13
  month: period.start.month,
16
14
  year: period.start.year,
17
15
  };
@@ -23,7 +21,7 @@ export const fetchActualMatchingEpic = (actions$, state$, zeniAPI) => actions$.p
23
21
  const matches = response.data.matches;
24
22
  const updateActions = [];
25
23
  if (matches.length > 0) {
26
- updateActions.push(updateActualMatchingMatches(matches));
24
+ updateActions.push(updateActualMatchingMatches(matches, getTenantDisplayCurrency(state)));
27
25
  }
28
26
  updateActions.push(fetchActualMatchingSuccess({
29
27
  matchIds: matches.map((match) => match.match_id),
@@ -4,14 +4,15 @@ import { updateActualMatchingMatches } from '../../../../entity/actualMatching/a
4
4
  import { createZeniAPIStatus, isSuccessResponse, } from '../../../../responsePayload';
5
5
  import { clearAll } from '../../../../rootActions';
6
6
  import { fetchActualMatchingMatch, fetchActualMatchingMatchFailure, fetchActualMatchingMatchSuccess, } from '../../reducers/actualMatchingViewReducer';
7
- export const fetchActualMatchingMatchEpic = (actions$, _state$, zeniAPI) => actions$.pipe(filter(fetchActualMatchingMatch.match), mergeMap((action) => {
7
+ import { getTenantDisplayCurrency } from '../../selectors/actualMatchingViewSelector';
8
+ export const fetchActualMatchingMatchEpic = (actions$, state$, zeniAPI) => actions$.pipe(filter(fetchActualMatchingMatch.match), mergeMap((action) => {
8
9
  const { matchId, refreshViewInBackground } = action.payload;
9
10
  return zeniAPI
10
11
  .getJSON(`${zeniAPI.apiEndPoints.accountMicroServiceBaseUrl}/1.0/accruals/actual-matching/${encodeURIComponent(matchId)}`)
11
12
  .pipe(mergeMap((response) => {
12
13
  if (isSuccessResponse(response) && response.data != null) {
13
14
  return from([
14
- updateActualMatchingMatches([response.data]),
15
+ updateActualMatchingMatches([response.data], getTenantDisplayCurrency(state$.value)),
15
16
  fetchActualMatchingMatchSuccess({
16
17
  matchId,
17
18
  refreshViewInBackground,
@@ -1,52 +1,107 @@
1
1
  import { from, of } from 'rxjs';
2
- import { catchError, filter, mergeMap, takeUntil } from 'rxjs/operators';
3
- import { convertToPeriod, toAbsoluteDay, } from '../../../../commonStateTypes/timePeriod';
4
- import { getCurrentTenant } from '../../../../entity/tenant/tenantSelector';
5
- import { createZeniAPIStatus, isSuccessResponse, } from '../../../../responsePayload';
2
+ import { catchError, exhaustMap, filter, mergeMap, takeUntil } from 'rxjs/operators';
3
+ import { getActualMatchingMatchById } from '../../../../entity/actualMatching/actualMatchingSelector';
4
+ import { openSnackbar } from '../../../../entity/snackbar/snackbarReducer';
5
+ import { createZeniAPIStatus, isSuccessResponse } from '../../../../responsePayload';
6
6
  import { clearAll } from '../../../../rootActions';
7
7
  import { toMatchAndReverseResult, } from '../../payload/actualMatchingPayload';
8
- import { clearMatchNewModal, fetchActualMatching, matchAndReverseAccruals, matchAndReverseAccrualsFailure, matchAndReverseAccrualsSuccess, } from '../../reducers/actualMatchingViewReducer';
9
- function refreshActualMatchingList(state) {
10
- const currentTenant = getCurrentTenant(state);
11
- const selectedMonthYear = state.expenseAutomationViewState.selectedPeriodByTenantId[currentTenant?.tenantId ?? ''];
12
- if (selectedMonthYear == null) {
13
- return [];
8
+ import { clearMatchNewModal, matchAndReverseAccruals, matchAndReverseAccrualsFailure, matchAndReverseAccrualsSuccess, } from '../../reducers/actualMatchingViewReducer';
9
+ import { refreshActualMatchingList } from './refreshActualMatchingList';
10
+ function isMatchCompleteStatus(status) {
11
+ return status === 'reversed' || status === 'noop';
12
+ }
13
+ function isMatchFailedStatus(status) {
14
+ return (status === 'conflict' || status === 'claim_lost' || status === 'error');
15
+ }
16
+ function matchAndReverseSnackbar(results, vendorName) {
17
+ const reversedCount = results.filter((result) => result.status === 'reversed').length;
18
+ const failedCount = results.filter((result) => isMatchFailedStatus(result.status)).length;
19
+ const variables = [
20
+ { variableName: '_count_', variableValue: String(reversedCount) },
21
+ { variableName: '_vendor_name_', variableValue: vendorName },
22
+ ];
23
+ if (results.length === 0 || (reversedCount === 0 && failedCount > 0)) {
24
+ return openSnackbar({
25
+ messageSection: 'actual_matching_match_and_reverse',
26
+ messageText: 'failed',
27
+ type: 'error',
28
+ variables,
29
+ });
14
30
  }
15
- const period = convertToPeriod(selectedMonthYear);
16
- const start = toAbsoluteDay(period.start);
17
- const end = toAbsoluteDay(period.end);
18
- if (start == null || end == null) {
19
- return [];
31
+ if (reversedCount > 0 && failedCount === 0) {
32
+ return openSnackbar({
33
+ messageSection: 'actual_matching_match_and_reverse',
34
+ messageText: 'success',
35
+ type: 'success',
36
+ variables,
37
+ });
20
38
  }
21
- return [fetchActualMatching({ start, end }, true)];
39
+ if (reversedCount > 0 && failedCount > 0) {
40
+ return openSnackbar({
41
+ messageSection: 'actual_matching_match_and_reverse',
42
+ messageText: 'notification',
43
+ type: 'warning',
44
+ variables,
45
+ });
46
+ }
47
+ return null;
48
+ }
49
+ function matchAndReverseHttpFailSnackbar(vendorName) {
50
+ return openSnackbar({
51
+ messageSection: 'actual_matching_match_and_reverse',
52
+ messageText: 'failed',
53
+ type: 'error',
54
+ variables: [
55
+ { variableName: '_count_', variableValue: '0' },
56
+ { variableName: '_vendor_name_', variableValue: vendorName },
57
+ ],
58
+ });
22
59
  }
23
- export const matchAndReverseAccrualsEpic = (actions$, state$, zeniAPI) => actions$.pipe(filter(matchAndReverseAccruals.match), mergeMap((action) => {
24
- const { transactionId, transactionIntegrationId, transactionType, lineId, accrualIds, transactionSyncToken, } = action.payload;
60
+ function matchVendorName(state, matchId) {
61
+ return getActualMatchingMatchById(state.actualMatchingState, matchId)?.vendorName ?? '';
62
+ }
63
+ export const matchAndReverseAccrualsEpic = (actions$, state$, zeniAPI) => actions$.pipe(filter(matchAndReverseAccruals.match),
64
+ // Posts reversing JEs; drop duplicate clicks while a request is in flight.
65
+ exhaustMap((action) => {
66
+ const { transactionId, transactionIntegrationId, transactionType, lineIds, accrualIds, transactionSyncToken, } = action.payload;
25
67
  const body = {
26
68
  transaction_id: transactionId,
27
69
  transaction_integration_id: transactionIntegrationId,
28
70
  transaction_type: transactionType,
29
- line_id: lineId,
71
+ line_ids: lineIds,
30
72
  transaction_sync_token: transactionSyncToken,
31
73
  accrual_ids: accrualIds,
32
74
  };
75
+ // Key leftover results and snackbar vendor to the confirmed bill, not the
76
+ // row Match New was opened from (user can pick a different txn).
77
+ const matchId = `${transactionType.toLowerCase()}:${transactionIntegrationId}`;
78
+ const vendorName = matchVendorName(state$.value, matchId);
33
79
  return zeniAPI
34
80
  .postAndGetJSON(`${zeniAPI.apiEndPoints.accountMicroServiceBaseUrl}/1.0/accruals/match-and-reverse`, body)
35
81
  .pipe(mergeMap((response) => {
36
82
  if (isSuccessResponse(response) && response.data != null) {
37
83
  const results = response.data.results.map(toMatchAndReverseResult);
38
84
  const successActions = [
39
- matchAndReverseAccrualsSuccess({ results }),
85
+ matchAndReverseAccrualsSuccess({ matchId, results }),
40
86
  ];
41
- const anyReversed = results.some((result) => result.status === 'reversed');
42
- if (anyReversed) {
43
- successActions.push(...refreshActualMatchingList(state$.value), clearMatchNewModal());
87
+ const occupancyChanged = results.some((result) => isMatchCompleteStatus(result.status));
88
+ const allComplete = results.length > 0 &&
89
+ results.every((result) => isMatchCompleteStatus(result.status));
90
+ if (occupancyChanged) {
91
+ successActions.push(...refreshActualMatchingList(state$.value));
92
+ }
93
+ const snackbar = matchAndReverseSnackbar(results, vendorName);
94
+ if (snackbar != null) {
95
+ successActions.push(snackbar);
96
+ }
97
+ if (allComplete) {
98
+ successActions.push(clearMatchNewModal());
44
99
  }
45
100
  return from(successActions);
46
101
  }
47
- return of(matchAndReverseAccrualsFailure({ status: response.status }));
102
+ return of(matchAndReverseAccrualsFailure({ status: response.status }), matchAndReverseHttpFailSnackbar(vendorName));
48
103
  }), catchError((error) => of(matchAndReverseAccrualsFailure({
49
104
  status: createZeniAPIStatus('Unexpected Error', 'Match and reverse accruals REST API call errored out' +
50
105
  JSON.stringify(error)),
51
- }))), takeUntil(actions$.pipe(filter(clearAll.match))));
106
+ }), matchAndReverseHttpFailSnackbar(vendorName))), takeUntil(actions$.pipe(filter(clearAll.match))));
52
107
  }));
@@ -0,0 +1,16 @@
1
+ import { convertToPeriod, toAbsoluteDay, } from '../../../../commonStateTypes/timePeriod';
2
+ import { fetchActualMatching } from '../../reducers/actualMatchingViewReducer';
3
+ import { getSelectedMonthYearForCurrentTenant } from '../../selectors/actualMatchingViewSelector';
4
+ export function refreshActualMatchingList(state) {
5
+ const selectedMonthYear = getSelectedMonthYearForCurrentTenant(state);
6
+ if (selectedMonthYear == null) {
7
+ return [];
8
+ }
9
+ const period = convertToPeriod(selectedMonthYear);
10
+ const start = toAbsoluteDay(period.start);
11
+ const end = toAbsoluteDay(period.end);
12
+ if (start == null || end == null) {
13
+ return [];
14
+ }
15
+ return [fetchActualMatching({ start, end }, true)];
16
+ }
@@ -1,10 +1,13 @@
1
1
  import { from, merge, of } from 'rxjs';
2
- import { catchError, debounceTime, filter, mergeMap, switchMap, } from 'rxjs/operators';
2
+ import { catchError, debounceTime, filter, mergeMap, switchMap, takeUntil, } from 'rxjs/operators';
3
+ import { withInheritedAccrualCurrency } from '../../../../entity/actualMatching/actualMatchingPayload';
3
4
  import { updateJEOneTimeAccruals } from '../../../../entity/jeSchedules/jeSchedulesReducer';
4
5
  import { createZeniAPIStatus, isSuccessResponse, } from '../../../../responsePayload';
6
+ import { clearAll } from '../../../../rootActions';
5
7
  import { searchMatchAccruals, searchMatchAccrualsFailure, searchMatchAccrualsSuccess, } from '../../reducers/actualMatchingViewReducer';
8
+ import { getTenantDisplayCurrency } from '../../selectors/actualMatchingViewSelector';
6
9
  import { MATCH_ACCRUAL_SEARCH_LIMIT, MATCH_SEARCH_DEBOUNCE_MS, } from '../../types/actualMatchingViewState';
7
- export const searchMatchAccrualsEpic = (actions$, _state$, zeniAPI) => {
10
+ export const searchMatchAccrualsEpic = (actions$, state$, zeniAPI) => {
8
11
  const searchActions$ = actions$.pipe(filter(searchMatchAccruals.match));
9
12
  const pagination$ = searchActions$.pipe(filter((action) => action.payload.pageToken != null));
10
13
  const newSearchDebounced$ = searchActions$.pipe(filter((action) => action.payload.pageToken == null), debounceTime(MATCH_SEARCH_DEBOUNCE_MS));
@@ -22,14 +25,18 @@ export const searchMatchAccrualsEpic = (actions$, _state$, zeniAPI) => {
22
25
  const nextOffset = offset + accruals.length;
23
26
  const total = response.data.total ?? nextOffset;
24
27
  const nextPageToken = nextOffset < total ? String(nextOffset) : null;
28
+ // toJEOneTimeAccrual still defaults a missing code to USD. Stamp
29
+ // tenant currency first, same as list members.
30
+ const fallbackCurrencyCode = getTenantDisplayCurrency(state$.value)?.currencyCode ?? '';
31
+ const stampedAccruals = accruals.map((accrual) => withInheritedAccrualCurrency(accrual, fallbackCurrencyCode));
25
32
  const actionsOut = [];
26
- if (accruals.length > 0) {
27
- actionsOut.push(updateJEOneTimeAccruals(accruals));
33
+ if (stampedAccruals.length > 0) {
34
+ actionsOut.push(updateJEOneTimeAccruals(stampedAccruals));
28
35
  }
29
36
  actionsOut.push(searchMatchAccrualsSuccess({
30
37
  append,
31
38
  nextPageToken,
32
- results: accruals,
39
+ results: stampedAccruals,
33
40
  }));
34
41
  return from(actionsOut);
35
42
  }
@@ -39,6 +46,9 @@ export const searchMatchAccrualsEpic = (actions$, _state$, zeniAPI) => {
39
46
  }), catchError((error) => of(searchMatchAccrualsFailure({
40
47
  error: createZeniAPIStatus('Unexpected Error', 'Search match accruals REST API call errored out' +
41
48
  JSON.stringify(error)),
42
- }))));
49
+ }))),
50
+ // On the HTTP stream only. Wrapping the outer merge would complete
51
+ // the epic on the first `clearAll` and drop later searches.
52
+ takeUntil(actions$.pipe(filter(clearAll.match))));
43
53
  }));
44
54
  };
@@ -1,18 +1,19 @@
1
1
  import { from, merge, of } from 'rxjs';
2
- import { catchError, debounceTime, filter, mergeMap, switchMap, } from 'rxjs/operators';
2
+ import { catchError, debounceTime, filter, mergeMap, switchMap, takeUntil, } from 'rxjs/operators';
3
3
  import { getActualMatchingMatchById } from '../../../../entity/actualMatching/actualMatchingSelector';
4
4
  import { createZeniAPIStatus, isSuccessResponse, } from '../../../../responsePayload';
5
+ import { clearAll } from '../../../../rootActions';
5
6
  import { toActualMatchingTransactionSearchResult, } from '../../payload/actualMatchingPayload';
6
7
  import { openMatchNewModal, searchMatchAccruals, searchMatchTransactions, searchMatchTransactionsFailure, searchMatchTransactionsSuccess, } from '../../reducers/actualMatchingViewReducer';
7
- import { parseActualMatchingMatchId } from '../../selectors/actualMatchingViewSelector';
8
+ import { getTenantDisplayCurrency, parseActualMatchingMatchId, } from '../../selectors/actualMatchingViewSelector';
8
9
  import { MATCH_SEARCH_DEBOUNCE_MS, MATCH_TRANSACTION_SEARCH_PAGE_SIZE, } from '../../types/actualMatchingViewState';
9
10
  export const searchMatchTransactionsEpic = (actions$, state$, zeniAPI) => {
10
11
  const openModal$ = actions$.pipe(filter(openMatchNewModal.match), mergeMap((action) => {
11
12
  const matchId = action.payload.matchId;
12
13
  if (matchId == null || matchId === '') {
13
14
  return from([
14
- searchMatchTransactions({ query: '' }),
15
- searchMatchAccruals({ query: '' }),
15
+ searchMatchTransactions(''),
16
+ searchMatchAccruals(''),
16
17
  ]);
17
18
  }
18
19
  const match = getActualMatchingMatchById(state$.value.actualMatchingState, matchId);
@@ -25,11 +26,7 @@ export const searchMatchTransactionsEpic = (actions$, state$, zeniAPI) => {
25
26
  ? match.transactionIntegrationId
26
27
  : parsed?.transactionIntegrationId;
27
28
  return from([
28
- searchMatchTransactions({
29
- query: '',
30
- transactionType,
31
- transactionIntegrationId,
32
- }),
29
+ searchMatchTransactions('', undefined, transactionType, transactionIntegrationId),
33
30
  ]);
34
31
  }));
35
32
  const searchActions$ = actions$.pipe(filter(searchMatchTransactions.match));
@@ -54,7 +51,8 @@ export const searchMatchTransactionsEpic = (actions$, state$, zeniAPI) => {
54
51
  if (isSuccessResponse(response) && response.data != null) {
55
52
  const { transactions, next_page_token } = response.data;
56
53
  const append = pageToken != null;
57
- const results = transactions.map(toActualMatchingTransactionSearchResult);
54
+ const fallbackCurrency = getTenantDisplayCurrency(state$.value);
55
+ const results = transactions.map((transaction) => toActualMatchingTransactionSearchResult(transaction, fallbackCurrency));
58
56
  return of(searchMatchTransactionsSuccess({
59
57
  append,
60
58
  nextPageToken: next_page_token ?? null,
@@ -67,7 +65,10 @@ export const searchMatchTransactionsEpic = (actions$, state$, zeniAPI) => {
67
65
  }), catchError((error) => of(searchMatchTransactionsFailure({
68
66
  error: createZeniAPIStatus('Unexpected Error', 'Search match transactions REST API call errored out' +
69
67
  JSON.stringify(error)),
70
- }))));
68
+ }))),
69
+ // On the HTTP stream only. Wrapping `merge(openModal$, search$)`
70
+ // would complete the epic on the first `clearAll` and drop later searches.
71
+ takeUntil(actions$.pipe(filter(clearAll.match))));
71
72
  }));
72
73
  return merge(openModal$, search$);
73
74
  };
@@ -1,52 +1,76 @@
1
1
  import { from, of } from 'rxjs';
2
- import { catchError, filter, mergeMap, takeUntil } from 'rxjs/operators';
3
- import { convertToPeriod, toAbsoluteDay, } from '../../../../commonStateTypes/timePeriod';
4
- import { getCurrentTenant } from '../../../../entity/tenant/tenantSelector';
2
+ import { catchError, exhaustMap, filter, mergeMap, takeUntil, } from 'rxjs/operators';
3
+ import { getActualMatchingMatchById } from '../../../../entity/actualMatching/actualMatchingSelector';
4
+ import { openSnackbar } from '../../../../entity/snackbar/snackbarReducer';
5
5
  import { createZeniAPIStatus, isSuccessResponse, } from '../../../../responsePayload';
6
6
  import { clearAll } from '../../../../rootActions';
7
7
  import { toUndoReversalResult, } from '../../payload/actualMatchingPayload';
8
- import { fetchActualMatching, fetchActualMatchingMatch, undoReversalAccruals, undoReversalAccrualsFailure, undoReversalAccrualsSuccess, } from '../../reducers/actualMatchingViewReducer';
9
- function refreshActualMatchingList(state) {
10
- const currentTenant = getCurrentTenant(state);
11
- const selectedMonthYear = state.expenseAutomationViewState.selectedPeriodByTenantId[currentTenant?.tenantId ?? ''];
12
- if (selectedMonthYear == null) {
13
- return [];
8
+ import { fetchActualMatchingMatch, undoReversalAccruals, undoReversalAccrualsFailure, undoReversalAccrualsSuccess, } from '../../reducers/actualMatchingViewReducer';
9
+ import { refreshActualMatchingList } from './refreshActualMatchingList';
10
+ function undoReversalSnackbar(results, vendorName) {
11
+ const undoneCount = results.filter((result) => result.status === 'undone').length;
12
+ const failedCount = results.length - undoneCount;
13
+ const variables = [
14
+ { variableName: '_count_', variableValue: String(undoneCount) },
15
+ { variableName: '_vendor_name_', variableValue: vendorName },
16
+ ];
17
+ if (results.length > 0 && failedCount === 0) {
18
+ return openSnackbar({
19
+ messageSection: 'actual_matching_undo_reversal',
20
+ messageText: 'success',
21
+ type: 'success',
22
+ variables,
23
+ });
14
24
  }
15
- const period = convertToPeriod(selectedMonthYear);
16
- const start = toAbsoluteDay(period.start);
17
- const end = toAbsoluteDay(period.end);
18
- if (start == null || end == null) {
19
- return [];
25
+ if (undoneCount > 0 && failedCount > 0) {
26
+ return openSnackbar({
27
+ messageSection: 'actual_matching_undo_reversal',
28
+ messageText: 'notification',
29
+ type: 'warning',
30
+ variables,
31
+ });
20
32
  }
21
- return [fetchActualMatching({ start, end }, true)];
33
+ return openSnackbar({
34
+ messageSection: 'actual_matching_undo_reversal',
35
+ messageText: 'failed',
36
+ type: 'error',
37
+ variables,
38
+ });
22
39
  }
23
- function refreshOpenMatchDetails(state) {
24
- const detailState = state.expenseAutomationActualMatchingViewState.detailFetchStateByMatchId;
25
- return Object.keys(detailState)
26
- .filter((matchId) => detailState[matchId]?.fetchState !== 'Not-Started')
27
- .map((matchId) => fetchActualMatchingMatch(matchId, true));
40
+ function matchVendorName(state, matchId) {
41
+ return (getActualMatchingMatchById(state.actualMatchingState, matchId)?.vendorName ??
42
+ '');
28
43
  }
29
- export const undoReversalAccrualsEpic = (actions$, state$, zeniAPI) => actions$.pipe(filter(undoReversalAccruals.match), mergeMap((action) => {
44
+ export const undoReversalAccrualsEpic = (actions$, state$, zeniAPI) => actions$.pipe(filter(undoReversalAccruals.match),
45
+ // Posts QBO deletes; drop duplicate clicks while the first request is in flight.
46
+ exhaustMap((action) => {
47
+ const { accrualIds, matchId } = action.payload;
30
48
  const body = {
31
- accrual_ids: action.payload.accrualIds,
49
+ accrual_ids: accrualIds,
32
50
  };
51
+ const vendorName = matchVendorName(state$.value, matchId);
33
52
  return zeniAPI
34
53
  .postAndGetJSON(`${zeniAPI.apiEndPoints.accountMicroServiceBaseUrl}/1.0/accruals/undo-reversal`, body)
35
54
  .pipe(mergeMap((response) => {
36
55
  if (isSuccessResponse(response) && response.data != null) {
37
56
  const results = response.data.results.map(toUndoReversalResult);
38
57
  const successActions = [
39
- undoReversalAccrualsSuccess({ results }),
58
+ undoReversalAccrualsSuccess(matchId, results),
40
59
  ];
41
60
  const anyUndone = results.some((result) => result.status === 'undone');
42
61
  if (anyUndone) {
43
- successActions.push(...refreshActualMatchingList(state$.value), ...refreshOpenMatchDetails(state$.value));
62
+ successActions.push(...refreshActualMatchingList(state$.value), fetchActualMatchingMatch(matchId, true));
44
63
  }
64
+ successActions.push(undoReversalSnackbar(results, vendorName));
45
65
  return from(successActions);
46
66
  }
47
- return of(undoReversalAccrualsFailure({ status: response.status }));
67
+ return of(undoReversalAccrualsFailure({
68
+ matchId,
69
+ status: response.status,
70
+ }), undoReversalSnackbar([], vendorName));
48
71
  }), catchError((error) => of(undoReversalAccrualsFailure({
72
+ matchId,
49
73
  status: createZeniAPIStatus('Unexpected Error', 'Undo reversal accruals REST API call errored out' +
50
74
  JSON.stringify(error)),
51
- }))), takeUntil(actions$.pipe(filter(clearAll.match))));
75
+ }), undoReversalSnackbar([], vendorName))), takeUntil(actions$.pipe(filter(clearAll.match))));
52
76
  }));
@@ -3,7 +3,6 @@ import { filter, switchMap } from 'rxjs/operators';
3
3
  import { convertToPeriod, toAbsoluteDay, toMonthYearPeriodId, } from '../../../../commonStateTypes/timePeriod';
4
4
  import { getCurrentTenant } from '../../../../entity/tenant/tenantSelector';
5
5
  import { fetchAllExpenseAutomationTabs } from '../../expenseAutomationViewReducer';
6
- import { fetchActualMatchingPage } from '../../reducers/actualMatchingViewReducer';
7
6
  import { fetchFluxAnalysisView } from '../../reducers/fluxAnalysisViewReducer';
8
7
  import { fetchJeSchedulesPage } from '../../reducers/jeSchedulesViewReducer';
9
8
  import { fetchMissingReceipts } from '../../reducers/missingReceiptsViewReducer';
@@ -11,7 +10,7 @@ import { fetchReconciliation } from '../../reducers/reconciliationViewReducer';
11
10
  import { fetchTransactionCategorizationView } from '../../reducers/transactionsViewReducer';
12
11
  import { getJESchedulePeriodTabKey } from '../../types/jeSchedulesViewState';
13
12
  export const fetchAllExpenseAutomationTabsEpic = (actions$, state$) => actions$.pipe(filter(fetchAllExpenseAutomationTabs.match), switchMap((action) => {
14
- const { cacheOverride, refreshViewInBackground } = action.payload;
13
+ const { cacheOverride, refreshViewInBackground, isAccrualEnabled } = action.payload;
15
14
  const state = state$.value;
16
15
  const { monthEndCloseChecksState } = state;
17
16
  const { monthEndCloseChecksByTenantId } = monthEndCloseChecksState;
@@ -32,14 +31,11 @@ export const fetchAllExpenseAutomationTabsEpic = (actions$, state$) => actions$.
32
31
  }
33
32
  if (cacheOverride === true) {
34
33
  const actions = [];
35
- const selectedPageTab = state.expenseAutomationJESchedulesViewState.uiState.selectedPageTab;
36
34
  actions.push(fetchTransactionCategorizationView(selectedTransactionCategorizationTab, { start: timePeriodStart, end: timePeriodEnd }, cacheOverride, false, undefined, undefined, refreshViewInBackground, false), fetchMissingReceipts({ start: timePeriodStart, end: timePeriodEnd }, {
37
35
  amount: 75.0,
38
36
  currencyCode: 'USD',
39
37
  currencySymbol: '$',
40
- }, cacheOverride, refreshViewInBackground), fetchFluxAnalysisView(selectedPeriod, cacheOverride, refreshViewInBackground, null), selectedPageTab === 'actual_matching'
41
- ? fetchActualMatchingPage({ start: timePeriodStart, end: timePeriodEnd }, cacheOverride)
42
- : fetchJeSchedulesPage({ start: timePeriodStart, end: timePeriodEnd }, cacheOverride));
38
+ }, cacheOverride, refreshViewInBackground), fetchFluxAnalysisView(selectedPeriod, cacheOverride, refreshViewInBackground, null), fetchJeSchedulesPage({ start: timePeriodStart, end: timePeriodEnd }, cacheOverride, isAccrualEnabled));
43
39
  if (reconciliationEnabled) {
44
40
  actions.push(fetchReconciliation(selectedPeriod, undefined, cacheOverride, refreshViewInBackground));
45
41
  }
@@ -66,7 +62,7 @@ export const fetchAllExpenseAutomationTabsEpic = (actions$, state$) => actions$.
66
62
  }
67
63
  // The two JE tabs cache separately, so only the tab actually being shown decides whether a
68
64
  // fetch is still needed. Actual Matching is a third tab on the same page with its own
69
- // endpoint and cache slot.
65
+ // endpoint; fetchJeSchedulesPage carries isAccrualEnabled so the prefetch epic can warm AM.
70
66
  const selectedPageTab = state.expenseAutomationJESchedulesViewState.uiState.selectedPageTab;
71
67
  if (selectedPageTab === 'actual_matching') {
72
68
  const actualMatchingPeriodKeys = state.expenseAutomationActualMatchingViewState.matchIdsByPeriod[monthYearPeriodId];
@@ -74,7 +70,7 @@ export const fetchAllExpenseAutomationTabsEpic = (actions$, state$) => actions$.
74
70
  'In-Progress' &&
75
71
  (!state.expenseAutomationActualMatchingViewState.hasValidState() ||
76
72
  actualMatchingPeriodKeys == null)) {
77
- fetchActions.push(fetchActualMatchingPage({ start: timePeriodStart, end: timePeriodEnd }, cacheOverride));
73
+ fetchActions.push(fetchJeSchedulesPage({ start: timePeriodStart, end: timePeriodEnd }, cacheOverride, isAccrualEnabled));
78
74
  }
79
75
  }
80
76
  else {
@@ -86,7 +82,7 @@ export const fetchAllExpenseAutomationTabsEpic = (actions$, state$) => actions$.
86
82
  .jeScheduleTransactionKeysByPeriodAndTab[jeSchedulesPeriodTabKey] == null ||
87
83
  state.expenseAutomationJESchedulesViewState
88
84
  .failedJeScheduleTransactionKeysByPeriodAndTab[jeSchedulesPeriodTabKey] == null)) {
89
- fetchActions.push(fetchJeSchedulesPage({ start: timePeriodStart, end: timePeriodEnd }, cacheOverride));
85
+ fetchActions.push(fetchJeSchedulesPage({ start: timePeriodStart, end: timePeriodEnd }, cacheOverride, isAccrualEnabled));
90
86
  }
91
87
  }
92
88
  if (state.expenseAutomationReconciliationViewState.fetchState !==
@@ -4,7 +4,6 @@ import { convertToPeriod, toAbsoluteDay, } from '../../../../commonStateTypes/ti
4
4
  import { getCurrentTenant } from '../../../../entity/tenant/tenantSelector';
5
5
  import { fetchCompanyMonthEndReportTemplates, fetchCompanyMonthEndReportView, } from '../../../companyMonthEndReportView/companyMonthEndReportViewReducer';
6
6
  import { refreshExpenseAutomationCurrentTab } from '../../expenseAutomationViewReducer';
7
- import { fetchActualMatchingPage } from '../../reducers/actualMatchingViewReducer';
8
7
  import { fetchFluxAnalysisView } from '../../reducers/fluxAnalysisViewReducer';
9
8
  import { fetchJeSchedulesPage } from '../../reducers/jeSchedulesViewReducer';
10
9
  import { fetchMissingReceipts } from '../../reducers/missingReceiptsViewReducer';
@@ -39,17 +38,10 @@ export const refreshExpenseAutomationCurrentTabEpic = (actions$, state$) => acti
39
38
  return from([
40
39
  fetchFluxAnalysisView(selectedPeriod, true, true, null),
41
40
  ]);
42
- case 'je_schedules': {
43
- const selectedPageTab = state.expenseAutomationJESchedulesViewState.uiState.selectedPageTab;
44
- if (selectedPageTab === 'actual_matching') {
45
- return from([
46
- fetchActualMatchingPage({ start: timePeriodStart, end: timePeriodEnd }, true),
47
- ]);
48
- }
41
+ case 'je_schedules':
49
42
  return from([
50
43
  fetchJeSchedulesPage({ start: timePeriodStart, end: timePeriodEnd }, true),
51
44
  ]);
52
- }
53
45
  case 'month_end_insights':
54
46
  return from([
55
47
  fetchCompanyMonthEndReportView(true, companyId, currentTenant.tenantId, period.end, true),