@zeniai/client-epic-state 5.2.11-betaRD1 → 5.2.11-betaRD3

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 (61) hide show
  1. package/lib/entity/jeSchedules/jeSchedulesPayload.js +3 -0
  2. package/lib/esm/entity/jeSchedules/jeSchedulesPayload.js +3 -0
  3. package/lib/esm/view/expenseAutomationView/epics/jeSchedule/fetchJeSchedulePageEpic.js +9 -1
  4. package/lib/esm/view/expenseAutomationView/epics/jeSchedule/newSchedule/createOneTimeAccrualEpic.js +7 -2
  5. package/lib/esm/view/expenseAutomationView/epics/jeSchedule/newSchedule/saveNewScheduleEpic.js +31 -5
  6. package/lib/esm/view/expenseAutomationView/epics/jeSchedule/oneTimeAccrualActionsEpic.js +33 -15
  7. package/lib/esm/view/expenseAutomationView/epics/jeSchedule/revertJeScheduleEpic.js +7 -2
  8. package/lib/esm/view/expenseAutomationView/epics/jeSchedule/switchJESchedulesTabEpic.js +6 -0
  9. package/lib/esm/view/expenseAutomationView/reducers/jeSchedulesViewReducer.js +8 -0
  10. package/lib/esm/view/expenseAutomationView/selectors/jeAccountSettingsViewSelector.js +1 -0
  11. package/lib/view/expenseAutomationView/epics/jeSchedule/fetchJeSchedulePageEpic.js +9 -1
  12. package/lib/view/expenseAutomationView/epics/jeSchedule/newSchedule/createOneTimeAccrualEpic.d.ts +2 -1
  13. package/lib/view/expenseAutomationView/epics/jeSchedule/newSchedule/createOneTimeAccrualEpic.js +6 -1
  14. package/lib/view/expenseAutomationView/epics/jeSchedule/newSchedule/saveNewScheduleEpic.d.ts +4 -40
  15. package/lib/view/expenseAutomationView/epics/jeSchedule/newSchedule/saveNewScheduleEpic.js +29 -3
  16. package/lib/view/expenseAutomationView/epics/jeSchedule/oneTimeAccrualActionsEpic.d.ts +2 -1
  17. package/lib/view/expenseAutomationView/epics/jeSchedule/oneTimeAccrualActionsEpic.js +32 -14
  18. package/lib/view/expenseAutomationView/epics/jeSchedule/revertJeScheduleEpic.d.ts +2 -1
  19. package/lib/view/expenseAutomationView/epics/jeSchedule/revertJeScheduleEpic.js +6 -1
  20. package/lib/view/expenseAutomationView/epics/jeSchedule/switchJESchedulesTabEpic.js +6 -0
  21. package/lib/view/expenseAutomationView/reducers/jeSchedulesViewReducer.js +8 -0
  22. package/lib/view/expenseAutomationView/selectorTypes/jeAccountSettingsViewSelectorTypes.d.ts +1 -0
  23. package/lib/view/expenseAutomationView/selectors/jeAccountSettingsViewSelector.js +1 -0
  24. package/lib/view/expenseAutomationView/types/jeSchedulesViewState.d.ts +1 -0
  25. package/package.json +1 -1
  26. package/lib/commonStateTypes/selectorTypes/pickAndMergeForecastSection.d.ts +0 -0
  27. package/lib/commonStateTypes/selectorTypes/pickAndMergeForecastSection.js +0 -1
  28. package/lib/entity/invoicing/coupon/couponConstants.d.ts +0 -11
  29. package/lib/entity/invoicing/coupon/couponConstants.js +0 -29
  30. package/lib/esm/commonStateTypes/selectorTypes/pickAndMergeForecastSection.js +0 -1
  31. package/lib/esm/entity/invoicing/coupon/couponConstants.js +0 -26
  32. package/lib/esm/view/invoicing/invoicingQboView/epics/fetchInvoicingQboAccountsEpic.js +0 -25
  33. package/lib/esm/view/invoicing/invoicingQboView/epics/fetchInvoicingQboClassesEpic.js +0 -15
  34. package/lib/esm/view/spendManagement/treasury/treasurySetUp/epic/updateTreasuryPromoIntroClosedByOutsideClickEpic.js +0 -18
  35. package/lib/esm/view/spendManagement/treasury/treasurySetUp/epic/updateTreasuryPromoRemindMeLaterClickedEpic.js +0 -18
  36. package/lib/esm/view/spendManagement/treasury/treasurySetUp/epic/updateTreasuryVideoViewedEpic.js +0 -18
  37. package/lib/esm/view/zeniAccountsPromoCard/zeniAccountsPromoCardEpic.js +0 -16
  38. package/lib/esm/view/zeniAccountsPromoCard/zeniAccountsPromoCardPayload.js +0 -1
  39. package/lib/esm/view/zeniAccountsPromoCard/zeniAccountsPromoCardReducer.js +0 -60
  40. package/lib/esm/view/zeniAccountsPromoCard/zeniAccountsPromoCardSelector.js +0 -12
  41. package/lib/esm/view/zeniAccountsPromoCard/zeniAccountsPromoCardState.js +0 -1
  42. package/lib/view/invoicing/invoicingQboView/epics/fetchInvoicingQboAccountsEpic.d.ts +0 -6
  43. package/lib/view/invoicing/invoicingQboView/epics/fetchInvoicingQboAccountsEpic.js +0 -29
  44. package/lib/view/invoicing/invoicingQboView/epics/fetchInvoicingQboClassesEpic.d.ts +0 -6
  45. package/lib/view/invoicing/invoicingQboView/epics/fetchInvoicingQboClassesEpic.js +0 -19
  46. package/lib/view/spendManagement/treasury/treasurySetUp/epic/updateTreasuryPromoIntroClosedByOutsideClickEpic.d.ts +0 -8
  47. package/lib/view/spendManagement/treasury/treasurySetUp/epic/updateTreasuryPromoIntroClosedByOutsideClickEpic.js +0 -22
  48. package/lib/view/spendManagement/treasury/treasurySetUp/epic/updateTreasuryPromoRemindMeLaterClickedEpic.d.ts +0 -8
  49. package/lib/view/spendManagement/treasury/treasurySetUp/epic/updateTreasuryPromoRemindMeLaterClickedEpic.js +0 -22
  50. package/lib/view/spendManagement/treasury/treasurySetUp/epic/updateTreasuryVideoViewedEpic.d.ts +0 -8
  51. package/lib/view/spendManagement/treasury/treasurySetUp/epic/updateTreasuryVideoViewedEpic.js +0 -22
  52. package/lib/view/zeniAccountsPromoCard/zeniAccountsPromoCardEpic.d.ts +0 -13
  53. package/lib/view/zeniAccountsPromoCard/zeniAccountsPromoCardEpic.js +0 -20
  54. package/lib/view/zeniAccountsPromoCard/zeniAccountsPromoCardPayload.d.ts +0 -11
  55. package/lib/view/zeniAccountsPromoCard/zeniAccountsPromoCardPayload.js +0 -2
  56. package/lib/view/zeniAccountsPromoCard/zeniAccountsPromoCardReducer.d.ts +0 -7
  57. package/lib/view/zeniAccountsPromoCard/zeniAccountsPromoCardReducer.js +0 -64
  58. package/lib/view/zeniAccountsPromoCard/zeniAccountsPromoCardSelector.d.ts +0 -12
  59. package/lib/view/zeniAccountsPromoCard/zeniAccountsPromoCardSelector.js +0 -15
  60. package/lib/view/zeniAccountsPromoCard/zeniAccountsPromoCardState.d.ts +0 -9
  61. package/lib/view/zeniAccountsPromoCard/zeniAccountsPromoCardState.js +0 -2
