@zeniai/client-epic-state 5.2.35-beta0MM → 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.
- package/lib/entity/actualMatching/actualMatchingPayload.d.ts +7 -1
- package/lib/entity/actualMatching/actualMatchingPayload.js +19 -12
- package/lib/entity/actualMatching/actualMatchingReducer.d.ts +3 -2
- package/lib/entity/actualMatching/actualMatchingReducer.js +3 -3
- package/lib/entity/actualMatching/actualMatchingState.d.ts +3 -2
- package/lib/entity/aiCfo/aiCfoReducer.js +78 -4
- package/lib/entity/aiCfo/aiCfoState.d.ts +16 -0
- package/lib/entity/jeSchedules/jeSchedulesPayload.d.ts +2 -0
- package/lib/entity/jeSchedules/jeSchedulesPayload.js +4 -0
- package/lib/entity/jeSchedules/jeSchedulesState.d.ts +2 -0
- package/lib/entity/snackbar/snackbarTypes.d.ts +1 -1
- package/lib/entity/snackbar/snackbarTypes.js +2 -0
- package/lib/esm/entity/actualMatching/actualMatchingPayload.js +18 -12
- package/lib/esm/entity/actualMatching/actualMatchingReducer.js +3 -3
- package/lib/esm/entity/aiCfo/aiCfoReducer.js +79 -5
- package/lib/esm/entity/jeSchedules/jeSchedulesPayload.js +4 -0
- package/lib/esm/entity/snackbar/snackbarTypes.js +2 -0
- package/lib/esm/index.js +6 -6
- package/lib/esm/view/expenseAutomationView/epics/actualMatching/fetchActualMatchingEpic.js +3 -5
- package/lib/esm/view/expenseAutomationView/epics/actualMatching/fetchActualMatchingMatchEpic.js +3 -2
- package/lib/esm/view/expenseAutomationView/epics/actualMatching/matchAndReverseAccrualsEpic.js +80 -25
- package/lib/esm/view/expenseAutomationView/epics/actualMatching/refreshActualMatchingList.js +16 -0
- package/lib/esm/view/expenseAutomationView/epics/actualMatching/searchMatchAccrualsEpic.js +16 -6
- package/lib/esm/view/expenseAutomationView/epics/actualMatching/searchMatchTransactionsEpic.js +12 -11
- package/lib/esm/view/expenseAutomationView/epics/actualMatching/undoReversalAccrualsEpic.js +50 -26
- package/lib/esm/view/expenseAutomationView/epics/common/fetchAllExpenseAutomationTabsEpic.js +5 -9
- package/lib/esm/view/expenseAutomationView/epics/common/refreshExpenseAutomationCurrentTabEpic.js +1 -9
- package/lib/esm/view/expenseAutomationView/epics/jeSchedule/fetchJeSchedulePageEpic.js +13 -3
- package/lib/esm/view/expenseAutomationView/epics/jeSchedule/prefetchOtherJeSchedulesTabEpic.js +34 -16
- package/lib/esm/view/expenseAutomationView/epics/jeSchedule/switchJESchedulesTabEpic.js +8 -8
- package/lib/esm/view/expenseAutomationView/expenseAutomationViewReducer.js +2 -1
- package/lib/esm/view/expenseAutomationView/expenseAutomationViewSelector.js +5 -2
- package/lib/esm/view/expenseAutomationView/expenseAutomationViewState.js +4 -2
- package/lib/esm/view/expenseAutomationView/payload/actualMatchingPayload.js +10 -10
- package/lib/esm/view/expenseAutomationView/reducers/actualMatchingViewReducer.js +90 -20
- package/lib/esm/view/expenseAutomationView/reducers/jeSchedulesViewReducer.js +2 -1
- package/lib/esm/view/expenseAutomationView/selectors/actualMatchingViewSelector.js +206 -61
- package/lib/esm/view/expenseAutomationView/selectors/jeSchedulesViewSelector.js +1 -0
- package/lib/esm/view/expenseAutomationView/types/actualMatchingViewState.js +3 -0
- package/lib/esm/view/expenseAutomationView/types/jeSchedulesViewState.js +3 -0
- package/lib/esm/view/financeStatement/financeStatementReducer.js +17 -27
- package/lib/esm/view/spendManagement/billPay/billList/billListSelector.js +3 -3
- package/lib/index.d.ts +7 -6
- package/lib/index.js +52 -48
- package/lib/view/expenseAutomationView/epics/actualMatching/fetchActualMatchingEpic.js +3 -5
- package/lib/view/expenseAutomationView/epics/actualMatching/fetchActualMatchingMatchEpic.d.ts +1 -1
- package/lib/view/expenseAutomationView/epics/actualMatching/fetchActualMatchingMatchEpic.js +3 -2
- package/lib/view/expenseAutomationView/epics/actualMatching/matchAndReverseAccrualsEpic.d.ts +2 -1
- package/lib/view/expenseAutomationView/epics/actualMatching/matchAndReverseAccrualsEpic.js +77 -22
- package/lib/view/expenseAutomationView/epics/actualMatching/refreshActualMatchingList.d.ts +3 -0
- package/lib/view/expenseAutomationView/epics/actualMatching/refreshActualMatchingList.js +19 -0
- package/lib/view/expenseAutomationView/epics/actualMatching/searchMatchAccrualsEpic.d.ts +3 -2
- package/lib/view/expenseAutomationView/epics/actualMatching/searchMatchAccrualsEpic.js +15 -5
- package/lib/view/expenseAutomationView/epics/actualMatching/searchMatchTransactionsEpic.js +10 -9
- package/lib/view/expenseAutomationView/epics/actualMatching/undoReversalAccrualsEpic.d.ts +2 -1
- package/lib/view/expenseAutomationView/epics/actualMatching/undoReversalAccrualsEpic.js +48 -24
- package/lib/view/expenseAutomationView/epics/common/fetchAllExpenseAutomationTabsEpic.d.ts +1 -2
- package/lib/view/expenseAutomationView/epics/common/fetchAllExpenseAutomationTabsEpic.js +5 -9
- package/lib/view/expenseAutomationView/epics/common/refreshExpenseAutomationCurrentTabEpic.d.ts +1 -2
- package/lib/view/expenseAutomationView/epics/common/refreshExpenseAutomationCurrentTabEpic.js +1 -9
- package/lib/view/expenseAutomationView/epics/jeSchedule/fetchJeSchedulePageEpic.d.ts +2 -1
- package/lib/view/expenseAutomationView/epics/jeSchedule/fetchJeSchedulePageEpic.js +13 -3
- package/lib/view/expenseAutomationView/epics/jeSchedule/prefetchOtherJeSchedulesTabEpic.d.ts +5 -1
- package/lib/view/expenseAutomationView/epics/jeSchedule/prefetchOtherJeSchedulesTabEpic.js +33 -15
- package/lib/view/expenseAutomationView/epics/jeSchedule/switchJESchedulesTabEpic.d.ts +1 -2
- package/lib/view/expenseAutomationView/epics/jeSchedule/switchJESchedulesTabEpic.js +8 -8
- package/lib/view/expenseAutomationView/expenseAutomationViewReducer.d.ts +2 -1
- package/lib/view/expenseAutomationView/expenseAutomationViewReducer.js +2 -1
- package/lib/view/expenseAutomationView/expenseAutomationViewSelector.js +5 -2
- package/lib/view/expenseAutomationView/expenseAutomationViewState.d.ts +2 -1
- package/lib/view/expenseAutomationView/expenseAutomationViewState.js +6 -3
- package/lib/view/expenseAutomationView/helpers/transactionCategorizationLocalDataHelper.d.ts +1 -1
- package/lib/view/expenseAutomationView/payload/actualMatchingPayload.d.ts +3 -3
- package/lib/view/expenseAutomationView/payload/actualMatchingPayload.js +10 -10
- package/lib/view/expenseAutomationView/reducers/actualMatchingViewReducer.d.ts +21 -16
- package/lib/view/expenseAutomationView/reducers/actualMatchingViewReducer.js +90 -20
- package/lib/view/expenseAutomationView/reducers/jeSchedulesViewReducer.d.ts +2 -1
- package/lib/view/expenseAutomationView/reducers/jeSchedulesViewReducer.js +2 -1
- package/lib/view/expenseAutomationView/selectorTypes/actualMatchingViewSelectorTypes.d.ts +13 -9
- package/lib/view/expenseAutomationView/selectorTypes/expenseAutomationViewSelectorTypes.d.ts +9 -0
- package/lib/view/expenseAutomationView/selectors/actualMatchingViewSelector.d.ts +11 -2
- package/lib/view/expenseAutomationView/selectors/actualMatchingViewSelector.js +209 -60
- package/lib/view/expenseAutomationView/selectors/jeSchedulesViewSelector.js +1 -0
- package/lib/view/expenseAutomationView/types/actualMatchingViewState.d.ts +8 -2
- package/lib/view/expenseAutomationView/types/actualMatchingViewState.js +4 -0
- package/lib/view/expenseAutomationView/types/jeSchedulesViewState.js +3 -0
- package/lib/view/financeStatement/financeStatementReducer.js +17 -27
- package/lib/view/spendManagement/billPay/billList/billListSelector.js +2 -2
- package/package.json +1 -1
|
@@ -2,24 +2,34 @@ import { concat, from } from 'rxjs';
|
|
|
2
2
|
import { filter, mergeMap } from 'rxjs/operators';
|
|
3
3
|
import { fetchAccountList } from '../../../accountList/accountListReducer';
|
|
4
4
|
import { getAccountsTypesForScheduleEpics } from '../../../scheduleView/scheduleListView/scheduleListHelper';
|
|
5
|
+
import { fetchActualMatchingPage } from '../../reducers/actualMatchingViewReducer';
|
|
5
6
|
import { fetchJeSchedules, fetchJeSchedulesPage, initializeAccountSettingsView, } from '../../reducers/jeSchedulesViewReducer';
|
|
6
7
|
export const fetchJeSchedulesPageEpic = (actions$, state$) => actions$.pipe(filter(fetchJeSchedulesPage.match), mergeMap((action) => {
|
|
7
8
|
const jeSchedulePageActions = [];
|
|
8
9
|
const { period, refreshViewInBackground } = action.payload;
|
|
10
|
+
const state = state$.value;
|
|
11
|
+
const { selectedPageTab } = state.expenseAutomationJESchedulesViewState.uiState;
|
|
9
12
|
const accountType = getAccountsTypesForScheduleEpics();
|
|
10
|
-
const accountListState = state
|
|
13
|
+
const accountListState = state.accountListState;
|
|
11
14
|
if (accountListState.byReportId[accountType.accountListKey].fetchState ===
|
|
12
15
|
'Not-Started') {
|
|
13
16
|
// call this only if the account list for schedules is not fetched already
|
|
14
17
|
jeSchedulePageActions.push(fetchAccountList(accountType.accountListKey, true, false, false, false, accountType.allowedAccountList));
|
|
15
18
|
}
|
|
16
|
-
|
|
19
|
+
// AM is chrome on this page but a different endpoint. fetchJeSchedules with
|
|
20
|
+
// selectedPageTab === 'actual_matching' would hit /1.0/schedules and write a bogus cache slot.
|
|
21
|
+
if (selectedPageTab === 'actual_matching') {
|
|
22
|
+
jeSchedulePageActions.push(fetchActualMatchingPage(period, refreshViewInBackground));
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
jeSchedulePageActions.push(fetchJeSchedules(period, refreshViewInBackground));
|
|
26
|
+
}
|
|
17
27
|
// Only on the first page load. Account settings cannot change from a tab switch or a
|
|
18
28
|
// refresh, and re-initializing puts accountSettingsView back to 'In-Progress' -- which the
|
|
19
29
|
// page reads as "still loading" and answers with a full skeleton over rows it already has.
|
|
20
30
|
// The action also resets localData, so re-dispatching it blindly would empty the settings
|
|
21
31
|
// form until the refetch lands. Saving account settings still re-initializes explicitly.
|
|
22
|
-
if (!state
|
|
32
|
+
if (!state.expenseAutomationJESchedulesViewState.accountSettings
|
|
23
33
|
.hasLoadedAccountSettings) {
|
|
24
34
|
jeSchedulePageActions.push(initializeAccountSettingsView());
|
|
25
35
|
}
|
package/lib/esm/view/expenseAutomationView/epics/jeSchedule/prefetchOtherJeSchedulesTabEpic.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { EMPTY,
|
|
1
|
+
import { EMPTY, from } from 'rxjs';
|
|
2
2
|
import { filter, mergeMap } from 'rxjs/operators';
|
|
3
3
|
import { toMonthYearPeriodId } from '../../../../commonStateTypes/timePeriod';
|
|
4
4
|
import { getCurrentTenant } from '../../../../entity/tenant/tenantSelector';
|
|
5
|
+
import { fetchActualMatchingPage } from '../../reducers/actualMatchingViewReducer';
|
|
5
6
|
import { fetchJeSchedules, fetchJeSchedulesPage, } from '../../reducers/jeSchedulesViewReducer';
|
|
6
7
|
import { ALL_JE_PAGE_TABS, getJESchedulePeriodTabKey, } from '../../types/jeSchedulesViewState';
|
|
7
8
|
/**
|
|
@@ -9,29 +10,46 @@ import { ALL_JE_PAGE_TABS, getJESchedulePeriodTabKey, } from '../../types/jeSche
|
|
|
9
10
|
*
|
|
10
11
|
* Listens for `fetchJeSchedulesPage` rather than `fetchJeSchedules` on purpose: the prefetch emits
|
|
11
12
|
* `fetchJeSchedules`, so listening for that would make this epic feed itself.
|
|
13
|
+
*
|
|
14
|
+
* Review <-> Existing share `/1.0/schedules`. Actual Matching is a different endpoint; it is
|
|
15
|
+
* warmed with `fetchActualMatchingPage` when the page action carries `isAccrualEnabled`.
|
|
12
16
|
*/
|
|
13
17
|
export const prefetchOtherJeSchedulesTabEpic = (actions$, state$) => actions$.pipe(filter(fetchJeSchedulesPage.match), mergeMap((action) => {
|
|
14
18
|
const state = state$.value;
|
|
15
|
-
const { period } = action.payload;
|
|
19
|
+
const { period, isAccrualEnabled } = action.payload;
|
|
16
20
|
const { selectedPageTab } = state.expenseAutomationJESchedulesViewState.uiState;
|
|
17
|
-
|
|
18
|
-
//
|
|
21
|
+
const actions = [];
|
|
22
|
+
// AM is chrome-only on ALL_JE_PAGE_TABS; warming it with fetchJeSchedules
|
|
23
|
+
// would hit the wrong endpoint.
|
|
19
24
|
const otherPageTab = ALL_JE_PAGE_TABS.find((pageTab) => pageTab !== selectedPageTab && pageTab !== 'actual_matching');
|
|
20
|
-
if (otherPageTab == null) {
|
|
21
|
-
return EMPTY;
|
|
22
|
-
}
|
|
23
25
|
const currentTenant = getCurrentTenant(state);
|
|
24
26
|
const selectedPeriod = state.expenseAutomationViewState.selectedPeriodByTenantId[currentTenant.tenantId];
|
|
25
|
-
if (selectedPeriod
|
|
26
|
-
|
|
27
|
+
if (otherPageTab != null && selectedPeriod != null) {
|
|
28
|
+
const otherPeriodTabKey = getJESchedulePeriodTabKey(otherPageTab, toMonthYearPeriodId(selectedPeriod));
|
|
29
|
+
const isOtherTabAlreadyLoaded = state.expenseAutomationJESchedulesViewState
|
|
30
|
+
.jeScheduleTransactionKeysByPeriodAndTab[otherPeriodTabKey] != null;
|
|
31
|
+
if (!isOtherTabAlreadyLoaded) {
|
|
32
|
+
actions.push(fetchJeSchedules(period, true, otherPageTab));
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
// GET /actual-matching is not Statsig-gated; CES has no Statsig. The screen
|
|
36
|
+
// passes is_accrual_enabled so disabled tenants do not pay for a list they
|
|
37
|
+
// cannot open. Visible AM is fetchJeSchedulesPageEpic's job.
|
|
38
|
+
if (isAccrualEnabled === true &&
|
|
39
|
+
selectedPageTab !== 'actual_matching') {
|
|
40
|
+
const periodId = toMonthYearPeriodId({
|
|
41
|
+
month: period.start.month,
|
|
42
|
+
year: period.start.year,
|
|
43
|
+
});
|
|
44
|
+
// `fetchState` is global across months. Skipping on In-Progress would drop
|
|
45
|
+
// October prefetch while September is still loading; September's success
|
|
46
|
+
// then marks the slice Completed and October looks loaded with no ids.
|
|
47
|
+
if (state.expenseAutomationActualMatchingViewState.matchIdsByPeriod[periodId] == null) {
|
|
48
|
+
actions.push(fetchActualMatchingPage(period, true));
|
|
49
|
+
}
|
|
27
50
|
}
|
|
28
|
-
|
|
29
|
-
// just left, since a switch also fires fetchJeSchedulesPage.
|
|
30
|
-
const otherPeriodTabKey = getJESchedulePeriodTabKey(otherPageTab, toMonthYearPeriodId(selectedPeriod));
|
|
31
|
-
const isOtherTabAlreadyLoaded = state.expenseAutomationJESchedulesViewState
|
|
32
|
-
.jeScheduleTransactionKeysByPeriodAndTab[otherPeriodTabKey] != null;
|
|
33
|
-
if (isOtherTabAlreadyLoaded) {
|
|
51
|
+
if (actions.length === 0) {
|
|
34
52
|
return EMPTY;
|
|
35
53
|
}
|
|
36
|
-
return
|
|
54
|
+
return from(actions);
|
|
37
55
|
}));
|
|
@@ -2,13 +2,17 @@ import { EMPTY, of } from 'rxjs';
|
|
|
2
2
|
import { filter, mergeMap } from 'rxjs/operators';
|
|
3
3
|
import { convertToPeriod, toAbsoluteDay, } from '../../../../commonStateTypes/timePeriod';
|
|
4
4
|
import { getCurrentTenant } from '../../../../entity/tenant/tenantSelector';
|
|
5
|
-
import { fetchActualMatchingPage } from '../../reducers/actualMatchingViewReducer';
|
|
6
5
|
import { fetchJeSchedulesPage, updateJESchedulesUIState, } from '../../reducers/jeSchedulesViewReducer';
|
|
7
|
-
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) => {
|
|
8
7
|
const state = state$.value;
|
|
8
|
+
const nextTab = action.payload.selectedPageTab;
|
|
9
9
|
const { fetchState, refreshStatus } = state.expenseAutomationJESchedulesViewState;
|
|
10
|
-
|
|
11
|
-
|
|
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')) {
|
|
12
16
|
return EMPTY;
|
|
13
17
|
}
|
|
14
18
|
const currentTenant = getCurrentTenant(state);
|
|
@@ -25,9 +29,5 @@ export const switchJESchedulesTabEpic = (actions$, state$) => actions$.pipe(filt
|
|
|
25
29
|
if (timePeriodStart == null || timePeriodEnd == null) {
|
|
26
30
|
return EMPTY;
|
|
27
31
|
}
|
|
28
|
-
const { selectedPageTab } = state.expenseAutomationJESchedulesViewState.uiState;
|
|
29
|
-
if (selectedPageTab === 'actual_matching') {
|
|
30
|
-
return of(fetchActualMatchingPage({ start: timePeriodStart, end: timePeriodEnd }, true));
|
|
31
|
-
}
|
|
32
32
|
return of(fetchJeSchedulesPage({ start: timePeriodStart, end: timePeriodEnd }, true));
|
|
33
33
|
}));
|
|
@@ -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
|
},
|
|
@@ -22,7 +22,7 @@ const isPreviousStepDisabled = (selectedTransactionCategorizationTab, allSteps,
|
|
|
22
22
|
return allSteps[stepIndex - 1].isDisabled;
|
|
23
23
|
};
|
|
24
24
|
export function getExpenseAutomationView(state) {
|
|
25
|
-
const { expenseAutomationViewState, tenantState, expenseAutomationMissingReceiptsViewState, expenseAutomationJESchedulesViewState,
|
|
25
|
+
const { expenseAutomationViewState, tenantState, expenseAutomationMissingReceiptsViewState, expenseAutomationJESchedulesViewState, expenseAutomationTransactionsViewState, expenseAutomationReconciliationViewState, monthEndCloseChecksState, companyState, } = state;
|
|
26
26
|
const { loggedInUser, currentTenantId } = tenantState;
|
|
27
27
|
const currentTenant = getCurrentTenant(state);
|
|
28
28
|
const { currentSelectedView, selectedPeriodByTenantId } = expenseAutomationViewState;
|
|
@@ -43,6 +43,7 @@ export function getExpenseAutomationView(state) {
|
|
|
43
43
|
const reconciliation = getExpenseAutomationReconciliationView(state);
|
|
44
44
|
const expenseAutomationTransactionsView = getExpenseAutomationTransactionView(state);
|
|
45
45
|
const jeSchedules = getExpenseAutomationJESchedulesView(state);
|
|
46
|
+
// Top-level sibling of `jeSchedules`: see ExpenseAutomationViewSelector.
|
|
46
47
|
const actualMatching = getExpenseAutomationActualMatchingView(state);
|
|
47
48
|
switch (currentSelectedView) {
|
|
48
49
|
case 'reconciliation':
|
|
@@ -101,10 +102,12 @@ export function getExpenseAutomationView(state) {
|
|
|
101
102
|
};
|
|
102
103
|
break;
|
|
103
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.
|
|
104
108
|
const fetchStateVariables = [
|
|
105
109
|
expenseAutomationMissingReceiptsViewState,
|
|
106
110
|
expenseAutomationJESchedulesViewState,
|
|
107
|
-
expenseAutomationActualMatchingViewState,
|
|
108
111
|
expenseAutomationTransactionsView,
|
|
109
112
|
expenseAutomationReconciliationViewState,
|
|
110
113
|
];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { stringToUnion } from '../../commonStateTypes/stringToUnion';
|
|
2
|
-
|
|
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);
|
|
@@ -3,8 +3,6 @@ import { toMatchAndReverseResultStatus, toUndoReversalResultStatus, } from '../t
|
|
|
3
3
|
export function toServiceMonthFromPeriod(period) {
|
|
4
4
|
return toString(period.start).slice(0, 7);
|
|
5
5
|
}
|
|
6
|
-
const DEFAULT_CURRENCY_CODE = 'USD';
|
|
7
|
-
const DEFAULT_CURRENCY_SYMBOL = '$';
|
|
8
6
|
function resolveMatchSearchAmount(payload) {
|
|
9
7
|
const raw = payload.total_amount ?? payload.amount;
|
|
10
8
|
if (typeof raw === 'number' && Number.isFinite(raw)) {
|
|
@@ -18,22 +16,24 @@ function resolveMatchSearchAmount(payload) {
|
|
|
18
16
|
}
|
|
19
17
|
return 0;
|
|
20
18
|
}
|
|
21
|
-
function resolveMatchSearchCurrencySymbol(payload, currencyCode) {
|
|
19
|
+
function resolveMatchSearchCurrencySymbol(payload, currencyCode, fallbackCurrency) {
|
|
22
20
|
if (payload.currency_symbol != null && payload.currency_symbol !== '') {
|
|
23
21
|
return payload.currency_symbol;
|
|
24
22
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
if (fallbackCurrency != null &&
|
|
24
|
+
currencyCode === fallbackCurrency.currencyCode) {
|
|
25
|
+
return fallbackCurrency.currencySymbol;
|
|
26
|
+
}
|
|
27
|
+
return currencyCode;
|
|
28
28
|
}
|
|
29
|
-
export function toActualMatchingTransactionSearchResult(payload) {
|
|
29
|
+
export function toActualMatchingTransactionSearchResult(payload, fallbackCurrency) {
|
|
30
30
|
const currencyCode = payload.currency_code != null && payload.currency_code !== ''
|
|
31
31
|
? payload.currency_code
|
|
32
|
-
:
|
|
32
|
+
: fallbackCurrency?.currencyCode ?? '';
|
|
33
33
|
return {
|
|
34
34
|
transactionId: payload.transaction_id,
|
|
35
35
|
transactionIntegrationId: payload.transaction_integration_id,
|
|
36
|
-
transactionType: payload.transaction_type,
|
|
36
|
+
transactionType: (payload.transaction_type || '').toLowerCase(),
|
|
37
37
|
lineId: payload.line_id,
|
|
38
38
|
vendorId: payload.vendor_id ?? undefined,
|
|
39
39
|
vendorName: payload.vendor_name ?? '',
|
|
@@ -45,7 +45,7 @@ export function toActualMatchingTransactionSearchResult(payload) {
|
|
|
45
45
|
accountingClassId: payload.accounting_class_id ?? undefined,
|
|
46
46
|
accountingClassName: payload.accounting_class_name ?? undefined,
|
|
47
47
|
currencyCode,
|
|
48
|
-
currencySymbol: resolveMatchSearchCurrencySymbol(payload, currencyCode),
|
|
48
|
+
currencySymbol: resolveMatchSearchCurrencySymbol(payload, currencyCode, fallbackCurrency),
|
|
49
49
|
transactionMemo: payload.transaction_memo ?? undefined,
|
|
50
50
|
transactionDescription: payload.transaction_description ?? undefined,
|
|
51
51
|
transactionTypeName: payload.transaction_type_name ?? undefined,
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { createSlice } from '@reduxjs/toolkit';
|
|
2
|
-
import { toMonthYearPeriodId
|
|
2
|
+
import { toMonthYearPeriodId } from '../../../commonStateTypes/timePeriod';
|
|
3
3
|
import { generateJEOneTimeAccrualKey } from '../../../entity/jeSchedules/jeScheduleHelper';
|
|
4
4
|
import { clearAll } from '../../../rootActions';
|
|
5
|
+
import { actualMatchingTransactionLineKey } from '../types/actualMatchingViewState';
|
|
5
6
|
export const initialTransactionSearchState = {
|
|
6
7
|
query: '',
|
|
7
8
|
results: [],
|
|
@@ -21,6 +22,7 @@ export const initialAccrualSearchState = {
|
|
|
21
22
|
export const initialMatchModalState = {
|
|
22
23
|
isOpen: false,
|
|
23
24
|
openedFromMatchId: undefined,
|
|
25
|
+
didPinPrefillTransactions: false,
|
|
24
26
|
pinnedTransactionResults: [],
|
|
25
27
|
transactionSearch: initialTransactionSearchState,
|
|
26
28
|
accrualSearch: initialAccrualSearchState,
|
|
@@ -43,11 +45,32 @@ export const initialState = {
|
|
|
43
45
|
scrollPosition: { scrollTop: 0, scrollLeft: undefined },
|
|
44
46
|
},
|
|
45
47
|
matchModal: initialMatchModalState,
|
|
46
|
-
|
|
48
|
+
undoReversalStatusByMatchId: {},
|
|
49
|
+
closeMatchAfterUndo: false,
|
|
50
|
+
lastMatchResultsByMatchId: {},
|
|
51
|
+
lastUndoResultsByMatchId: {},
|
|
47
52
|
hasValidState() {
|
|
48
53
|
return this.fetchState == 'Completed';
|
|
49
54
|
},
|
|
50
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
|
+
}
|
|
51
74
|
function resetMatchModalSearches(draft) {
|
|
52
75
|
draft.matchModal.transactionSearch = initialTransactionSearchState;
|
|
53
76
|
draft.matchModal.accrualSearch = initialAccrualSearchState;
|
|
@@ -57,6 +80,7 @@ function resetMatchModalSearches(draft) {
|
|
|
57
80
|
};
|
|
58
81
|
draft.matchModal.lastBatchResults = undefined;
|
|
59
82
|
draft.matchModal.openedFromMatchId = undefined;
|
|
83
|
+
draft.matchModal.didPinPrefillTransactions = false;
|
|
60
84
|
draft.matchModal.pinnedTransactionResults = [];
|
|
61
85
|
}
|
|
62
86
|
const expenseAutomationActualMatchingView = createSlice({
|
|
@@ -81,6 +105,9 @@ const expenseAutomationActualMatchingView = createSlice({
|
|
|
81
105
|
const { refreshViewInBackground } = action.payload;
|
|
82
106
|
if (refreshViewInBackground) {
|
|
83
107
|
draft.refreshStatus = { fetchState: 'In-Progress', error: undefined };
|
|
108
|
+
if (draft.fetchState === 'Not-Started') {
|
|
109
|
+
draft.fetchState = 'In-Progress';
|
|
110
|
+
}
|
|
84
111
|
}
|
|
85
112
|
else {
|
|
86
113
|
draft.fetchState = 'In-Progress';
|
|
@@ -102,6 +129,10 @@ const expenseAutomationActualMatchingView = createSlice({
|
|
|
102
129
|
draft.matchIdsByPeriod[periodId] = matchIds;
|
|
103
130
|
if (refreshViewInBackground) {
|
|
104
131
|
draft.refreshStatus = { fetchState: 'Completed', error: undefined };
|
|
132
|
+
if (draft.fetchState !== 'Completed') {
|
|
133
|
+
draft.fetchState = 'Completed';
|
|
134
|
+
draft.error = undefined;
|
|
135
|
+
}
|
|
105
136
|
}
|
|
106
137
|
else {
|
|
107
138
|
draft.fetchState = 'Completed';
|
|
@@ -112,6 +143,10 @@ const expenseAutomationActualMatchingView = createSlice({
|
|
|
112
143
|
const { status, refreshViewInBackground } = action.payload;
|
|
113
144
|
if (refreshViewInBackground) {
|
|
114
145
|
draft.refreshStatus = { fetchState: 'Error', error: status };
|
|
146
|
+
if (draft.fetchState !== 'Completed') {
|
|
147
|
+
draft.fetchState = 'Error';
|
|
148
|
+
draft.error = status;
|
|
149
|
+
}
|
|
115
150
|
}
|
|
116
151
|
else {
|
|
117
152
|
draft.fetchState = 'Error';
|
|
@@ -197,10 +232,13 @@ const expenseAutomationActualMatchingView = createSlice({
|
|
|
197
232
|
}
|
|
198
233
|
},
|
|
199
234
|
},
|
|
235
|
+
// Dismiss (Cancel / overlay). Store reset matches `clearMatchNewModal`;
|
|
236
|
+
// the actions stay distinct so confirm-success vs user-dismiss stay observable.
|
|
200
237
|
closeMatchNewModal(draft) {
|
|
201
238
|
draft.matchModal.isOpen = false;
|
|
202
239
|
resetMatchModalSearches(draft);
|
|
203
240
|
},
|
|
241
|
+
// Confirm-success / host reset. Same slice write as close; different action type.
|
|
204
242
|
clearMatchNewModal(draft) {
|
|
205
243
|
draft.matchModal.isOpen = false;
|
|
206
244
|
resetMatchModalSearches(draft);
|
|
@@ -221,7 +259,7 @@ const expenseAutomationActualMatchingView = createSlice({
|
|
|
221
259
|
draft.matchModal.transactionSearch.error = undefined;
|
|
222
260
|
}
|
|
223
261
|
},
|
|
224
|
-
prepare(
|
|
262
|
+
prepare(query, pageToken, transactionType, transactionIntegrationId) {
|
|
225
263
|
return {
|
|
226
264
|
payload: {
|
|
227
265
|
query,
|
|
@@ -240,7 +278,7 @@ const expenseAutomationActualMatchingView = createSlice({
|
|
|
240
278
|
...results,
|
|
241
279
|
];
|
|
242
280
|
const byKey = new Map(merged.map((result) => [
|
|
243
|
-
|
|
281
|
+
actualMatchingTransactionLineKey(result.transactionType, result.transactionIntegrationId, result.lineId),
|
|
244
282
|
result,
|
|
245
283
|
]));
|
|
246
284
|
draft.matchModal.transactionSearch.results = Array.from(byKey.values());
|
|
@@ -248,7 +286,8 @@ const expenseAutomationActualMatchingView = createSlice({
|
|
|
248
286
|
else {
|
|
249
287
|
draft.matchModal.transactionSearch.results = results;
|
|
250
288
|
if (draft.matchModal.openedFromMatchId != null &&
|
|
251
|
-
draft.matchModal.
|
|
289
|
+
!draft.matchModal.didPinPrefillTransactions) {
|
|
290
|
+
draft.matchModal.didPinPrefillTransactions = true;
|
|
252
291
|
draft.matchModal.pinnedTransactionResults = results;
|
|
253
292
|
}
|
|
254
293
|
}
|
|
@@ -278,7 +317,7 @@ const expenseAutomationActualMatchingView = createSlice({
|
|
|
278
317
|
draft.matchModal.accrualSearch.error = undefined;
|
|
279
318
|
}
|
|
280
319
|
},
|
|
281
|
-
prepare(
|
|
320
|
+
prepare(query, pageToken) {
|
|
282
321
|
return { payload: { query, pageToken: pageToken ?? undefined } };
|
|
283
322
|
},
|
|
284
323
|
},
|
|
@@ -309,8 +348,17 @@ const expenseAutomationActualMatchingView = createSlice({
|
|
|
309
348
|
error: undefined,
|
|
310
349
|
};
|
|
311
350
|
},
|
|
312
|
-
prepare(
|
|
313
|
-
return {
|
|
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
|
+
};
|
|
314
362
|
},
|
|
315
363
|
},
|
|
316
364
|
matchAndReverseAccrualsSuccess(draft, action) {
|
|
@@ -319,6 +367,10 @@ const expenseAutomationActualMatchingView = createSlice({
|
|
|
319
367
|
error: undefined,
|
|
320
368
|
};
|
|
321
369
|
draft.matchModal.lastBatchResults = action.payload.results;
|
|
370
|
+
if (action.payload.matchId != null) {
|
|
371
|
+
draft.lastMatchResultsByMatchId[action.payload.matchId] =
|
|
372
|
+
action.payload.results;
|
|
373
|
+
}
|
|
322
374
|
},
|
|
323
375
|
matchAndReverseAccrualsFailure(draft, action) {
|
|
324
376
|
draft.matchModal.confirmStatus = {
|
|
@@ -327,25 +379,43 @@ const expenseAutomationActualMatchingView = createSlice({
|
|
|
327
379
|
};
|
|
328
380
|
},
|
|
329
381
|
undoReversalAccruals: {
|
|
330
|
-
reducer(draft) {
|
|
331
|
-
draft.
|
|
382
|
+
reducer(draft, action) {
|
|
383
|
+
draft.undoReversalStatusByMatchId[action.payload.matchId] = {
|
|
332
384
|
fetchState: 'In-Progress',
|
|
333
385
|
error: undefined,
|
|
334
386
|
};
|
|
335
387
|
},
|
|
336
|
-
prepare(
|
|
337
|
-
return { payload:
|
|
388
|
+
prepare(matchId, accrualIds) {
|
|
389
|
+
return { payload: { matchId, accrualIds } };
|
|
338
390
|
},
|
|
339
391
|
},
|
|
340
|
-
undoReversalAccrualsSuccess
|
|
341
|
-
draft
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
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;
|
|
346
416
|
},
|
|
347
417
|
undoReversalAccrualsFailure(draft, action) {
|
|
348
|
-
draft.
|
|
418
|
+
draft.undoReversalStatusByMatchId[action.payload.matchId] = {
|
|
349
419
|
fetchState: 'Error',
|
|
350
420
|
error: action.payload.status,
|
|
351
421
|
};
|
|
@@ -360,5 +430,5 @@ const expenseAutomationActualMatchingView = createSlice({
|
|
|
360
430
|
});
|
|
361
431
|
},
|
|
362
432
|
});
|
|
363
|
-
export const { 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;
|
|
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;
|
|
364
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
|
},
|