@zeniai/client-epic-state 5.2.49 → 5.2.50-betaRD1

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 (32) hide show
  1. package/lib/entity/jeSchedules/jeSchedulesReducer.d.ts +4 -1
  2. package/lib/entity/jeSchedules/jeSchedulesReducer.js +47 -5
  3. package/lib/epic.d.ts +2 -1
  4. package/lib/epic.js +6 -4
  5. package/lib/esm/entity/jeSchedules/jeSchedulesReducer.js +46 -4
  6. package/lib/esm/epic.js +6 -4
  7. package/lib/esm/index.js +3 -3
  8. package/lib/esm/view/expenseAutomationView/epics/jeSchedule/fetchFailedJePostingsEpic.js +40 -0
  9. package/lib/esm/view/expenseAutomationView/epics/jeSchedule/fetchJeSchedulePageEpic.js +9 -1
  10. package/lib/esm/view/expenseAutomationView/epics/jeSchedule/fetchJeSchedulesEpic.js +10 -13
  11. package/lib/esm/view/expenseAutomationView/epics/jeSchedule/initializeJeScheduleLocalDataEpic.js +29 -15
  12. package/lib/esm/view/expenseAutomationView/epics/jeSchedule/retryJeSchedulesEpic.js +12 -1
  13. package/lib/esm/view/expenseAutomationView/reducers/jeSchedulesViewReducer.js +60 -1
  14. package/lib/esm/view/expenseAutomationView/selectors/jeSchedulesViewSelector.js +11 -11
  15. package/lib/index.d.ts +3 -3
  16. package/lib/index.js +50 -49
  17. package/lib/tsconfig.mocks.tsbuildinfo +1 -0
  18. package/lib/view/expenseAutomationView/epics/jeSchedule/fetchFailedJePostingsEpic.d.ts +20 -0
  19. package/lib/view/expenseAutomationView/epics/jeSchedule/fetchFailedJePostingsEpic.js +44 -0
  20. package/lib/view/expenseAutomationView/epics/jeSchedule/fetchJeSchedulePageEpic.d.ts +2 -2
  21. package/lib/view/expenseAutomationView/epics/jeSchedule/fetchJeSchedulePageEpic.js +8 -0
  22. package/lib/view/expenseAutomationView/epics/jeSchedule/fetchJeSchedulesEpic.js +8 -11
  23. package/lib/view/expenseAutomationView/epics/jeSchedule/initializeJeScheduleLocalDataEpic.js +29 -15
  24. package/lib/view/expenseAutomationView/epics/jeSchedule/retryJeSchedulesEpic.d.ts +2 -2
  25. package/lib/view/expenseAutomationView/epics/jeSchedule/retryJeSchedulesEpic.js +12 -1
  26. package/lib/view/expenseAutomationView/payload/jeSchedulesPayload.d.ts +18 -0
  27. package/lib/view/expenseAutomationView/reducers/jeSchedulesViewReducer.d.ts +8 -2
  28. package/lib/view/expenseAutomationView/reducers/jeSchedulesViewReducer.js +62 -3
  29. package/lib/view/expenseAutomationView/selectorTypes/jeSchedulesSelectorTypes.d.ts +2 -0
  30. package/lib/view/expenseAutomationView/selectors/jeSchedulesViewSelector.js +11 -11
  31. package/lib/view/expenseAutomationView/types/jeSchedulesViewState.d.ts +15 -0
  32. package/package.json +1 -1
@@ -0,0 +1,20 @@
1
+ import { ActionsObservable, StateObservable } from 'redux-observable';
2
+ import { Observable } from 'rxjs';
3
+ import { updateJESchedules, updateJESchedulesDetails } from '../../../../entity/jeSchedules/jeSchedulesReducer';
4
+ import { clearAll } from '../../../../rootActions';
5
+ import { RootState } from '../../../../rootStateTypes';
6
+ import { ZeniAPI } from '../../../../zeniAPI';
7
+ import { fetchFailedJePostings, fetchFailedJePostingsFailure, fetchFailedJePostingsSuccess, initializeJeScheduleLocalData } from '../../reducers/jeSchedulesViewReducer';
8
+ export type ActionType = ReturnType<typeof clearAll> | ReturnType<typeof fetchFailedJePostings> | ReturnType<typeof fetchFailedJePostingsFailure> | ReturnType<typeof fetchFailedJePostingsSuccess> | ReturnType<typeof initializeJeScheduleLocalData> | ReturnType<typeof updateJESchedules> | ReturnType<typeof updateJESchedulesDetails>;
9
+ /**
10
+ * Fetches every currently-failing posting for the tenant, on its own endpoint and with no window.
11
+ *
12
+ * Sends no query at all. The endpoint takes an optional `expected_post_start_date` floor and the
13
+ * client deliberately omits it: a failure is outstanding work until someone resolves it, whatever
14
+ * month it belongs to, and windowing this list is exactly what kept older failures off the only
15
+ * screen that can resolve them.
16
+ *
17
+ * `switchMap`, not `mergeMap`: there is one list per tenant, so a second trigger supersedes the
18
+ * first rather than racing it.
19
+ */
20
+ export declare const fetchFailedJePostingsEpic: (actions$: ActionsObservable<ActionType>, _state$: StateObservable<RootState>, zeniAPI: ZeniAPI) => Observable<ActionType>;
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.fetchFailedJePostingsEpic = void 0;
4
+ const rxjs_1 = require("rxjs");
5
+ const operators_1 = require("rxjs/operators");
6
+ const jeSchedulesReducer_1 = require("../../../../entity/jeSchedules/jeSchedulesReducer");
7
+ const responsePayload_1 = require("../../../../responsePayload");
8
+ const rootActions_1 = require("../../../../rootActions");
9
+ const jeSchedulesViewReducer_1 = require("../../reducers/jeSchedulesViewReducer");
10
+ /**
11
+ * Fetches every currently-failing posting for the tenant, on its own endpoint and with no window.
12
+ *
13
+ * Sends no query at all. The endpoint takes an optional `expected_post_start_date` floor and the
14
+ * client deliberately omits it: a failure is outstanding work until someone resolves it, whatever
15
+ * month it belongs to, and windowing this list is exactly what kept older failures off the only
16
+ * screen that can resolve them.
17
+ *
18
+ * `switchMap`, not `mergeMap`: there is one list per tenant, so a second trigger supersedes the
19
+ * first rather than racing it.
20
+ */
21
+ const fetchFailedJePostingsEpic = (actions$, _state$, zeniAPI) => actions$.pipe((0, operators_1.filter)(jeSchedulesViewReducer_1.fetchFailedJePostings.match), (0, operators_1.switchMap)(() => zeniAPI
22
+ .getJSON(`${zeniAPI.apiEndPoints.accountMicroServiceBaseUrl}/1.0/expenses_automation/failed_je_postings`)
23
+ .pipe((0, operators_1.switchMap)((response) => {
24
+ if (!(0, responsePayload_1.isSuccessResponse)(response) || response.data == null) {
25
+ return (0, rxjs_1.of)((0, jeSchedulesViewReducer_1.fetchFailedJePostingsFailure)(response.status));
26
+ }
27
+ const failedJeSchedules = response.data.failed_je_postings?.je_schedules ?? [];
28
+ const failedScheduledJournalEntries = response.data.failed_je_postings?.scheduled_journal_entries ?? [];
29
+ // Order matters and matches fetchJeSchedulesEpic: updateJESchedules fans out to the
30
+ // vendor / account / class / user entities, updateJESchedulesDetails then attaches the
31
+ // journal entries, and only then can initializeJeScheduleLocalData read them back.
32
+ return (0, rxjs_1.from)([
33
+ (0, jeSchedulesReducer_1.updateJESchedules)(failedJeSchedules),
34
+ (0, jeSchedulesReducer_1.updateJESchedulesDetails)(failedJeSchedules, failedScheduledJournalEntries),
35
+ (0, jeSchedulesViewReducer_1.initializeJeScheduleLocalData)(failedJeSchedules),
36
+ (0, jeSchedulesViewReducer_1.fetchFailedJePostingsSuccess)(failedJeSchedules),
37
+ ]);
38
+ }), (0, operators_1.catchError)((error) => (0, rxjs_1.of)((0, jeSchedulesViewReducer_1.fetchFailedJePostingsFailure)((0, responsePayload_1.createZeniAPIStatus)('Unexpected Error', 'fetch failed JE postings REST API call errored out' +
39
+ JSON.stringify(error))))),
40
+ // A tenant switch clears the store while this request is still in flight. Without this
41
+ // the old tenant's rows are written back into the freshly cleared store and stay on
42
+ // screen under the new tenant until a manual refresh.
43
+ (0, operators_1.takeUntil)(actions$.pipe((0, operators_1.filter)(rootActions_1.clearAll.match))))));
44
+ exports.fetchFailedJePostingsEpic = fetchFailedJePostingsEpic;
@@ -3,6 +3,6 @@ import { Observable } from 'rxjs';
3
3
  import { RootState } from '../../../../rootStateTypes';