@@ -329,6 +329,9 @@ function toJEAccruedScheduledTransaction(payload) {
329
329
  endDate: payload.end_date != null ? (0, zeniDayJS_1.date)(payload.end_date) : undefined,
330
330
  period: payload.period != null ? Number(payload.period) : undefined,
331
331
  updatedByUserID: payload.updated_by.user_id,
332
+ // The accrued joiner resolves createdByUserID into the "Created by" cell. Without this the
333
+ // payload's created_by was dropped here and every accrued row rendered a hyphen.
334
+ createdByUserID: payload.created_by?.user_id ?? undefined,
332
335
  balanceAsOfToday: payload.balance ?? undefined,
333
336
  updatedAt: payload.updated_at != null ? (0, zeniDayJS_1.date)(payload.updated_at) : undefined,
334
337
  jeCredit: {
@@ -318,6 +318,9 @@ export function toJEAccruedScheduledTransaction(payload) {
318
318
  endDate: payload.end_date != null ? date(payload.end_date) : undefined,
319
319
  period: payload.period != null ? Number(payload.period) : undefined,
320
320
  updatedByUserID: payload.updated_by.user_id,
321
+ // The accrued joiner resolves createdByUserID into the "Created by" cell. Without this the
322
+ // payload's created_by was dropped here and every accrued row rendered a hyphen.
323
+ createdByUserID: payload.created_by?.user_id ?? undefined,
321
324
  balanceAsOfToday: payload.balance ?? undefined,
322
325
  updatedAt: payload.updated_at != null ? date(payload.updated_at) : undefined,
323
326
  jeCredit: {
@@ -14,6 +14,14 @@ export const fetchJeSchedulesPageEpic = (actions$, state$) => actions$.pipe(filt
14
14
  jeSchedulePageActions.push(fetchAccountList(accountType.accountListKey, true, false, false, false, accountType.allowedAccountList));
15
15
  }
16
16
  jeSchedulePageActions.push(fetchJeSchedules(period, refreshViewInBackground));
17
- jeSchedulePageActions.push(initializeAccountSettingsView());
17
+ // Only on the first page load. Account settings cannot change from a tab switch or a
18
+ // refresh, and re-initializing puts accountSettingsView back to 'In-Progress' -- which the
19
+ // page reads as "still loading" and answers with a full skeleton over rows it already has.
20
+ // The action also resets localData, so re-dispatching it blindly would empty the settings
21
+ // form until the refetch lands. Saving account settings still re-initializes explicitly.
22
+ if (!state$.value.expenseAutomationJESchedulesViewState.accountSettings
23
+ .hasLoadedAccountSettings) {
24
+ jeSchedulePageActions.push(initializeAccountSettingsView());
25
+ }
18
26
  return concat(from(jeSchedulePageActions));
19
27
  }));
@@ -1,9 +1,10 @@
1
1
  import { from } from 'rxjs';
2
- import { catchError, filter, mergeMap } from 'rxjs/operators';
2
+ import { catchError, filter, mergeMap, takeUntil } from 'rxjs/operators';
3
3
  import { convertToPeriod, toAbsoluteDay, } from '../../../../../commonStateTypes/timePeriod';
4
4
  import { updateJEOneTimeAccruals } from '../../../../../entity/jeSchedules/jeSchedulesReducer';
5
5
  import { openSnackbar } from '../../../../../entity/snackbar/snackbarReducer';
6
6
  import { getCurrentTenant } from '../../../../../entity/tenant/tenantSelector';
7
+ import { clearAll } from '../../../../../rootActions';
7
8
  import { createZeniAPIStatus, isSuccessResponse, } from '../../../../../responsePayload';
8
9
  import { prepareCreateOneTimeAccrualPayload, } from '../../../payload/jeSchedulesPayload';
9
10
  import { createOneTimeAccrual, createOneTimeAccrualFailure, createOneTimeAccrualSuccess, fetchJeSchedules, } from '../../../reducers/jeSchedulesViewReducer';
@@ -95,5 +96,9 @@ export const createOneTimeAccrualEpic = (actions$, state$, zeniAPI) => actions$.
95
96
  createOneTimeAccrualFailure({
96
97
  status: createZeniAPIStatus('Unexpected Error', `Create One Time Accrual REST API call errored out ${JSON.stringify(error)}`),
97
98
  }),
98
- ])));
99
+ ])),
100
+ // A tenant switch clears the store while this request is still in flight.
101
+ // Without this the old tenant's response is written back into the freshly
102
+ // cleared store and stays on screen under the new tenant.
103
+ takeUntil(actions$.pipe(filter(clearAll.match))));
99
104
  }));
@@ -1,12 +1,15 @@
1
1
  import { from } from 'rxjs';
2
- import { catchError, filter, mergeMap } from 'rxjs/operators';
2
+ import { catchError, filter, mergeMap, takeUntil } from 'rxjs/operators';
3
+ import { convertToPeriod, toAbsoluteDay, } from '../../../../../commonStateTypes/timePeriod';
4
+ import { getCurrentTenant } from '../../../../../entity/tenant/tenantSelector';
5
+ import { clearAll } from '../../../../../rootActions';
3
6
  import { updateJESchedules, updateJESchedulesDetails, } from '../../../../../entity/jeSchedules/jeSchedulesReducer';
4
7
  import { getJEScheduleTransactionKey } from '../../../../../entity/jeSchedules/jeSchedulesTypes';
5
8
  import { openSnackbar } from '../../../../../entity/snackbar/snackbarReducer';
6
9
  import { getScheduleTransactionById } from '../../../../../entity/transaction/transactionSelector';
7
10
  import { createZeniAPIStatus, isSuccessResponse, } from '../../../../../responsePayload';
8
11
  import { prepareNewSchedulePayload, } from '../../../helpers/newScheduleLocalDataHelper';
