@zeniai/client-epic-state 5.2.22-betaAM4 → 5.2.22-betaAM5

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 (48) hide show
  1. package/lib/entity/actualMatching/actualMatchingPayload.d.ts +25 -0
  2. package/lib/entity/actualMatching/actualMatchingPayload.js +42 -0
  3. package/lib/entity/actualMatching/actualMatchingReducer.d.ts +8 -0
  4. package/lib/entity/actualMatching/actualMatchingReducer.js +33 -0
  5. package/lib/entity/actualMatching/actualMatchingSelector.d.ts +3 -0
  6. package/lib/entity/actualMatching/actualMatchingSelector.js +6 -0
  7. package/lib/entity/actualMatching/actualMatchingState.d.ts +22 -0
  8. package/lib/entity/actualMatching/actualMatchingState.js +2 -0
  9. package/lib/entity/actualMatching/actualMatchingTypes.d.ts +1 -0
  10. package/lib/entity/actualMatching/actualMatchingTypes.js +2 -0
  11. package/lib/epic.d.ts +2 -1
  12. package/lib/epic.js +2 -0
  13. package/lib/esm/entity/actualMatching/actualMatchingPayload.js +39 -0
  14. package/lib/esm/entity/actualMatching/actualMatchingReducer.js +30 -0
  15. package/lib/esm/entity/actualMatching/actualMatchingSelector.js +3 -0
  16. package/lib/esm/entity/actualMatching/actualMatchingState.js +1 -0
  17. package/lib/esm/entity/actualMatching/actualMatchingTypes.js +1 -0
  18. package/lib/esm/epic.js +2 -0
  19. package/lib/esm/index.js +3 -3
  20. package/lib/esm/reducer.js +3 -0
  21. package/lib/esm/view/expenseAutomationView/epics/actualMatching/fetchActualMatchingEpic.js +6 -6
  22. package/lib/esm/view/expenseAutomationView/epics/actualMatching/fetchActualMatchingMatchEpic.js +35 -0
  23. package/lib/esm/view/expenseAutomationView/epics/actualMatching/searchMatchTransactionsEpic.js +35 -6
  24. package/lib/esm/view/expenseAutomationView/epics/common/fetchAllExpenseAutomationTabsEpic.js +1 -1
  25. package/lib/esm/view/expenseAutomationView/reducers/actualMatchingViewReducer.js +87 -8
  26. package/lib/esm/view/expenseAutomationView/selectors/actualMatchingViewSelector.js +143 -98
  27. package/lib/esm/view/expenseAutomationView/types/actualMatchingViewState.js +4 -0
  28. package/lib/index.d.ts +6 -3
  29. package/lib/index.js +21 -18
  30. package/lib/reducer.d.ts +1 -0
  31. package/lib/reducer.js +3 -0
  32. package/lib/rootStateTypes.d.ts +2 -0
  33. package/lib/view/expenseAutomationView/epics/actualMatching/fetchActualMatchingEpic.d.ts +2 -2
  34. package/lib/view/expenseAutomationView/epics/actualMatching/fetchActualMatchingEpic.js +6 -6
  35. package/lib/view/expenseAutomationView/epics/actualMatching/fetchActualMatchingMatchEpic.d.ts +12 -0
  36. package/lib/view/expenseAutomationView/epics/actualMatching/fetchActualMatchingMatchEpic.js +39 -0
  37. package/lib/view/expenseAutomationView/epics/actualMatching/searchMatchTransactionsEpic.d.ts +1 -1
  38. package/lib/view/expenseAutomationView/epics/actualMatching/searchMatchTransactionsEpic.js +35 -6
  39. package/lib/view/expenseAutomationView/epics/common/fetchAllExpenseAutomationTabsEpic.js +1 -1
  40. package/lib/view/expenseAutomationView/payload/actualMatchingPayload.d.ts +3 -6
  41. package/lib/view/expenseAutomationView/reducers/actualMatchingViewReducer.d.ts +20 -3
  42. package/lib/view/expenseAutomationView/reducers/actualMatchingViewReducer.js +88 -9
  43. package/lib/view/expenseAutomationView/selectorTypes/actualMatchingViewSelectorTypes.d.ts +15 -2
  44. package/lib/view/expenseAutomationView/selectors/actualMatchingViewSelector.d.ts +16 -2
  45. package/lib/view/expenseAutomationView/selectors/actualMatchingViewSelector.js +146 -98
  46. package/lib/view/expenseAutomationView/types/actualMatchingViewState.d.ts +7 -2
  47. package/lib/view/expenseAutomationView/types/actualMatchingViewState.js +4 -0
  48. package/package.json +1 -1
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  var _a;
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.updateActualMatchingUIState = exports.searchMatchTransactionsSuccess = exports.searchMatchTransactionsFailure = exports.searchMatchTransactions = exports.searchMatchAccrualsSuccess = exports.searchMatchAccrualsFailure = exports.searchMatchAccruals = exports.openMatchNewModal = exports.matchAndReverseAccrualsSuccess = exports.matchAndReverseAccrualsFailure = exports.matchAndReverseAccruals = exports.fetchActualMatchingSuccess = exports.fetchActualMatchingPage = exports.fetchActualMatchingFailure = exports.fetchActualMatching = exports.closeMatchNewModal = exports.clearMatchNewModal = exports.clearActualMatchingView = exports.initialState = exports.initialMatchModalState = exports.initialAccrualSearchState = exports.initialTransactionSearchState = void 0;
4
+ exports.updateActualMatchingUIState = exports.searchMatchTransactionsSuccess = exports.searchMatchTransactionsFailure = exports.searchMatchTransactions = exports.searchMatchAccrualsSuccess = exports.searchMatchAccrualsFailure = exports.searchMatchAccruals = exports.openMatchNewModal = exports.matchAndReverseAccrualsSuccess = exports.matchAndReverseAccrualsFailure = exports.matchAndReverseAccruals = exports.fetchActualMatchingSuccess = exports.fetchActualMatchingPage = exports.fetchActualMatchingMatchSuccess = exports.fetchActualMatchingMatchFailure = exports.fetchActualMatchingMatch = exports.fetchActualMatchingFailure = exports.fetchActualMatching = exports.closeMatchNewModal = exports.clearMatchNewModal = exports.clearActualMatchingView = exports.initialState = exports.initialMatchModalState = exports.initialAccrualSearchState = exports.initialTransactionSearchState = void 0;
5
5
  const toolkit_1 = require("@reduxjs/toolkit");
