@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.
- 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 +107 -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 +107 -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,48 @@ 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
|
+
}
|
|
74
|
+
function pruneLastUndoResultsForMatched(draft, matchId, results) {
|
|
75
|
+
const matchedIds = new Set(results.map((result) => result.accrualId));
|
|
76
|
+
if (matchedIds.size === 0) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const existing = draft.lastUndoResultsByMatchId[matchId];
|
|
80
|
+
if (existing == null) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
const kept = existing.filter((result) => !matchedIds.has(result.accrualId));
|
|
84
|
+
if (kept.length === 0) {
|
|
85
|
+
delete draft.lastUndoResultsByMatchId[matchId];
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
draft.lastUndoResultsByMatchId[matchId] = kept;
|
|
89
|
+
}
|
|
51
90
|
function resetMatchModalSearches(draft) {
|
|
52
91
|
draft.matchModal.transactionSearch = initialTransactionSearchState;
|
|
53
92
|
draft.matchModal.accrualSearch = initialAccrualSearchState;
|
|
@@ -57,6 +96,7 @@ function resetMatchModalSearches(draft) {
|
|
|
57
96
|
};
|
|
58
97
|
draft.matchModal.lastBatchResults = undefined;
|
|
59
98
|
draft.matchModal.openedFromMatchId = undefined;
|
|
99
|
+
draft.matchModal.didPinPrefillTransactions = false;
|
|
60
100
|
draft.matchModal.pinnedTransactionResults = [];
|
|
61
101
|
}
|
|
62
102
|
const expenseAutomationActualMatchingView = createSlice({
|
|
@@ -81,6 +121,9 @@ const expenseAutomationActualMatchingView = createSlice({
|
|
|
81
121
|
const { refreshViewInBackground } = action.payload;
|
|
82
122
|
if (refreshViewInBackground) {
|
|
83
123
|
draft.refreshStatus = { fetchState: 'In-Progress', error: undefined };
|
|
124
|
+
if (draft.fetchState === 'Not-Started') {
|
|
125
|
+
draft.fetchState = 'In-Progress';
|
|
126
|
+
}
|
|
84
127
|
}
|
|
85
128
|
else {
|
|
86
129
|
draft.fetchState = 'In-Progress';
|
|
@@ -102,6 +145,10 @@ const expenseAutomationActualMatchingView = createSlice({
|
|
|
102
145
|
draft.matchIdsByPeriod[periodId] = matchIds;
|
|
103
146
|
if (refreshViewInBackground) {
|
|
104
147
|
draft.refreshStatus = { fetchState: 'Completed', error: undefined };
|
|
148
|
+
if (draft.fetchState !== 'Completed') {
|
|
149
|
+
draft.fetchState = 'Completed';
|
|
150
|
+
draft.error = undefined;
|
|
151
|
+
}
|
|
105
152
|
}
|
|
106
153
|
else {
|
|
107
154
|
draft.fetchState = 'Completed';
|
|
@@ -112,6 +159,10 @@ const expenseAutomationActualMatchingView = createSlice({
|
|
|
112
159
|
const { status, refreshViewInBackground } = action.payload;
|
|
113
160
|
if (refreshViewInBackground) {
|
|
114
161
|
draft.refreshStatus = { fetchState: 'Error', error: status };
|
|
162
|
+
if (draft.fetchState !== 'Completed') {
|
|
163
|
+
draft.fetchState = 'Error';
|
|
164
|
+
draft.error = status;
|
|
165
|
+
}
|
|
115
166
|
}
|
|
116
167
|
else {
|
|
117
168
|
draft.fetchState = 'Error';
|
|
@@ -197,10 +248,13 @@ const expenseAutomationActualMatchingView = createSlice({
|
|
|
197
248
|
}
|
|
198
249
|
},
|
|
199
250
|
},
|
|
251
|
+
// Dismiss (Cancel / overlay). Store reset matches `clearMatchNewModal`;
|
|
252
|
+
// the actions stay distinct so confirm-success vs user-dismiss stay observable.
|
|
200
253
|
closeMatchNewModal(draft) {
|
|
201
254
|
draft.matchModal.isOpen = false;
|
|
202
255
|
resetMatchModalSearches(draft);
|
|
203
256
|
},
|
|
257
|
+
// Confirm-success / host reset. Same slice write as close; different action type.
|
|
204
258
|
clearMatchNewModal(draft) {
|
|
205
259
|
draft.matchModal.isOpen = false;
|
|
206
260
|
resetMatchModalSearches(draft);
|
|
@@ -221,7 +275,7 @@ const expenseAutomationActualMatchingView = createSlice({
|
|
|
221
275
|
draft.matchModal.transactionSearch.error = undefined;
|
|
222
276
|
}
|
|
223
277
|
},
|
|
224
|
-
prepare(
|
|
278
|
+
prepare(query, pageToken, transactionType, transactionIntegrationId) {
|
|
225
279
|
return {
|
|
226
280
|
payload: {
|
|
227
281
|
query,
|
|
@@ -240,7 +294,7 @@ const expenseAutomationActualMatchingView = createSlice({
|
|
|
240
294
|
...results,
|
|
241
295
|
];
|
|
242
296
|
const byKey = new Map(merged.map((result) => [
|
|
243
|
-
|
|
297
|
+
actualMatchingTransactionLineKey(result.transactionType, result.transactionIntegrationId, result.lineId),
|
|
244
298
|
result,
|
|
245
299
|
]));
|
|
246
300
|
draft.matchModal.transactionSearch.results = Array.from(byKey.values());
|
|
@@ -248,7 +302,8 @@ const expenseAutomationActualMatchingView = createSlice({
|
|
|
248
302
|
else {
|
|
249
303
|
draft.matchModal.transactionSearch.results = results;
|
|
250
304
|
if (draft.matchModal.openedFromMatchId != null &&
|
|
251
|
-
draft.matchModal.
|
|
305
|
+
!draft.matchModal.didPinPrefillTransactions) {
|
|
306
|
+
draft.matchModal.didPinPrefillTransactions = true;
|
|
252
307
|
draft.matchModal.pinnedTransactionResults = results;
|
|
253
308
|
}
|
|
254
309
|
}
|
|
@@ -278,7 +333,7 @@ const expenseAutomationActualMatchingView = createSlice({
|
|
|
278
333
|
draft.matchModal.accrualSearch.error = undefined;
|
|
279
334
|
}
|
|
280
335
|
},
|
|
281
|
-
prepare(
|
|
336
|
+
prepare(query, pageToken) {
|
|
282
337
|
return { payload: { query, pageToken: pageToken ?? undefined } };
|
|
283
338
|
},
|
|
284
339
|
},
|
|
@@ -309,8 +364,17 @@ const expenseAutomationActualMatchingView = createSlice({
|
|
|
309
364
|
error: undefined,
|
|
310
365
|
};
|
|
311
366
|
},
|
|
312
|
-
prepare(
|
|
313
|
-
return {
|
|
367
|
+
prepare(transactionId, transactionIntegrationId, transactionType, lineIds, transactionSyncToken, accrualIds) {
|
|
368
|
+
return {
|
|
369
|
+
payload: {
|
|
370
|
+
transactionId,
|
|
371
|
+
transactionIntegrationId,
|
|
372
|
+
transactionType,
|
|
373
|
+
lineIds,
|
|
374
|
+
transactionSyncToken,
|
|
375
|
+
accrualIds,
|
|
376
|
+
},
|
|
377
|
+
};
|
|
314
378
|
},
|
|
315
379
|
},
|
|
316
380
|
matchAndReverseAccrualsSuccess(draft, action) {
|
|
@@ -319,6 +383,11 @@ const expenseAutomationActualMatchingView = createSlice({
|
|
|
319
383
|
error: undefined,
|
|
320
384
|
};
|
|
321
385
|
draft.matchModal.lastBatchResults = action.payload.results;
|
|
386
|
+
if (action.payload.matchId != null) {
|
|
387
|
+
draft.lastMatchResultsByMatchId[action.payload.matchId] =
|
|
388
|
+
action.payload.results;
|
|
389
|
+
pruneLastUndoResultsForMatched(draft, action.payload.matchId, action.payload.results);
|
|
390
|
+
}
|
|
322
391
|
},
|
|
323
392
|
matchAndReverseAccrualsFailure(draft, action) {
|
|
324
393
|
draft.matchModal.confirmStatus = {
|
|
@@ -327,25 +396,43 @@ const expenseAutomationActualMatchingView = createSlice({
|
|
|
327
396
|
};
|
|
328
397
|
},
|
|
329
398
|
undoReversalAccruals: {
|
|
330
|
-
reducer(draft) {
|
|
331
|
-
draft.
|
|
399
|
+
reducer(draft, action) {
|
|
400
|
+
draft.undoReversalStatusByMatchId[action.payload.matchId] = {
|
|
332
401
|
fetchState: 'In-Progress',
|
|
333
402
|
error: undefined,
|
|
334
403
|
};
|
|
335
404
|
},
|
|
336
|
-
prepare(
|
|
337
|
-
return { payload:
|
|
405
|
+
prepare(matchId, accrualIds) {
|
|
406
|
+
return { payload: { matchId, accrualIds } };
|
|
338
407
|
},
|
|
339
408
|
},
|
|
340
|
-
undoReversalAccrualsSuccess
|
|
341
|
-
draft
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
409
|
+
undoReversalAccrualsSuccess: {
|
|
410
|
+
reducer(draft, action) {
|
|
411
|
+
const { matchId, results } = action.payload;
|
|
412
|
+
draft.undoReversalStatusByMatchId[matchId] = {
|
|
413
|
+
fetchState: 'Completed',
|
|
414
|
+
error: undefined,
|
|
415
|
+
};
|
|
416
|
+
const allUndone = results.length > 0 &&
|
|
417
|
+
results.every((result) => result.status === 'undone');
|
|
418
|
+
draft.closeMatchAfterUndo = allUndone;
|
|
419
|
+
pruneLastMatchResultsForUndone(draft, matchId, results);
|
|
420
|
+
if (allUndone) {
|
|
421
|
+
delete draft.lastUndoResultsByMatchId[matchId];
|
|
422
|
+
}
|
|
423
|
+
else {
|
|
424
|
+
draft.lastUndoResultsByMatchId[matchId] = results;
|
|
425
|
+
}
|
|
426
|
+
},
|
|
427
|
+
prepare(matchId, results) {
|
|
428
|
+
return { payload: { matchId, results } };
|
|
429
|
+
},
|
|
430
|
+
},
|
|
431
|
+
ackCloseMatchAfterUndo(draft) {
|
|
432
|
+
draft.closeMatchAfterUndo = false;
|
|
346
433
|
},
|
|
347
434
|
undoReversalAccrualsFailure(draft, action) {
|
|
348
|
-
draft.
|
|
435
|
+
draft.undoReversalStatusByMatchId[action.payload.matchId] = {
|
|
349
436
|
fetchState: 'Error',
|
|
350
437
|
error: action.payload.status,
|
|
351
438
|
};
|
|
@@ -360,5 +447,5 @@ const expenseAutomationActualMatchingView = createSlice({
|
|
|
360
447
|
});
|
|
361
448
|
},
|
|
362
449
|
});
|
|
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;
|
|
450
|
+
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
451
|
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
|
},
|