9
- import { resetNewScheduleLocalData, saveNewSchedule, saveNewScheduleFailure, saveNewScheduleSuccess, } from '../../../reducers/jeSchedulesViewReducer';
12
+ import { fetchJeSchedules, resetNewScheduleLocalData, saveNewSchedule, saveNewScheduleFailure, saveNewScheduleSuccess, } from '../../../reducers/jeSchedulesViewReducer';
10
13
  const FAILURE_REASON_MESSAGES = {
11
14
  missing_third_party_id: 'This transaction is missing a QuickBooks reference and cannot be scheduled.',
12
15
  missing_vendor: 'Select a vendor before saving this schedule.',
@@ -58,11 +61,21 @@ export const saveNewScheduleEpic = (actions$, state$, zeniAPI) => actions$.pipe(
58
61
  je_schedule_type: scheduleType,
59
62
  };
60
63
  const payload = { ...result.payload };
64
+ // convertToPeriod destructures, so an unset period would throw here and take the epic
65
+ // subscription down for the whole session -- a missed refresh is the cheaper failure.
66
+ const selectedMonthYear = state.expenseAutomationViewState.selectedPeriodByTenantId[getCurrentTenant(state).tenantId];
67
+ const selectedPeriod = selectedMonthYear != null
68
+ ? convertToPeriod(selectedMonthYear)
69
+ : undefined;
70
+ const periodStart = selectedPeriod != null
71
+ ? toAbsoluteDay(selectedPeriod.start)
72
+ : undefined;
73
+ const periodEnd = selectedPeriod != null ? toAbsoluteDay(selectedPeriod.end) : undefined;
61
74
  return zeniAPI
62
75
  .postAndGetJSON(`${zeniAPI.apiEndPoints.accountMicroServiceBaseUrl}/1.0/schedules?query=${encodeURIComponent(JSON.stringify(queryParam))}`, payload)
63
76
  .pipe(mergeMap((response) => {
64
77
  if (isSuccessResponse(response) && response.data != null) {
65
- return from([
78
+ const successActions = [
66
79
  updateJESchedules(response.data.je_schedules),
67
80
  updateJESchedulesDetails(response.data.je_schedules, response.data.scheduled_journal_entries),
68
81
  resetNewScheduleLocalData({ scheduleType }),
@@ -75,7 +88,16 @@ export const saveNewScheduleEpic = (actions$, state$, zeniAPI) => actions$.pipe(
75
88
  { variableName: '_vendor_name_', variableValue: vendorName },
76
89
  ],
77
90
  }),
78
- ]);
91
+ ];
92
+ // Writing the entity is not enough: the list reads
93
+ // jeScheduleTransactionKeysByPeriodAndTab, which is rebuilt wholesale per fetch, so
94
+ // without this the new schedule stays invisible until some later fetch. The page
95
+ // only refetches when the save also changes tab, so saving from the tab the row
96
+ // lands on showed nothing. Same reasoning as createOneTimeAccrualEpic.
97
+ if (periodStart != null && periodEnd != null) {
98
+ successActions.push(fetchJeSchedules({ start: periodStart, end: periodEnd }, true));
99
+ }
100
+ return from(successActions);
79
101
  }
80
102
  return from([
81
103
  saveNewScheduleFailure({ status: response.status }),
@@ -106,5 +128,9 @@ export const saveNewScheduleEpic = (actions$, state$, zeniAPI) => actions$.pipe(
106
128
  saveNewScheduleFailure({
107
129
  status: createZeniAPIStatus('Unexpected Error', `Save New Schedule REST API call errored out ${JSON.stringify(error)}`),
108
130
  }),
109
- ])));
131
+ ])),
132
+ // A tenant switch clears the store while this request is still in flight.
133
+ // Without this the old tenant's response is written back into the freshly
134
+ // cleared store and stays on screen under the new tenant.
135
+ takeUntil(actions$.pipe(filter(clearAll.match))));
110
136
  }));
@@ -1,17 +1,22 @@
1
1
  import { from, merge } from 'rxjs';
2
- import { catchError, filter, mergeMap } from 'rxjs/operators';
2
+ import { catchError, filter, mergeMap, takeUntil } from 'rxjs/operators';
3
3
  import { convertToPeriod, toAbsoluteDay, } from '../../../../commonStateTypes/timePeriod';
4
4
  import { generateJEOneTimeAccrualKey } from '../../../../entity/jeSchedules/jeScheduleHelper';
5
5
  import { updateJEOneTimeAccruals } from '../../../../entity/jeSchedules/jeSchedulesReducer';
6
6
  import { getJEOneTimeAccrualByKey } from '../../../../entity/jeSchedules/jeSchedulesSelector';
7
7
  import { openSnackbar } from '../../../../entity/snackbar/snackbarReducer';
8
8
  import { getCurrentTenant } from '../../../../entity/tenant/tenantSelector';
9
+ import { clearAll } from '../../../../rootActions';
9
10
  import { createZeniAPIStatus, isSuccessResponse, } from '../../../../responsePayload';
10
11
  import { fetchJeSchedules, ignoreOneTimeAccrual, oneTimeAccrualActionFailure, oneTimeAccrualActionSuccess, promoteOneTimeAccrual, revertOneTimeAccrual, } from '../../reducers/jeSchedulesViewReducer';
11
12
  /**
12
13
  * The page the accrual came from, so a promoted or ignored row lands in the right bucket. Guarded
13
14
  * because convertToPeriod destructures: an unset period would throw and take the epic subscription
14
15
  * down for the session, and a missed refresh is the cheaper failure.
16
+ *
17
+ * Read state$.value at response time rather than the snapshot taken when the action was dispatched:
18
+ * if the user changes month while the request is in flight, the snapshot refreshes the month they
19
+ * have left and the month they are now looking at stays stale.
15
20
  */
16
21
  function refreshCurrentPage(state) {
17
22
  const selectedMonthYear = state.expenseAutomationViewState.selectedPeriodByTenantId[getCurrentTenant(state).tenantId];
@@ -26,14 +31,17 @@ function refreshCurrentPage(state) {
26
31
  }
27
32
  return [fetchJeSchedules({ start, end }, true)];
28
33
  }
29
- function failureActions(accrualId, message, status) {
34
+ function failureActions(
35
+ // Per action, not fixed: every failure used to read "Save schedule failed", so skipping or
36
+ // reverting an accrual reported an operation the user had not performed.
37
+ messageSection, accrualId, message, status) {
30
38
  return [
31
39
  oneTimeAccrualActionFailure({
32
40
  accrualId,
33
41
  status: status ?? createZeniAPIStatus('Unexpected Error', message),
34
42
  }),
35
43
  openSnackbar({
36
- messageSection: 'save_schedule',
44
+ messageSection,
37
45
  messageText: 'failed',
38
46
  type: 'error',
39
47
  variables: [{ variableName: '_api-error_', variableValue: message }],
@@ -47,7 +55,7 @@ export const promoteOneTimeAccrualEpic = (actions$, state$, zeniAPI) => actions$
47
55
  if (accrual?.jeDebitAccountId == null ||
48
56
  accrual.jeCreditAccountId == null) {
49
57
  // Promote posts a journal entry, so it cannot proceed without both sides of it.
50
- return from(failureActions(accrualId, 'This accrual is missing an expense or liability account.'));
58
+ return from(failureActions('save_schedule', accrualId, 'This accrual is missing an expense or liability account.'));
51
59
  }
52
60
  // vendor_id, vendor_name and service_month are deliberately absent: promote takes them from
53
61
  // the stored draft and ignores them in the body, so sending them would imply a reassignment
@@ -76,14 +84,17 @@ export const promoteOneTimeAccrualEpic = (actions$, state$, zeniAPI) => actions$
76
84
  },
77
85
  ],
78
86
  }),
79
- ...refreshCurrentPage(state),
87
+ ...refreshCurrentPage(state$.value),
80
88
  ]);
