@zeniai/client-epic-state 5.2.11-betaRD2 → 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.
- package/lib/entity/jeSchedules/jeSchedulesPayload.js +3 -0
- package/lib/esm/entity/jeSchedules/jeSchedulesPayload.js +3 -0
- package/lib/esm/view/expenseAutomationView/epics/jeSchedule/newSchedule/createOneTimeAccrualEpic.js +7 -2
- package/lib/esm/view/expenseAutomationView/epics/jeSchedule/newSchedule/saveNewScheduleEpic.js +31 -5
- package/lib/esm/view/expenseAutomationView/epics/jeSchedule/oneTimeAccrualActionsEpic.js +33 -15
- package/lib/esm/view/expenseAutomationView/epics/jeSchedule/revertJeScheduleEpic.js +7 -2
- package/lib/esm/view/expenseAutomationView/epics/jeSchedule/switchJESchedulesTabEpic.js +6 -0
- package/lib/esm/view/expenseAutomationView/reducers/jeSchedulesViewReducer.js +3 -0
- package/lib/view/expenseAutomationView/epics/jeSchedule/newSchedule/createOneTimeAccrualEpic.d.ts +2 -1
- package/lib/view/expenseAutomationView/epics/jeSchedule/newSchedule/createOneTimeAccrualEpic.js +6 -1
- package/lib/view/expenseAutomationView/epics/jeSchedule/newSchedule/saveNewScheduleEpic.d.ts +4 -40
- package/lib/view/expenseAutomationView/epics/jeSchedule/newSchedule/saveNewScheduleEpic.js +29 -3
- package/lib/view/expenseAutomationView/epics/jeSchedule/oneTimeAccrualActionsEpic.d.ts +2 -1
- package/lib/view/expenseAutomationView/epics/jeSchedule/oneTimeAccrualActionsEpic.js +32 -14
- package/lib/view/expenseAutomationView/epics/jeSchedule/revertJeScheduleEpic.d.ts +2 -1
- package/lib/view/expenseAutomationView/epics/jeSchedule/revertJeScheduleEpic.js +6 -1
- package/lib/view/expenseAutomationView/epics/jeSchedule/switchJESchedulesTabEpic.js +6 -0
- package/lib/view/expenseAutomationView/reducers/jeSchedulesViewReducer.js +3 -0
- package/package.json +1 -1
|
@@ -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: {
|
package/lib/esm/view/expenseAutomationView/epics/jeSchedule/newSchedule/createOneTimeAccrualEpic.js
CHANGED
|
@@ -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
|
}));
|
package/lib/esm/view/expenseAutomationView/epics/jeSchedule/newSchedule/saveNewScheduleEpic.js
CHANGED
|
@@ -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
|
-
|
|
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(
|
|
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
|
|
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);
|
|
@@ -172,6 +172,9 @@ const expenseAutomationJESchedulesView = createSlice({
|
|
|
172
172
|
draft.jeScheduleTransactionKeysByPeriodAndTab[periodTabKey] = [];
|
|
173
173
|
draft.jeScheduleAccruedKeysByPeriodAndTab[periodTabKey] = [];
|
|
174
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] = [];
|
|
175
178
|
}
|
|
176
179
|
},
|
|
177
180
|
retryJeSchedule(draft, action) {
|
package/lib/view/expenseAutomationView/epics/jeSchedule/newSchedule/createOneTimeAccrualEpic.d.ts
CHANGED
|
@@ -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>;
|
package/lib/view/expenseAutomationView/epics/jeSchedule/newSchedule/createOneTimeAccrualEpic.js
CHANGED
|
@@ -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;
|
package/lib/view/expenseAutomationView/epics/jeSchedule/newSchedule/saveNewScheduleEpic.d.ts
CHANGED
|
@@ -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
|
-
|
|
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(
|
|
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
|
|
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));
|
|
@@ -3,10 +3,11 @@ import { updateJESchedules, updateJESchedulesDetails } from '../../../../entity/
|
|
|
3
3
|
import { openSnackbar } from '../../../../entity/snackbar/snackbarReducer';
|
|
4
4
|
import { JEScheduleKey, JEScheduleTransactionKey } from '../../../../entity/jeSchedules/jeSchedulesTypes';
|
|
5
5
|
import { RootState } from '../../../../reducer';
|
|
6
|
+
import { clearAll } from '../../../../rootActions';
|
|
6
7
|
import { ZeniAPI } from '../../../../zeniAPI';
|
|
7
8
|
import { fetchMonthEndCloseChecks } from '../../../monthEndCloseChecksView/monthEndCloseChecksViewReducer';
|
|
8
9
|
import { removeJeAccruedScheduleKey, removeJeScheduleTransactionKey, revertJeSchedule, revertJeScheduleFailure, revertJeScheduleSuccess } from '../../reducers/jeSchedulesViewReducer';
|
|
9
|
-
export type ActionType = ReturnType<typeof fetchMonthEndCloseChecks> | ReturnType<typeof openSnackbar> | ReturnType<typeof removeJeAccruedScheduleKey> | ReturnType<typeof removeJeScheduleTransactionKey> | ReturnType<typeof revertJeSchedule> | ReturnType<typeof revertJeScheduleFailure> | ReturnType<typeof revertJeScheduleSuccess> | ReturnType<typeof updateJESchedules> | ReturnType<typeof updateJESchedulesDetails>;
|
|
10
|
+
export type ActionType = ReturnType<typeof clearAll> | ReturnType<typeof fetchMonthEndCloseChecks> | ReturnType<typeof openSnackbar> | ReturnType<typeof removeJeAccruedScheduleKey> | ReturnType<typeof removeJeScheduleTransactionKey> | ReturnType<typeof revertJeSchedule> | ReturnType<typeof revertJeScheduleFailure> | ReturnType<typeof revertJeScheduleSuccess> | ReturnType<typeof updateJESchedules> | ReturnType<typeof updateJESchedulesDetails>;
|
|
10
11
|
/**
|
|
11
12
|
* Undo a posted prepaid / fixed-asset / accrued schedule: deletes its posted QBO journal entry, then
|
|
12
13
|
* soft-deletes the scheduled entries and the schedule itself.
|
|
@@ -5,6 +5,7 @@ const rxjs_1 = require("rxjs");
|
|
|
5
5
|
const operators_1 = require("rxjs/operators");
|
|
6
6
|
const snackbarReducer_1 = require("../../../../entity/snackbar/snackbarReducer");
|
|
7
7
|
const tenantSelector_1 = require("../../../../entity/tenant/tenantSelector");
|
|
8
|
+
const rootActions_1 = require("../../../../rootActions");
|
|
8
9
|
const responsePayload_1 = require("../../../../responsePayload");
|
|
9
10
|
const monthEndCloseChecksViewReducer_1 = require("../../../monthEndCloseChecksView/monthEndCloseChecksViewReducer");
|
|
10
11
|
const jeSchedulesViewReducer_1 = require("../../reducers/jeSchedulesViewReducer");
|
|
@@ -105,6 +106,10 @@ const revertJeScheduleEpic = (actions$, state$, zeniAPI) => actions$.pipe((0, op
|
|
|
105
106
|
messageText: 'failed',
|
|
106
107
|
type: 'error',
|
|
107
108
|
}),
|
|
108
|
-
]))
|
|
109
|
+
])),
|
|
110
|
+
// A tenant switch clears the store while this request is still in flight.
|
|
111
|
+
// Without this the old tenant's response is written back into the freshly
|
|
112
|
+
// cleared store and stays on screen under the new tenant.
|
|
113
|
+
(0, operators_1.takeUntil)(actions$.pipe((0, operators_1.filter)(rootActions_1.clearAll.match))));
|
|
109
114
|
}));
|
|
110
115
|
exports.revertJeScheduleEpic = revertJeScheduleEpic;
|
|
@@ -15,6 +15,12 @@ const switchJESchedulesTabEpic = (actions$, state$) => actions$.pipe((0, operato
|
|
|
15
15
|
}
|
|
16
16
|
const currentTenant = (0, tenantSelector_1.getCurrentTenant)(state);
|
|
17
17
|
const selectedPeriod = state.expenseAutomationViewState.selectedPeriodByTenantId[currentTenant.tenantId];
|
|
18
|
+
// convertToPeriod destructures, so an unset period throws and takes this epic's subscription
|
|
19
|
+
// down for the rest of the session -- every later tab switch would then stop refreshing. A
|
|
20
|
+
// skipped refresh is the cheaper failure, as in refreshCurrentPage.
|
|
21
|
+
if (selectedPeriod == null) {
|
|
22
|
+
return rxjs_1.EMPTY;
|
|
23
|
+
}
|
|
18
24
|
const period = (0, timePeriod_1.convertToPeriod)(selectedPeriod);
|
|
19
25
|
const timePeriodStart = (0, timePeriod_1.toAbsoluteDay)(period.start);
|
|
20
26
|
const timePeriodEnd = (0, timePeriod_1.toAbsoluteDay)(period.end);
|
|
@@ -178,6 +178,9 @@ const expenseAutomationJESchedulesView = (0, toolkit_1.createSlice)({
|
|
|
178
178
|
draft.jeScheduleTransactionKeysByPeriodAndTab[periodTabKey] = [];
|
|
179
179
|
draft.jeScheduleAccruedKeysByPeriodAndTab[periodTabKey] = [];
|
|
180
180
|
draft.jeScheduleOneTimeAccrualKeysByPeriodAndTab[periodTabKey] = [];
|
|
181
|
+
// Cleared with the rest: leaving it behind kept the previous fetch's resolve rows on an
|
|
182
|
+
// otherwise empty errored tab, which reads as "these are all the schedules there are".
|
|
183
|
+
draft.failedJeScheduleTransactionKeysByPeriodAndTab[periodTabKey] = [];
|
|
181
184
|
}
|
|
182
185
|
},
|
|
183
186
|
retryJeSchedule(draft, action) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zeniai/client-epic-state",
|
|
3
|
-
"version": "5.2.11-
|
|
3
|
+
"version": "5.2.11-betaRD3",
|
|
4
4
|
"description": "Shared module between Web & Mobile containing required abstractions for state management, async network communication. ",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"module": "lib/esm/index.js",
|