4
4
  import { fetchAccountList } from '../../../accountList/accountListReducer';
5
5
  import { fetchActualMatchingPage } from '../../reducers/actualMatchingViewReducer';
6
- import { fetchJeSchedules, fetchJeSchedulesPage, initializeAccountSettingsView } from '../../reducers/jeSchedulesViewReducer';
7
- export type ActionType = ReturnType<typeof fetchAccountList> | ReturnType<typeof fetchActualMatchingPage> | ReturnType<typeof fetchJeSchedules> | ReturnType<typeof fetchJeSchedulesPage> | ReturnType<typeof initializeAccountSettingsView>;
6
+ import { fetchFailedJePostings, fetchJeSchedules, fetchJeSchedulesPage, initializeAccountSettingsView } from '../../reducers/jeSchedulesViewReducer';
7
+ export type ActionType = ReturnType<typeof fetchAccountList> | ReturnType<typeof fetchActualMatchingPage> | ReturnType<typeof fetchFailedJePostings> | ReturnType<typeof fetchJeSchedules> | ReturnType<typeof fetchJeSchedulesPage> | ReturnType<typeof initializeAccountSettingsView>;
8
8
  export declare const fetchJeSchedulesPageEpic: (actions$: ActionsObservable<ActionType>, state$: StateObservable<RootState>) => Observable<ActionType>;