81
89
  }
82
- return from(failureActions(accrualId, response.status.message, response.status));
83
- }), catchError((error) => from(failureActions(accrualId, `Promote accrual REST API call errored out ${JSON.stringify(error)}`))));
90
+ return from(failureActions('save_schedule', accrualId, response.status.message, response.status));
91
+ }), catchError((error) => from(failureActions('save_schedule', accrualId, `Promote accrual REST API call errored out ${JSON.stringify(error)}`))),
92
+ // A tenant switch clears the store while this request is still in flight.
93
+ // Without this the old tenant's response is written back into the freshly
94
+ // cleared store and stays on screen under the new tenant.
95
+ takeUntil(actions$.pipe(filter(clearAll.match))));
84
96
  }));
85
97
  export const ignoreOneTimeAccrualEpic = (actions$, state$, zeniAPI) => actions$.pipe(filter(ignoreOneTimeAccrual.match), mergeMap((action) => {
86
- const state = state$.value;
87
98
  const { accrualId, reason, note } = action.payload;
88
99
  // `scope` is deliberately never sent: the API returns 400 on it, which is why the design's
89
100
  // "Apply to" selector cannot be built yet.
@@ -103,14 +114,17 @@ export const ignoreOneTimeAccrualEpic = (actions$, state$, zeniAPI) => actions$.
103
114
  messageText: 'success',
104
115
  type: 'success',
105
116
  }),
106
- ...refreshCurrentPage(state),
117
+ ...refreshCurrentPage(state$.value),
107
118
  ]);
108
119
  }
109
- return from(failureActions(accrualId, response.status.message, response.status));
110
- }), catchError((error) => from(failureActions(accrualId, `Ignore accrual REST API call errored out ${JSON.stringify(error)}`))));
120
+ return from(failureActions('ignore_accrual', accrualId, response.status.message, response.status));
121
+ }), catchError((error) => from(failureActions('ignore_accrual', accrualId, `Ignore accrual REST API call errored out ${JSON.stringify(error)}`))),
122
+ // A tenant switch clears the store while this request is still in flight.
123
+ // Without this the old tenant's response is written back into the freshly
124
+ // cleared store and stays on screen under the new tenant.
125
+ takeUntil(actions$.pipe(filter(clearAll.match))));
111
126
  }));
112
127
  export const revertOneTimeAccrualEpic = (actions$, state$, zeniAPI) => actions$.pipe(filter(revertOneTimeAccrual.match), mergeMap((action) => {
113
- const state = state$.value;
114
128
  const { accrualId, reason, note } = action.payload;
115
129
  // Deletes the accrual's journal entry in QBO and soft-deletes the row. `scope` is never sent:
116
130
  // the endpoint 400s on it, exactly as ignore does.
@@ -130,10 +144,14 @@ export const revertOneTimeAccrualEpic = (actions$, state$, zeniAPI) => actions$.
130
144
  messageText: 'success',
131
145
  type: 'success',
132
146
  }),
133
- ...refreshCurrentPage(state),
147
+ ...refreshCurrentPage(state$.value),
134
148
  ]);
135
149
  }
136
- return from(failureActions(accrualId, response.status.message, response.status));
137
- }), catchError((error) => from(failureActions(accrualId, `Revert accrual REST API call errored out ${JSON.stringify(error)}`))));
150
+ return from(failureActions('revert_accrual', accrualId, response.status.message, response.status));
151
+ }), catchError((error) => from(failureActions('revert_accrual', accrualId, `Revert accrual REST API call errored out ${JSON.stringify(error)}`))),
152
+ // A tenant switch clears the store while this request is still in flight.
153
+ // Without this the old tenant's response is written back into the freshly
154
+ // cleared store and stays on screen under the new tenant.
155
+ takeUntil(actions$.pipe(filter(clearAll.match))));
138
156
  }));
139
157
  export const oneTimeAccrualActionsEpic = (actions$, state$, zeniAPI) => merge(promoteOneTimeAccrualEpic(actions$, state$, zeniAPI), ignoreOneTimeAccrualEpic(actions$, state$, zeniAPI), revertOneTimeAccrualEpic(actions$, state$, zeniAPI));
@@ -1,7 +1,8 @@
1
1
  import { from } from 'rxjs';
2
- import { catchError, filter, mergeMap } from 'rxjs/operators';
2
+ import { catchError, filter, mergeMap, takeUntil } from 'rxjs/operators';
3
3
  import { openSnackbar } from '../../../../entity/snackbar/snackbarReducer';
4
4
  import { getCurrentTenant } from '../../../../entity/tenant/tenantSelector';
5
+ import { clearAll } from '../../../../rootActions';
5
6
  import { createZeniAPIStatus, isSuccessResponse, } from '../../../../responsePayload';
6
7
  import { fetchMonthEndCloseChecks } from '../../../monthEndCloseChecksView/monthEndCloseChecksViewReducer';
7
8
  import { removeJeAccruedScheduleKey, removeJeScheduleTransactionKey, revertJeSchedule, revertJeScheduleFailure, revertJeScheduleSuccess, } from '../../reducers/jeSchedulesViewReducer';
@@ -102,5 +103,9 @@ export const revertJeScheduleEpic = (actions$, state$, zeniAPI) => actions$.pipe
102
103
  messageText: 'failed',
103
104
  type: 'error',
104
105
  }),
105
- ])));
106
+ ])),
107
+ // A tenant switch clears the store while this request is still in flight.
108
+ // Without this the old tenant's response is written back into the freshly
109
+ // cleared store and stays on screen under the new tenant.
110
+ takeUntil(actions$.pipe(filter(clearAll.match))));
106
111
  }));
@@ -12,6 +12,12 @@ export const switchJESchedulesTabEpic = (actions$, state$) => actions$.pipe(filt
12
12
  }
13
13
  const currentTenant = getCurrentTenant(state);
14
14
  const selectedPeriod = state.expenseAutomationViewState.selectedPeriodByTenantId[currentTenant.tenantId];
15
+ // convertToPeriod destructures, so an unset period throws and takes this epic's subscription
16
+ // down for the rest of the session -- every later tab switch would then stop refreshing. A
17
+ // skipped refresh is the cheaper failure, as in refreshCurrentPage.
18
+ if (selectedPeriod == null) {
19
+ return EMPTY;
20
+ }
15
21
  const period = convertToPeriod(selectedPeriod);
