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

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.
@@ -135,6 +135,7 @@ export interface JEOneTimeAccrualClearedPayload {
135
135
  match_tier?: string | null;
136
136
  matched_amount?: number | null;
137
137
  matched_line_id?: string | null;
138
+ matched_transaction_date?: string | null;
138
139
  matched_transaction_id?: string | null;
139
140
  matched_transaction_integration_id?: string | null;
140
141
  matched_transaction_type?: string | null;
@@ -144,6 +145,7 @@ export interface JEOneTimeAccrualMatchSuggestionPayload {
144
145
  match_tier?: string | null;
145
146
  matched_amount?: number | null;
146
147
  matched_line_id?: string | null;
148
+ matched_transaction_date?: string | null;
147
149
  matched_transaction_id?: string | null;
148
150
  matched_transaction_integration_id?: string | null;
149
151
  matched_transaction_type?: string | null;
@@ -473,6 +473,9 @@ function toJEOneTimeAccrualCleared(payload, currencyCode) {
473
473
  : undefined,
474
474
  accrualIds: payload.accrual_ids ?? undefined,
475
475
  reversedAt: payload.reversed_at != null ? (0, zeniDayJS_1.date)(payload.reversed_at) : undefined,
476
+ matchedTransactionDate: payload.matched_transaction_date != null
477
+ ? (0, zeniDayJS_1.date)(payload.matched_transaction_date)
478
+ : undefined,
476
479
  matchTier: payload.match_tier ?? undefined,
477
480
  matchShape: payload.match_shape ?? undefined,
478
481
  };
@@ -491,6 +494,9 @@ function toJEOneTimeAccrualMatchSuggestion(payload, currencyCode) {
491
494
  matchedAmount: payload.matched_amount != null
492
495
  ? toOneTimeAccrualAmount(payload.matched_amount, currencyCode)
493
496
  : undefined,
497
+ matchedTransactionDate: payload.matched_transaction_date != null
498
+ ? (0, zeniDayJS_1.date)(payload.matched_transaction_date)
499
+ : undefined,
494
500
  };
495
501
  }
496
502
  function toJEOneTimeAccrual(payload) {
@@ -204,6 +204,7 @@ export interface JEOneTimeAccrualCleared {
204
204
  groupSum?: Amount;
205
205
  matchedAmount?: Amount;
206
206
  matchedLineId?: string;
207
+ matchedTransactionDate?: ZeniDate;
207
208
  matchedTransactionId?: string;
208
209
  matchedTransactionIntegrationId?: string;
209
210
  matchedTransactionType?: string;
@@ -215,6 +216,7 @@ export interface JEOneTimeAccrualCleared {
215
216
  export interface JEOneTimeAccrualMatchSuggestion {
216
217
  matchedAmount?: Amount;
217
218
  matchedLineId?: string;
219
+ matchedTransactionDate?: ZeniDate;
218
220
  matchedTransactionId?: string;
219
221
  matchedTransactionIntegrationId?: string;
220
222
  matchedTransactionType?: string;
@@ -462,6 +462,9 @@ function toJEOneTimeAccrualCleared(payload, currencyCode) {
462
462
  : undefined,
463
463
  accrualIds: payload.accrual_ids ?? undefined,
464
464
  reversedAt: payload.reversed_at != null ? date(payload.reversed_at) : undefined,
465
+ matchedTransactionDate: payload.matched_transaction_date != null
466
+ ? date(payload.matched_transaction_date)
467
+ : undefined,
465
468
  matchTier: payload.match_tier ?? undefined,
466
469
  matchShape: payload.match_shape ?? undefined,
467
470
  };
@@ -480,6 +483,9 @@ function toJEOneTimeAccrualMatchSuggestion(payload, currencyCode) {
480
483
  matchedAmount: payload.matched_amount != null
481
484
  ? toOneTimeAccrualAmount(payload.matched_amount, currencyCode)
482
485
  : undefined,
486
+ matchedTransactionDate: payload.matched_transaction_date != null
487
+ ? date(payload.matched_transaction_date)
488
+ : undefined,
483
489
  };
484
490
  }
485
491
  export function toJEOneTimeAccrual(payload) {
@@ -20,26 +20,6 @@ export function isPendingReviewAccrual(accrual) {
20
20
  export function getClearedMatchedAmount(accrual) {
21
21
  return accrual.cleared?.matchedAmount?.amount;
22
22
  }
23
- function getActualMatchingGroupKey(accrual) {
24
- const ids = accrual.cleared?.accrualIds;
25
- if (ids != null && ids.length > 1) {
26
- return `cleared:${[...ids].sort().join(',')}`;
27
- }
28
- return accrual.accrualId;
29
- }
30
- function sumUniqueActualMatchingAmounts(accruals) {
31
- const seen = new Set();
32
- let sum = 0;
33
- for (const accrual of accruals) {
34
- const key = getActualMatchingGroupKey(accrual);
35
- if (seen.has(key)) {
36
- continue;
37
- }
38
- seen.add(key);
39
- sum += getActualMatchingAmount(accrual);
40
- }
41
- return sum;
42
- }
43
23
  export function getActualMatchingAmount(accrual) {
44
24
  if (isPendingReviewAccrual(accrual)) {
45
25
  return (accrual.matchSuggestion?.matchedAmount?.amount ??
@@ -51,20 +31,88 @@ export function getActualMatchingAmount(accrual) {
51
31
  return accrual.estimatedAmount.amount;
52
32
  }
53
33
  export function getActualMatchingReversalDate(accrual) {
54
- if (!isAutoReversedAccrual(accrual) && !isManuallyReversedAccrual(accrual)) {
34
+ if (isPendingReviewAccrual(accrual)) {
35
+ return accrual.matchSuggestion?.matchedTransactionDate;
36
+ }
37
+ return accrual.cleared?.matchedTransactionDate;
38
+ }
39
+ function getActualMatchingBillCollapseKey(accrual) {
40
+ const stamp = isPendingReviewAccrual(accrual)
41
+ ? accrual.matchSuggestion
42
+ : accrual.cleared;
43
+ const txnId = stamp?.matchedTransactionId;
44
+ if (txnId != null && txnId !== '') {
45
+ return `txn:${txnId}`;
46
+ }
47
+ const integrationId = stamp?.matchedTransactionIntegrationId;
48
+ if (integrationId != null && integrationId !== '') {
49
+ return `int:${integrationId}`;
50
+ }
51
+ const ids = accrual.cleared?.accrualIds;
52
+ if (ids != null && ids.length > 1) {
53
+ return `cleared:${[...ids].sort().join(',')}`;
54
+ }
55
+ return `accrual:${accrual.accrualId}`;
56
+ }
57
+ function collectGroupedAccrualIds(members) {
58
+ const stampIds = members[0]?.cleared?.accrualIds;
59
+ const memberIds = members.map((member) => member.accrualId);
60
+ if (stampIds != null && stampIds.length >= memberIds.length) {
61
+ return stampIds;
62
+ }
63
+ const seen = new Set();
64
+ const grouped = [];
65
+ for (const id of [...(stampIds ?? []), ...memberIds]) {
66
+ if (!seen.has(id)) {
67
+ seen.add(id);
68
+ grouped.push(id);
69
+ }
70
+ }
71
+ return grouped;
72
+ }
73
+ function collectGroupedMembers(members) {
74
+ if (members.length <= 1) {
55
75
  return undefined;
56
76
  }
57
- return accrual.actualReversalPostDate ?? accrual.cleared?.reversedAt;
77
+ return members.map((member) => ({
78
+ accrualId: member.accrualId,
79
+ estimatedAmount: member.estimatedAmount,
80
+ jeDebitAccountName: member.jeDebitAccountName,
81
+ memo: member.memo,
82
+ serviceMonth: member.serviceMonth,
83
+ }));
58
84
  }
59
- function toActualMatchingRow(accrual, kind) {
85
+ function toActualMatchingRow(members, kind) {
86
+ const accrual = members[0];
60
87
  return {
61
88
  ...accrual,
62
89
  kind,
63
- reversalDate: kind === 'pending_review'
64
- ? undefined
65
- : getActualMatchingReversalDate(accrual),
90
+ groupedAccrualIds: collectGroupedAccrualIds(members),
91
+ groupedMembers: collectGroupedMembers(members),
92
+ reversalDate: getActualMatchingReversalDate(accrual),
66
93
  };
67
94
  }
95
+ function collapseActualMatchingRows(accruals, kind) {
96
+ const groups = new Map();
97
+ for (const accrual of accruals) {
98
+ const key = getActualMatchingBillCollapseKey(accrual);
99
+ const members = groups.get(key);
100
+ if (members != null) {
101
+ members.push(accrual);
102
+ }
103
+ else {
104
+ groups.set(key, [accrual]);
105
+ }
106
+ }
107
+ const rows = [];
108
+ for (const members of groups.values()) {
109
+ rows.push(toActualMatchingRow(members, kind));
110
+ }
111
+ return rows;
112
+ }
113
+ function sumCollapsedActualMatchingAmounts(rows) {
114
+ return rows.reduce((sum, row) => sum + getActualMatchingAmount(row), 0);
115
+ }
68
116
  function matchesSearch(accrual, searchString) {
69
117
  const trimmed = searchString.trim().toLowerCase();
70
118
  if (trimmed.length === 0) {
@@ -120,38 +168,30 @@ export function getExpenseAutomationActualMatchingView(state) {
120
168
  .map((key) => getJEOneTimeAccrualByKey(key, state.jeSchedulesState))
121
169
  .filter((accrual) => accrual != null);
122
170
  const filtered = accruals.filter((accrual) => matchesSearch(accrual, uiState.searchString));
123
- const autoReversed = sortActualMatchingRows(filtered
124
- .filter(isAutoReversedAccrual)
125
- .map((accrual) => toActualMatchingRow(accrual, 'auto_reversed')), uiState);
126
- const pendingReview = sortActualMatchingRows(filtered
127
- .filter(isPendingReviewAccrual)
128
- .map((accrual) => toActualMatchingRow(accrual, 'pending_review')), uiState);
129
- const manuallyReversed = sortActualMatchingRows(filtered
130
- .filter(isManuallyReversedAccrual)
131
- .map((accrual) => toActualMatchingRow(accrual, 'manually_reversed')), uiState);
132
- const listedAccruals = accruals.filter((accrual) => isAutoReversedAccrual(accrual) ||
133
- isPendingReviewAccrual(accrual) ||
134
- isManuallyReversedAccrual(accrual));
135
- const kpiSummary = listedAccruals.length === 0
171
+ const autoReversed = sortActualMatchingRows(collapseActualMatchingRows(filtered.filter(isAutoReversedAccrual), 'auto_reversed'), uiState);
172
+ const pendingReview = sortActualMatchingRows(collapseActualMatchingRows(filtered.filter(isPendingReviewAccrual), 'pending_review'), uiState);
173
+ const manuallyReversed = sortActualMatchingRows(collapseActualMatchingRows(filtered.filter(isManuallyReversedAccrual), 'manually_reversed'), uiState);
174
+ const listedRows = [...autoReversed, ...pendingReview, ...manuallyReversed];
175
+ const kpiSummary = listedRows.length === 0
136
176
  ? emptyKpiSummary()
137
177
  : {
138
178
  autoReversed: {
139
- count: listedAccruals.filter(isAutoReversedAccrual).length,
140
- amount: sumUniqueActualMatchingAmounts(listedAccruals.filter(isAutoReversedAccrual)),
179
+ count: autoReversed.length,
180
+ amount: sumCollapsedActualMatchingAmounts(autoReversed),
141
181
  },
142
182
  pendingReview: {
143
- count: listedAccruals.filter(isPendingReviewAccrual).length,
144
- amount: sumUniqueActualMatchingAmounts(listedAccruals.filter(isPendingReviewAccrual)),
183
+ count: pendingReview.length,
184
+ amount: sumCollapsedActualMatchingAmounts(pendingReview),
145
185
  },
146
186
  totalThisClose: {
147
- count: listedAccruals.length,
148
- amount: sumUniqueActualMatchingAmounts(listedAccruals),
187
+ count: listedRows.length,
188
+ amount: sumCollapsedActualMatchingAmounts(listedRows),
149
189
  },
150
190
  ai: {
151
- count: listedAccruals.filter((accrual) => accrual.origin === 'agent').length,
191
+ count: listedRows.filter((row) => row.origin === 'agent').length,
152
192
  },
153
193
  manual: {
154
- count: listedAccruals.filter((accrual) => accrual.origin === 'human').length,
194
+ count: listedRows.filter((row) => row.origin === 'human').length,
155
195
  },
156
196
  };
157
197
  const accrualSearchResults = matchModal.accrualSearch.results
@@ -1,11 +1,21 @@
1
- import { FetchStateAndError } from '../../../commonStateTypes/common';
1
+ import { Amount } from '../../../commonStateTypes/amount';
2
+ import { FetchStateAndError, ID } from '../../../commonStateTypes/common';
2
3
  import { SelectorView } from '../../../commonStateTypes/viewAndReport/viewAndReport';
3
4
  import { JEOneTimeAccrual } from '../../../entity/jeSchedules/jeSchedulesSelector';
4
5
  import { ZeniDate } from '../../../zeniDayJS';
5
6
  import { ActualMatchingTransactionSearchResult, ActualMatchingViewUIState, MatchAndReverseResult } from '../types/actualMatchingViewState';
6
7
  export type ActualMatchingRowKind = 'auto_reversed' | 'pending_review' | 'manually_reversed';
8
+ export type ActualMatchingGroupedAccrual = {
9
+ accrualId: ID;
10
+ estimatedAmount?: Amount;
11
+ jeDebitAccountName?: string;
12
+ memo?: string;
13
+ serviceMonth?: string;
14
+ };
7
15
  export interface ActualMatchingRow extends JEOneTimeAccrual {
8
16
  kind: ActualMatchingRowKind;
17
+ groupedAccrualIds?: ID[];
18
+ groupedMembers?: ActualMatchingGroupedAccrual[];
9
19
  reversalDate?: ZeniDate;
10
20
  }
11
21
  export interface ActualMatchingKpiSummary {
@@ -32,26 +32,6 @@ function isPendingReviewAccrual(accrual) {
32
32
  function getClearedMatchedAmount(accrual) {
33
33
  return accrual.cleared?.matchedAmount?.amount;
34
34
  }
35
- function getActualMatchingGroupKey(accrual) {
36
- const ids = accrual.cleared?.accrualIds;
37
- if (ids != null && ids.length > 1) {
38
- return `cleared:${[...ids].sort().join(',')}`;
39
- }
40
- return accrual.accrualId;
41
- }
42
- function sumUniqueActualMatchingAmounts(accruals) {
43
- const seen = new Set();
44
- let sum = 0;
45
- for (const accrual of accruals) {
46
- const key = getActualMatchingGroupKey(accrual);
47
- if (seen.has(key)) {
48
- continue;
49
- }
50
- seen.add(key);
51
- sum += getActualMatchingAmount(accrual);
52
- }
53
- return sum;
54
- }
55
35
  function getActualMatchingAmount(accrual) {
56
36
  if (isPendingReviewAccrual(accrual)) {
57
37
  return (accrual.matchSuggestion?.matchedAmount?.amount ??
@@ -63,20 +43,88 @@ function getActualMatchingAmount(accrual) {
63
43
  return accrual.estimatedAmount.amount;
64
44
  }
65
45
  function getActualMatchingReversalDate(accrual) {
66
- if (!isAutoReversedAccrual(accrual) && !isManuallyReversedAccrual(accrual)) {
46
+ if (isPendingReviewAccrual(accrual)) {
47
+ return accrual.matchSuggestion?.matchedTransactionDate;
48
+ }
49
+ return accrual.cleared?.matchedTransactionDate;
50
+ }
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
+ function collectGroupedMembers(members) {
86
+ if (members.length <= 1) {
67
87
  return undefined;
68
88
  }
69
- return accrual.actualReversalPostDate ?? accrual.cleared?.reversedAt;
89
+ return members.map((member) => ({
90
+ accrualId: member.accrualId,
91
+ estimatedAmount: member.estimatedAmount,
92
+ jeDebitAccountName: member.jeDebitAccountName,
93
+ memo: member.memo,
94
+ serviceMonth: member.serviceMonth,
95
+ }));
70
96
  }
71
- function toActualMatchingRow(accrual, kind) {
97
+ function toActualMatchingRow(members, kind) {
98
+ const accrual = members[0];
72
99
  return {
73
100
  ...accrual,
74
101
  kind,
75
- reversalDate: kind === 'pending_review'
76
- ? undefined
77
- : getActualMatchingReversalDate(accrual),
102
+ groupedAccrualIds: collectGroupedAccrualIds(members),
103
+ groupedMembers: collectGroupedMembers(members),
104
+ reversalDate: getActualMatchingReversalDate(accrual),
78
105
  };
79
106
  }
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
+ }
80
128
  function matchesSearch(accrual, searchString) {
81
129
  const trimmed = searchString.trim().toLowerCase();
82
130
  if (trimmed.length === 0) {
@@ -132,38 +180,30 @@ function getExpenseAutomationActualMatchingView(state) {
132
180
  .map((key) => (0, jeSchedulesSelector_1.getJEOneTimeAccrualByKey)(key, state.jeSchedulesState))
133
181
  .filter((accrual) => accrual != null);
134
182
  const filtered = accruals.filter((accrual) => matchesSearch(accrual, uiState.searchString));
135
- const autoReversed = sortActualMatchingRows(filtered
136
- .filter(isAutoReversedAccrual)
137
- .map((accrual) => toActualMatchingRow(accrual, 'auto_reversed')), uiState);
138
- const pendingReview = sortActualMatchingRows(filtered
139
- .filter(isPendingReviewAccrual)
140
- .map((accrual) => toActualMatchingRow(accrual, 'pending_review')), uiState);
141
- const manuallyReversed = sortActualMatchingRows(filtered
142
- .filter(isManuallyReversedAccrual)
143
- .map((accrual) => toActualMatchingRow(accrual, 'manually_reversed')), uiState);
144
- const listedAccruals = accruals.filter((accrual) => isAutoReversedAccrual(accrual) ||
145
- isPendingReviewAccrual(accrual) ||
146
- isManuallyReversedAccrual(accrual));
147
- const kpiSummary = listedAccruals.length === 0
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];
187
+ const kpiSummary = listedRows.length === 0
148
188
  ? emptyKpiSummary()
149
189
  : {
150
190
  autoReversed: {
151
- count: listedAccruals.filter(isAutoReversedAccrual).length,
152
- amount: sumUniqueActualMatchingAmounts(listedAccruals.filter(isAutoReversedAccrual)),
191
+ count: autoReversed.length,
192
+ amount: sumCollapsedActualMatchingAmounts(autoReversed),
153
193
  },
154
194
  pendingReview: {
155
- count: listedAccruals.filter(isPendingReviewAccrual).length,
156
- amount: sumUniqueActualMatchingAmounts(listedAccruals.filter(isPendingReviewAccrual)),
195
+ count: pendingReview.length,
196
+ amount: sumCollapsedActualMatchingAmounts(pendingReview),
157
197
  },
158
198
  totalThisClose: {
159
- count: listedAccruals.length,
160
- amount: sumUniqueActualMatchingAmounts(listedAccruals),
199
+ count: listedRows.length,
200
+ amount: sumCollapsedActualMatchingAmounts(listedRows),
161
201
  },
162
202
  ai: {
163
- count: listedAccruals.filter((accrual) => accrual.origin === 'agent').length,
203
+ count: listedRows.filter((row) => row.origin === 'agent').length,
164
204
  },
165
205
  manual: {
166
- count: listedAccruals.filter((accrual) => accrual.origin === 'human').length,
206
+ count: listedRows.filter((row) => row.origin === 'human').length,
167
207
  },
168
208
  };
169
209
  const accrualSearchResults = matchModal.accrualSearch.results
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zeniai/client-epic-state",
3
- "version": "5.2.22-betaAM2",
3
+ "version": "5.2.22-betaAM4",
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",