6
6
  const timePeriod_1 = require("../../../commonStateTypes/timePeriod");
7
7
  const jeScheduleHelper_1 = require("../../../entity/jeSchedules/jeScheduleHelper");
@@ -24,18 +24,22 @@ exports.initialAccrualSearchState = {
24
24
  };
25
25
  exports.initialMatchModalState = {
26
26
  isOpen: false,
27
+ openedFromMatchId: undefined,
28
+ pinnedTransactionResults: [],
27
29
  transactionSearch: exports.initialTransactionSearchState,
28
30
  accrualSearch: exports.initialAccrualSearchState,
29
31
  confirmStatus: { fetchState: 'Not-Started', error: undefined },
30
32
  };
31
33
  exports.initialState = {
32
- accrualKeysByPeriod: {},
34
+ matchIdsByPeriod: {},
33
35
  fetchState: 'Not-Started',
34
36
  error: undefined,
35
37
  refreshStatus: {
36
38
  fetchState: 'Not-Started',
37
39
  error: undefined,
38
40
  },
41
+ detailFetchStateByMatchId: {},
42
+ detailRefreshStatusByMatchId: {},
39
43
  uiState: {
40
44
  sortKey: 'date',
41
45
  sortOrder: 'descending',
@@ -55,6 +59,8 @@ function resetMatchModalSearches(draft) {
55
59
  error: undefined,
56
60
  };
57
61
  draft.matchModal.lastBatchResults = undefined;
62
+ draft.matchModal.openedFromMatchId = undefined;
63
+ draft.matchModal.pinnedTransactionResults = [];
58
64
  }
59
65
  const expenseAutomationActualMatchingView = (0, toolkit_1.createSlice)({
60
66
  name: 'expenseAutomationActualMatchingView',
@@ -94,9 +100,9 @@ const expenseAutomationActualMatchingView = (0, toolkit_1.createSlice)({
94
100
  },
95
101
  },
96
102
  fetchActualMatchingSuccess(draft, action) {
97
- const { accruals, selectedPeriod, refreshViewInBackground } = action.payload;
103
+ const { matchIds, selectedPeriod, refreshViewInBackground } = action.payload;
98
104
  const periodId = (0, timePeriod_1.toMonthYearPeriodId)(selectedPeriod);
99
- draft.accrualKeysByPeriod[periodId] = accruals.map((accrual) => (0, jeScheduleHelper_1.generateJEOneTimeAccrualKey)(accrual._id));
105
+ draft.matchIdsByPeriod[periodId] = matchIds;
100
106
  if (refreshViewInBackground) {
101
107
  draft.refreshStatus = { fetchState: 'Completed', error: undefined };
102
108
  }
@@ -115,6 +121,56 @@ const expenseAutomationActualMatchingView = (0, toolkit_1.createSlice)({
115
121
  draft.error = status;
116
122
  }
117
123
  },
124
+ fetchActualMatchingMatch: {
125
+ prepare(matchId, refreshViewInBackground = false) {
126
+ return { payload: { matchId, refreshViewInBackground } };
127
+ },
128
+ reducer(draft, action) {
129
+ const { matchId, refreshViewInBackground } = action.payload;
130
+ if (refreshViewInBackground) {
131
+ draft.detailRefreshStatusByMatchId[matchId] = {
132
+ fetchState: 'In-Progress',
133
+ error: undefined,
134
+ };
135
+ }
136
+ else {
137
+ draft.detailFetchStateByMatchId[matchId] = {
138
+ fetchState: 'In-Progress',
139
+ error: undefined,
140
+ };
141
+ }
142
+ },
143
+ },
144
+ fetchActualMatchingMatchSuccess(draft, action) {
145
+ const { matchId, refreshViewInBackground } = action.payload;
146
+ if (refreshViewInBackground) {
147
+ draft.detailRefreshStatusByMatchId[matchId] = {
148
+ fetchState: 'Completed',
149
+ error: undefined,
150
+ };
151
+ }
152
+ else {
153
+ draft.detailFetchStateByMatchId[matchId] = {
154
+ fetchState: 'Completed',
155
+ error: undefined,
156
+ };
157
+ }
158
+ },
159
+ fetchActualMatchingMatchFailure(draft, action) {
160
+ const { matchId, refreshViewInBackground, status } = action.payload;
161
+ if (refreshViewInBackground) {
162
+ draft.detailRefreshStatusByMatchId[matchId] = {
163
+ fetchState: 'Error',
164
+ error: status,
165
+ };
166
+ }
167
+ else {
168
+ draft.detailFetchStateByMatchId[matchId] = {
169
+ fetchState: 'Error',
170
+ error: status,
171
+ };
172
+ }
173
+ },
118
174
  updateActualMatchingUIState(draft, action) {
119
175
  if (action.payload.sortKey != null) {
120
176
  draft.uiState.sortKey = action.payload.sortKey;
@@ -129,8 +185,20 @@ const expenseAutomationActualMatchingView = (0, toolkit_1.createSlice)({
129
185
  draft.uiState.scrollPosition = action.payload.scrollPosition;
130
186
  }
131
187
  },
132
- openMatchNewModal(draft) {
133
- draft.matchModal.isOpen = true;
188
+ openMatchNewModal: {
189
+ prepare(matchId) {
190
+ return { payload: { matchId } };
191
+ },
192
+ reducer(draft, action) {
193
+ resetMatchModalSearches(draft);
194
+ draft.matchModal.isOpen = true;
195
+ const matchId = action.payload.matchId;
196
+ draft.matchModal.openedFromMatchId =
197
+ matchId != null && matchId !== '' ? matchId : undefined;
198
+ if (draft.matchModal.openedFromMatchId != null) {
199
+ draft.matchModal.accrualSearch.fetchState = 'Completed';
200
+ }
201
+ },
134
202
  },
135
203
  closeMatchNewModal(draft) {
136
204
  draft.matchModal.isOpen = false;
@@ -156,8 +224,15 @@ const expenseAutomationActualMatchingView = (0, toolkit_1.createSlice)({
156
224
  draft.matchModal.transactionSearch.error = undefined;
157
225
  }
158
226
  },
159
- prepare({ query, pageToken }) {
160
- return { payload: { query, pageToken: pageToken ?? undefined } };
227
+ prepare({ query, pageToken, transactionType, transactionIntegrationId, }) {
228
+ return {
229
+ payload: {
230
+ query,
231
+ pageToken: pageToken ?? undefined,
232
+ transactionType,
233
+ transactionIntegrationId,
234
+ },
235
+ };
161
236
  },
162
237
  },
163
238
  searchMatchTransactionsSuccess(draft, action) {
@@ -175,6 +250,10 @@ const expenseAutomationActualMatchingView = (0, toolkit_1.createSlice)({
175
250
  }
176
251
  else {
177
252
  draft.matchModal.transactionSearch.results = results;
253
+ if (draft.matchModal.openedFromMatchId != null &&
254
+ draft.matchModal.pinnedTransactionResults.length === 0) {
255
+ draft.matchModal.pinnedTransactionResults = results;
256
+ }
178
257
  }
179
258
  draft.matchModal.transactionSearch.nextPageToken = nextPageToken;
180
259
  draft.matchModal.transactionSearch.isLoadingMore = false;
@@ -260,5 +339,5 @@ const expenseAutomationActualMatchingView = (0, toolkit_1.createSlice)({
260
339
  });
261
340
  },
262
341
  });
263
- _a = expenseAutomationActualMatchingView.actions, exports.clearActualMatchingView = _a.clearActualMatchingView, exports.clearMatchNewModal = _a.clearMatchNewModal, exports.closeMatchNewModal = _a.closeMatchNewModal, exports.fetchActualMatching = _a.fetchActualMatching, exports.fetchActualMatchingFailure = _a.fetchActualMatchingFailure, exports.fetchActualMatchingPage = _a.fetchActualMatchingPage, exports.fetchActualMatchingSuccess = _a.fetchActualMatchingSuccess, exports.matchAndReverseAccruals = _a.matchAndReverseAccruals, exports.matchAndReverseAccrualsFailure = _a.matchAndReverseAccrualsFailure, exports.matchAndReverseAccrualsSuccess = _a.matchAndReverseAccrualsSuccess, exports.openMatchNewModal = _a.openMatchNewModal, exports.searchMatchAccruals = _a.searchMatchAccruals, exports.searchMatchAccrualsFailure = _a.searchMatchAccrualsFailure, exports.searchMatchAccrualsSuccess = _a.searchMatchAccrualsSuccess, exports.searchMatchTransactions = _a.searchMatchTransactions, exports.searchMatchTransactionsFailure = _a.searchMatchTransactionsFailure, exports.searchMatchTransactionsSuccess = _a.searchMatchTransactionsSuccess, exports.updateActualMatchingUIState = _a.updateActualMatchingUIState;
342
+ _a = expenseAutomationActualMatchingView.actions, exports.clearActualMatchingView = _a.clearActualMatchingView, exports.clearMatchNewModal = _a.clearMatchNewModal, exports.closeMatchNewModal = _a.closeMatchNewModal, exports.fetchActualMatching = _a.fetchActualMatching, exports.fetchActualMatchingFailure = _a.fetchActualMatchingFailure, exports.fetchActualMatchingMatch = _a.fetchActualMatchingMatch, exports.fetchActualMatchingMatchFailure = _a.fetchActualMatchingMatchFailure, exports.fetchActualMatchingMatchSuccess = _a.fetchActualMatchingMatchSuccess, exports.fetchActualMatchingPage = _a.fetchActualMatchingPage, exports.fetchActualMatchingSuccess = _a.fetchActualMatchingSuccess, exports.matchAndReverseAccruals = _a.matchAndReverseAccruals, exports.matchAndReverseAccrualsFailure = _a.matchAndReverseAccrualsFailure, exports.matchAndReverseAccrualsSuccess = _a.matchAndReverseAccrualsSuccess, exports.openMatchNewModal = _a.openMatchNewModal, exports.searchMatchAccruals = _a.searchMatchAccruals, exports.searchMatchAccrualsFailure = _a.searchMatchAccrualsFailure, exports.searchMatchAccrualsSuccess = _a.searchMatchAccrualsSuccess, exports.searchMatchTransactions = _a.searchMatchTransactions, exports.searchMatchTransactionsFailure = _a.searchMatchTransactionsFailure, exports.searchMatchTransactionsSuccess = _a.searchMatchTransactionsSuccess, exports.updateActualMatchingUIState = _a.updateActualMatchingUIState;
264
343
  exports.default = expenseAutomationActualMatchingView.reducer;
@@ -13,10 +13,17 @@ export type ActualMatchingGroupedAccrual = {
13
13
  serviceMonth?: string;
14
14
  };
15
15
  export interface ActualMatchingRow extends JEOneTimeAccrual {
16
+ accruedAmount: Amount;
17
+ actualAmount: Amount;
16
18
  kind: ActualMatchingRowKind;
19
+ matchId: string;
17
20
  groupedAccrualIds?: ID[];
18
21
  groupedMembers?: ActualMatchingGroupedAccrual[];
22
+ matchShape?: string;
23
+ matchTier?: string;
19
24
  reversalDate?: ZeniDate;
25
+ transactionDate?: string;
26
+ transactionType?: string;
20
27
  }
21
28
  export interface ActualMatchingKpiSummary {
22
29
  ai: {
@@ -56,14 +63,20 @@ export interface ActualMatchingMatchModalView {
56
63
  results: ActualMatchingTransactionSearchResult[];
57
64
  };
58
65
  lastBatchResults?: MatchAndReverseResult[];
66
+ openedFromMatchId?: string;
67
+ prefill?: {
68
+ selectedAccrualIds: ID[];
69
+ actualAmount?: number;
70
+ matchedLineId?: string;
71
+ matchedTransactionId?: string;
72
+ };
59
73
  }
60
74
  export interface ExpenseAutomationActualMatchingViewSelector extends SelectorView {
61
- autoReversed: ActualMatchingRow[];
62
75
  hasLoadedForSelectedPeriod: boolean;
63
76
  kpiSummary: ActualMatchingKpiSummary;
64
- manuallyReversed: ActualMatchingRow[];
65
77
  matchModal: ActualMatchingMatchModalView;
66
78
  pendingReview: ActualMatchingRow[];
67
79
  refreshStatus: FetchStateAndError;
80
+ reversed: ActualMatchingRow[];
68
81
  uiState: ActualMatchingViewUIState;
69
82
  }
@@ -1,11 +1,25 @@
1
+ import { FetchStateAndError } from '../../../commonStateTypes/common';
2
+ import { ActualMatchingMatch } from '../../../entity/actualMatching/actualMatchingState';
3
+ import { ActualMatchingMatchId } from '../../../entity/actualMatching/actualMatchingTypes';
1
4
  import { JEOneTimeAccrual } from '../../../entity/jeSchedules/jeSchedulesSelector';
2
5
  import { RootState } from '../../../rootStateTypes';
3
6
  import { ZeniDate } from '../../../zeniDayJS';
4
- import { ExpenseAutomationActualMatchingViewSelector } from '../selectorTypes/actualMatchingViewSelectorTypes';
7
+ import { ActualMatchingRow, ActualMatchingRowKind, ExpenseAutomationActualMatchingViewSelector } from '../selectorTypes/actualMatchingViewSelectorTypes';
8
+ export declare function parseActualMatchingMatchId(matchId: string): {
9
+ transactionIntegrationId: string;
10
+ transactionType: string;
11
+ } | null;
5
12
  export declare function isAutoReversedAccrual(accrual: JEOneTimeAccrual): boolean;
6
13
  export declare function isManuallyReversedAccrual(accrual: JEOneTimeAccrual): boolean;
7
14
  export declare function isPendingReviewAccrual(accrual: JEOneTimeAccrual): boolean;
15
+ export declare function isReversedMatchKind(kind: ActualMatchingRowKind): boolean;
8
16
  export declare function getClearedMatchedAmount(accrual: JEOneTimeAccrual): number | undefined;
9
- export declare function getActualMatchingAmount(accrual: JEOneTimeAccrual): number;
17
+ export declare function getActualMatchingAmount(row: ActualMatchingRow): number;
10
18
  export declare function getActualMatchingReversalDate(accrual: JEOneTimeAccrual): ZeniDate | undefined;
19
+ export interface ExpenseAutomationActualMatchingMatchView {
20
+ detailFetchState: FetchStateAndError;
21
+ refreshStatus: FetchStateAndError;
22
+ match?: ActualMatchingMatch;
23
+ }
24
+ export declare function getExpenseAutomationActualMatchingMatchView(state: RootState, matchId: ActualMatchingMatchId): ExpenseAutomationActualMatchingMatchView;
11
25
  export declare function getExpenseAutomationActualMatchingView(state: RootState): ExpenseAutomationActualMatchingViewSelector;
@@ -3,17 +3,66 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.parseActualMatchingMatchId = parseActualMatchingMatchId;
6
7
  exports.isAutoReversedAccrual = isAutoReversedAccrual;
7
8
  exports.isManuallyReversedAccrual = isManuallyReversedAccrual;
8
9
  exports.isPendingReviewAccrual = isPendingReviewAccrual;
10
+ exports.isReversedMatchKind = isReversedMatchKind;
9
11
  exports.getClearedMatchedAmount = getClearedMatchedAmount;
10
12
  exports.getActualMatchingAmount = getActualMatchingAmount;
11
13
  exports.getActualMatchingReversalDate = getActualMatchingReversalDate;
14
+ exports.getExpenseAutomationActualMatchingMatchView = getExpenseAutomationActualMatchingMatchView;
12
15
  exports.getExpenseAutomationActualMatchingView = getExpenseAutomationActualMatchingView;
13
16
  const orderBy_1 = __importDefault(require("lodash/orderBy"));
14
17
  const timePeriod_1 = require("../../../commonStateTypes/timePeriod");
18
+ const actualMatchingSelector_1 = require("../../../entity/actualMatching/actualMatchingSelector");
19
+ const jeScheduleHelper_1 = require("../../../entity/jeSchedules/jeScheduleHelper");
15
20
  const jeSchedulesSelector_1 = require("../../../entity/jeSchedules/jeSchedulesSelector");
16
21
  const tenantSelector_1 = require("../../../entity/tenant/tenantSelector");
22
+ function toMatchModalAccrual(member) {
23
+ if ('jeOneTimeAccrualKey' in member && member.jeOneTimeAccrualKey != null) {
24
+ return member;
25
+ }
26
+ return {
27
+ ...member,
28
+ jeOneTimeAccrualKey: (0, jeScheduleHelper_1.generateJEOneTimeAccrualKey)(member.accrualId),
29
+ };
30
+ }
31
+ function uniqueMatchModalAccruals(pinned, searched) {
32
+ const byId = new Map();
33
+ pinned.forEach((row) => byId.set(row.accrualId, row));
34
+ searched.forEach((row) => {
35
+ if (!byId.has(row.accrualId)) {
36
+ byId.set(row.accrualId, row);
37
+ }
38
+ });
39
+ return Array.from(byId.values());
40
+ }
41
+ function uniqueTransactionSearchResults(pinned, searched) {
42
+ const byKey = new Map();
43
+ pinned.forEach((row) => byKey.set(`${row.transactionId}:${row.lineId}`, row));
44
+ searched.forEach((row) => {
45
+ const key = `${row.transactionId}:${row.lineId}`;
46
+ if (!byKey.has(key)) {
47
+ byKey.set(key, row);
48
+ }
49
+ });
50
+ return Array.from(byKey.values());
51
+ }
52
+ function parseActualMatchingMatchId(matchId) {
53
+ const separator = matchId.indexOf(':');
54
+ if (separator <= 0 || separator === matchId.length - 1) {
55
+ return null;
56
+ }
57
+ return {
58
+ transactionType: matchId.slice(0, separator).toLowerCase(),
59
+ transactionIntegrationId: matchId.slice(separator + 1),
60
+ };
61
+ }
62
+ const NOT_STARTED = {
63
+ fetchState: 'Not-Started',
64
+ error: undefined,
65
+ };
17
66
  function isAutoReversedAccrual(accrual) {
18
67
  return (accrual.accrualStatus === 'reversed' &&
19
68
  accrual.reversalTrigger === 'bill_match');
@@ -29,18 +78,14 @@ function isPendingReviewAccrual(accrual) {
29
78
  const resolution = accrual.matchSuggestion.resolution;
30
79
  return resolution == null || resolution.length === 0;
31
80
  }
81
+ function isReversedMatchKind(kind) {
82
+ return kind === 'auto_reversed' || kind === 'manually_reversed';
83
+ }
32
84
  function getClearedMatchedAmount(accrual) {
33
85
  return accrual.cleared?.matchedAmount?.amount;
34
86
  }
35
- function getActualMatchingAmount(accrual) {
36
- if (isPendingReviewAccrual(accrual)) {
37
- return (accrual.matchSuggestion?.matchedAmount?.amount ??
38
- accrual.estimatedAmount.amount);
39
- }
40
- if (isAutoReversedAccrual(accrual) || isManuallyReversedAccrual(accrual)) {
41
- return getClearedMatchedAmount(accrual) ?? accrual.estimatedAmount.amount;
42
- }
43
- return accrual.estimatedAmount.amount;
87
+ function getActualMatchingAmount(row) {
88
+ return row.actualAmount?.amount ?? row.estimatedAmount.amount;
44
89
  }
45
90
  function getActualMatchingReversalDate(accrual) {
46
91
  if (isPendingReviewAccrual(accrual)) {
@@ -48,40 +93,6 @@ function getActualMatchingReversalDate(accrual) {
48
93
  }
49
94
  return accrual.cleared?.matchedTransactionDate;
50
95
  }
51
- function getActualMatchingBillCollapseKey(accrual) {
52
- const stamp = isPendingReviewAccrual(accrual)
53
- ? accrual.matchSuggestion
54
- : accrual.cleared;
55
- const txnId = stamp?.matchedTransactionId;
56
- if (txnId != null && txnId !== '') {
57
- return `txn:${txnId}`;
58
- }
59
- const integrationId = stamp?.matchedTransactionIntegrationId;
60
- if (integrationId != null && integrationId !== '') {
61
- return `int:${integrationId}`;
62
- }
63
- const ids = accrual.cleared?.accrualIds;
64
- if (ids != null && ids.length > 1) {
65
- return `cleared:${[...ids].sort().join(',')}`;
66
- }
67
- return `accrual:${accrual.accrualId}`;
68
- }
69
- function collectGroupedAccrualIds(members) {
70
- const stampIds = members[0]?.cleared?.accrualIds;
71
- const memberIds = members.map((member) => member.accrualId);
72
- if (stampIds != null && stampIds.length >= memberIds.length) {
73
- return stampIds;
74
- }
75
- const seen = new Set();
76
- const grouped = [];
77
- for (const id of [...(stampIds ?? []), ...memberIds]) {
78
- if (!seen.has(id)) {
79
- seen.add(id);
80
- grouped.push(id);
81
- }
82
- }
83
- return grouped;
84
- }
85
96
  function collectGroupedMembers(members) {
86
97
  if (members.length <= 1) {
87
98
  return undefined;
@@ -94,51 +105,48 @@ function collectGroupedMembers(members) {
94
105
  serviceMonth: member.serviceMonth,
95
106
  }));
96
107
  }
97
- function toActualMatchingRow(members, kind) {
98
- const accrual = members[0];
108
+ function toActualMatchingRow(match) {
109
+ const firstMember = match.members[0];
110
+ if (firstMember == null) {
111
+ throw new Error('Actual matching row requires at least one member');
112
+ }
113
+ const first = toMatchModalAccrual(firstMember);
99
114
  return {
100
- ...accrual,
101
- kind,
102
- groupedAccrualIds: collectGroupedAccrualIds(members),
103
- groupedMembers: collectGroupedMembers(members),
104
- reversalDate: getActualMatchingReversalDate(accrual),
115
+ ...first,
116
+ matchId: match.matchId,
117
+ kind: match.kind,
118
+ accruedAmount: match.accruedAmount,
119
+ actualAmount: match.actualAmount,
120
+ matchTier: match.matchTier,
121
+ matchShape: match.matchShape,
122
+ transactionType: match.transactionType,
123
+ transactionDate: match.transactionDate,
124
+ groupedAccrualIds: match.members.map((member) => member.accrualId),
125
+ groupedMembers: collectGroupedMembers(match.members),
126
+ reversalDate: getActualMatchingReversalDate(first),
127
+ vendorName: match.vendorName !== '' ? match.vendorName : first.vendorName,
128
+ estimatedAmount: match.accruedAmount,
105
129
  };
106
130
  }
107
- function collapseActualMatchingRows(accruals, kind) {
108
- const groups = new Map();
109
- for (const accrual of accruals) {
110
- const key = getActualMatchingBillCollapseKey(accrual);
111
- const members = groups.get(key);
112
- if (members != null) {
113
- members.push(accrual);
114
- }
115
- else {
116
- groups.set(key, [accrual]);
117
- }
118
- }
119
- const rows = [];
120
- for (const members of groups.values()) {
121
- rows.push(toActualMatchingRow(members, kind));
122
- }
123
- return rows;
124
- }
125
- function sumCollapsedActualMatchingAmounts(rows) {
126
- return rows.reduce((sum, row) => sum + getActualMatchingAmount(row), 0);
127
- }
128
- function matchesSearch(accrual, searchString) {
131
+ function matchesSearch(row, searchString) {
129
132
  const trimmed = searchString.trim().toLowerCase();
130
133
  if (trimmed.length === 0) {
131
134
  return true;
132
135
  }
133
- return (accrual.vendorName.toLowerCase().includes(trimmed) ||
134
- accrual.memo.toLowerCase().includes(trimmed));
136
+ if (row.vendorName.toLowerCase().includes(trimmed)) {
137
+ return true;
138
+ }
139
+ if (row.memo.toLowerCase().includes(trimmed)) {
140
+ return true;
141
+ }
142
+ return (row.groupedMembers ?? []).some((member) => (member.memo ?? '').toLowerCase().includes(trimmed));
135
143
  }
136
144
  function getActualMatchingSortValue(row, sortKey) {
137
145
  switch (sortKey) {
138
146
  case 'vendor':
139
147
  return row.vendorName.toLowerCase();
140
148
  case 'date':
141
- return row.reversalDate?.valueOf();
149
+ return row.reversalDate?.valueOf() ?? row.transactionDate;
142
150
  case 'amount':
143
151
  return getActualMatchingAmount(row);
144
152
  case 'category':
@@ -146,10 +154,23 @@ function getActualMatchingSortValue(row, sortKey) {
146
154
  case 'class':
147
155
  return row.jeDebitAccountingClassName?.toLowerCase();
148
156
  case 'type':
149
- return (row.cleared?.matchedTransactionType ??
150
- row.matchSuggestion?.matchedTransactionType)?.toLowerCase();
157
+ return row.transactionType?.toLowerCase();
151
158
  case 'lifecycle':
152
159
  return row.kind;
160
+ case 'service':
161
+ return row.serviceMonth;
162
+ case 'accrued':
163
+ return row.accruedAmount?.amount ?? row.estimatedAmount.amount;
164
+ case 'actual':
165
+ return row.actualAmount?.amount;
166
+ case 'variance': {
167
+ const accrued = row.accruedAmount?.amount ?? row.estimatedAmount.amount;
168
+ const actual = row.actualAmount?.amount;
169
+ if (actual == null || accrued === 0) {
170
+ return undefined;
171
+ }
172
+ return ((actual - accrued) / Math.abs(accrued)) * 100;
173
+ }
153
174
  default:
154
175
  return undefined;
155
176
  }
@@ -167,37 +188,50 @@ function emptyKpiSummary() {
167
188
  manual: { count: 0 },
168
189
  };
169
190
  }
191
+ function sumRowAmounts(rows) {
192
+ return rows.reduce((sum, row) => sum + getActualMatchingAmount(row), 0);
193
+ }
194
+ function getExpenseAutomationActualMatchingMatchView(state, matchId) {
195
+ const view = state.expenseAutomationActualMatchingViewState;
196
+ return {
197
+ match: (0, actualMatchingSelector_1.getActualMatchingMatchById)(matchId, state.actualMatchingState),
198
+ detailFetchState: view.detailFetchStateByMatchId[matchId] ?? NOT_STARTED,
199
+ refreshStatus: view.detailRefreshStatusByMatchId[matchId] ?? NOT_STARTED,
200
+ };
201
+ }
170
202
  function getExpenseAutomationActualMatchingView(state) {
171
203
  const { expenseAutomationActualMatchingViewState, expenseAutomationViewState } = state;
172
- const { accrualKeysByPeriod, fetchState, error, refreshStatus, uiState, matchModal, } = expenseAutomationActualMatchingViewState;
204
+ const { matchIdsByPeriod, fetchState, error, refreshStatus, uiState, matchModal, } = expenseAutomationActualMatchingViewState;
173
205
  const currentTenant = (0, tenantSelector_1.getCurrentTenant)(state);
174
206
  const selectedPeriod = expenseAutomationViewState.selectedPeriodByTenantId[currentTenant.tenantId];
175
207
  const monthYearPeriodId = selectedPeriod != null ? (0, timePeriod_1.toMonthYearPeriodId)(selectedPeriod) : undefined;
176
- const accrualKeys = monthYearPeriodId != null
177
- ? accrualKeysByPeriod[monthYearPeriodId]
208
+ const matchIds = monthYearPeriodId != null
209
+ ? matchIdsByPeriod[monthYearPeriodId]
178
210
  : undefined;
179
- const accruals = (accrualKeys ?? [])
180
- .map((key) => (0, jeSchedulesSelector_1.getJEOneTimeAccrualByKey)(key, state.jeSchedulesState))
181
- .filter((accrual) => accrual != null);
182
- const filtered = accruals.filter((accrual) => matchesSearch(accrual, uiState.searchString));
183
- const autoReversed = sortActualMatchingRows(collapseActualMatchingRows(filtered.filter(isAutoReversedAccrual), 'auto_reversed'), uiState);
184
- const pendingReview = sortActualMatchingRows(collapseActualMatchingRows(filtered.filter(isPendingReviewAccrual), 'pending_review'), uiState);
185
- const manuallyReversed = sortActualMatchingRows(collapseActualMatchingRows(filtered.filter(isManuallyReversedAccrual), 'manually_reversed'), uiState);
186
- const listedRows = [...autoReversed, ...pendingReview, ...manuallyReversed];
211
+ const rows = (matchIds ?? [])
212
+ .map((matchId) => (0, actualMatchingSelector_1.getActualMatchingMatchById)(matchId, state.actualMatchingState))
213
+ .filter((match) => match != null)
214
+ .filter((match) => match.members.length > 0)
215
+ .map(toActualMatchingRow)
216
+ .filter((row) => matchesSearch(row, uiState.searchString));
217
+ const reversed = sortActualMatchingRows(rows.filter((row) => isReversedMatchKind(row.kind)), uiState);
218
+ const pendingReview = sortActualMatchingRows(rows.filter((row) => row.kind === 'pending_review'), uiState);
219
+ const autoReversedForKpi = reversed.filter((row) => row.kind === 'auto_reversed');
220
+ const listedRows = [...reversed, ...pendingReview];
187
221
  const kpiSummary = listedRows.length === 0
188
222
  ? emptyKpiSummary()
189
223
  : {
190
224
  autoReversed: {
191
- count: autoReversed.length,
192
- amount: sumCollapsedActualMatchingAmounts(autoReversed),
225
+ count: autoReversedForKpi.length,
226
+ amount: sumRowAmounts(autoReversedForKpi),
193
227
  },
194
228
  pendingReview: {
195
229
  count: pendingReview.length,
196
- amount: sumCollapsedActualMatchingAmounts(pendingReview),
230
+ amount: sumRowAmounts(pendingReview),
197
231
  },
198
232
  totalThisClose: {
199
233
  count: listedRows.length,
200
- amount: sumCollapsedActualMatchingAmounts(listedRows),
234
+ amount: sumRowAmounts(listedRows),
201
235
  },
202
236
  ai: {
203
237
  count: listedRows.filter((row) => row.origin === 'agent').length,
@@ -206,26 +240,40 @@ function getExpenseAutomationActualMatchingView(state) {
206
240
  count: listedRows.filter((row) => row.origin === 'human').length,
207
241
  },
208
242
  };
209
- const accrualSearchResults = matchModal.accrualSearch.results
243
+ const searchedAccruals = matchModal.accrualSearch.results
210
244
  .map((key) => (0, jeSchedulesSelector_1.getJEOneTimeAccrualByKey)(key, state.jeSchedulesState))
211
245
  .filter((accrual) => accrual != null);
246
+ const openedMatch = matchModal.openedFromMatchId != null
247
+ ? (0, actualMatchingSelector_1.getActualMatchingMatchById)(matchModal.openedFromMatchId, state.actualMatchingState)
248
+ : undefined;
249
+ const pinnedAccruals = (openedMatch?.members ?? []).map(toMatchModalAccrual);
250
+ const firstSuggestion = openedMatch?.members[0]?.matchSuggestion;
251
+ const prefill = openedMatch == null
252
+ ? undefined
253
+ : {
254
+ selectedAccrualIds: openedMatch.members.map((member) => member.accrualId),
255
+ actualAmount: openedMatch.actualAmount.amount,
256
+ matchedLineId: firstSuggestion?.matchedLineId,
257
+ matchedTransactionId: firstSuggestion?.matchedTransactionId,
258
+ };
212
259
  return {
213
260
  fetchState,
214
261
  error,
215
- autoReversed,
262
+ reversed,
216
263
  pendingReview,
217
- manuallyReversed,
218
264
  kpiSummary,
219
265
  uiState,
220
266
  refreshStatus,
221
- hasLoadedForSelectedPeriod: accrualKeys != null,
267
+ hasLoadedForSelectedPeriod: matchIds != null,
222
268
  matchModal: {
223
269
  isOpen: matchModal.isOpen,
270
+ openedFromMatchId: matchModal.openedFromMatchId,
271
+ prefill,
224
272
  confirmStatus: matchModal.confirmStatus,
225
273
  lastBatchResults: matchModal.lastBatchResults,
226
274
  transactionSearch: {
227
275
  query: matchModal.transactionSearch.query,
228
- results: matchModal.transactionSearch.results,
276
+ results: uniqueTransactionSearchResults(matchModal.pinnedTransactionResults, matchModal.transactionSearch.results),
229
277
  fetchState: {
230
278
  fetchState: matchModal.transactionSearch.fetchState,
231
279
  error: matchModal.transactionSearch.error,
@@ -235,7 +283,7 @@ function getExpenseAutomationActualMatchingView(state) {
235
283
  },
236
284
  accrualSearch: {
237
285
  query: matchModal.accrualSearch.query,
238
- results: accrualSearchResults,
286
+ results: uniqueMatchModalAccruals(pinnedAccruals, searchedAccruals),
239
287
  fetchState: {
240
288
  fetchState: matchModal.accrualSearch.fetchState,
241
289
  error: matchModal.accrualSearch.error,
@@ -1,8 +1,9 @@
1
1
  import { FetchStateAndError, FetchedState, ID } from '../../../commonStateTypes/common';
2
2
  import { SortOrder } from '../../../commonStateTypes/selectorTypes/sortOrderTypes';
3
3
  import { MonthYearPeriodId } from '../../../commonStateTypes/timePeriod';
4
+ import { ActualMatchingMatchId } from '../../../entity/actualMatching/actualMatchingTypes';
4
5
  import { JEOneTimeAccrualKey } from '../../../entity/jeSchedules/jeSchedulesTypes';
5
- export declare const toActualMatchingSortKey: (v: string) => "type" | "date" | "vendor" | "category" | "class" | "amount" | "lifecycle";
6
+ export declare const toActualMatchingSortKey: (v: string) => "type" | "date" | "vendor" | "category" | "class" | "amount" | "variance" | "lifecycle" | "service" | "accrued" | "actual";
6
7
  export type ActualMatchingSortKey = ReturnType<typeof toActualMatchingSortKey>;
7
8
  export declare const MATCH_TRANSACTION_SEARCH_PAGE_SIZE = 50;
8
9
  export declare const MATCH_ACCRUAL_SEARCH_LIMIT = 50;
@@ -70,11 +71,15 @@ export interface ActualMatchingMatchModalState {
70
71
  accrualSearch: ActualMatchingAccrualSearchState;
71
72
  confirmStatus: FetchStateAndError;
72
73
  isOpen: boolean;
74
+ pinnedTransactionResults: ActualMatchingTransactionSearchResult[];
73
75
  transactionSearch: ActualMatchingTransactionSearchState;
74
76
  lastBatchResults?: MatchAndReverseResult[];
77
+ openedFromMatchId?: ActualMatchingMatchId;
75
78
  }
76
79
  export interface ActualMatchingViewState extends FetchedState {
77
- accrualKeysByPeriod: Record<MonthYearPeriodId, JEOneTimeAccrualKey[]>;
80
+ detailFetchStateByMatchId: Record<ActualMatchingMatchId, FetchStateAndError>;
81
+ detailRefreshStatusByMatchId: Record<ActualMatchingMatchId, FetchStateAndError>;
82
+ matchIdsByPeriod: Record<MonthYearPeriodId, ActualMatchingMatchId[]>;
78
83
  matchModal: ActualMatchingMatchModalState;
79
84
  refreshStatus: FetchStateAndError;
80
85
  uiState: ActualMatchingViewUIState;
@@ -10,6 +10,10 @@ const ACTUAL_MATCHING_SORT_KEYS = [
10
10
  'class',
11
11
  'type',
12
12
  'lifecycle',
13
+ 'service',
14
+ 'accrued',
15
+ 'actual',
16
+ 'variance',
13
17
  ];
14
18
  const toActualMatchingSortKey = (v) => (0, stringToUnion_1.stringToUnion)(v, ACTUAL_MATCHING_SORT_KEYS);
15
19
  exports.toActualMatchingSortKey = toActualMatchingSortKey;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zeniai/client-epic-state",
3
- "version": "5.2.22-betaAM4",
3
+ "version": "5.2.22-betaAM5",
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",