@zeniai/client-epic-state 5.2.34 → 5.2.35-beta10MM

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 (115) hide show
  1. package/lib/entity/actualMatching/actualMatchingPayload.d.ts +31 -0
  2. package/lib/entity/actualMatching/actualMatchingPayload.js +50 -0
  3. package/lib/entity/actualMatching/actualMatchingReducer.d.ts +9 -0
  4. package/lib/entity/actualMatching/actualMatchingReducer.js +33 -0
  5. package/lib/entity/actualMatching/actualMatchingSelector.d.ts +3 -0
  6. package/lib/entity/actualMatching/actualMatchingSelector.js +6 -0
  7. package/lib/entity/actualMatching/actualMatchingState.d.ts +23 -0
  8. package/lib/entity/actualMatching/actualMatchingState.js +2 -0
  9. package/lib/entity/actualMatching/actualMatchingTypes.d.ts +1 -0
  10. package/lib/entity/actualMatching/actualMatchingTypes.js +2 -0
  11. package/lib/entity/aiCfo/aiCfoReducer.js +78 -4
  12. package/lib/entity/aiCfo/aiCfoState.d.ts +16 -0
  13. package/lib/entity/jeSchedules/jeSchedulesPayload.d.ts +31 -0
  14. package/lib/entity/jeSchedules/jeSchedulesPayload.js +53 -0
  15. package/lib/entity/jeSchedules/jeSchedulesState.d.ts +33 -0
  16. package/lib/entity/snackbar/snackbarTypes.d.ts +1 -1
  17. package/lib/entity/snackbar/snackbarTypes.js +2 -0
  18. package/lib/epic.d.ts +8 -1
  19. package/lib/epic.js +14 -0
  20. package/lib/esm/entity/actualMatching/actualMatchingPayload.js +46 -0
  21. package/lib/esm/entity/actualMatching/actualMatchingReducer.js +30 -0
  22. package/lib/esm/entity/actualMatching/actualMatchingSelector.js +3 -0
  23. package/lib/esm/entity/actualMatching/actualMatchingState.js +1 -0
  24. package/lib/esm/entity/actualMatching/actualMatchingTypes.js +1 -0
  25. package/lib/esm/entity/aiCfo/aiCfoReducer.js +79 -5
  26. package/lib/esm/entity/jeSchedules/jeSchedulesPayload.js +53 -0
  27. package/lib/esm/entity/snackbar/snackbarTypes.js +2 -0
  28. package/lib/esm/epic.js +14 -0
  29. package/lib/esm/index.js +7 -3
  30. package/lib/esm/reducer.js +6 -0
  31. package/lib/esm/view/expenseAutomationView/epics/actualMatching/fetchActualMatchingEpic.js +44 -0
  32. package/lib/esm/view/expenseAutomationView/epics/actualMatching/fetchActualMatchingMatchEpic.js +36 -0
  33. package/lib/esm/view/expenseAutomationView/epics/actualMatching/fetchActualMatchingPageEpic.js +7 -0
  34. package/lib/esm/view/expenseAutomationView/epics/actualMatching/matchAndReverseAccrualsEpic.js +107 -0
  35. package/lib/esm/view/expenseAutomationView/epics/actualMatching/refreshActualMatchingList.js +16 -0
  36. package/lib/esm/view/expenseAutomationView/epics/actualMatching/searchMatchAccrualsEpic.js +54 -0
  37. package/lib/esm/view/expenseAutomationView/epics/actualMatching/searchMatchTransactionsEpic.js +74 -0
  38. package/lib/esm/view/expenseAutomationView/epics/actualMatching/undoReversalAccrualsEpic.js +76 -0
  39. package/lib/esm/view/expenseAutomationView/epics/common/fetchAllExpenseAutomationTabsEpic.js +25 -12
  40. package/lib/esm/view/expenseAutomationView/epics/jeSchedule/fetchJeSchedulePageEpic.js +13 -3
  41. package/lib/esm/view/expenseAutomationView/epics/jeSchedule/prefetchOtherJeSchedulesTabEpic.js +35 -15
  42. package/lib/esm/view/expenseAutomationView/epics/jeSchedule/switchJESchedulesTabEpic.js +8 -3
  43. package/lib/esm/view/expenseAutomationView/expenseAutomationViewReducer.js +2 -1
  44. package/lib/esm/view/expenseAutomationView/expenseAutomationViewSelector.js +24 -6
  45. package/lib/esm/view/expenseAutomationView/expenseAutomationViewState.js +4 -2
  46. package/lib/esm/view/expenseAutomationView/payload/actualMatchingPayload.js +68 -0
  47. package/lib/esm/view/expenseAutomationView/reducers/actualMatchingViewReducer.js +434 -0
  48. package/lib/esm/view/expenseAutomationView/reducers/jeSchedulesViewReducer.js +2 -1
  49. package/lib/esm/view/expenseAutomationView/selectorTypes/actualMatchingViewSelectorTypes.js +1 -0
  50. package/lib/esm/view/expenseAutomationView/selectors/actualMatchingViewSelector.js +426 -0
  51. package/lib/esm/view/expenseAutomationView/selectors/jeSchedulesViewSelector.js +4 -0
  52. package/lib/esm/view/expenseAutomationView/types/actualMatchingViewState.js +35 -0
  53. package/lib/esm/view/expenseAutomationView/types/jeSchedulesViewState.js +8 -1
  54. package/lib/esm/view/financeStatement/financeStatementReducer.js +17 -27
  55. package/lib/esm/view/invoicing/editInvoicingCustomerDetailView/editInvoicingCustomerDetailViewPayload.js +4 -0
  56. package/lib/esm/view/spendManagement/billPay/billList/billListSelector.js +3 -3
  57. package/lib/index.d.ts +15 -6
  58. package/lib/index.js +82 -48
  59. package/lib/reducer.d.ts +2 -0
  60. package/lib/reducer.js +6 -0
  61. package/lib/rootStateTypes.d.ts +4 -0
  62. package/lib/view/expenseAutomationView/epics/actualMatching/fetchActualMatchingEpic.d.ts +9 -0
  63. package/lib/view/expenseAutomationView/epics/actualMatching/fetchActualMatchingEpic.js +48 -0
  64. package/lib/view/expenseAutomationView/epics/actualMatching/fetchActualMatchingMatchEpic.d.ts +12 -0
  65. package/lib/view/expenseAutomationView/epics/actualMatching/fetchActualMatchingMatchEpic.js +40 -0
  66. package/lib/view/expenseAutomationView/epics/actualMatching/fetchActualMatchingPageEpic.d.ts +5 -0
  67. package/lib/view/expenseAutomationView/epics/actualMatching/fetchActualMatchingPageEpic.js +11 -0
  68. package/lib/view/expenseAutomationView/epics/actualMatching/matchAndReverseAccrualsEpic.d.ts +9 -0
  69. package/lib/view/expenseAutomationView/epics/actualMatching/matchAndReverseAccrualsEpic.js +111 -0
  70. package/lib/view/expenseAutomationView/epics/actualMatching/refreshActualMatchingList.d.ts +3 -0
  71. package/lib/view/expenseAutomationView/epics/actualMatching/refreshActualMatchingList.js +19 -0
  72. package/lib/view/expenseAutomationView/epics/actualMatching/searchMatchAccrualsEpic.d.ts +9 -0
  73. package/lib/view/expenseAutomationView/epics/actualMatching/searchMatchAccrualsEpic.js +58 -0
  74. package/lib/view/expenseAutomationView/epics/actualMatching/searchMatchTransactionsEpic.d.ts +8 -0
  75. package/lib/view/expenseAutomationView/epics/actualMatching/searchMatchTransactionsEpic.js +78 -0
  76. package/lib/view/expenseAutomationView/epics/actualMatching/undoReversalAccrualsEpic.d.ts +9 -0
  77. package/lib/view/expenseAutomationView/epics/actualMatching/undoReversalAccrualsEpic.js +80 -0
  78. package/lib/view/expenseAutomationView/epics/common/fetchAllExpenseAutomationTabsEpic.js +25 -12
  79. package/lib/view/expenseAutomationView/epics/jeSchedule/fetchJeSchedulePageEpic.d.ts +2 -1
  80. package/lib/view/expenseAutomationView/epics/jeSchedule/fetchJeSchedulePageEpic.js +13 -3
  81. package/lib/view/expenseAutomationView/epics/jeSchedule/prefetchOtherJeSchedulesTabEpic.d.ts +5 -1
  82. package/lib/view/expenseAutomationView/epics/jeSchedule/prefetchOtherJeSchedulesTabEpic.js +34 -14
  83. package/lib/view/expenseAutomationView/epics/jeSchedule/switchJESchedulesTabEpic.js +8 -3
  84. package/lib/view/expenseAutomationView/expenseAutomationViewReducer.d.ts +2 -1
  85. package/lib/view/expenseAutomationView/expenseAutomationViewReducer.js +2 -1
  86. package/lib/view/expenseAutomationView/expenseAutomationViewSelector.js +24 -6
  87. package/lib/view/expenseAutomationView/expenseAutomationViewState.d.ts +2 -1
  88. package/lib/view/expenseAutomationView/expenseAutomationViewState.js +6 -3
  89. package/lib/view/expenseAutomationView/helpers/transactionCategorizationLocalDataHelper.d.ts +1 -1
  90. package/lib/view/expenseAutomationView/payload/actualMatchingPayload.d.ts +85 -0
  91. package/lib/view/expenseAutomationView/payload/actualMatchingPayload.js +74 -0
  92. package/lib/view/expenseAutomationView/reducers/actualMatchingViewReducer.d.ts +79 -0
  93. package/lib/view/expenseAutomationView/reducers/actualMatchingViewReducer.js +438 -0
  94. package/lib/view/expenseAutomationView/reducers/jeSchedulesViewReducer.d.ts +6 -5
  95. package/lib/view/expenseAutomationView/reducers/jeSchedulesViewReducer.js +2 -1
  96. package/lib/view/expenseAutomationView/selectorTypes/actualMatchingViewSelectorTypes.d.ts +87 -0
  97. package/lib/view/expenseAutomationView/selectorTypes/actualMatchingViewSelectorTypes.js +2 -0
  98. package/lib/view/expenseAutomationView/selectorTypes/expenseAutomationViewSelectorTypes.d.ts +11 -0
  99. package/lib/view/expenseAutomationView/selectors/actualMatchingViewSelector.d.ts +38 -0
  100. package/lib/view/expenseAutomationView/selectors/actualMatchingViewSelector.js +445 -0
  101. package/lib/view/expenseAutomationView/selectors/jeSchedulesViewSelector.js +4 -0
  102. package/lib/view/expenseAutomationView/types/actualMatchingViewState.d.ts +105 -0
  103. package/lib/view/expenseAutomationView/types/actualMatchingViewState.js +42 -0
  104. package/lib/view/expenseAutomationView/types/jeSchedulesViewState.d.ts +1 -1
  105. package/lib/view/expenseAutomationView/types/jeSchedulesViewState.js +8 -1
  106. package/lib/view/financeStatement/financeStatementReducer.js +17 -27
  107. package/lib/view/invoicing/editInvoicingCustomerDetailView/editInvoicingCustomerDetailViewPayload.d.ts +1 -11
  108. package/lib/view/invoicing/editInvoicingCustomerDetailView/editInvoicingCustomerDetailViewPayload.js +4 -0
  109. package/lib/view/invoicing/editInvoicingCustomerDetailView/editInvoicingCustomerDetailViewReducer.d.ts +1 -2
  110. package/lib/view/invoicing/editInvoicingCustomerDetailView/editInvoicingCustomerDetailViewSelector.d.ts +1 -2
  111. package/lib/view/invoicing/editInvoicingCustomerDetailView/editInvoicingCustomerDetailViewState.d.ts +15 -1
  112. package/lib/view/invoicing/invoicingCustomerListView/invoicingCustomerListViewReducer.d.ts +1 -1
  113. package/lib/view/invoicing/invoicingCustomerListView/invoicingCustomerListViewState.d.ts +1 -1
  114. package/lib/view/spendManagement/billPay/billList/billListSelector.js +2 -2
  115. package/package.json +1 -1