16
22
  const timePeriodStart = toAbsoluteDay(period.start);
17
23
  const timePeriodEnd = toAbsoluteDay(period.end);
@@ -25,6 +25,7 @@ export const accountSettingsInitialState = {
25
25
  error: undefined,
26
26
  fetchState: 'Not-Started',
27
27
  },
28
+ hasLoadedAccountSettings: false,
28
29
  recommendationFetchState: {
29
30
  error: undefined,
30
31
  fetchState: 'Not-Started',
@@ -171,6 +172,9 @@ const expenseAutomationJESchedulesView = createSlice({
171
172
  draft.jeScheduleTransactionKeysByPeriodAndTab[periodTabKey] = [];
172
173
  draft.jeScheduleAccruedKeysByPeriodAndTab[periodTabKey] = [];
173
174
  draft.jeScheduleOneTimeAccrualKeysByPeriodAndTab[periodTabKey] = [];
175
+ // Cleared with the rest: leaving it behind kept the previous fetch's resolve rows on an
176
+ // otherwise empty errored tab, which reads as "these are all the schedules there are".
177
+ draft.failedJeScheduleTransactionKeysByPeriodAndTab[periodTabKey] = [];
174
178
  }
175
179
  },
176
180
  retryJeSchedule(draft, action) {
@@ -426,6 +430,9 @@ const expenseAutomationJESchedulesView = createSlice({
426
430
  error: status,
427
431
  fetchState: 'Error',
428
432
  };
433
+ // Set on failure too: a tenant whose account list errors must still reach the page rather
434
+ // than sit under a skeleton forever.
435
+ draft.accountSettings.hasLoadedAccountSettings = true;
429
436
  },
430
437
  updateAccountSettingsListForAccountTypes(draft, action) {
431
438
  doUpdateAccountSettingsListForAccountTypes(draft.accountSettings, action.payload.accountPayload);
@@ -741,4 +748,5 @@ const doUpdateAccountSettingsListForAccountTypes = (draft, accountPayloadList) =
741
748
  error: undefined,
742
749
  fetchState: 'Completed',
743
750
  };
751
+ draft.hasLoadedAccountSettings = true;
744
752
  };
@@ -22,6 +22,7 @@ export const getJEAccountSettingsView = createSelector((state) => state.expenseA
22
22
  fixedAssetsAccountHierarchy: jeAccountMappingState.fixedAssetsNestedAccountListHierarchy,
23
23
  accruedExpensesAccountHierarchy: jeAccountMappingState.accruedExpensesNestedAccountListHierarchy,
24
24
  fetchState: fetchState.fetchState,
25
+ hasLoaded: jeAccountMappingState.hasLoadedAccountSettings,
25
26
  saveState,
26
27
  };
27
28
  });
@@ -17,7 +17,15 @@ const fetchJeSchedulesPageEpic = (actions$, state$) => actions$.pipe((0, operato
17
17
  jeSchedulePageActions.push((0, accountListReducer_1.fetchAccountList)(accountType.accountListKey, true, false, false, false, accountType.allowedAccountList));
18
18
  }
19
19
  jeSchedulePageActions.push((0, jeSchedulesViewReducer_1.fetchJeSchedules)(period, refreshViewInBackground));
20
- jeSchedulePageActions.push((0, jeSchedulesViewReducer_1.initializeAccountSettingsView)());
20
+ // Only on the first page load. Account settings cannot change from a tab switch or a
21
+ // refresh, and re-initializing puts accountSettingsView back to 'In-Progress' -- which the
22
+ // page reads as "still loading" and answers with a full skeleton over rows it already has.
23
+ // The action also resets localData, so re-dispatching it blindly would empty the settings
24
+ // form until the refetch lands. Saving account settings still re-initializes explicitly.
25
+ if (!state$.value.expenseAutomationJESchedulesViewState.accountSettings
26
+ .hasLoadedAccountSettings) {
27
+ jeSchedulePageActions.push((0, jeSchedulesViewReducer_1.initializeAccountSettingsView)());
28
+ }
21
29
  return (0, rxjs_1.concat)((0, rxjs_1.from)(jeSchedulePageActions));
22
30
  }));
23
31
  exports.fetchJeSchedulesPageEpic = fetchJeSchedulesPageEpic;
@@ -2,7 +2,8 @@ import { ActionsObservable, StateObservable } from 'redux-observable';
2
2
  import { updateJEOneTimeAccruals } from '../../../../../entity/jeSchedules/jeSchedulesReducer';
3
3
  import { openSnackbar } from '../../../../../entity/snackbar/snackbarReducer';
4
4
  import { RootState } from '../../../../../reducer';
5
+ import { clearAll } from '../../../../../rootActions';
5
6
  import { ZeniAPI } from '../../../../../zeniAPI';
6
7
  import { createOneTimeAccrual, createOneTimeAccrualFailure, createOneTimeAccrualSuccess, fetchJeSchedules } from '../../../reducers/jeSchedulesViewReducer';
7
- export type ActionType = ReturnType<typeof createOneTimeAccrual> | ReturnType<typeof createOneTimeAccrualSuccess> | ReturnType<typeof createOneTimeAccrualFailure> | ReturnType<typeof updateJEOneTimeAccruals> | ReturnType<typeof fetchJeSchedules> | ReturnType<typeof openSnackbar>;
8
+ export type ActionType = ReturnType<typeof clearAll> | ReturnType<typeof createOneTimeAccrual> | ReturnType<typeof createOneTimeAccrualSuccess> | ReturnType<typeof createOneTimeAccrualFailure> | ReturnType<typeof updateJEOneTimeAccruals> | ReturnType<typeof fetchJeSchedules> | ReturnType<typeof openSnackbar>;
8
9
  export declare const createOneTimeAccrualEpic: (actions$: ActionsObservable<ActionType>, state$: StateObservable<RootState>, zeniAPI: ZeniAPI) => import("rxjs").Observable<ActionType>;
@@ -7,6 +7,7 @@ const timePeriod_1 = require("../../../../../commonStateTypes/timePeriod");
7
7
  const jeSchedulesReducer_1 = require("../../../../../entity/jeSchedules/jeSchedulesReducer");
8
8
  const snackbarReducer_1 = require("../../../../../entity/snackbar/snackbarReducer");
9
9
  const tenantSelector_1 = require("../../../../../entity/tenant/tenantSelector");
10
+ const rootActions_1 = require("../../../../../rootActions");
10
11
  const responsePayload_1 = require("../../../../../responsePayload");
11
12
  const jeSchedulesPayload_1 = require("../../../payload/jeSchedulesPayload");
12
13
  const jeSchedulesViewReducer_1 = require("../../../reducers/jeSchedulesViewReducer");
