@zeniai/client-epic-state 5.0.91 → 5.0.92-betaNB1

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 (62) hide show
  1. package/lib/commonStateTypes/viewAndReport/agingReportStateTypes.d.ts +1 -1
  2. package/lib/entity/accountRecon/accountReconPayload.d.ts +20 -1
  3. package/lib/entity/accountRecon/accountReconPayload.js +23 -1
  4. package/lib/entity/accountRecon/accountReconSelector.d.ts +6 -1
  5. package/lib/entity/accountRecon/accountReconSelector.js +5 -0
  6. package/lib/entity/accountRecon/accountReconState.d.ts +19 -0
  7. package/lib/entity/monthEndCloseChecks/monthEndCloseChecksPayload.d.ts +7 -0
  8. package/lib/entity/monthEndCloseChecks/monthEndCloseChecksPayload.js +9 -0
  9. package/lib/entity/monthEndCloseChecks/monthEndCloseChecksState.d.ts +7 -0
  10. package/lib/entity/task/taskPayload.d.ts +3 -1
  11. package/lib/entity/task/taskPayload.js +2 -0
  12. package/lib/entity/task/taskState.d.ts +2 -0
  13. package/lib/epic.d.ts +7 -3
  14. package/lib/epic.js +7 -3
  15. package/lib/esm/entity/accountRecon/accountReconPayload.js +23 -1
  16. package/lib/esm/entity/accountRecon/accountReconSelector.js +5 -0
  17. package/lib/esm/entity/monthEndCloseChecks/monthEndCloseChecksPayload.js +9 -0
  18. package/lib/esm/entity/task/taskPayload.js +2 -0
  19. package/lib/esm/epic.js +7 -3
  20. package/lib/esm/index.js +5 -5
  21. package/lib/esm/view/companyTaskManagerView/companyTaskManagerViewReducer.js +39 -11
  22. package/lib/esm/view/companyTaskManagerView/companyTaskManagerViewSelector.js +14 -4
  23. package/lib/esm/view/companyTaskManagerView/epics/fetchCockpitContextEpic.js +38 -0
  24. package/lib/esm/view/companyTaskManagerView/epics/fetchCompanyTaskManagerViewEpic.js +37 -28
  25. package/lib/esm/view/companyTaskManagerView/epics/updateCompanyTaskManagerViewFiltersEpic.js +16 -0
  26. package/lib/esm/view/companyView/epic/fetchAllCockpitViewsEpic.js +2 -2
  27. package/lib/esm/view/expenseAutomationView/epics/accountRecon/parseStatementEpic.js +34 -0
  28. package/lib/esm/view/expenseAutomationView/epics/accountRecon/updateStatementInfoEpic.js +42 -0
  29. package/lib/esm/view/expenseAutomationView/payload/reconciliationPayload.js +73 -0
  30. package/lib/esm/view/expenseAutomationView/reducers/reconciliationViewReducer.js +89 -3
  31. package/lib/esm/view/expenseAutomationView/selectors/reconciliationViewSelector.js +20 -0
  32. package/lib/esm/view/expenseAutomationView/types/reconciliationViewState.js +20 -0
  33. package/lib/index.d.ts +11 -10
  34. package/lib/index.js +43 -33
  35. package/lib/view/companyTaskManagerView/companyTaskManagerViewPayload.d.ts +19 -11
  36. package/lib/view/companyTaskManagerView/companyTaskManagerViewReducer.d.ts +10 -4
  37. package/lib/view/companyTaskManagerView/companyTaskManagerViewReducer.js +40 -12
  38. package/lib/view/companyTaskManagerView/companyTaskManagerViewSelector.d.ts +2 -0
  39. package/lib/view/companyTaskManagerView/companyTaskManagerViewSelector.js +13 -3
  40. package/lib/view/companyTaskManagerView/companyTaskManagerViewState.d.ts +11 -3
  41. package/lib/view/companyTaskManagerView/epics/fetchCockpitContextEpic.d.ts +10 -0
  42. package/lib/view/companyTaskManagerView/epics/fetchCockpitContextEpic.js +42 -0
  43. package/lib/view/companyTaskManagerView/epics/fetchCompanyTaskManagerViewEpic.d.ts +1 -3
  44. package/lib/view/companyTaskManagerView/epics/fetchCompanyTaskManagerViewEpic.js +37 -28
  45. package/lib/view/companyTaskManagerView/epics/updateCompanyTaskManagerViewFiltersEpic.d.ts +6 -0
  46. package/lib/view/companyTaskManagerView/epics/updateCompanyTaskManagerViewFiltersEpic.js +20 -0
  47. package/lib/view/companyView/epic/fetchAllCockpitViewsEpic.js +2 -2
  48. package/lib/view/companyView/types/cockpitTypes.d.ts +2 -1
  49. package/lib/view/expenseAutomationView/epics/accountRecon/parseStatementEpic.d.ts +6 -0
  50. package/lib/view/expenseAutomationView/epics/accountRecon/parseStatementEpic.js +38 -0
  51. package/lib/view/expenseAutomationView/epics/accountRecon/updateStatementInfoEpic.d.ts +19 -0
  52. package/lib/view/expenseAutomationView/epics/accountRecon/updateStatementInfoEpic.js +46 -0
  53. package/lib/view/expenseAutomationView/payload/reconciliationPayload.d.ts +83 -1
  54. package/lib/view/expenseAutomationView/payload/reconciliationPayload.js +75 -0
  55. package/lib/view/expenseAutomationView/reducers/reconciliationViewReducer.d.ts +35 -3
  56. package/lib/view/expenseAutomationView/reducers/reconciliationViewReducer.js +90 -4
  57. package/lib/view/expenseAutomationView/selectorTypes/reconciliationViewSelectorTypes.d.ts +5 -1
  58. package/lib/view/expenseAutomationView/selectors/reconciliationViewSelector.js +20 -0
  59. package/lib/view/expenseAutomationView/types/reconciliationViewState.d.ts +128 -0
  60. package/lib/view/expenseAutomationView/types/reconciliationViewState.js +21 -0
  61. package/lib/view/vendorFiling1099/vendorFiling1099List/vendorFiling1099ListState.d.ts +1 -1
  62. package/package.json +1 -1