@@ -27,6 +27,14 @@ const fetchJeSchedulesPageEpic = (actions$, state$) => actions$.pipe((0, operato
27
27
  else {
28
28
  jeSchedulePageActions.push((0, jeSchedulesViewReducer_1.fetchJeSchedules)(period, refreshViewInBackground));
29
29
  }
30
+ // Failed postings are tenant-wide and carry no period, so a period change or a tab switch
31
+ // cannot invalidate them -- fetch once and let retry keep the list current from then on.
32
+ // Refreshes still refetch, silently, so a failure resolved elsewhere leaves eventually.
33
+ const { fetchState: failedPostingsFetchState } = state.expenseAutomationJESchedulesViewState.failedJePostings.fetchState;
34
+ const isBackgroundRefresh = refreshViewInBackground === true;
35
+ if (failedPostingsFetchState === 'Not-Started' || isBackgroundRefresh) {
36
+ jeSchedulePageActions.push((0, jeSchedulesViewReducer_1.fetchFailedJePostings)(isBackgroundRefresh));
37
+ }
30
38
  // Only on the first page load. Account settings cannot change from a tab switch or a
31
39
  // refresh, and re-initializing puts accountSettingsView back to 'In-Progress' -- which the
32
40
  // page reads as "still loading" and answers with a full skeleton over rows it already has.
@@ -84,8 +84,9 @@ const fetchJeSchedulesEpic = (actions$, state$, zeniAPI) => actions$.pipe((0, op
84
84
  one_time_accrual_service_month_end: parsedEndDate.format(jeSchedulesPayload_1.SERVICE_MONTH_FORMAT),
85
85
  // Existing lists what actually reached the books.
86
86
  one_time_accrual_statuses: ['posted'],
87
- // A failed posting from an earlier month still needs to surface here, so this list
88
- // is deliberately not windowed.
87
+ // Kept only so this tab's response shape is unchanged. Nothing reads the failed
88
+ // postings it returns any more -- they have their own endpoint and their own group
89
+ // on Review. Dropping the key from the response is a server-side follow-up.
89
90
  filter_failed_je_postings_by_date: false,
90
91
  }),
91
92
  };
@@ -96,12 +97,10 @@ const fetchJeSchedulesEpic = (actions$, state$, zeniAPI) => actions$.pipe((0, op
96
97
  const updateActions = [];
97
98
  const accruedJeSchedules = response.data.accrued_je_schedules?.je_schedules ?? [];
98
99
  const oneTimeAccruals = response.data.one_time_accruals ?? [];
99
- updateActions.push((0, jeSchedulesReducer_1.updateJESchedules)([
100
- ...response.data.je_schedules,
101
- ...response.data.failed_je_postings.je_schedules,
102
- ]), (0, jeSchedulesReducer_1.updateJESchedulesDetails)([...response.data.failed_je_postings.je_schedules], response.data.failed_je_postings.scheduled_journal_entries), (0, jeSchedulesViewReducer_1.initializeJeScheduleLocalData)([
103
- ...response.data.failed_je_postings.je_schedules,
104
- ]));
100
+ // failed_je_postings is deliberately ignored. This endpoint windows it to the close
101
+ // month, which is what hid older failures; fetchFailedJePostingsEpic owns that list
102
+ // now and fetches it unwindowed.
103
+ updateActions.push((0, jeSchedulesReducer_1.updateJESchedules)([...response.data.je_schedules]));
105
104
  if (accruedJeSchedules.length > 0) {
106
105
  updateActions.push((0, jeSchedulesReducer_1.updateAccruedJESchedules)(accruedJeSchedules), (0, jeSchedulesReducer_1.updateAccruedJESchedulesDetails)(accruedJeSchedules, response.data.accrued_je_schedules
107
106
  ?.scheduled_journal_entries));
@@ -114,9 +113,7 @@ const fetchJeSchedulesEpic = (actions$, state$, zeniAPI) => actions$.pipe((0, op
114
113
  }
115
114
  updateActions.push((0, jeSchedulesViewReducer_1.fetchJeSchedulesSuccess)({
116
115
  jeSchedules: [...response.data.je_schedules],
117
- failedJeSchedules: [
118
- ...response.data.failed_je_postings.je_schedules,
119
- ],
116
+ failedJeSchedules: [],
120
117
  accruedJeSchedules: accruedJeSchedules,
121
118
  oneTimeAccruals: oneTimeAccruals,
122
119
  selectedPeriod: selectedPeriod,
@@ -10,22 +10,36 @@ const initializeJeScheduleLocalDataEpic = (actions$, state$) => actions$.pipe((0
10
10
  const actions = [];
11
11
  const { jeScheduleTransactionKeys } = action.payload;
12
12
  jeScheduleTransactionKeys.forEach((jeScheduleTransactionKey) => {
13
+ // Checked before the selector runs, not after: the selector destructures the record it
14
+ // looks up, so a key that is not in the store throws a TypeError rather than returning
15
+ // undefined. This epic is a mergeMap with no catchError, so that throw would take its
16
+ // subscription down for the rest of the session and silently stop local-data
17
+ // initialisation everywhere -- not just for this row.
18
+ if (state.jeSchedulesState.schedulesByTransactionKey[jeScheduleTransactionKey] == null) {
19
+ return;
20
+ }
13
21
  const jeScheduledTransaction = (0, jeSchedulesSelector_1.getJEScheduledTransactionByJEScheduleTransactionKey)(jeScheduleTransactionKey, state);
14
- const initLocalData = {
15
- jeScheduleType: jeScheduledTransaction.jeScheduleType,
16
- jeScheduleID: jeScheduledTransaction.jeScheduleId,
17
- scheduledJournalEntryID: jeScheduledTransaction.scheduledJournalEntry[0]
18
- .scheduledJournalEntryID,
19
- updatedVendor: undefined,
20
- updatedCategoryId: undefined,
21
- updatedAccumulatedDepreciationCategoryID: undefined,
22
- updatedClassId: undefined,
23
- };
24
- actions.push((0, jeSchedulesViewReducer_1.updateJeScheduleLocalDataById)({
25
- localData: initLocalData,
26
- scheduledJournalEntryID: jeScheduledTransaction.scheduledJournalEntry[0]
27
- .scheduledJournalEntryID,
28
- }));
22
+ // One record per journal entry, not per schedule. A schedule that failed in two months is
23
+ // two rows and each needs its own vendor / category edits -- keying only the first left
24
+ // every later row without local data, which the list renders read-only and without a
25
+ // Retry button. An empty array is possible when details have not landed yet; it must not
26
+ // throw, because this runs in a mergeMap with no catchError and the throw would take the
27
+ // epic's subscription down for the rest of the session.
28
+ jeScheduledTransaction.scheduledJournalEntry.forEach((journalEntry) => {
29
+ const { scheduledJournalEntryID } = journalEntry;
30
+ actions.push((0, jeSchedulesViewReducer_1.updateJeScheduleLocalDataById)({
31
+ localData: {
32
+ jeScheduleType: jeScheduledTransaction.jeScheduleType,
33
+ jeScheduleID: jeScheduledTransaction.jeScheduleId,
34
+ scheduledJournalEntryID,
35
+ updatedVendor: undefined,
36
+ updatedCategoryId: undefined,
37
+ updatedAccumulatedDepreciationCategoryID: undefined,
38
+ updatedClassId: undefined,
39
+ },
40
+ scheduledJournalEntryID,
41
+ }));
42
+ });
29
43
  });
30
44
  return (0, rxjs_1.from)(actions);
31
45
  }));
@@ -1,10 +1,10 @@
1
1
  import { ActionsObservable, StateObservable } from 'redux-observable';
2
2
  import { Observable } from 'rxjs';
3
- import { updateJESchedules, updateJESchedulesDetails } from '../../../../entity/jeSchedules/jeSchedulesReducer';
3
+ import { removeScheduledJournalEntryFromJESchedule, updateJESchedules, updateJESchedulesDetails } from '../../../../entity/jeSchedules/jeSchedulesReducer';
4
4
  import { openSnackbar } from '../../../../entity/snackbar/snackbarReducer';
5
5
  import { RootState } from '../../../../rootStateTypes';
6
6
  import { ZeniAPI } from '../../../../zeniAPI';
7
7
  import { fetchMonthEndCloseChecks } from '../../../monthEndCloseChecksView/monthEndCloseChecksViewReducer';
8
8
  import { removeFailedJeScheduleTransactionKey, removeJeScheduleLocalDataById, retryJeSchedule, retryJeScheduleFailure, retryJeScheduleSuccess, updateJeScheduleTransactionKeys } from '../../reducers/jeSchedulesViewReducer';
9
- export type ActionType = ReturnType<typeof fetchMonthEndCloseChecks> | ReturnType<typeof openSnackbar> | ReturnType<typeof removeFailedJeScheduleTransactionKey> | ReturnType<typeof removeJeScheduleLocalDataById> | ReturnType<typeof retryJeSchedule> | ReturnType<typeof retryJeScheduleFailure> | ReturnType<typeof retryJeScheduleSuccess> | ReturnType<typeof updateJESchedules> | ReturnType<typeof updateJESchedulesDetails> | ReturnType<typeof updateJeScheduleTransactionKeys>;
9
+ export type ActionType = ReturnType<typeof fetchMonthEndCloseChecks> | ReturnType<typeof openSnackbar> | ReturnType<typeof removeFailedJeScheduleTransactionKey> | ReturnType<typeof removeJeScheduleLocalDataById> | ReturnType<typeof removeScheduledJournalEntryFromJESchedule> | ReturnType<typeof retryJeSchedule> | ReturnType<typeof retryJeScheduleFailure> | ReturnType<typeof retryJeScheduleSuccess> | ReturnType<typeof updateJESchedules> | ReturnType<typeof updateJESchedulesDetails> | ReturnType<typeof updateJeScheduleTransactionKeys>;
10
10
  export declare const retryJeScheduleEpic: (actions$: ActionsObservable<ActionType>, state$: StateObservable<RootState>, zeniAPI: ZeniAPI) => Observable<ActionType>;
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.retryJeScheduleEpic = void 0;
4
4
  const rxjs_1 = require("rxjs");
5
5
  const operators_1 = require("rxjs/operators");
6
+ const jeSchedulesReducer_1 = require("../../../../entity/jeSchedules/jeSchedulesReducer");
6
7
  const snackbarReducer_1 = require("../../../../entity/snackbar/snackbarReducer");
7
8
  const tenantSelector_1 = require("../../../../entity/tenant/tenantSelector");
8
9
  const responsePayload_1 = require("../../../../responsePayload");
@@ -25,9 +26,16 @@ const retryJeScheduleEpic = (actions$, state$, zeniAPI) => actions$.pipe((0, ope
25
26
  .pipe((0, operators_1.mergeMap)((response) => {
26
27
  if ((0, responsePayload_1.isSuccessStatus)(response)) {
27
28
  const updateActions = [];
29
+ // Retire this entry, and the schedule itself only when this was its last failed
30
+ // one. A schedule that failed in several months is several rows sharing one
31
+ // schedule key, so dropping the key outright took the still-failing months off the
32
+ // list with it and nothing would have surfaced them again.
33
+ const failedEntriesForSchedule = state.jeSchedulesState.schedulesByTransactionKey[jeScheduleTransactionKey]?.scheduledJournalEntry ?? [];
34
+ const wasLastFailedEntryForSchedule = failedEntriesForSchedule.filter((journalEntry) => journalEntry.scheduledJournalEntryID !==
35
+ scheduledJournalEntryID).length === 0;
28
36
  updateActions.push((0, jeSchedulesViewReducer_1.retryJeScheduleSuccess)({
29
37
  scheduledJournalEntryID,
30
- }), (0, jeSchedulesViewReducer_1.removeFailedJeScheduleTransactionKey)(jeScheduleTransactionKey, selectedPeriod), (0, jeSchedulesViewReducer_1.removeJeScheduleLocalDataById)({ scheduledJournalEntryID }), // Important to remove localdata after success.
38
+ }), (0, jeSchedulesReducer_1.removeScheduledJournalEntryFromJESchedule)(jeScheduleTransactionKey, scheduledJournalEntryID), (0, jeSchedulesViewReducer_1.removeJeScheduleLocalDataById)({ scheduledJournalEntryID }), // Important to remove localdata after success.
31
39
  (0, monthEndCloseChecksViewReducer_1.fetchMonthEndCloseChecks)({
32
40
  tenantId: currentTenant.tenantId,
33
41
  period: selectedPeriod,
@@ -37,6 +45,9 @@ const retryJeScheduleEpic = (actions$, state$, zeniAPI) => actions$.pipe((0, ope
37
45
  messageText: 'success',
38
46
  type: 'success',
39
47
  }));
48
+ if (wasLastFailedEntryForSchedule) {
49
+ updateActions.push((0, jeSchedulesViewReducer_1.removeFailedJeScheduleTransactionKey)(jeScheduleTransactionKey, selectedPeriod));
50
+ }
40
51
  return (0, rxjs_1.from)(updateActions);
41
52
  }
42
53
  else {
@@ -78,6 +78,24 @@ interface ExpenseAutomationJESchedulesPayload {
78
78
  };
79
79
  }
80
80
  export type ExpenseAutomationJESchedulesResponse = ZeniAPIResponse<ExpenseAutomationJESchedulesPayload>;
81
+ /**
82
+ * `GET /1.0/expenses_automation/failed_je_postings`.
83
+ *
84
+ * Deliberately the same `failed_je_postings` shape the schedules endpoint sends under that key, so
85
+ * both responses feed the same mappers. The difference is what the server does before answering:
86
+ * this endpoint applies no date window, so a posting that failed months ago still comes back.
87
+ */
88
+ export interface FailedJePostingsPayload {
89
+ je_schedules: JEScheduledTransactionPayload[];
90
+ scheduled_journal_entries: ScheduledJournalEntryPayload[];
91
+ }
92
+ interface ExpenseAutomationFailedJePostingsPayload {
93
+ failed_je_postings: FailedJePostingsPayload;
94
+ query?: {
95
+ expected_post_start_date: string | null;
96
+ };
97
+ }
98
+ export type ExpenseAutomationFailedJePostingsResponse = ZeniAPIResponse<ExpenseAutomationFailedJePostingsPayload>;
81
99
  export declare const SERVICE_MONTH_FORMAT = "YYYY-MM";
82
100
  export interface CreateOneTimeAccrualPayload {
83
101
  amount: number;
@@ -47,7 +47,13 @@ export declare const clearExpenseAutomationJESchedulesView: import("@reduxjs/too
47
47
  period: TimePeriod;
48
48
  refreshViewInBackground: any;
49
49
  selectedPageTab: "schedules" | "actual_matching" | "existing_schedules" | undefined;
50
- }, "expenseAutomationJESchedulesView/fetchJeSchedules", never, never>, fetchJeSchedulesFailure: import("@reduxjs/toolkit").ActionCreatorWithPayload<{
50
+ }, "expenseAutomationJESchedulesView/fetchJeSchedules", never, never>, fetchFailedJePostings: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[refreshInBackground?: any], {
51
+ refreshInBackground: any;
52
+ }, "expenseAutomationJESchedulesView/fetchFailedJePostings", never, never>, fetchFailedJePostingsFailure: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[status: ZeniAPIStatus<Record<string, unknown>>], {
53
+ status: ZeniAPIStatus<Record<string, unknown>>;
54
+ }, "expenseAutomationJESchedulesView/fetchFailedJePostingsFailure", never, never>, fetchFailedJePostingsSuccess: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[failedJeSchedules: JEScheduledTransactionPayload[]], {
55
+ failedJeSchedules: JEScheduledTransactionPayload[];
56
+ }, "expenseAutomationJESchedulesView/fetchFailedJePostingsSuccess", never, never>, fetchJeSchedulesFailure: import("@reduxjs/toolkit").ActionCreatorWithPayload<{
51
57
  selectedPageTab: JEPageTab;
52
58
  selectedPeriod: MonthYearPeriod;
53
59
  status: ZeniAPIStatus;
@@ -132,7 +138,7 @@ export declare const clearExpenseAutomationJESchedulesView: import("@reduxjs/too
132
138
  status: ZeniAPIStatus;
133
139
  }, "expenseAutomationJESchedulesView/searchTransactionsForNewScheduleFailure">, searchTransactionsForNewScheduleSuccess: import("@reduxjs/toolkit").ActionCreatorWithPayload<{
134
140
  searchResults: JEScheduleTransactionKey[];
135
- }, "expenseAutomationJESchedulesView/searchTransactionsForNewScheduleSuccess">, toggleJECreatedGroupCollapsed: import("@reduxjs/toolkit").ActionCreatorWithoutPayload<"expenseAutomationJESchedulesView/toggleJECreatedGroupCollapsed">, toggleJEPendingGroupCollapsed: import("@reduxjs/toolkit").ActionCreatorWithoutPayload<"expenseAutomationJESchedulesView/toggleJEPendingGroupCollapsed">, updateAccountSettingsListForAccountTypes: import("@reduxjs/toolkit").ActionCreatorWithPayload<{
141
+ }, "expenseAutomationJESchedulesView/searchTransactionsForNewScheduleSuccess">, toggleJECreatedGroupCollapsed: import("@reduxjs/toolkit").ActionCreatorWithoutPayload<"expenseAutomationJESchedulesView/toggleJECreatedGroupCollapsed">, toggleJEFailedGroupCollapsed: import("@reduxjs/toolkit").ActionCreatorWithoutPayload<"expenseAutomationJESchedulesView/toggleJEFailedGroupCollapsed">, toggleJEPendingGroupCollapsed: import("@reduxjs/toolkit").ActionCreatorWithoutPayload<"expenseAutomationJESchedulesView/toggleJEPendingGroupCollapsed">, updateAccountSettingsListForAccountTypes: import("@reduxjs/toolkit").ActionCreatorWithPayload<{
136
142
  accountPayload: AccountMetadataPayload[];
137
143
  }, "expenseAutomationJESchedulesView/updateAccountSettingsListForAccountTypes">, updateAccountSettingsListForAccountTypesFailure: import("@reduxjs/toolkit").ActionCreatorWithPayload<{
138
144
  status: ZeniAPIStatus;
@@ -1,8 +1,8 @@
1
1
  "use strict";
2
2
  var _a;
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.saveNewScheduleFailure = exports.saveNewSchedule = exports.saveAccountSettingsSuccess = exports.saveAccountSettingsLocalData = exports.saveAccountSettingsFailure = exports.saveAccountSettings = exports.retryJeScheduleSuccess = exports.retryJeScheduleFailure = exports.retryJeSchedule = exports.resetNewScheduleLocalData = exports.removeJeScheduleTransactionKey = exports.removeJeAccruedScheduleKey = exports.removeJeScheduleLocalDataById = exports.removeJeOneTimeAccrualLocalData = exports.removeFailedJeScheduleTransactionKey = exports.initializeNewSchedule = exports.initializeJeScheduleLocalData = exports.initializeAccountSettingsView = exports.ignoreRecommendedJeScheduleSuccess = exports.ignoreRecommendedJeScheduleFailure = exports.ignoreRecommendedJeSchedule = exports.fetchRecommendationForAccountSettings = exports.fetchOneTimeAccrualSuccess = exports.fetchOneTimeAccrualFailure = exports.fetchOneTimeAccrual = exports.fetchJeSchedulesSuccess = exports.fetchJeSchedulesPage = exports.fetchJeSchedulesFailure = exports.fetchJeSchedules = exports.fetchAccountSettingsListForAccountTypes = exports.revertOneTimeAccrual = exports.revertJeScheduleSuccess = exports.revertJeScheduleFailure = exports.revertJeSchedule = exports.promoteOneTimeAccrual = exports.oneTimeAccrualActionSuccess = exports.oneTimeAccrualActionFailure = exports.ignoreOneTimeAccrual = exports.createOneTimeAccrualSuccess = exports.createOneTimeAccrualFailure = exports.createOneTimeAccrual = exports.clearSearchTransactionsForNewSchedule = exports.clearJeScheduleLocalData = exports.clearJeAccountSettingsLocalData = exports.clearExpenseAutomationJESchedulesView = exports.initialState = exports.searchTransactionsInitialState = exports.newScheduleInitialState = exports.getBaseNewScheduleLocalData = exports.accountSettingsInitialState = void 0;
5
- exports.updateRecommendationForAccountSettingsFailure = exports.updateRecommendationForAccountSettings = exports.updateNewScheduleLocalData = exports.updateJeScheduleTransactionKeys = exports.updateJeScheduleLocalDataById = exports.updateJeOneTimeAccrualLocalData = exports.updateJEScheduleTypeFilter = exports.updateJESchedulesUIState = exports.updateAccountSettingsListForAccountTypesFailure = exports.updateAccountSettingsListForAccountTypes = exports.toggleJEPendingGroupCollapsed = exports.toggleJECreatedGroupCollapsed = exports.searchTransactionsForNewScheduleSuccess = exports.searchTransactionsForNewScheduleFailure = exports.searchTransactionsForNewSchedule = exports.saveNewScheduleSuccess = void 0;
4
+ exports.saveAccountSettingsLocalData = exports.saveAccountSettingsFailure = exports.saveAccountSettings = exports.retryJeScheduleSuccess = exports.retryJeScheduleFailure = exports.retryJeSchedule = exports.resetNewScheduleLocalData = exports.removeJeScheduleTransactionKey = exports.removeJeAccruedScheduleKey = exports.removeJeScheduleLocalDataById = exports.removeJeOneTimeAccrualLocalData = exports.removeFailedJeScheduleTransactionKey = exports.initializeNewSchedule = exports.initializeJeScheduleLocalData = exports.initializeAccountSettingsView = exports.ignoreRecommendedJeScheduleSuccess = exports.ignoreRecommendedJeScheduleFailure = exports.ignoreRecommendedJeSchedule = exports.fetchRecommendationForAccountSettings = exports.fetchOneTimeAccrualSuccess = exports.fetchOneTimeAccrualFailure = exports.fetchOneTimeAccrual = exports.fetchJeSchedulesSuccess = exports.fetchJeSchedulesPage = exports.fetchJeSchedulesFailure = exports.fetchFailedJePostingsSuccess = exports.fetchFailedJePostingsFailure = exports.fetchFailedJePostings = exports.fetchJeSchedules = exports.fetchAccountSettingsListForAccountTypes = exports.revertOneTimeAccrual = exports.revertJeScheduleSuccess = exports.revertJeScheduleFailure = exports.revertJeSchedule = exports.promoteOneTimeAccrual = exports.oneTimeAccrualActionSuccess = exports.oneTimeAccrualActionFailure = exports.ignoreOneTimeAccrual = exports.createOneTimeAccrualSuccess = exports.createOneTimeAccrualFailure = exports.createOneTimeAccrual = exports.clearSearchTransactionsForNewSchedule = exports.clearJeScheduleLocalData = exports.clearJeAccountSettingsLocalData = exports.clearExpenseAutomationJESchedulesView = exports.initialState = exports.searchTransactionsInitialState = exports.newScheduleInitialState = exports.getBaseNewScheduleLocalData = exports.accountSettingsInitialState = void 0;
5
+ exports.updateRecommendationForAccountSettingsFailure = exports.updateRecommendationForAccountSettings = exports.updateNewScheduleLocalData = exports.updateJeScheduleTransactionKeys = exports.updateJeScheduleLocalDataById = exports.updateJeOneTimeAccrualLocalData = exports.updateJEScheduleTypeFilter = exports.updateJESchedulesUIState = exports.updateAccountSettingsListForAccountTypesFailure = exports.updateAccountSettingsListForAccountTypes = exports.toggleJEPendingGroupCollapsed = exports.toggleJEFailedGroupCollapsed = exports.toggleJECreatedGroupCollapsed = exports.searchTransactionsForNewScheduleSuccess = exports.searchTransactionsForNewScheduleFailure = exports.searchTransactionsForNewSchedule = exports.saveNewScheduleSuccess = exports.saveNewScheduleFailure = exports.saveNewSchedule = exports.saveAccountSettingsSuccess = void 0;
6
6
  const toolkit_1 = require("@reduxjs/toolkit");
7
7
  const getNestedAccountIDHierarchyForReport_1 = require("../../../commonStateTypes/accountView/getNestedAccountIDHierarchyForReport");
8
8
  const timePeriod_1 = require("../../../commonStateTypes/timePeriod");
@@ -68,6 +68,10 @@ exports.initialState = {
68
68
  jeScheduleAccruedKeysByPeriodAndTab: {},
69
69
  jeScheduleOneTimeAccrualKeysByPeriodAndTab: {},
70
70
  failedJeScheduleTransactionKeysByPeriodAndTab: {},
71
+ failedJePostings: {
72
+ fetchState: { fetchState: 'Not-Started', error: undefined },
73
+ jeScheduleTransactionKeys: [],
74
+ },
71
75
  postStatusById: {},
72
76
  ignoreStatusById: {},
73
77
  revertStatusById: {},
@@ -77,6 +81,9 @@ exports.initialState = {
77
81
  oneTimeAccrualFetchStatusById: {},
78
82
  uiState: {
79
83
  createdGroupCollapsed: false,
84
+ // Expanded, like the other two. A failed posting is the only thing on this page that is
85
+ // actively wrong, so folding it away by default would hide the reason to open the tab.
86
+ failedGroupCollapsed: false,
80
87
  pendingGroupCollapsed: false,
81
88
  scrollPosition: { scrollTop: 0, scrollLeft: undefined },
82
89
  searchString: '',
@@ -148,6 +155,53 @@ const expenseAutomationJESchedulesView = (0, toolkit_1.createSlice)({
148
155
  };
149
156
  },
150
157
  },
158
+ /**
159
+ * The Failed Postings list, fetched separately from the schedules it belongs to.
160
+ *
161
+ * No period and no tab in any of these three: the endpoint applies no date window, so there is
162
+ * one list per tenant and one slot to hold it. `refreshInBackground` keeps a silent refetch
163
+ * from dropping a skeleton over rows already on screen, the same way refreshStatus does for
164
+ * the schedules list.
165
+ */
166
+ fetchFailedJePostings: {
167
+ reducer(draft, action) {
168
+ if (!action.payload.refreshInBackground) {
169
+ draft.failedJePostings.fetchState = {
170
+ fetchState: 'In-Progress',
171
+ error: undefined,
172
+ };
173
+ }
174
+ },
175
+ prepare(refreshInBackground = false) {
176
+ return { payload: { refreshInBackground } };
177
+ },
178
+ },
179
+ fetchFailedJePostingsSuccess: {
180
+ reducer(draft, action) {
181
+ draft.failedJePostings = {
182
+ fetchState: { fetchState: 'Completed', error: undefined },
183
+ jeScheduleTransactionKeys: action.payload.failedJeSchedules.map((schedule) => (0, jeSchedulesPayload_1.getJEScheduleTransactionDetailKeyPayload)(schedule.base_transaction)),
184
+ };
185
+ },
186
+ prepare(failedJeSchedules) {
187
+ return { payload: { failedJeSchedules } };
188
+ },
189
+ },
190
+ fetchFailedJePostingsFailure: {
191
+ reducer(draft, action) {
192
+ // Keys are deliberately left alone. The schedules list clears its buckets on failure so an
193
+ // errored tab cannot read as "these are all the schedules there are", but this list is a
194
+ // set of outstanding problems -- showing the last known ones beside an error beats
195
+ // implying there are none.
196
+ draft.failedJePostings.fetchState = {
197
+ fetchState: 'Error',
198
+ error: action.payload.status,
199
+ };
200
+ },
201
+ prepare(status) {
202
+ return { payload: { status } };
203
+ },
204
+ },
151
205
  fetchJeSchedulesSuccess(draft, action) {
152
206
  const { jeSchedules, selectedPeriod, selectedPageTab, failedJeSchedules, accruedJeSchedules, oneTimeAccruals, refreshViewInBackground, } = action.payload;
153
207
  const periodTabKey = (0, jeSchedulesViewState_1.getJESchedulePeriodTabKey)(selectedPageTab, (0, timePeriod_1.toMonthYearPeriodId)(selectedPeriod));
@@ -364,6 +418,8 @@ const expenseAutomationJESchedulesView = (0, toolkit_1.createSlice)({
364
418
  reducer(draft, action) {
365
419
  const { failedJeScheduleKey } = action.payload;
366
420
  removeKeyFromEveryTabSlot(draft.failedJeScheduleTransactionKeysByPeriodAndTab, failedJeScheduleKey);
421
+ draft.failedJePostings.jeScheduleTransactionKeys =
422
+ draft.failedJePostings.jeScheduleTransactionKeys.filter((jeScheduleKey) => jeScheduleKey !== failedJeScheduleKey);
367
423
  },
368
424
  },
369
425
  initializeJeScheduleLocalData: {
@@ -706,6 +762,9 @@ const expenseAutomationJESchedulesView = (0, toolkit_1.createSlice)({
706
762
  draft.uiState.createdGroupCollapsed =
707
763
  !draft.uiState.createdGroupCollapsed;
708
764
  },
765
+ toggleJEFailedGroupCollapsed(draft) {
766
+ draft.uiState.failedGroupCollapsed = !draft.uiState.failedGroupCollapsed;
767
+ },
709
768
  clearExpenseAutomationJESchedulesView(draft) {
710
769
  Object.assign(draft, exports.initialState);
711
770
  },
@@ -716,7 +775,7 @@ const expenseAutomationJESchedulesView = (0, toolkit_1.createSlice)({
716
775
  });
717
776
  },
718
777
  });
719
- _a = expenseAutomationJESchedulesView.actions, exports.clearExpenseAutomationJESchedulesView = _a.clearExpenseAutomationJESchedulesView, exports.clearJeAccountSettingsLocalData = _a.clearJeAccountSettingsLocalData, exports.clearJeScheduleLocalData = _a.clearJeScheduleLocalData, exports.clearSearchTransactionsForNewSchedule = _a.clearSearchTransactionsForNewSchedule, exports.createOneTimeAccrual = _a.createOneTimeAccrual, exports.createOneTimeAccrualFailure = _a.createOneTimeAccrualFailure, exports.createOneTimeAccrualSuccess = _a.createOneTimeAccrualSuccess, exports.ignoreOneTimeAccrual = _a.ignoreOneTimeAccrual, exports.oneTimeAccrualActionFailure = _a.oneTimeAccrualActionFailure, exports.oneTimeAccrualActionSuccess = _a.oneTimeAccrualActionSuccess, exports.promoteOneTimeAccrual = _a.promoteOneTimeAccrual, exports.revertJeSchedule = _a.revertJeSchedule, exports.revertJeScheduleFailure = _a.revertJeScheduleFailure, exports.revertJeScheduleSuccess = _a.revertJeScheduleSuccess, exports.revertOneTimeAccrual = _a.revertOneTimeAccrual, exports.fetchAccountSettingsListForAccountTypes = _a.fetchAccountSettingsListForAccountTypes, exports.fetchJeSchedules = _a.fetchJeSchedules, exports.fetchJeSchedulesFailure = _a.fetchJeSchedulesFailure, exports.fetchJeSchedulesPage = _a.fetchJeSchedulesPage, exports.fetchJeSchedulesSuccess = _a.fetchJeSchedulesSuccess, exports.fetchOneTimeAccrual = _a.fetchOneTimeAccrual, exports.fetchOneTimeAccrualFailure = _a.fetchOneTimeAccrualFailure, exports.fetchOneTimeAccrualSuccess = _a.fetchOneTimeAccrualSuccess, exports.fetchRecommendationForAccountSettings = _a.fetchRecommendationForAccountSettings, exports.ignoreRecommendedJeSchedule = _a.ignoreRecommendedJeSchedule, exports.ignoreRecommendedJeScheduleFailure = _a.ignoreRecommendedJeScheduleFailure, exports.ignoreRecommendedJeScheduleSuccess = _a.ignoreRecommendedJeScheduleSuccess, exports.initializeAccountSettingsView = _a.initializeAccountSettingsView, exports.initializeJeScheduleLocalData = _a.initializeJeScheduleLocalData, exports.initializeNewSchedule = _a.initializeNewSchedule, exports.removeFailedJeScheduleTransactionKey = _a.removeFailedJeScheduleTransactionKey, exports.removeJeOneTimeAccrualLocalData = _a.removeJeOneTimeAccrualLocalData, exports.removeJeScheduleLocalDataById = _a.removeJeScheduleLocalDataById, exports.removeJeAccruedScheduleKey = _a.removeJeAccruedScheduleKey, exports.removeJeScheduleTransactionKey = _a.removeJeScheduleTransactionKey, exports.resetNewScheduleLocalData = _a.resetNewScheduleLocalData, exports.retryJeSchedule = _a.retryJeSchedule, exports.retryJeScheduleFailure = _a.retryJeScheduleFailure, exports.retryJeScheduleSuccess = _a.retryJeScheduleSuccess, exports.saveAccountSettings = _a.saveAccountSettings, exports.saveAccountSettingsFailure = _a.saveAccountSettingsFailure, exports.saveAccountSettingsLocalData = _a.saveAccountSettingsLocalData, exports.saveAccountSettingsSuccess = _a.saveAccountSettingsSuccess, exports.saveNewSchedule = _a.saveNewSchedule, exports.saveNewScheduleFailure = _a.saveNewScheduleFailure, exports.saveNewScheduleSuccess = _a.saveNewScheduleSuccess, exports.searchTransactionsForNewSchedule = _a.searchTransactionsForNewSchedule, exports.searchTransactionsForNewScheduleFailure = _a.searchTransactionsForNewScheduleFailure, exports.searchTransactionsForNewScheduleSuccess = _a.searchTransactionsForNewScheduleSuccess, exports.toggleJECreatedGroupCollapsed = _a.toggleJECreatedGroupCollapsed, exports.toggleJEPendingGroupCollapsed = _a.toggleJEPendingGroupCollapsed, exports.updateAccountSettingsListForAccountTypes = _a.updateAccountSettingsListForAccountTypes, exports.updateAccountSettingsListForAccountTypesFailure = _a.updateAccountSettingsListForAccountTypesFailure, exports.updateJESchedulesUIState = _a.updateJESchedulesUIState, exports.updateJEScheduleTypeFilter = _a.updateJEScheduleTypeFilter, exports.updateJeOneTimeAccrualLocalData = _a.updateJeOneTimeAccrualLocalData, exports.updateJeScheduleLocalDataById = _a.updateJeScheduleLocalDataById, exports.updateJeScheduleTransactionKeys = _a.updateJeScheduleTransactionKeys, exports.updateNewScheduleLocalData = _a.updateNewScheduleLocalData, exports.updateRecommendationForAccountSettings = _a.updateRecommendationForAccountSettings, exports.updateRecommendationForAccountSettingsFailure = _a.updateRecommendationForAccountSettingsFailure;
778
+ _a = expenseAutomationJESchedulesView.actions, exports.clearExpenseAutomationJESchedulesView = _a.clearExpenseAutomationJESchedulesView, exports.clearJeAccountSettingsLocalData = _a.clearJeAccountSettingsLocalData, exports.clearJeScheduleLocalData = _a.clearJeScheduleLocalData, exports.clearSearchTransactionsForNewSchedule = _a.clearSearchTransactionsForNewSchedule, exports.createOneTimeAccrual = _a.createOneTimeAccrual, exports.createOneTimeAccrualFailure = _a.createOneTimeAccrualFailure, exports.createOneTimeAccrualSuccess = _a.createOneTimeAccrualSuccess, exports.ignoreOneTimeAccrual = _a.ignoreOneTimeAccrual, exports.oneTimeAccrualActionFailure = _a.oneTimeAccrualActionFailure, exports.oneTimeAccrualActionSuccess = _a.oneTimeAccrualActionSuccess, exports.promoteOneTimeAccrual = _a.promoteOneTimeAccrual, exports.revertJeSchedule = _a.revertJeSchedule, exports.revertJeScheduleFailure = _a.revertJeScheduleFailure, exports.revertJeScheduleSuccess = _a.revertJeScheduleSuccess, exports.revertOneTimeAccrual = _a.revertOneTimeAccrual, exports.fetchAccountSettingsListForAccountTypes = _a.fetchAccountSettingsListForAccountTypes, exports.fetchJeSchedules = _a.fetchJeSchedules, exports.fetchFailedJePostings = _a.fetchFailedJePostings, exports.fetchFailedJePostingsFailure = _a.fetchFailedJePostingsFailure, exports.fetchFailedJePostingsSuccess = _a.fetchFailedJePostingsSuccess, exports.fetchJeSchedulesFailure = _a.fetchJeSchedulesFailure, exports.fetchJeSchedulesPage = _a.fetchJeSchedulesPage, exports.fetchJeSchedulesSuccess = _a.fetchJeSchedulesSuccess, exports.fetchOneTimeAccrual = _a.fetchOneTimeAccrual, exports.fetchOneTimeAccrualFailure = _a.fetchOneTimeAccrualFailure, exports.fetchOneTimeAccrualSuccess = _a.fetchOneTimeAccrualSuccess, exports.fetchRecommendationForAccountSettings = _a.fetchRecommendationForAccountSettings, exports.ignoreRecommendedJeSchedule = _a.ignoreRecommendedJeSchedule, exports.ignoreRecommendedJeScheduleFailure = _a.ignoreRecommendedJeScheduleFailure, exports.ignoreRecommendedJeScheduleSuccess = _a.ignoreRecommendedJeScheduleSuccess, exports.initializeAccountSettingsView = _a.initializeAccountSettingsView, exports.initializeJeScheduleLocalData = _a.initializeJeScheduleLocalData, exports.initializeNewSchedule = _a.initializeNewSchedule, exports.removeFailedJeScheduleTransactionKey = _a.removeFailedJeScheduleTransactionKey, exports.removeJeOneTimeAccrualLocalData = _a.removeJeOneTimeAccrualLocalData, exports.removeJeScheduleLocalDataById = _a.removeJeScheduleLocalDataById, exports.removeJeAccruedScheduleKey = _a.removeJeAccruedScheduleKey, exports.removeJeScheduleTransactionKey = _a.removeJeScheduleTransactionKey, exports.resetNewScheduleLocalData = _a.resetNewScheduleLocalData, exports.retryJeSchedule = _a.retryJeSchedule, exports.retryJeScheduleFailure = _a.retryJeScheduleFailure, exports.retryJeScheduleSuccess = _a.retryJeScheduleSuccess, exports.saveAccountSettings = _a.saveAccountSettings, exports.saveAccountSettingsFailure = _a.saveAccountSettingsFailure, exports.saveAccountSettingsLocalData = _a.saveAccountSettingsLocalData, exports.saveAccountSettingsSuccess = _a.saveAccountSettingsSuccess, exports.saveNewSchedule = _a.saveNewSchedule, exports.saveNewScheduleFailure = _a.saveNewScheduleFailure, exports.saveNewScheduleSuccess = _a.saveNewScheduleSuccess, exports.searchTransactionsForNewSchedule = _a.searchTransactionsForNewSchedule, exports.searchTransactionsForNewScheduleFailure = _a.searchTransactionsForNewScheduleFailure, exports.searchTransactionsForNewScheduleSuccess = _a.searchTransactionsForNewScheduleSuccess, exports.toggleJECreatedGroupCollapsed = _a.toggleJECreatedGroupCollapsed, exports.toggleJEFailedGroupCollapsed = _a.toggleJEFailedGroupCollapsed, exports.toggleJEPendingGroupCollapsed = _a.toggleJEPendingGroupCollapsed, exports.updateAccountSettingsListForAccountTypes = _a.updateAccountSettingsListForAccountTypes, exports.updateAccountSettingsListForAccountTypesFailure = _a.updateAccountSettingsListForAccountTypesFailure, exports.updateJESchedulesUIState = _a.updateJESchedulesUIState, exports.updateJEScheduleTypeFilter = _a.updateJEScheduleTypeFilter, exports.updateJeOneTimeAccrualLocalData = _a.updateJeOneTimeAccrualLocalData, exports.updateJeScheduleLocalDataById = _a.updateJeScheduleLocalDataById, exports.updateJeScheduleTransactionKeys = _a.updateJeScheduleTransactionKeys, exports.updateNewScheduleLocalData = _a.updateNewScheduleLocalData, exports.updateRecommendationForAccountSettings = _a.updateRecommendationForAccountSettings, exports.updateRecommendationForAccountSettingsFailure = _a.updateRecommendationForAccountSettingsFailure;
720
779
  exports.default = expenseAutomationJESchedulesView.reducer;
721
780
  const doUpdateRecommendationForAccountSettings = (draft, payload) => {
722
781
  const { prepaidExpensesRecommendations, fixedAssetsRecommendations, accruedExpensesRecommendations, } = (0, accountTypeRecommendationPayload_1.mapAccountTypeRecommendationPayloadToAccountRecommendationByType)(payload);
@@ -47,6 +47,7 @@ export interface ExpenseAutomationJESchedulesViewSelector extends SelectorView {
47
47
  completionStatus: CompletionStatusType;
48
48
  createdSchedules: JEScheduleRow[];
49
49
  createdTotal: number;
50
+ failedPostings: JEScheduledTransactionWithFailedEntries[];
50
51
  hasLoadedForSelectedTab: boolean;
51
52
  ignoreStatusById: Record<JEScheduleTransactionKey, FetchStateAndError>;
52
53
  jeScheduleLocalDataById: Record<ID, JEScheduleLocalData>;
@@ -61,6 +62,7 @@ export interface ExpenseAutomationJESchedulesViewSelector extends SelectorView {
61
62
  pendingReviewTotal: number;
62
63
  postStatusById: Record<ID, FetchStateAndError>;
63
64
  refreshStatus: FetchStateAndError;
65
+ /** @deprecated Alias of `failedPostings`. Remove once webc and app-ui are repointed. */
64
66
  resolveSchedules: JEScheduledTransactionWithFailedEntries[];
65
67
  revertStatusById: Record<JEScheduleKey | JEScheduleTransactionKey, FetchStateAndError>;
66
68
  scheduleCountsByTab: Partial<Record<JEPageTab, number>>;
@@ -225,7 +225,7 @@ function getExpenseAutomationJESchedulesView(state) {
225
225
  const accruedSchedules = [];
226
226
  const oneTimeAccruals = [];
227
227
  const { accountState, accountListState, classState, classListState, expenseAutomationJESchedulesViewState, expenseAutomationViewState, monthEndCloseChecksState, } = state;
228
- const { jeScheduleTransactionKeysByPeriodAndTab, jeScheduleAccruedKeysByPeriodAndTab, jeScheduleOneTimeAccrualKeysByPeriodAndTab, failedJeScheduleTransactionKeysByPeriodAndTab, jeScheduleLocalDataById, postStatusById, ignoreStatusById, revertStatusById, oneTimeAccrualActionStatusById, uiState, error, fetchState, refreshStatus, newSchedule, } = expenseAutomationJESchedulesViewState;
228
+ const { jeScheduleTransactionKeysByPeriodAndTab, jeScheduleAccruedKeysByPeriodAndTab, jeScheduleOneTimeAccrualKeysByPeriodAndTab, failedJePostings, jeScheduleLocalDataById, postStatusById, ignoreStatusById, revertStatusById, oneTimeAccrualActionStatusById, uiState, error, fetchState, refreshStatus, newSchedule, } = expenseAutomationJESchedulesViewState;
229
229
  const accountSettingsView = (0, jeAccountSettingsViewSelector_1.getJEAccountSettingsView)(state);
230
230
  const newScheduleView = newSchedule.selectedDetailScheduleType === 'fixed_assets' ||
231
231
  newSchedule.selectedDetailScheduleType === 'prepaid_expenses'
@@ -264,15 +264,11 @@ function getExpenseAutomationJESchedulesView(state) {
264
264
  const jeScheduleTransactionKeysForSelectedTab = selectedPeriodTabKey != null
265
265
  ? jeScheduleTransactionKeysByPeriodAndTab[selectedPeriodTabKey]
266
266
  : undefined;
267
- const failedJeScheduleTransactionKeysForSelectedTab = selectedPeriodTabKey != null
268
- ? failedJeScheduleTransactionKeysByPeriodAndTab[selectedPeriodTabKey]
269
- : undefined;
270
267
  // Distinguishes "this tab has never been fetched for this period" from "fetched and genuinely
271
268
  // empty". Without it the page cannot tell a first visit from a real empty state, and renders an
272
269
  // empty table where it should render a skeleton.
273
270
  const hasLoadedForSelectedTab = jeScheduleTransactionKeysForSelectedTab != null;
274
271
  const jeScheduleTransactionKeys = jeScheduleTransactionKeysForSelectedTab ?? [];
275
- const failedJeScheduleTransactionKeys = failedJeScheduleTransactionKeysForSelectedTab ?? [];
276
272
  const jeScheduleAccruedKeys = (selectedPeriodTabKey != null
277
273
  ? jeScheduleAccruedKeysByPeriodAndTab[selectedPeriodTabKey]
278
274
  : undefined) ?? [];
@@ -295,11 +291,16 @@ function getExpenseAutomationJESchedulesView(state) {
295
291
  accruedSchedules.push(accruedScheduleDetails);
296
292
  }
297
293
  });
298
- failedJeScheduleTransactionKeys.forEach((jeScheduleKey) => {
294
+ // Sourced from the tenant-wide bucket, not the per-period one: a failed posting belongs to no
295
+ // close month, and windowing it is what kept older failures off the only tab that can resolve
296
+ // them.
297
+ failedJePostings.jeScheduleTransactionKeys.forEach((jeScheduleKey) => {
299
298
  const jeScheduleDetails = (0, jeSchedulesSelector_1.getJEScheduledTransactionByJEScheduleTransactionKey)(jeScheduleKey, state);
300
299
  failedJeScheduledTransaction.push(jeScheduleDetails);
301
300
  });
302
301
  const { sortKey, sortOrder, selectedJETypeFilter } = uiState;
302
+ const matchesTypeFilter = (schedule) => selectedJETypeFilter == null ||
303
+ getJEScheduleRowType(schedule) === selectedJETypeFilter;
303
304
  const unsortedResolveSchedules = [];
304
305
  failedJeScheduledTransaction.forEach((schedule) => {
305
306
  schedule.scheduledJournalEntry.forEach((journalEntry) => {
@@ -309,10 +310,7 @@ function getExpenseAutomationJESchedulesView(state) {
309
310
  });
310
311
  });
311
312
  });
312
- const resolveSchedules = sortJEScheduledTransactions(unsortedResolveSchedules, sortKey, sortOrder);
313
- const validResolveForPending = unsortedResolveSchedules.filter((r) => jeScheduleLocalDataById[r.scheduledJournalEntry.scheduledJournalEntryID] != null);
314
- const matchesTypeFilter = (schedule) => selectedJETypeFilter == null ||
315
- getJEScheduleRowType(schedule) === selectedJETypeFilter;
313
+ const resolveSchedules = sortJEScheduledTransactions(unsortedResolveSchedules.filter(matchesTypeFilter), sortKey, sortOrder);
316
314
  const isCreatedStatus = (schedule) => {
317
315
  // An accrual is "created" once it is not review work: posted, reversing, reversed, or in a
318
316
  // transient posting claim.
@@ -330,7 +328,6 @@ function getExpenseAutomationJESchedulesView(state) {
330
328
  ];
331
329
  const pendingReviewSchedules = sortJEScheduledTransactions([
332
330
  ...allScheduleRows.filter((schedule) => isPendingReviewRow(schedule) && matchesTypeFilter(schedule)),
333
- ...validResolveForPending,
334
331
  ], sortKey, sortOrder);
335
332
  const createdSchedules = sortJEScheduledTransactions(allScheduleRows.filter((schedule) => isCreatedStatus(schedule) && matchesTypeFilter(schedule)), sortKey, sortOrder);
336
333
  // KPI summary — derived from unfiltered schedules so counts are always accurate
@@ -449,6 +446,9 @@ function getExpenseAutomationJESchedulesView(state) {
449
446
  kpiSummary,
450
447
  pendingReviewSchedules,
451
448
  pendingReviewTotal,
449
+ failedPostings: resolveSchedules,
450
+ // Deprecated alias of failedPostings. Still read by the section's footer-form effect and by
451
+ // app-ui analytics; remove once both are repointed.
452
452
  resolveSchedules: resolveSchedules,
453
453
  accountSettingsView,
454
454
  newScheduleView,
@@ -19,6 +19,7 @@ export type JESchedulePeriodTabKey = `${JEPageTab}_${MonthYearPeriodId}`;
19
19
  export declare const getJESchedulePeriodTabKey: (selectedPageTab: JEPageTab, monthYearPeriodId: MonthYearPeriodId) => JESchedulePeriodTabKey;
20
20
  export interface JESchedulesViewUIState {
21
21
  createdGroupCollapsed: boolean;
22
+ failedGroupCollapsed: boolean;
22
23
  pendingGroupCollapsed: boolean;
23
24
  scrollPosition: {
24
25
  scrollTop: number;
@@ -41,8 +42,22 @@ export interface JEScheduleLocalData {
41
42
  updatedClassId?: ID;
42
43
  updatedVendor?: VendorBase;
43
44
  }
45
+ /**
46
+ * The Failed Postings bucket, and the only list on this page that is not scoped to a period.
47
+ *
48
+ * A failed posting is broken until someone resolves it, whatever month it belongs to, so windowing
49
+ * it the way the schedule lists are windowed is what hid a March failure from the September close.
50
+ * It therefore has no period-tab key, and its own fetch state rather than the slice's: the list is
51
+ * fetched separately from `/1.0/expenses_automation/failed_je_postings`, so a slow or failing
52
+ * lookup here must not blank the schedules table, or vice versa.
53
+ */
54
+ export interface FailedJePostingsState {
55
+ fetchState: FetchStateAndError;
56
+ jeScheduleTransactionKeys: JEScheduleTransactionKey[];
57
+ }
44
58
  export interface JESchedulesViewState extends FetchedState {
45
59
  accountSettings: AccountSettingsState;
60
+ failedJePostings: FailedJePostingsState;
46
61
  failedJeScheduleTransactionKeysByPeriodAndTab: Record<JESchedulePeriodTabKey, JEScheduleTransactionKey[]>;
47
62
  ignoreStatusById: Record<JEScheduleTransactionKey, FetchStateAndError>;
48
63
  jeScheduleAccruedKeysByPeriodAndTab: Record<JESchedulePeriodTabKey, JEScheduleKey[]>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zeniai/client-epic-state",
3
- "version": "5.2.49",
3
+ "version": "5.2.50-betaRD1",
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",