@@ -98,6 +99,10 @@ const createOneTimeAccrualEpic = (actions$, state$, zeniAPI) => actions$.pipe((0
98
99
  (0, jeSchedulesViewReducer_1.createOneTimeAccrualFailure)({
99
100
  status: (0, responsePayload_1.createZeniAPIStatus)('Unexpected Error', `Create One Time Accrual REST API call errored out ${JSON.stringify(error)}`),
100
101
  }),
101
- ])));
102
+ ])),
103
+ // A tenant switch clears the store while this request is still in flight.
104
+ // Without this the old tenant's response is written back into the freshly
105
+ // cleared store and stays on screen under the new tenant.
106
+ (0, operators_1.takeUntil)(actions$.pipe((0, operators_1.filter)(rootActions_1.clearAll.match))));
102
107
  }));
103
108
  exports.createOneTimeAccrualEpic = createOneTimeAccrualEpic;
@@ -1,45 +1,9 @@
1
1
  import { ActionsObservable, StateObservable } from 'redux-observable';
2
+ import { clearAll } from '../../../../../rootActions';
2
3
  import { updateJESchedules, updateJESchedulesDetails } from '../../../../../entity/jeSchedules/jeSchedulesReducer';
3
4
  import { openSnackbar } from '../../../../../entity/snackbar/snackbarReducer';
4
5
  import { RootState } from '../../../../../reducer';
5
6
  import { ZeniAPI } from '../../../../../zeniAPI';
6
- import { resetNewScheduleLocalData, saveNewSchedule, saveNewScheduleFailure, saveNewScheduleSuccess } from '../../../reducers/jeSchedulesViewReducer';
7
- export type ActionType = ReturnType<typeof saveNewSchedule> | ReturnType<typeof saveNewScheduleSuccess> | ReturnType<typeof saveNewScheduleFailure> | ReturnType<typeof updateJESchedules> | ReturnType<typeof updateJESchedulesDetails> | ReturnType<typeof resetNewScheduleLocalData> | ReturnType<typeof openSnackbar>;
8
- export declare const saveNewScheduleEpic: (actions$: ActionsObservable<ActionType>, state$: StateObservable<RootState>, zeniAPI: ZeniAPI) => import("rxjs").Observable<{
9
- payload: {
10
- messageSection: import("../../../../..").SnackbarMessageSections;
11
- messageText: import("../../../../..").SnackbarMessageSectionTexts;
12
- type: import("../../../../..").SnackbarMessageType;
13
- showStatusIcon?: boolean;
14
- variables?: {
15
- variableName: string;
16
- variableValue: string;
17
- }[];
18
- };
19
- type: "snackbar/openSnackbar";
20
- } | {
21
- payload: {
22
- jeSchedulesTransactionsPayload: import("../../../../../entity/jeSchedules/jeSchedulesPayload").JEScheduledTransactionPayload[];
23
- };
24
- type: "jeSchedules/updateJESchedules";
25
- } | {
26
- payload: {
27
- jeSchedulesPayload: import("../../../../../entity/jeSchedules/jeSchedulesPayload").JEScheduledTransactionPayload[];
28
- scheduledJEntriesPayload: import("../../../../../entity/jeSchedules/jeSchedulesPayload").ScheduledJournalEntryPayload[] | undefined;
29
- updateType: import("../../../../..").UpdateType;
30
- };
31
- type: "jeSchedules/updateJESchedulesDetails";
32
- } | {
33
- payload: {
34
- scheduleType: "prepaid_expenses" | "fixed_assets";
35
- };
36
- type: "expenseAutomationJESchedulesView/resetNewScheduleLocalData";
37
- } | {
38
- payload: undefined;
39
- type: "expenseAutomationJESchedulesView/saveNewScheduleSuccess";
40
- } | {
41
- payload: {
42
- status: import("../../../../../zeniAPIStatus").ZeniAPIStatus;
43
- };
44
- type: "expenseAutomationJESchedulesView/saveNewScheduleFailure";
45
- }>;
7
+ import { fetchJeSchedules, resetNewScheduleLocalData, saveNewSchedule, saveNewScheduleFailure, saveNewScheduleSuccess } from '../../../reducers/jeSchedulesViewReducer';
8
+ export type ActionType = ReturnType<typeof saveNewSchedule> | ReturnType<typeof saveNewScheduleSuccess> | ReturnType<typeof saveNewScheduleFailure> | ReturnType<typeof updateJESchedules> | ReturnType<typeof updateJESchedulesDetails> | ReturnType<typeof resetNewScheduleLocalData> | ReturnType<typeof fetchJeSchedules> | ReturnType<typeof clearAll> | ReturnType<typeof openSnackbar>;
9
+ export declare const saveNewScheduleEpic: (actions$: ActionsObservable<ActionType>, state$: StateObservable<RootState>, zeniAPI: ZeniAPI) => import("rxjs").Observable<ActionType>;
@@ -3,6 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.saveNewScheduleEpic = void 0;
4
4
  const rxjs_1 = require("rxjs");
5
5
  const operators_1 = require("rxjs/operators");
6
+ const timePeriod_1 = require("../../../../../commonStateTypes/timePeriod");
7
+ const tenantSelector_1 = require("../../../../../entity/tenant/tenantSelector");
8
+ const rootActions_1 = require("../../../../../rootActions");
6
9
  const jeSchedulesReducer_1 = require("../../../../../entity/jeSchedules/jeSchedulesReducer");
7
10
  const jeSchedulesTypes_1 = require("../../../../../entity/jeSchedules/jeSchedulesTypes");
8
11
  const snackbarReducer_1 = require("../../../../../entity/snackbar/snackbarReducer");