@@ -3,11 +3,16 @@ import { filter, mergeMap } from 'rxjs/operators';
3
3
  import { convertToPeriod, toAbsoluteDay, } from '../../../../commonStateTypes/timePeriod';
4
4
  import { getCurrentTenant } from '../../../../entity/tenant/tenantSelector';
5
5
  import { fetchJeSchedulesPage, updateJESchedulesUIState, } from '../../reducers/jeSchedulesViewReducer';
6
- export const switchJESchedulesTabEpic = (actions$, state$) => actions$.pipe(filter(updateJESchedulesUIState.match), filter((action) => action.payload.selectedPageTab != null), mergeMap(() => {
6
+ export const switchJESchedulesTabEpic = (actions$, state$) => actions$.pipe(filter(updateJESchedulesUIState.match), filter((action) => action.payload.selectedPageTab != null), mergeMap((action) => {
7
7
  const state = state$.value;
8
+ const nextTab = action.payload.selectedPageTab;
8
9
  const { fetchState, refreshStatus } = state.expenseAutomationJESchedulesViewState;
9
- if (fetchState === 'In-Progress' ||
10
- refreshStatus.fetchState === 'In-Progress') {
10
+ // Review/Existing share `/1.0/schedules`; skip a second JE fetch while one
11
+ // is in flight. AM is a different endpoint — do not block it on JE
12
+ // In-Progress or the tab stays on a skeleton until the user leaves.
13
+ if (nextTab !== 'actual_matching' &&
14
+ (fetchState === 'In-Progress' ||
15
+ refreshStatus.fetchState === 'In-Progress')) {
11
16
  return EMPTY;
12
17
  }
13
18
  const currentTenant = getCurrentTenant(state);
@@ -19,11 +19,12 @@ const expenseAutomationView = createSlice({
19
19
  reducer() {
20
20
  //do nothing
21
21
  },
22
- prepare(cacheOverride = false, refreshViewInBackground = false) {
22
+ prepare(cacheOverride = false, refreshViewInBackground = false, isAccrualEnabled = false) {
23
23
  return {
24
24
  payload: {
25
25
  cacheOverride,
26
26
  refreshViewInBackground,
27
+ isAccrualEnabled,
27
28
  },
28
29
  };
29
30
  },
@@ -4,6 +4,7 @@ import { getMonthEndCloseChecksForATenantForSelectedPeriod } from '../../entity/
4
4
  import { getCurrentTenant, } from '../../entity/tenant/tenantSelector';
5
5
  import { getCompanyMonthEndReportSelectorView } from '../companyMonthEndReportView/companyMonthEndReportViewSelector';
6
6
  import { getMonthEndCloseChecksViewByTenantId, isMonthEndInsightDisabled, } from '../monthEndCloseChecksView/monthEndCloseChecksViewSelector';
7
+ import { getExpenseAutomationActualMatchingView } from './selectors/actualMatchingViewSelector';
7
8
  import { getExpenseAutomationFluxAnalysisView } from './selectors/fluxAnalysisViewSelector';
8
9
  import { getAllSteps } from './selectors/getAllStepsHelper';
9
10
  import { getExpenseAutomationJESchedulesView } from './selectors/jeSchedulesViewSelector';
@@ -42,6 +43,8 @@ export function getExpenseAutomationView(state) {
42
43
  const reconciliation = getExpenseAutomationReconciliationView(state);
43
44
  const expenseAutomationTransactionsView = getExpenseAutomationTransactionView(state);
44
45
  const jeSchedules = getExpenseAutomationJESchedulesView(state);
46
+ // Top-level sibling of `jeSchedules`: see ExpenseAutomationViewSelector.
47
+ const actualMatching = getExpenseAutomationActualMatchingView(state);
45
48
  switch (currentSelectedView) {
46
49
  case 'reconciliation':
47
50
  currentSelectedViewFetchState = reconciliation.fetchStatus;
@@ -54,12 +57,23 @@ export function getExpenseAutomationView(state) {
54
57
  fluxAnalysis.refreshStatus;
55
58
  break;
56
59
  case 'je_schedules':
57
- currentSelectedViewFetchState = {
58
- fetchState: jeSchedules.fetchState,
59
- error: jeSchedules.error,
60
- };
61
- currentSelectedViewBackgroundRefreshFetchState =
62
- jeSchedules.refreshStatus;
60
+ if (expenseAutomationJESchedulesViewState.uiState.selectedPageTab ===
61
+ 'actual_matching') {
62
+ currentSelectedViewFetchState = {
63
+ fetchState: actualMatching.fetchState,
64
+ error: actualMatching.error,
65
+ };
66
+ currentSelectedViewBackgroundRefreshFetchState =
67
+ actualMatching.refreshStatus;
68
+ }
69
+ else {
70
+ currentSelectedViewFetchState = {
71
+ fetchState: jeSchedules.fetchState,
72
+ error: jeSchedules.error,
73
+ };
74
+ currentSelectedViewBackgroundRefreshFetchState =
75
+ jeSchedules.refreshStatus;
76
+ }
63
77
  break;
64
78
  case 'missing_receipts':
65
79
  currentSelectedViewFetchState = {
@@ -88,6 +102,9 @@ export function getExpenseAutomationView(state) {
88
102
  };
89
103
  break;
90
104
  }
105
+ // AM is optional (flag-off / background prefetch) and must not keep the
106
+ // page-level AND-complete fetchState stuck on Not-Started. Tab chrome uses
107
+ // currentSelectedViewFetchState, which already switches to AM when selected.
91
108
  const fetchStateVariables = [
92
109
  expenseAutomationMissingReceiptsViewState,
93
110
  expenseAutomationJESchedulesViewState,
@@ -176,6 +193,7 @@ export function getExpenseAutomationView(state) {
176
193
  ]);
177
194
  return {
178
195
  jeSchedules,
196
+ actualMatching,
179
197
  transactionView: expenseAutomationTransactionsView,
180
198
  missingReceipts,
181
199
  reconciliation,
@@ -1,5 +1,5 @@
1
- import { stringToUnion } from '../../commonStateTypes/stringToUnion';
2
- export const EXPENSE_AUTOMATION_VIEW_TYPES = [
1
+ import { stringToUnion, stringToUnionStrict, } from '../../commonStateTypes/stringToUnion';
2
+ const EXPENSE_AUTOMATION_VIEW_TYPES = [
3
3
  'transaction_categorization',
4
4
  'je_schedules',
5
5
  'reconciliation',
@@ -8,3 +8,5 @@ export const EXPENSE_AUTOMATION_VIEW_TYPES = [
8
8
  'month_end_insights',
9
9
  ];
10
10
  export const toExpenseAutomationViewType = (v) => stringToUnion(v, EXPENSE_AUTOMATION_VIEW_TYPES);
11
+ /** Unlike `toExpenseAutomationViewType`, unknown strings return `undefined`. */
12
+ export const toExpenseAutomationViewTypeStrict = (v) => stringToUnionStrict(v, EXPENSE_AUTOMATION_VIEW_TYPES);
@@ -0,0 +1,68 @@
1
+ import { toString } from '../../../commonStateTypes/timePeriod';
2
+ import { toMatchAndReverseResultStatus, toUndoReversalResultStatus, } from '../types/actualMatchingViewState';
3
+ export function toServiceMonthFromPeriod(period) {
4
+ return toString(period.start).slice(0, 7);
5
+ }
6
+ function resolveMatchSearchAmount(payload) {
7
+ const raw = payload.total_amount ?? payload.amount;
8
+ if (typeof raw === 'number' && Number.isFinite(raw)) {
9
+ return raw;
10
+ }
11
+ if (typeof raw === 'string' && raw !== '') {
12
+ const parsed = Number(raw);
13
+ if (Number.isFinite(parsed)) {
14
+ return parsed;
15
+ }
16
+ }
17
+ return 0;
18
+ }
19
+ function resolveMatchSearchCurrencySymbol(payload, currencyCode, fallbackCurrency) {
20
+ if (payload.currency_symbol != null && payload.currency_symbol !== '') {
21
+ return payload.currency_symbol;
22
+ }
23
+ if (fallbackCurrency != null &&
24
+ currencyCode === fallbackCurrency.currencyCode) {
25
+ return fallbackCurrency.currencySymbol;
26
+ }
27
+ return currencyCode;
28
+ }
29
+ export function toActualMatchingTransactionSearchResult(payload, fallbackCurrency) {
30
+ const currencyCode = payload.currency_code != null && payload.currency_code !== ''
31
+ ? payload.currency_code
32
+ : fallbackCurrency?.currencyCode ?? '';
33
+ return {
34
+ transactionId: payload.transaction_id,
35
+ transactionIntegrationId: payload.transaction_integration_id,
36
+ transactionType: (payload.transaction_type || '').toLowerCase(),
37
+ lineId: payload.line_id,
38
+ vendorId: payload.vendor_id ?? undefined,
39
+ vendorName: payload.vendor_name ?? '',
40
+ amount: resolveMatchSearchAmount(payload),
41
+ transactionDate: payload.transaction_date,
42
+ accountId: payload.account_id ?? undefined,
43
+ accountName: payload.account_name ?? undefined,
44
+ accountDescription: payload.account_description ?? undefined,
45
+ accountingClassId: payload.accounting_class_id ?? undefined,
46
+ accountingClassName: payload.accounting_class_name ?? undefined,
47
+ currencyCode,
48
+ currencySymbol: resolveMatchSearchCurrencySymbol(payload, currencyCode, fallbackCurrency),
49
+ transactionMemo: payload.transaction_memo ?? undefined,
50
+ transactionDescription: payload.transaction_description ?? undefined,
51
+ transactionTypeName: payload.transaction_type_name ?? undefined,
52
+ syncToken: payload.sync_token == null ? '' : String(payload.sync_token),
53
+ };
54
+ }
55
+ export function toMatchAndReverseResult(payload) {
56
+ return {
57
+ accrualId: payload.accrual_id,
58
+ status: toMatchAndReverseResultStatus(payload.status),
59
+ reason: payload.reason ?? undefined,
60
+ };
61
+ }
62
+ export function toUndoReversalResult(payload) {
63
+ return {
64
+ accrualId: payload.accrual_id,
65
+ status: toUndoReversalResultStatus(payload.status),
66
+ reason: payload.reason ?? undefined,
67
+ };
68
+ }
@@ -0,0 +1,434 @@
1
+ import { createSlice } from '@reduxjs/toolkit';
2
+ import { toMonthYearPeriodId } from '../../../commonStateTypes/timePeriod';
3
+ import { generateJEOneTimeAccrualKey } from '../../../entity/jeSchedules/jeScheduleHelper';
4
+ import { clearAll } from '../../../rootActions';
5
+ import { actualMatchingTransactionLineKey } from '../types/actualMatchingViewState';
6
+ export const initialTransactionSearchState = {
7
+ query: '',
8
+ results: [],
9
+ fetchState: 'Not-Started',
10
+ error: undefined,
11
+ nextPageToken: null,
12
+ isLoadingMore: false,
13
+ };
14
+ export const initialAccrualSearchState = {
15
+ query: '',
16
+ results: [],
17
+ fetchState: 'Not-Started',
18
+ error: undefined,
19
+ nextPageToken: null,
20
+ isLoadingMore: false,
21
+ };
22
+ export const initialMatchModalState = {
23
+ isOpen: false,
24
+ openedFromMatchId: undefined,
25
+ didPinPrefillTransactions: false,
26
+ pinnedTransactionResults: [],
27
+ transactionSearch: initialTransactionSearchState,
28
+ accrualSearch: initialAccrualSearchState,
29
+ confirmStatus: { fetchState: 'Not-Started', error: undefined },
30
+ };
31
+ export const initialState = {
32
+ matchIdsByPeriod: {},
33
+ fetchState: 'Not-Started',
34
+ error: undefined,
35
+ refreshStatus: {
36
+ fetchState: 'Not-Started',
37
+ error: undefined,
38
+ },
39
+ detailFetchStateByMatchId: {},
40
+ detailRefreshStatusByMatchId: {},
41
+ uiState: {
42
+ sortKey: 'date',
43
+ sortOrder: 'descending',
44
+ searchString: '',
45
+ scrollPosition: { scrollTop: 0, scrollLeft: undefined },
46
+ },
47
+ matchModal: initialMatchModalState,
48
+ undoReversalStatusByMatchId: {},
49
+ closeMatchAfterUndo: false,
50
+ lastMatchResultsByMatchId: {},
51
+ lastUndoResultsByMatchId: {},
52
+ hasValidState() {
53
+ return this.fetchState == 'Completed';
54
+ },
55
+ };
56
+ function pruneLastMatchResultsForUndone(draft, matchId, results) {
57
+ const undoneIds = new Set(results
58
+ .filter((result) => result.status === 'undone')
59
+ .map((result) => result.accrualId));
60
+ if (undoneIds.size === 0) {
61
+ return;
62
+ }
63
+ const existing = draft.lastMatchResultsByMatchId[matchId];
64
+ if (existing == null) {
65
+ return;
66
+ }
67
+ const kept = existing.filter((result) => !undoneIds.has(result.accrualId));
68
+ if (kept.length === 0) {
69
+ delete draft.lastMatchResultsByMatchId[matchId];
70
+ return;
71
+ }
72
+ draft.lastMatchResultsByMatchId[matchId] = kept;
73
+ }
74
+ function resetMatchModalSearches(draft) {
75
+ draft.matchModal.transactionSearch = initialTransactionSearchState;
76
+ draft.matchModal.accrualSearch = initialAccrualSearchState;
77
+ draft.matchModal.confirmStatus = {
78
+ fetchState: 'Not-Started',
79
+ error: undefined,
80
+ };
81
+ draft.matchModal.lastBatchResults = undefined;
82
+ draft.matchModal.openedFromMatchId = undefined;
83
+ draft.matchModal.didPinPrefillTransactions = false;
84
+ draft.matchModal.pinnedTransactionResults = [];
85
+ }
86
+ const expenseAutomationActualMatchingView = createSlice({
87
+ name: 'expenseAutomationActualMatchingView',
88
+ initialState,
89
+ reducers: {
90
+ fetchActualMatchingPage: {
91
+ prepare(period, refreshViewInBackground = false) {
92
+ return {
93
+ payload: {
94
+ period,
95
+ refreshViewInBackground,
96
+ },
97
+ };
98
+ },
99
+ reducer() {
100
+ // Epic-only fan-out.
101
+ },
102
+ },
103
+ fetchActualMatching: {
104
+ reducer(draft, action) {
105
+ const { refreshViewInBackground } = action.payload;
106
+ if (refreshViewInBackground) {
107
+ draft.refreshStatus = { fetchState: 'In-Progress', error: undefined };
108
+ if (draft.fetchState === 'Not-Started') {
109
+ draft.fetchState = 'In-Progress';
110
+ }
111
+ }
112
+ else {
113
+ draft.fetchState = 'In-Progress';
114
+ draft.error = undefined;
115
+ }
116
+ },
117
+ prepare(period, refreshViewInBackground = false) {
118
+ return {
119
+ payload: {
120
+ period,
121
+ refreshViewInBackground,
122
+ },
123
+ };
124
+ },
125
+ },
126
+ fetchActualMatchingSuccess(draft, action) {
127
+ const { matchIds, selectedPeriod, refreshViewInBackground } = action.payload;
128
+ const periodId = toMonthYearPeriodId(selectedPeriod);
129
+ draft.matchIdsByPeriod[periodId] = matchIds;
130
+ if (refreshViewInBackground) {
131
+ draft.refreshStatus = { fetchState: 'Completed', error: undefined };
132
+ if (draft.fetchState !== 'Completed') {
133
+ draft.fetchState = 'Completed';
134
+ draft.error = undefined;
135
+ }
136
+ }
137
+ else {
138
+ draft.fetchState = 'Completed';
139
+ draft.error = undefined;
140
+ }
141
+ },
142
+ fetchActualMatchingFailure(draft, action) {
143
+ const { status, refreshViewInBackground } = action.payload;
144
+ if (refreshViewInBackground) {
145
+ draft.refreshStatus = { fetchState: 'Error', error: status };
146
+ if (draft.fetchState !== 'Completed') {
147
+ draft.fetchState = 'Error';
148
+ draft.error = status;
149
+ }
150
+ }
151
+ else {
152
+ draft.fetchState = 'Error';
153
+ draft.error = status;
154
+ }
155
+ },
156
+ fetchActualMatchingMatch: {
157
+ prepare(matchId, refreshViewInBackground = false) {
158
+ return { payload: { matchId, refreshViewInBackground } };
159
+ },
160
+ reducer(draft, action) {
161
+ const { matchId, refreshViewInBackground } = action.payload;
162
+ if (refreshViewInBackground) {
163
+ draft.detailRefreshStatusByMatchId[matchId] = {
164
+ fetchState: 'In-Progress',
165
+ error: undefined,
166
+ };
167
+ }
168
+ else {
169
+ draft.detailFetchStateByMatchId[matchId] = {
170
+ fetchState: 'In-Progress',
171
+ error: undefined,
172
+ };
173
+ }
174
+ },
175
+ },
176
+ fetchActualMatchingMatchSuccess(draft, action) {
177
+ const { matchId, refreshViewInBackground } = action.payload;
178
+ if (refreshViewInBackground) {
179
+ draft.detailRefreshStatusByMatchId[matchId] = {
180
+ fetchState: 'Completed',
181
+ error: undefined,
182
+ };
183
+ }
184
+ else {
185
+ draft.detailFetchStateByMatchId[matchId] = {
186
+ fetchState: 'Completed',
187
+ error: undefined,
188
+ };
189
+ }
190
+ },
191
+ fetchActualMatchingMatchFailure(draft, action) {
192
+ const { matchId, refreshViewInBackground, status } = action.payload;
193
+ if (refreshViewInBackground) {
194
+ draft.detailRefreshStatusByMatchId[matchId] = {
195
+ fetchState: 'Error',
196
+ error: status,
197
+ };
198
+ }
199
+ else {
200
+ draft.detailFetchStateByMatchId[matchId] = {
201
+ fetchState: 'Error',
202
+ error: status,
203
+ };
204
+ }
205
+ },
206
+ updateActualMatchingUIState(draft, action) {
207
+ if (action.payload.sortKey != null) {
208
+ draft.uiState.sortKey = action.payload.sortKey;
209
+ }
210
+ if (action.payload.sortOrder != null) {
211
+ draft.uiState.sortOrder = action.payload.sortOrder;
212
+ }
213
+ if (action.payload.searchString != null) {
214
+ draft.uiState.searchString = action.payload.searchString;
215
+ }
216
+ if (action.payload.scrollPosition != null) {
217
+ draft.uiState.scrollPosition = action.payload.scrollPosition;
218
+ }
219
+ },
220
+ openMatchNewModal: {
221
+ prepare(matchId) {
222
+ return { payload: { matchId } };
223
+ },
224
+ reducer(draft, action) {
225
+ resetMatchModalSearches(draft);
226
+ draft.matchModal.isOpen = true;
227
+ const matchId = action.payload.matchId;
228
+ draft.matchModal.openedFromMatchId =
229
+ matchId != null && matchId !== '' ? matchId : undefined;
230
+ if (draft.matchModal.openedFromMatchId != null) {
231
+ draft.matchModal.accrualSearch.fetchState = 'Completed';
232
+ }
233
+ },
234
+ },
235
+ // Dismiss (Cancel / overlay). Store reset matches `clearMatchNewModal`;
236
+ // the actions stay distinct so confirm-success vs user-dismiss stay observable.
237
+ closeMatchNewModal(draft) {
238
+ draft.matchModal.isOpen = false;
239
+ resetMatchModalSearches(draft);
240
+ },
241
+ // Confirm-success / host reset. Same slice write as close; different action type.
242
+ clearMatchNewModal(draft) {
243
+ draft.matchModal.isOpen = false;
244
+ resetMatchModalSearches(draft);
245
+ },
246
+ searchMatchTransactions: {
247
+ reducer(draft, action) {
248
+ const { query, pageToken } = action.payload;
249
+ const isLoadMore = pageToken != null;
250
+ draft.matchModal.transactionSearch.query = query;
251
+ if (isLoadMore) {
252
+ draft.matchModal.transactionSearch.isLoadingMore = true;
253
+ }
254
+ else {
255
+ draft.matchModal.transactionSearch.isLoadingMore = false;
256
+ draft.matchModal.transactionSearch.results = [];
257
+ draft.matchModal.transactionSearch.nextPageToken = null;
258
+ draft.matchModal.transactionSearch.fetchState = 'In-Progress';
259
+ draft.matchModal.transactionSearch.error = undefined;
260
+ }
261
+ },
262
+ prepare(query, pageToken, transactionType, transactionIntegrationId) {
263
+ return {
264
+ payload: {
265
+ query,
266
+ pageToken: pageToken ?? undefined,
267
+ transactionType,
268
+ transactionIntegrationId,
269
+ },
270
+ };
271
+ },
272
+ },
273
+ searchMatchTransactionsSuccess(draft, action) {
274
+ const { append, nextPageToken, results } = action.payload;
275
+ if (append) {
276
+ const merged = [
277
+ ...draft.matchModal.transactionSearch.results,
278
+ ...results,
279
+ ];
280
+ const byKey = new Map(merged.map((result) => [
281
+ actualMatchingTransactionLineKey(result.transactionType, result.transactionIntegrationId, result.lineId),
282
+ result,
283
+ ]));
284
+ draft.matchModal.transactionSearch.results = Array.from(byKey.values());
285
+ }
286
+ else {
287
+ draft.matchModal.transactionSearch.results = results;
288
+ if (draft.matchModal.openedFromMatchId != null &&
289
+ !draft.matchModal.didPinPrefillTransactions) {
290
+ draft.matchModal.didPinPrefillTransactions = true;
291
+ draft.matchModal.pinnedTransactionResults = results;
292
+ }
293
+ }
294
+ draft.matchModal.transactionSearch.nextPageToken = nextPageToken;
295
+ draft.matchModal.transactionSearch.isLoadingMore = false;
296
+ draft.matchModal.transactionSearch.fetchState = 'Completed';
297
+ draft.matchModal.transactionSearch.error = undefined;
298
+ },
299
+ searchMatchTransactionsFailure(draft, action) {
300
+ draft.matchModal.transactionSearch.isLoadingMore = false;
301
+ draft.matchModal.transactionSearch.fetchState = 'Error';
302
+ draft.matchModal.transactionSearch.error = action.payload.error;
303
+ },
304
+ searchMatchAccruals: {
305
+ reducer(draft, action) {
306
+ const { query, pageToken } = action.payload;
307
+ const isLoadMore = pageToken != null;
308
+ draft.matchModal.accrualSearch.query = query;
309
+ if (isLoadMore) {
310
+ draft.matchModal.accrualSearch.isLoadingMore = true;
311
+ }
312
+ else {
313
+ draft.matchModal.accrualSearch.isLoadingMore = false;
314
+ draft.matchModal.accrualSearch.results = [];
315
+ draft.matchModal.accrualSearch.nextPageToken = null;
316
+ draft.matchModal.accrualSearch.fetchState = 'In-Progress';
317
+ draft.matchModal.accrualSearch.error = undefined;
318
+ }
319
+ },
320
+ prepare(query, pageToken) {
321
+ return { payload: { query, pageToken: pageToken ?? undefined } };
322
+ },
323
+ },
324
+ searchMatchAccrualsSuccess(draft, action) {
325
+ const { append, nextPageToken, results } = action.payload;
326
+ const keys = results.map((accrual) => generateJEOneTimeAccrualKey(accrual._id));
327
+ if (append) {
328
+ const merged = [...draft.matchModal.accrualSearch.results, ...keys];
329
+ draft.matchModal.accrualSearch.results = Array.from(new Set(merged));
330
+ }
331
+ else {
332
+ draft.matchModal.accrualSearch.results = keys;
333
+ }
334
+ draft.matchModal.accrualSearch.nextPageToken = nextPageToken;
335
+ draft.matchModal.accrualSearch.isLoadingMore = false;
336
+ draft.matchModal.accrualSearch.fetchState = 'Completed';
337
+ draft.matchModal.accrualSearch.error = undefined;
338
+ },
339
+ searchMatchAccrualsFailure(draft, action) {
340
+ draft.matchModal.accrualSearch.isLoadingMore = false;
341
+ draft.matchModal.accrualSearch.fetchState = 'Error';
342
+ draft.matchModal.accrualSearch.error = action.payload.error;
343
+ },
344
+ matchAndReverseAccruals: {
345
+ reducer(draft) {
346
+ draft.matchModal.confirmStatus = {
347
+ fetchState: 'In-Progress',
348
+ error: undefined,
349
+ };
350
+ },
351
+ prepare(transactionId, transactionIntegrationId, transactionType, lineIds, transactionSyncToken, accrualIds) {
352
+ return {
353
+ payload: {
354
+ transactionId,
355
+ transactionIntegrationId,
356
+ transactionType,
357
+ lineIds,
358
+ transactionSyncToken,
359
+ accrualIds,
360
+ },
361
+ };
362
+ },
363
+ },
364
+ matchAndReverseAccrualsSuccess(draft, action) {
365
+ draft.matchModal.confirmStatus = {
366
+ fetchState: 'Completed',
367
+ error: undefined,
368
+ };
369
+ draft.matchModal.lastBatchResults = action.payload.results;
370
+ if (action.payload.matchId != null) {
371
+ draft.lastMatchResultsByMatchId[action.payload.matchId] =
372
+ action.payload.results;
373
+ }
374
+ },
375
+ matchAndReverseAccrualsFailure(draft, action) {
376
+ draft.matchModal.confirmStatus = {
377
+ fetchState: 'Error',
378
+ error: action.payload.status,
379
+ };
380
+ },
381
+ undoReversalAccruals: {
382
+ reducer(draft, action) {
383
+ draft.undoReversalStatusByMatchId[action.payload.matchId] = {
384
+ fetchState: 'In-Progress',
385
+ error: undefined,
386
+ };
387
+ },
388
+ prepare(matchId, accrualIds) {
389
+ return { payload: { matchId, accrualIds } };
390
+ },
391
+ },
392
+ undoReversalAccrualsSuccess: {
393
+ reducer(draft, action) {
394
+ const { matchId, results } = action.payload;
395
+ draft.undoReversalStatusByMatchId[matchId] = {
396
+ fetchState: 'Completed',
397
+ error: undefined,
398
+ };
399
+ const allUndone = results.length > 0 &&
400
+ results.every((result) => result.status === 'undone');
401
+ draft.closeMatchAfterUndo = allUndone;
402
+ pruneLastMatchResultsForUndone(draft, matchId, results);
403
+ if (allUndone) {
404
+ delete draft.lastUndoResultsByMatchId[matchId];
405
+ }
406
+ else {
407
+ draft.lastUndoResultsByMatchId[matchId] = results;
408
+ }
409
+ },
410
+ prepare(matchId, results) {
411
+ return { payload: { matchId, results } };
412
+ },
413
+ },
414
+ ackCloseMatchAfterUndo(draft) {
415
+ draft.closeMatchAfterUndo = false;
416
+ },
417
+ undoReversalAccrualsFailure(draft, action) {
418
+ draft.undoReversalStatusByMatchId[action.payload.matchId] = {
419
+ fetchState: 'Error',
420
+ error: action.payload.status,
421
+ };
422
+ },
423
+ clearActualMatchingView(draft) {
424
+ Object.assign(draft, initialState);
425
+ },
426
+ },
427
+ extraReducers: (builder) => {
428
+ builder.addCase(clearAll, (draft) => {
429
+ Object.assign(draft, initialState);
430
+ });
431
+ },
432
+ });
433
+ export const { ackCloseMatchAfterUndo, clearActualMatchingView, clearMatchNewModal, closeMatchNewModal, fetchActualMatching, fetchActualMatchingFailure, fetchActualMatchingMatch, fetchActualMatchingMatchFailure, fetchActualMatchingMatchSuccess, fetchActualMatchingPage, fetchActualMatchingSuccess, matchAndReverseAccruals, matchAndReverseAccrualsFailure, matchAndReverseAccrualsSuccess, openMatchNewModal, searchMatchAccruals, searchMatchAccrualsFailure, searchMatchAccrualsSuccess, searchMatchTransactions, searchMatchTransactionsFailure, searchMatchTransactionsSuccess, undoReversalAccruals, undoReversalAccrualsFailure, undoReversalAccrualsSuccess, updateActualMatchingUIState, } = expenseAutomationActualMatchingView.actions;
434
+ export default expenseAutomationActualMatchingView.reducer;
@@ -105,11 +105,12 @@ const expenseAutomationJESchedulesView = createSlice({
105
105
  initialState,
106
106
  reducers: {
107
107
  fetchJeSchedulesPage: {
108
- prepare(period, refreshViewInBackground = false) {
108
+ prepare(period, refreshViewInBackground = false, isAccrualEnabled = false) {
109
109
  return {
110
110
  payload: {
111
111
  period,
112
112
  refreshViewInBackground,
113
+ isAccrualEnabled,
113
114
  },
114
115
  };
115
116
  },