@@ -3,7 +3,8 @@ export const initialUIState = {
3
3
  filterText: '',
4
4
  sortKey: 'dueDate',
5
5
  sortOrder: 'descending',
6
- pageToken: null,
6
+ from: 0,
7
+ filters: {},
7
8
  };
8
9
  const initialMetrics = {
9
10
  closed: 0,
@@ -18,6 +19,13 @@ export const initialState = {
18
19
  metrics: initialMetrics,
19
20
  metricsFetchState: 'Not-Started',
20
21
  uiState: initialUIState,
22
+ cockpitContext: {
23
+ fetchState: 'Not-Started',
24
+ error: undefined,
25
+ companyIds: [],
26
+ userIds: [],
27
+ userGroupIds: [],
28
+ },
21
29
  hasValidState() {
22
30
  return this.fetchState == 'Completed';
23
31
  },
@@ -28,10 +36,11 @@ const companyTaskManagerView = createSlice({
28
36
  initialState,
29
37
  reducers: {
30
38
  fetchCompanyTaskManagerView: {
31
- prepare(pageToken, sortKey, sortOrder, keepExistingListItems = true, searchString) {
39
+ prepare(from, sortKey, sortOrder, keepExistingListItems = true, searchString, filters) {
32
40
  return {
33
41
  payload: {
34
- pageToken,
42
+ from,
43
+ filters,
35
44
  searchString,
36
45
  sortKey,
37
46
  sortOrder,
@@ -40,10 +49,10 @@ const companyTaskManagerView = createSlice({
40
49
  };
41
50
  },
42
51
  reducer(draft, action) {
43
- const { pageToken, searchString, sortKey, sortOrder, keepExistingListItems, } = action.payload;
52
+ const { from, searchString, sortKey, sortOrder, keepExistingListItems } = action.payload;
44
53
  draft.uiState.sortKey = sortKey;
45
54
  draft.uiState.sortOrder = sortOrder;
46
- draft.uiState.pageToken = pageToken;
55
+ draft.uiState.from = from;
47
56
  if (keepExistingListItems === false) {
48
57
  draft.taskIds = [];
49
58
  }
@@ -56,15 +65,14 @@ const companyTaskManagerView = createSlice({
56
65
  },
57
66
  updateCompanyTaskManagerViewOnSuccess(draft, action) {
58
67
  const { data } = action.payload;
59
- const { tasks, query, total_count } = data;
68
+ const tasks = data.aggregations.group_by_company_name.buckets.flatMap((bucket) => bucket.tasks);
60
69
  draft.fetchState = 'Completed';
61
70
  draft.error = undefined;
62
71
  draft.taskIds = [...draft.taskIds, ...tasks.map((task) => task.task_id)];
63
- // consider total count only when fetching for first time
64
- if (draft.uiState.filterText === '' && draft.uiState.pageToken == null) {
65
- draft.totalTaskCount = total_count;
72
+ if (draft.uiState.filterText === '' && draft.uiState.from === 0) {
73
+ draft.totalTaskCount = data.total;
66
74
  }
67
- draft.uiState.pageToken = query.page_token;
75
+ draft.uiState.from = data.from + data.limit;
68
76
  },
69
77
  updateCompanyTaskManagerViewOnFailure: {
70
78
  reducer(draft, action) {
@@ -85,10 +93,30 @@ const companyTaskManagerView = createSlice({
85
93
  updateTaskManagerMetricsOnFailure(draft) {
86
94
  draft.metricsFetchState = 'Error';
87
95
  },
96
+ updateCompanyTaskManagerViewFilters(draft, action) {
97
+ if (action.payload.filters != null) {
98
+ draft.uiState.filters = action.payload.filters;
99
+ }
100
+ },
101
+ fetchCockpitContext(draft) {
102
+ draft.cockpitContext.fetchState = 'In-Progress';
103
+ draft.cockpitContext.error = undefined;
104
+ },
105
+ updateCockpitContextOnSuccess(draft, action) {
106
+ draft.cockpitContext.fetchState = 'Completed';
107
+ draft.cockpitContext.error = undefined;
108
+ draft.cockpitContext.companyIds = action.payload.companyIds;
109
+ draft.cockpitContext.userIds = action.payload.userIds;
110
+ draft.cockpitContext.userGroupIds = action.payload.userGroupIds;
111
+ },
112
+ updateCockpitContextOnFailure(draft, action) {
113
+ draft.cockpitContext.fetchState = 'Error';
114
+ draft.cockpitContext.error = action.payload;
115
+ },
88
116
  clearCompanyTaskManagerView(draft) {
89
117
  Object.assign(draft, initialState);
90
118
  },
91
119
  },
92
120
  });
93
- export const { fetchCompanyTaskManagerView, updateCompanyTaskManagerViewOnSuccess, updateCompanyTaskManagerViewOnFailure, fetchTaskManagerMetrics, updateTaskManagerMetrics, updateTaskManagerMetricsOnFailure, clearCompanyTaskManagerView, } = companyTaskManagerView.actions;
121
+ export const { fetchCompanyTaskManagerView, updateCompanyTaskManagerViewOnSuccess, updateCompanyTaskManagerViewOnFailure, fetchTaskManagerMetrics, updateTaskManagerMetrics, updateTaskManagerMetricsOnFailure, clearCompanyTaskManagerView, updateCompanyTaskManagerViewFilters, fetchCockpitContext, updateCockpitContextOnSuccess, updateCockpitContextOnFailure, } = companyTaskManagerView.actions;
94
122
  export default companyTaskManagerView.reducer;
@@ -1,10 +1,11 @@
1
1
  import { createSelector } from '@reduxjs/toolkit';
2
- import { getCompanyByCompanyId } from '../../entity/company/companySelector';
2
+ import { reduceAnyFetchState } from '../../commonStateTypes/reduceFetchState';
3
+ import { getCompanyByCompanyId, getCompanyByCompanyIds, } from '../../entity/company/companySelector';
3
4
  import { getTagsByIds } from '../../entity/tag/tagSelector';
4
5
  import { getTaskById } from '../../entity/task/taskSelector';
5
6
  import { getUserByUserId, getUsersByUserIds, } from '../../entity/user/userSelector';
6
7
  export const getCompanyTaskManagerView = createSelector((state) => state.companyTaskManagerViewState, (state) => state.companyState, (state) => state.userState, (state) => state.taskState, (state) => state.tagState, (companyTaskManagerViewState, companyState, userState, taskState, tagState) => {
7
- const { taskIds, uiState, fetchState, error, totalTaskCount, metrics, metricsFetchState, } = companyTaskManagerViewState;
8
+ const { taskIds, uiState, totalTaskCount, metrics, metricsFetchState } = companyTaskManagerViewState;
8
9
  const tasksList = [];
9
10
  taskIds.forEach((taskId) => {
10
11
  const task = getTaskById(taskState, taskId);
@@ -26,13 +27,22 @@ export const getCompanyTaskManagerView = createSelector((state) => state.company
26
27
  }
27
28
  }
28
29
  });
30
+ const allCompanyIds = companyTaskManagerViewState.cockpitContext.companyIds;
31
+ const companies = getCompanyByCompanyIds(companyState, allCompanyIds).map((c) => c.company);
32
+ const users = getUsersByUserIds(userState, companyTaskManagerViewState.cockpitContext.userIds);
33
+ const fetchStateAndError = reduceAnyFetchState([
34
+ companyTaskManagerViewState,
35
+ companyTaskManagerViewState.cockpitContext,
36
+ ]);
29
37
  return {
38
+ companies,
30
39
  metrics,
31
40
  metricsFetchState,
32
41
  tasksList,
33
42
  uiState,
34
- fetchState,
35
- error,
43
+ fetchState: fetchStateAndError.fetchState,
44
+ error: fetchStateAndError.error,
36
45
  totalTaskCount,
46
+ users,
37
47
  };
38
48
  });
@@ -0,0 +1,38 @@
1
+ import { from, of } from 'rxjs';
2
+ import { catchError, filter, mergeMap, switchMap } from 'rxjs/operators';
3
+ import { updateCompanies } from '../../../entity/company/companyReducer';
4
+ import { updateAllUsers } from '../../../entity/user/userReducer';
5
+ import { updateAllUserGroups } from '../../../entity/userGroups/userGroupsReducer';
6
+ import { createZeniAPIStatus, isSuccessResponse } from '../../../responsePayload';
7
+ import { fetchCockpitContext, updateCockpitContextOnFailure, updateCockpitContextOnSuccess, } from '../companyTaskManagerViewReducer';
8
+ export const fetchCockpitContextEpic = (actions$, _state$, zeniAPI) => actions$.pipe(filter(fetchCockpitContext.match), switchMap(() => zeniAPI
9
+ .getJSON(`${zeniAPI.apiEndPoints.tenantMicroServiceBaseUrl}/1.0/cockpit-panels/cockpit-context`)
10
+ .pipe(mergeMap((response) => {
11
+ if (isSuccessResponse(response) && response.data != null) {
12
+ const companies = response.data?.companies ?? [];
13
+ const users = response.data?.users ?? [];
14
+ const userGroups = response.data?.user_groups ?? [];
15
+ const companyIds = companies.map((company) => company.company_id);
16
+ const userIds = users.map((user) => user.user_id);
17
+ const userGroupIds = userGroups.map((userGroup) => userGroup.id);
18
+ const actions = [
19
+ updateCompanies({
20
+ payload: response.data.companies,
21
+ schema: {},
22
+ }),
23
+ updateAllUsers({ users: response.data.users }),
24
+ updateAllUserGroups({
25
+ userGroups: response.data.user_groups,
26
+ }),
27
+ updateCockpitContextOnSuccess({
28
+ companyIds,
29
+ userIds,
30
+ userGroupIds,
31
+ }),
32
+ ];
33
+ return from(actions);
34
+ }
35
+ else {
36
+ return of(updateCockpitContextOnFailure(response.status));
37
+ }
38
+ }), catchError((error) => of(updateCockpitContextOnFailure(createZeniAPIStatus('Unexpected error', 'Cockpit context fetch errored out' + JSON.stringify(error))))))));
@@ -1,51 +1,35 @@
1
1
  import { from } from 'rxjs';
2
2
  import { catchError, filter, mergeMap, switchMap } from 'rxjs/operators';
3
3
  import { getSortOrder } from '../../../commonPayloadTypes/sortOrderPayload';
4
- import { updateCompanies } from '../../../entity/company/companyReducer';
5
4
  import { updateTags } from '../../../entity/tag/tagReducer';
6
5
  import { updateTasks } from '../../../entity/task/taskReducer';
7
- import { updateAllUsers } from '../../../entity/user/userReducer';
8
6
  import { createZeniAPIStatus, isSuccessResponse } from '../../../responsePayload';
9
7
  import { fetchCompanyTaskManagerView, updateCompanyTaskManagerViewOnFailure, updateCompanyTaskManagerViewOnSuccess, } from '../companyTaskManagerViewReducer';
10
- const toSortKeyPayload = (sortKey) => {
11
- switch (sortKey) {
12
- case 'taskName':
13
- return 'name';
14
- case 'dueDate':
15
- return 'due_date';
16
- case 'createdBy':
17
- return 'created_by';
18
- case 'name':
19
- return 'company_name';
20
- default:
21
- return sortKey;
22
- }
23
- };
8
+ const DEFAULT_PAGE_SIZE = 100;
24
9
  export const fetchCompanyTaskManagerViewEpic = (actions$, _state$, zeniAPI) => actions$.pipe(filter(fetchCompanyTaskManagerView.match), switchMap((action) => {
25
- const { pageToken, sortKey, sortOrder, searchString } = action.payload;
10
+ const { from: fromOffset, searchString, sortKey, sortOrder, filters, } = action.payload;
26
11
  const queryValue = {
27
- page_token: pageToken,
12
+ size: DEFAULT_PAGE_SIZE,
13
+ from: fromOffset,
14
+ global_search: searchString ?? '',
28
15
  sort_by: toSortKeyPayload(sortKey),
29
16
  sort_order: getSortOrder(sortOrder),
30
- search_text: searchString ?? '',
31
- view: 'cockpit_task_manager_view',
17
+ filter_join: filters?.categoryCombinationOperator === 'AND' ? 'and' : 'or',
18
+ ...(filters?.categories != null && filters.categories.length > 0
19
+ ? { filter_by: toFilterByPayload(filters) }
20
+ : {}),
32
21
  };
33
22
  return zeniAPI
34
- .getJSON(`${zeniAPI.apiEndPoints.tenantMicroServiceBaseUrl}/1.0/cockpit-panels/tasks?query=${encodeURIComponent(JSON.stringify(queryValue))}`)
23
+ .getJSON(`${zeniAPI.apiEndPoints.tenantMicroServiceBaseUrl}/1.0/cockpit-panels/tasks-es?query=${encodeURIComponent(JSON.stringify(queryValue))}`)
35
24
  .pipe(mergeMap((response) => {
36
25
  if (isSuccessResponse(response) && response.data != null) {
37
- const tasksList = response.data.tasks;
26
+ const tasksList = response.data.aggregations.group_by_company_name.buckets.flatMap((bucket) => bucket.tasks);
38
27
  const actions = [
39
28
  updateCompanyTaskManagerViewOnSuccess({
40
29
  data: response.data,
41
30
  }),
42
31
  updateTasks(tasksList),
43
- updateCompanies({
44
- payload: response.data.companies,
45
- schema: {},
46
- }),
47
- updateTags(tasksList.map((task) => task.tags).flat()),
48
- updateAllUsers({ users: response.data.users }),
32
+ updateTags(tasksList.flatMap((task) => task.tags)),
49
33
  ];
50
34
  return from(actions);
51
35
  }
@@ -62,3 +46,28 @@ export const fetchCompanyTaskManagerViewEpic = (actions$, _state$, zeniAPI) => a
62
46
  return from(actions);
63
47
  }));
64
48
  }));
49
+ const toSortKeyPayload = (sortKey) => {
50
+ switch (sortKey) {
51
+ case 'taskName':
52
+ return 'name';
53
+ case 'dueDate':
54
+ return 'due_date';
55
+ case 'createdBy':
56
+ return 'created_by';
57
+ case 'name':
58
+ return 'company_name';
59
+ default:
60
+ return sortKey;
61
+ }
62
+ };
63
+ const toFilterByPayload = (filters) => {
64
+ if (filters.categories == null) {
65
+ return [];
66
+ }
67
+ return filters.categories.map((category) => ({
68
+ field: category.field ?? '',
69
+ operator: category.matchingOperator === 'equal' ? 'is' : 'is_not',
70
+ values: category.values.map((v) => String(v)),
71
+ join: category.valuesCombinationOperator === 'ALL' ? 'and' : 'or',
72
+ }));
73
+ };
@@ -0,0 +1,16 @@
1
+ import { EMPTY, of } from 'rxjs';
2
+ import { filter, switchMap } from 'rxjs/operators';
3
+ import { fetchCompanyTaskManagerView, updateCompanyTaskManagerViewFilters, } from '../companyTaskManagerViewReducer';
4
+ export const updateCompanyTaskManagerViewFiltersEpic = (actions$, state$) => actions$.pipe(filter(updateCompanyTaskManagerViewFilters.match), switchMap((action) => {
5
+ const filters = action.payload.filters;
6
+ const allCategoriesValid = filters?.categories == null
7
+ ? true
8
+ : filters?.categories?.every((category) => category.field != null &&
9
+ category.values != null &&
10
+ category.values.length > 0);
11
+ if (!allCategoriesValid) {
12
+ return EMPTY;
13
+ }
14
+ const { uiState } = state$.value.companyTaskManagerViewState;
15
+ return of(fetchCompanyTaskManagerView(0, uiState.sortKey, uiState.sortOrder, false, uiState.filterText, filters));
16
+ }));
@@ -21,7 +21,7 @@ export const fetchAllCockpitViewsEpic = (actions$, state$) => actions$.pipe(filt
21
21
  fetchReviewCompanyView(),
22
22
  fetchCompanyHealthMetricView(),
23
23
  fetchCompanyMetaData(),
24
- fetchCompanyTaskManagerView(null, 'dueDate', 'descending', false),
24
+ fetchCompanyTaskManagerView(0, 'dueDate', 'descending', false),
25
25
  fetchCompanyHealthMetricConfig(),
26
26
  ]);
27
27
  }
@@ -74,7 +74,7 @@ export const fetchAllCockpitViewsEpic = (actions$, state$) => actions$.pipe(filt
74
74
  }
75
75
  if (state.companyTaskManagerViewState.fetchState !== 'In-Progress' &&
76
76
  state.companyTaskManagerViewState.hasValidState() === false) {
77
- fetchActions.push(fetchCompanyTaskManagerView(null, 'dueDate', 'descending', false));
77
+ fetchActions.push(fetchCompanyTaskManagerView(0, 'dueDate', 'descending', false));
78
78
  }
79
79
  return from(fetchActions);
80
80
  }
@@ -0,0 +1,34 @@
1
+ import { from, of } from 'rxjs';
2
+ import { catchError, filter, mergeMap } from 'rxjs/operators';
3
+ import { createZeniAPIStatus, isSuccessResponse, } from '../../../../responsePayload';
4
+ import { transformParseStatementPayloadToState, } from '../../payload/reconciliationPayload';
5
+ import { parseStatement, parseStatementFailure, parseStatementSuccess, updateParsedStatementData, } from '../../reducers/reconciliationViewReducer';
6
+ export const parseStatementEpic = (actions$, _state$, zeniAPI) => actions$.pipe(filter(parseStatement.match), mergeMap((action) => {
7
+ const { accountId, selectedPeriod, statementUploadId } = action.payload;
8
+ const parseStatementApi$ = zeniAPI.getJSON(`${zeniAPI.apiEndPoints.reconciliationMicroServiceBaseUrl}/2.0/statement-uploads/${statementUploadId}`);
9
+ return parseStatementApi$.pipe(mergeMap((response) => {
10
+ if (isSuccessResponse(response) === true && response.data != null) {
11
+ const actions = [];
12
+ const transformedData = transformParseStatementPayloadToState(response.data);
13
+ actions.push(updateParsedStatementData({
14
+ accountId,
15
+ selectedPeriod,
16
+ parsedStatementData: transformedData,
17
+ }));
18
+ actions.push(parseStatementSuccess({ accountId, selectedPeriod }));
19
+ return from(actions);
20
+ }
21
+ else {
22
+ return of(parseStatementFailure({
23
+ accountId,
24
+ selectedPeriod,
25
+ error: response.status,
26
+ }));
27
+ }
28
+ }), catchError((error) => of(parseStatementFailure({
29
+ accountId,
30
+ selectedPeriod,
31
+ error: createZeniAPIStatus('Unexpected Error', 'Parse Statement REST API call errored out ' +
32
+ JSON.stringify(error)),
33
+ }))));
34
+ }));
@@ -0,0 +1,42 @@
1
+ import { of } from 'rxjs';
2
+ import { catchError, filter, mergeMap } from 'rxjs/operators';
3
+ import { toMonthYearPeriodId } from '../../../../commonStateTypes/timePeriod';
4
+ import { createZeniAPIStatus, isSuccessResponse, } from '../../../../responsePayload';
5
+ import { transformStatementUpdateStateToPayload, } from '../../payload/reconciliationPayload';
6
+ import { submitStatementUpdate, submitStatementUpdateFailure, submitStatementUpdateSuccess, } from '../../reducers/reconciliationViewReducer';
7
+ export const updateStatementInfoEpic = (actions$, state$, zeniAPI) => actions$.pipe(filter(submitStatementUpdate.match), mergeMap((action) => {
8
+ const { accountId, selectedPeriod, statementUploadId } = action.payload;
9
+ const state = state$.value;
10
+ const reconciliationView = state.expenseAutomationReconciliationViewState;
11
+ const accountRecon = reconciliationView.accountReconciliationRecordsBySelectedPeriod[toMonthYearPeriodId(selectedPeriod)]?.reconciliationByAccountID[accountId];
12
+ const localData = accountRecon?.localData?.statementUpdateLocalData;
13
+ if (!localData) {
14
+ return of(submitStatementUpdateFailure({
15
+ accountId,
16
+ selectedPeriod,
17
+ error: createZeniAPIStatus('Missing Data', 'No statement update data found'),
18
+ }));
19
+ }
20
+ const payload = transformStatementUpdateStateToPayload(localData);
21
+ const updateStatementApi$ = zeniAPI.putAndGetJSON(`${zeniAPI.apiEndPoints.reconciliationMicroServiceBaseUrl}/2.0/statement-uploads/${statementUploadId}`, payload);
22
+ return updateStatementApi$.pipe(mergeMap((response) => {
23
+ if (isSuccessResponse(response) === true && response.data != null) {
24
+ return of(submitStatementUpdateSuccess({
25
+ accountId,
26
+ selectedPeriod,
27
+ }));
28
+ }
29
+ else {
30
+ return of(submitStatementUpdateFailure({
31
+ accountId,
32
+ selectedPeriod,
33
+ error: response.status,
34
+ }));
35
+ }
36
+ }), catchError((error) => of(submitStatementUpdateFailure({
37
+ accountId,
38
+ selectedPeriod,
39
+ error: createZeniAPIStatus('Unexpected Error', 'Update Statement REST API call errored out ' +
40
+ JSON.stringify(error)),
41
+ }))));
42
+ }));
@@ -67,3 +67,76 @@ function createReviewTransactionPayload(accountReconciliationEntity) {
67
67
  });
68
68
  return reviewTransactionsPayload;
69
69
  }
70
+ export function transformParseStatementPayloadToState(payload) {
71
+ return {
72
+ statementUpload: {
73
+ account: {
74
+ accountId: payload.statement_upload.account.account_id,
75
+ accountName: payload.statement_upload.account.account_name,
76
+ accountType: payload.statement_upload.account.account_type,
77
+ currencyCode: payload.statement_upload.account.currency_code,
78
+ last4Digits: payload.statement_upload.account.last_4_digits,
79
+ },
80
+ file: {
81
+ fileId: payload.statement_upload.file.file_id,
82
+ fileName: payload.statement_upload.file.file_name,
83
+ signedUrl: payload.statement_upload.file.signed_url,
84
+ },
85
+ statementMeta: {
86
+ closingBalance: payload.statement_upload.statement_meta.closing_balance,
87
+ openingBalance: payload.statement_upload.statement_meta.opening_balance,
88
+ statementDataStatus: payload.statement_upload.statement_meta.statement_data_status,
89
+ statementEndDate: payload.statement_upload.statement_meta.statement_end_date,
90
+ statementStartDate: payload.statement_upload.statement_meta.statement_start_date,
91
+ statementStatus: payload.statement_upload.statement_meta.statement_status,
92
+ statementUploadId: payload.statement_upload.statement_meta.statement_upload_id,
93
+ totalDeposits: payload.statement_upload.statement_meta.total_deposits,
94
+ totalPayments: payload.statement_upload.statement_meta.total_payments,
95
+ },
96
+ statementTransactions: payload.statement_upload.statement_transactions.map((txn) => ({
97
+ amount: txn.amount,
98
+ citations: txn.citation.map((c) => ({
99
+ boundingBoxes: c.bounding_boxes,
100
+ page: c.page,
101
+ referenceText: c.reference_text,
102
+ })),
103
+ isUserAdded: txn.is_user_added,
104
+ isUserEdited: txn.is_user_edited,
105
+ statementTransactionId: txn.statement_transaction_id,
106
+ transactionDate: txn.transaction_date,
107
+ transactionDirection: txn.transaction_direction,
108
+ transactionMemo: txn.transaction_memo,
109
+ })),
110
+ },
111
+ };
112
+ }
113
+ export function transformStatementUpdateStateToPayload(localData) {
114
+ const { statementMeta, statementTransactions } = localData;
115
+ return {
116
+ statement_meta: {
117
+ opening_balance: statementMeta.openingBalance,
118
+ statement_end_date: statementMeta.statementEndDate,
119
+ statement_start_date: statementMeta.statementStartDate,
120
+ total_deposits: statementMeta.totalDeposits,
121
+ total_payments: statementMeta.totalPayments,
122
+ },
123
+ statement_transactions: {
124
+ added: statementTransactions.added.map((txn) => ({
125
+ amount: txn.amount,
126
+ transaction_date: txn.transactionDate,
127
+ transaction_direction: txn.transactionDirection,
128
+ transaction_memo: txn.transactionMemo,
129
+ })),
130
+ deleted_ids: statementTransactions.deletedIds,
131
+ updated: statementTransactions.updated
132
+ .filter((txn) => txn.statementTransactionId != null)
133
+ .map((txn) => ({
134
+ amount: txn.amount,
135
+ statement_transaction_id: txn.statementTransactionId,
136
+ transaction_date: txn.transactionDate,
137
+ transaction_direction: txn.transactionDirection,
138
+ transaction_memo: txn.transactionMemo,
139
+ })),
140
+ },
141
+ };
142
+ }
@@ -2,6 +2,7 @@ import { createSlice } from '@reduxjs/toolkit';
2
2
  import { toMonthYearPeriodId, } from '../../../commonStateTypes/timePeriod';
3
3
  import { toReconciliationAccountSource } from '../../../entity/account/accountState';
4
4
  import { toBankStatusCodeType } from '../../../entity/accountRecon/accountReconState';
5
+ import { toReconciliationViewSummary, } from '../types/reconciliationViewState';
5
6
  // Initial state
6
7
  export const initialReconciliationTabsState = {
7
8
  balances: {
@@ -67,6 +68,7 @@ export const initialState = {
67
68
  refreshStatus: { fetchState: 'Not-Started', error: undefined },
68
69
  actionFetchState: initialActionFetchState,
69
70
  selectedAccountId: undefined,
71
+ statementProcessingFailed: false,
70
72
  statementUploadChosen: false,
71
73
  reconListUIState: {
72
74
  nodeCollapseState: {},
@@ -75,6 +77,7 @@ export const initialState = {
75
77
  },
76
78
  },
77
79
  excludedAccountIDs: [],
80
+ summary: undefined,
78
81
  };
79
82
  // Create slice with reducers
80
83
  const expenseAutomationReconciliationView = createSlice({
@@ -143,7 +146,7 @@ const expenseAutomationReconciliationView = createSlice({
143
146
  fetchReconciliationSuccess: {
144
147
  reducer(draft, action) {
145
148
  if (action.payload.accountId == null) {
146
- updateAllReconciliation(draft, action.payload.reconciliation.accounts, action.payload.selectedPeriod, action.payload.reconciliation.reconciliation, action.payload.refreshViewInBackground, action.payload.reconciliation.excluded_accounts ?? []);
149
+ updateAllReconciliation(draft, action.payload.reconciliation.accounts, action.payload.selectedPeriod, action.payload.reconciliation.reconciliation, action.payload.refreshViewInBackground, action.payload.reconciliation.excluded_accounts ?? [], action.payload.reconciliation.summary);
147
150
  }
148
151
  else if (action.payload.reconciliation.reconciliation.length > 0 &&
149
152
  action.payload.reconciliation.accounts.length > 0) {
@@ -287,6 +290,10 @@ const expenseAutomationReconciliationView = createSlice({
287
290
  updateSelectedDrawerAccountId: (draft, action) => {
288
291
  draft.selectedDrawerAccountId = action.payload.selectedDrawerAccountId;
289
292
  },
293
+ updateStatementProcessingFailed: (draft, action) => {
294
+ draft.statementProcessingFailed =
295
+ action.payload.statementProcessingFailed;
296
+ },
290
297
  initialiseLocalDataForSelectedAccountId: () => { },
291
298
  updateSelectedTab: (draft, action) => {
292
299
  const { selectedTab } = action.payload;
@@ -419,6 +426,76 @@ const expenseAutomationReconciliationView = createSlice({
419
426
  error: action.payload.error,
420
427
  };
421
428
  },
429
+ parseStatement: (draft, action) => {
430
+ const { accountId, selectedPeriod } = action.payload;
431
+ draft.accountReconciliationRecordsBySelectedPeriod[toMonthYearPeriodId(selectedPeriod)].reconciliationByAccountID[accountId].statementParseStatus = {
432
+ fetchState: 'In-Progress',
433
+ error: undefined,
434
+ };
435
+ },
436
+ parseStatementSuccess: (draft, action) => {
437
+ const { accountId, selectedPeriod } = action.payload;
438
+ draft.accountReconciliationRecordsBySelectedPeriod[toMonthYearPeriodId(selectedPeriod)].reconciliationByAccountID[accountId].statementParseStatus = {
439
+ fetchState: 'Completed',
440
+ error: undefined,
441
+ };
442
+ draft.accountReconciliationRecordsBySelectedPeriod[toMonthYearPeriodId(selectedPeriod)].reconciliationByAccountID[accountId].statementParseInProgress = false;
443
+ },
444
+ parseStatementFailure: (draft, action) => {
445
+ const { accountId, selectedPeriod } = action.payload;
446
+ draft.accountReconciliationRecordsBySelectedPeriod[toMonthYearPeriodId(selectedPeriod)].reconciliationByAccountID[accountId].statementParseStatus = {
447
+ fetchState: 'Error',
448
+ error: action.payload.error,
449
+ };
450
+ draft.accountReconciliationRecordsBySelectedPeriod[toMonthYearPeriodId(selectedPeriod)].reconciliationByAccountID[accountId].statementParseInProgress = false;
451
+ },
452
+ updateParsedStatementData: (draft, action) => {
453
+ const { accountId, selectedPeriod, parsedStatementData } = action.payload;
454
+ draft.accountReconciliationRecordsBySelectedPeriod[toMonthYearPeriodId(selectedPeriod)].reconciliationByAccountID[accountId].parsedStatementData =
455
+ parsedStatementData;
456
+ },
457
+ updateStatementUpdateLocalData: (draft, action) => {
458
+ const { accountId, selectedPeriod, statementUpdateLocalData } = action.payload;
459
+ const accountRecon = draft.accountReconciliationRecordsBySelectedPeriod[toMonthYearPeriodId(selectedPeriod)].reconciliationByAccountID[accountId];
460
+ if (accountRecon.localData == null) {
461
+ accountRecon.localData = {
462
+ statementUpdateLocalData,
463
+ };
464
+ }
465
+ else {
466
+ accountRecon.localData.statementUpdateLocalData =
467
+ statementUpdateLocalData;
468
+ }
469
+ },
470
+ submitStatementUpdate: (draft, action) => {
471
+ const { accountId, selectedPeriod } = action.payload;
472
+ draft.accountReconciliationRecordsBySelectedPeriod[toMonthYearPeriodId(selectedPeriod)].reconciliationByAccountID[accountId].statementUpdateStatus = {
473
+ fetchState: 'In-Progress',
474
+ error: undefined,
475
+ };
476
+ },
477
+ submitStatementUpdateSuccess: (draft, action) => {
478
+ const { accountId, selectedPeriod } = action.payload;
479
+ const accountRecon = draft.accountReconciliationRecordsBySelectedPeriod[toMonthYearPeriodId(selectedPeriod)].reconciliationByAccountID[accountId];
480
+ accountRecon.statementUpdateStatus = {
481
+ fetchState: 'Completed',
482
+ error: undefined,
483
+ };
484
+ if (accountRecon.localData != null) {
485
+ accountRecon.localData.statementUpdateLocalData = undefined;
486
+ }
487
+ },
488
+ submitStatementUpdateFailure: (draft, action) => {
489
+ const { accountId, selectedPeriod } = action.payload;
490
+ const accountRecon = draft.accountReconciliationRecordsBySelectedPeriod[toMonthYearPeriodId(selectedPeriod)].reconciliationByAccountID[accountId];
491
+ accountRecon.statementUpdateStatus = {
492
+ fetchState: 'Error',
493
+ error: action.payload.error,
494
+ };
495
+ if (accountRecon.localData != null) {
496
+ accountRecon.localData.statementUpdateLocalData = undefined;
497
+ }
498
+ },
422
499
  updateAccountReconciliationLocalData: (draft, action) => {
423
500
  const { accountId, selectedPeriod, localData } = action.payload;
424
501
  const oldLocalData = draft.accountReconciliationRecordsBySelectedPeriod[toMonthYearPeriodId(selectedPeriod)].reconciliationByAccountID[accountId].localData;
@@ -524,10 +601,10 @@ const expenseAutomationReconciliationView = createSlice({
524
601
  },
525
602
  });
526
603
  // Export actions
527
- export const { fetchReconciliation, fetchReconciliationFailure, fetchReconciliationSuccess, saveReconciliationDetail, saveReconciliationDetailFailure, updateSelectedAccountId, setConnectionInProgressForAccount, setStatementParseInProgress, updateReconcileTabLocalData, updateReconcileTabListSortState, updateReconcileTabListScrollState, saveReconciliationDetailSuccess, updateSelectedTab, updateBalancesLocalData, initialiseLocalDataForSelectedAccountId, clearExpenseAutomationReconciliationView, updateSelectedDrawerAccountId, updateReviewTabSortState, initializeReconciliationReviewTabLocalData, updateReviewTabLocalData, saveReconciliationReview, updateSaveReconciliationReviewFetchStatus, updateReconListScrollPosition, updateAccountReconciliationLocalData, deleteAccountStatement, deleteAccountStatementSuccess, deleteAccountStatementFailure, excludeAccountFromReconciliation, excludeAccountFromReconciliationSuccess, excludeAccountFromReconciliationFailure, includeAccountInReconciliation, includeAccountInReconciliationSuccess, includeAccountInReconciliationFailure, uploadAccountStatement, uploadAccountStatementSuccess, uploadAccountStatementFailure, updateStatementUploadChosen, updateNodeCollapseState, } = expenseAutomationReconciliationView.actions;
604
+ export const { fetchReconciliation, fetchReconciliationFailure, fetchReconciliationSuccess, saveReconciliationDetail, saveReconciliationDetailFailure, updateSelectedAccountId, setConnectionInProgressForAccount, setStatementParseInProgress, updateReconcileTabLocalData, updateReconcileTabListSortState, updateReconcileTabListScrollState, saveReconciliationDetailSuccess, updateSelectedTab, updateBalancesLocalData, initialiseLocalDataForSelectedAccountId, clearExpenseAutomationReconciliationView, updateSelectedDrawerAccountId, updateStatementProcessingFailed, updateReviewTabSortState, initializeReconciliationReviewTabLocalData, updateReviewTabLocalData, saveReconciliationReview, updateSaveReconciliationReviewFetchStatus, updateReconListScrollPosition, updateAccountReconciliationLocalData, deleteAccountStatement, deleteAccountStatementSuccess, deleteAccountStatementFailure, excludeAccountFromReconciliation, excludeAccountFromReconciliationSuccess, excludeAccountFromReconciliationFailure, includeAccountInReconciliation, includeAccountInReconciliationSuccess, includeAccountInReconciliationFailure, uploadAccountStatement, uploadAccountStatementSuccess, uploadAccountStatementFailure, parseStatement, parseStatementSuccess, parseStatementFailure, updateParsedStatementData, updateStatementUpdateLocalData, submitStatementUpdate, submitStatementUpdateSuccess, submitStatementUpdateFailure, updateStatementUploadChosen, updateNodeCollapseState, } = expenseAutomationReconciliationView.actions;
528
605
  // Export reducer
529
606
  export default expenseAutomationReconciliationView.reducer;
530
- function updateAllReconciliation(draft, accounts, selectedPeriod, reconciliationData, refreshViewInBackground, excludedAccounts) {
607
+ function updateAllReconciliation(draft, accounts, selectedPeriod, reconciliationData, refreshViewInBackground, excludedAccounts, summary) {
531
608
  draft.excludedAccountIDs = excludedAccounts.map((ea) => ea.account_id);
532
609
  draft.excludedAccountExclusionInfo = {};
533
610
  excludedAccounts.forEach((ea) => {
@@ -556,6 +633,7 @@ function updateAllReconciliation(draft, accounts, selectedPeriod, reconciliation
556
633
  : undefined,
557
634
  };
558
635
  });
636
+ const oldReconciliationByAccountID = draft.accountReconciliationRecordsBySelectedPeriod[toMonthYearPeriodId(selectedPeriod)]?.reconciliationByAccountID ?? {};
559
637
  draft.accountReconciliationRecordsBySelectedPeriod[toMonthYearPeriodId(selectedPeriod)] = {
560
638
  accountIDs: [],
561
639
  reconciliationByAccountID: {},
@@ -579,6 +657,7 @@ function updateAllReconciliation(draft, accounts, selectedPeriod, reconciliation
579
657
  paymentAccountId: account?.payment_account_id ?? undefined,
580
658
  };
581
659
  }
660
+ const oldRecord = oldReconciliationByAccountID[reconciliation.account_id];
582
661
  accountReconciliationRecords.reconciliationByAccountID[reconciliation.account_id] = {
583
662
  accountId: reconciliation.account_id,
584
663
  reconciliationId: reconciliation.reconciliation_id ?? undefined,
@@ -588,14 +667,17 @@ function updateAllReconciliation(draft, accounts, selectedPeriod, reconciliation
588
667
  statementUploadStatus: { fetchState: 'Not-Started', error: undefined },
589
668
  statementUpdateStatus: { fetchState: 'Not-Started', error: undefined },
590
669
  statementParseInProgress: reconciliation.bank_status.code === 'parsing',
670
+ statementParseStatus: { fetchState: 'Not-Started', error: undefined },
591
671
  localData: {
592
672
  accountSource: account?.reconciliation_source != null
593
673
  ? toReconciliationAccountSource(account.reconciliation_source)
594
674
  : undefined,
675
+ ...oldRecord?.localData,
595
676
  },
596
677
  accountDetectionReason: account?.detection_info != null && account.detection_reason != null
597
678
  ? account.detection_reason
598
679
  : undefined,
680
+ parsedStatementData: oldRecord?.parsedStatementData,
599
681
  };
600
682
  accountReconciliationRecords.accountIDs.push(reconciliation.account_id);
601
683
  });
@@ -609,6 +691,9 @@ function updateAllReconciliation(draft, accounts, selectedPeriod, reconciliation
609
691
  draft.fetchState = 'Completed';
610
692
  draft.error = undefined;
611
693
  }
694
+ if (summary !== undefined) {
695
+ draft.summary = toReconciliationViewSummary(summary);
696
+ }
612
697
  }
613
698
  function updateReconciliationByAccountID(draft, accounts, selectedPeriod, accountId, reconciliationData, refreshViewInBackground) {
614
699
  const oldRecord = draft.accountReconciliationRecordsBySelectedPeriod[toMonthYearPeriodId(selectedPeriod)].reconciliationByAccountID[accountId];
@@ -676,6 +761,7 @@ const updateReconciliationByAccountIDOnIncludeAccount = (draft, accountId, recon
676
761
  statementUploadStatus: { fetchState: 'Not-Started', error: undefined },
677
762
  statementUpdateStatus: { fetchState: 'Not-Started', error: undefined },
678
763
  statementParseInProgress: reconciliation.bank_status.code === 'parsing',
764
+ statementParseStatus: { fetchState: 'Not-Started', error: undefined },
679
765
  localData: {},
680
766
  accountDetectionReason: undefined,
681
767
  };