@@ -61,11 +64,21 @@ const saveNewScheduleEpic = (actions$, state$, zeniAPI) => actions$.pipe((0, ope
61
64
  je_schedule_type: scheduleType,
62
65
  };
63
66
  const payload = { ...result.payload };
67
+ // convertToPeriod destructures, so an unset period would throw here and take the epic
68
+ // subscription down for the whole session -- a missed refresh is the cheaper failure.
69
+ const selectedMonthYear = state.expenseAutomationViewState.selectedPeriodByTenantId[(0, tenantSelector_1.getCurrentTenant)(state).tenantId];
70
+ const selectedPeriod = selectedMonthYear != null
71
+ ? (0, timePeriod_1.convertToPeriod)(selectedMonthYear)
72
+ : undefined;
73
+ const periodStart = selectedPeriod != null
74
+ ? (0, timePeriod_1.toAbsoluteDay)(selectedPeriod.start)
75
+ : undefined;
76
+ const periodEnd = selectedPeriod != null ? (0, timePeriod_1.toAbsoluteDay)(selectedPeriod.end) : undefined;
64
77
  return zeniAPI
65
78
  .postAndGetJSON(`${zeniAPI.apiEndPoints.accountMicroServiceBaseUrl}/1.0/schedules?query=${encodeURIComponent(JSON.stringify(queryParam))}`, payload)
66
79
  .pipe((0, operators_1.mergeMap)((response) => {
67
80
  if ((0, responsePayload_1.isSuccessResponse)(response) && response.data != null) {
68
- return (0, rxjs_1.from)([
81
+ const successActions = [
69
82
  (0, jeSchedulesReducer_1.updateJESchedules)(response.data.je_schedules),
70
83
  (0, jeSchedulesReducer_1.updateJESchedulesDetails)(response.data.je_schedules, response.data.scheduled_journal_entries),
71
84
  (0, jeSchedulesViewReducer_1.resetNewScheduleLocalData)({ scheduleType }),
@@ -78,7 +91,16 @@ const saveNewScheduleEpic = (actions$, state$, zeniAPI) => actions$.pipe((0, ope
78
91
  { variableName: '_vendor_name_', variableValue: vendorName },
79
92
  ],
80
93
  }),
81
- ]);
94
+ ];
95
+ // Writing the entity is not enough: the list reads
96
+ // jeScheduleTransactionKeysByPeriodAndTab, which is rebuilt wholesale per fetch, so
97
+ // without this the new schedule stays invisible until some later fetch. The page
98
+ // only refetches when the save also changes tab, so saving from the tab the row
99
+ // lands on showed nothing. Same reasoning as createOneTimeAccrualEpic.
100
+ if (periodStart != null && periodEnd != null) {
101
+ successActions.push((0, jeSchedulesViewReducer_1.fetchJeSchedules)({ start: periodStart, end: periodEnd }, true));
102
+ }
103
+ return (0, rxjs_1.from)(successActions);
82
104
  }
83
105
  return (0, rxjs_1.from)([
84
106
  (0, jeSchedulesViewReducer_1.saveNewScheduleFailure)({ status: response.status }),
@@ -109,6 +131,10 @@ const saveNewScheduleEpic = (actions$, state$, zeniAPI) => actions$.pipe((0, ope
109
131
  (0, jeSchedulesViewReducer_1.saveNewScheduleFailure)({
110
132
  status: (0, responsePayload_1.createZeniAPIStatus)('Unexpected Error', `Save New Schedule REST API call errored out ${JSON.stringify(error)}`),
111
133
  }),
112
- ])));
134
+ ])),
135
+ // A tenant switch clears the store while this request is still in flight.
136
+ // Without this the old tenant's response is written back into the freshly
137
+ // cleared store and stays on screen under the new tenant.
138
+ (0, operators_1.takeUntil)(actions$.pipe((0, operators_1.filter)(rootActions_1.clearAll.match))));
113
139
  }));
114
140
  exports.saveNewScheduleEpic = saveNewScheduleEpic;
@@ -3,9 +3,10 @@ import { Observable } from 'rxjs';
3
3
  import { updateJEOneTimeAccruals } from '../../../../entity/jeSchedules/jeSchedulesReducer';
4
4
  import { openSnackbar } from '../../../../entity/snackbar/snackbarReducer';
5
5
  import { RootState } from '../../../../reducer';
6
+ import { clearAll } from '../../../../rootActions';
6
7
  import { ZeniAPI } from '../../../../zeniAPI';
7
8
  import { fetchJeSchedules, ignoreOneTimeAccrual, oneTimeAccrualActionFailure, oneTimeAccrualActionSuccess, promoteOneTimeAccrual, revertOneTimeAccrual } from '../../reducers/jeSchedulesViewReducer';
8
- export type ActionType = ReturnType<typeof promoteOneTimeAccrual> | ReturnType<typeof revertOneTimeAccrual> | ReturnType<typeof ignoreOneTimeAccrual> | ReturnType<typeof oneTimeAccrualActionSuccess> | ReturnType<typeof oneTimeAccrualActionFailure> | ReturnType<typeof updateJEOneTimeAccruals> | ReturnType<typeof fetchJeSchedules> | ReturnType<typeof openSnackbar>;
9
+ export type ActionType = ReturnType<typeof clearAll> | ReturnType<typeof promoteOneTimeAccrual> | ReturnType<typeof revertOneTimeAccrual> | ReturnType<typeof ignoreOneTimeAccrual> | ReturnType<typeof oneTimeAccrualActionSuccess> | ReturnType<typeof oneTimeAccrualActionFailure> | ReturnType<typeof updateJEOneTimeAccruals> | ReturnType<typeof fetchJeSchedules> | ReturnType<typeof openSnackbar>;
9
10
  export declare const promoteOneTimeAccrualEpic: (actions$: ActionsObservable<ActionType>, state$: StateObservable<RootState>, zeniAPI: ZeniAPI) => Observable<ActionType>;
10
11
  export declare const ignoreOneTimeAccrualEpic: (actions$: ActionsObservable<ActionType>, state$: StateObservable<RootState>, zeniAPI: ZeniAPI) => Observable<ActionType>;
11
12
  export declare const revertOneTimeAccrualEpic: (actions$: ActionsObservable<ActionType>, state$: StateObservable<RootState>, zeniAPI: ZeniAPI) => Observable<ActionType>;
@@ -9,12 +9,17 @@ const jeSchedulesReducer_1 = require("../../../../entity/jeSchedules/jeSchedules
9
9
  const jeSchedulesSelector_1 = require("../../../../entity/jeSchedules/jeSchedulesSelector");
10
10
  const snackbarReducer_1 = require("../../../../entity/snackbar/snackbarReducer");
11
11
  const tenantSelector_1 = require("../../../../entity/tenant/tenantSelector");
12
+ const rootActions_1 = require("../../../../rootActions");
12
13
  const responsePayload_1 = require("../../../../responsePayload");
13
14
  const jeSchedulesViewReducer_1 = require("../../reducers/jeSchedulesViewReducer");
14
15
  /**
15
16
  * The page the accrual came from, so a promoted or ignored row lands in the right bucket. Guarded
16
17
  * because convertToPeriod destructures: an unset period would throw and take the epic subscription
17
18
  * down for the session, and a missed refresh is the cheaper failure.
19
+ *
20
+ * Read state$.value at response time rather than the snapshot taken when the action was dispatched:
21
+ * if the user changes month while the request is in flight, the snapshot refreshes the month they
22
+ * have left and the month they are now looking at stays stale.
18
23
  */
19
24
  function refreshCurrentPage(state) {
20
25
  const selectedMonthYear = state.expenseAutomationViewState.selectedPeriodByTenantId[(0, tenantSelector_1.getCurrentTenant)(state).tenantId];
@@ -29,14 +34,17 @@ function refreshCurrentPage(state) {
29
34
  }
30
35
  return [(0, jeSchedulesViewReducer_1.fetchJeSchedules)({ start, end }, true)];
31
36
  }
32
- function failureActions(accrualId, message, status) {
37
+ function failureActions(
38
+ // Per action, not fixed: every failure used to read "Save schedule failed", so skipping or
39
+ // reverting an accrual reported an operation the user had not performed.
40
+ messageSection, accrualId, message, status) {
33
41
  return [
34
42
  (0, jeSchedulesViewReducer_1.oneTimeAccrualActionFailure)({
35
43
  accrualId,
36
44
  status: status ?? (0, responsePayload_1.createZeniAPIStatus)('Unexpected Error', message),
37
45
  }),
38
46
  (0, snackbarReducer_1.openSnackbar)({
39
- messageSection: 'save_schedule',
47
+ messageSection,
40
48
  messageText: 'failed',
41
49
  type: 'error',
42
50
  variables: [{ variableName: '_api-error_', variableValue: message }],
@@ -50,7 +58,7 @@ const promoteOneTimeAccrualEpic = (actions$, state$, zeniAPI) => actions$.pipe((
50
58
  if (accrual?.jeDebitAccountId == null ||
51
59
  accrual.jeCreditAccountId == null) {
52
60
  // Promote posts a journal entry, so it cannot proceed without both sides of it.
53
- return (0, rxjs_1.from)(failureActions(accrualId, 'This accrual is missing an expense or liability account.'));
61
+ return (0, rxjs_1.from)(failureActions('save_schedule', accrualId, 'This accrual is missing an expense or liability account.'));
54
62
  }
55
63
  // vendor_id, vendor_name and service_month are deliberately absent: promote takes them from
56
64
  // the stored draft and ignores them in the body, so sending them would imply a reassignment
@@ -79,15 +87,18 @@ const promoteOneTimeAccrualEpic = (actions$, state$, zeniAPI) => actions$.pipe((
79
87
  },
80
88
  ],
81
89
  }),
82
- ...refreshCurrentPage(state),
90
+ ...refreshCurrentPage(state$.value),
83
91
  ]);
84
92
  }
85
- return (0, rxjs_1.from)(failureActions(accrualId, response.status.message, response.status));
86
- }), (0, operators_1.catchError)((error) => (0, rxjs_1.from)(failureActions(accrualId, `Promote accrual REST API call errored out ${JSON.stringify(error)}`))));
93
+ return (0, rxjs_1.from)(failureActions('save_schedule', accrualId, response.status.message, response.status));
94
+ }), (0, operators_1.catchError)((error) => (0, rxjs_1.from)(failureActions('save_schedule', accrualId, `Promote accrual REST API call errored out ${JSON.stringify(error)}`))),
95
+ // A tenant switch clears the store while this request is still in flight.
96
+ // Without this the old tenant's response is written back into the freshly
97
+ // cleared store and stays on screen under the new tenant.
98
+ (0, operators_1.takeUntil)(actions$.pipe((0, operators_1.filter)(rootActions_1.clearAll.match))));
87
99
  }));
88
100
  exports.promoteOneTimeAccrualEpic = promoteOneTimeAccrualEpic;
89
101
  const ignoreOneTimeAccrualEpic = (actions$, state$, zeniAPI) => actions$.pipe((0, operators_1.filter)(jeSchedulesViewReducer_1.ignoreOneTimeAccrual.match), (0, operators_1.mergeMap)((action) => {
90
- const state = state$.value;
91
102
  const { accrualId, reason, note } = action.payload;
92
103
  // `scope` is deliberately never sent: the API returns 400 on it, which is why the design's
93
104
  // "Apply to" selector cannot be built yet.
@@ -107,15 +118,18 @@ const ignoreOneTimeAccrualEpic = (actions$, state$, zeniAPI) => actions$.pipe((0
107
118
  messageText: 'success',
108
119
  type: 'success',
109
120
  }),
110
- ...refreshCurrentPage(state),
121
+ ...refreshCurrentPage(state$.value),
111
122
  ]);
112
123
  }
113
- return (0, rxjs_1.from)(failureActions(accrualId, response.status.message, response.status));
114
- }), (0, operators_1.catchError)((error) => (0, rxjs_1.from)(failureActions(accrualId, `Ignore accrual REST API call errored out ${JSON.stringify(error)}`))));
124
+ return (0, rxjs_1.from)(failureActions('ignore_accrual', accrualId, response.status.message, response.status));
125
+ }), (0, operators_1.catchError)((error) => (0, rxjs_1.from)(failureActions('ignore_accrual', accrualId, `Ignore accrual REST API call errored out ${JSON.stringify(error)}`))),
126
+ // A tenant switch clears the store while this request is still in flight.
127
+ // Without this the old tenant's response is written back into the freshly
128
+ // cleared store and stays on screen under the new tenant.
129
+ (0, operators_1.takeUntil)(actions$.pipe((0, operators_1.filter)(rootActions_1.clearAll.match))));
115
130
  }));
116
131
  exports.ignoreOneTimeAccrualEpic = ignoreOneTimeAccrualEpic;
117
132
  const revertOneTimeAccrualEpic = (actions$, state$, zeniAPI) => actions$.pipe((0, operators_1.filter)(jeSchedulesViewReducer_1.revertOneTimeAccrual.match), (0, operators_1.mergeMap)((action) => {
118
- const state = state$.value;
119
133
  const { accrualId, reason, note } = action.payload;
120
134
  // Deletes the accrual's journal entry in QBO and soft-deletes the row. `scope` is never sent:
121
135
  // the endpoint 400s on it, exactly as ignore does.
@@ -135,11 +149,15 @@ const revertOneTimeAccrualEpic = (actions$, state$, zeniAPI) => actions$.pipe((0
135
149
  messageText: 'success',
136
150
  type: 'success',
137
151
  }),
138
- ...refreshCurrentPage(state),
152
+ ...refreshCurrentPage(state$.value),
139
153
  ]);
140
154
  }
141
- return (0, rxjs_1.from)(failureActions(accrualId, response.status.message, response.status));
142
- }), (0, operators_1.catchError)((error) => (0, rxjs_1.from)(failureActions(accrualId, `Revert accrual REST API call errored out ${JSON.stringify(error)}`))));
155
+ return (0, rxjs_1.from)(failureActions('revert_accrual', accrualId, response.status.message, response.status));
156
+ }), (0, operators_1.catchError)((error) => (0, rxjs_1.from)(failureActions('revert_accrual', accrualId, `Revert accrual REST API call errored out ${JSON.stringify(error)}`))),
157
+ // A tenant switch clears the store while this request is still in flight.
158
+ // Without this the old tenant's response is written back into the freshly
159
+ // cleared store and stays on screen under the new tenant.
160
+ (0, operators_1.takeUntil)(actions$.pipe((0, operators_1.filter)(rootActions_1.clearAll.match))));
143
161
  }));
144
162
  exports.revertOneTimeAccrualEpic = revertOneTimeAccrualEpic;
145
163
  const oneTimeAccrualActionsEpic = (actions$, state$, zeniAPI) => (0, rxjs_1.merge)((0, exports.promoteOneTimeAccrualEpic)(actions$, state$, zeniAPI), (0, exports.ignoreOneTimeAccrualEpic)(actions$, state$, zeniAPI), (0, exports.revertOneTimeAccrualEpic)(actions$, state$, zeniAPI));