@zeniai/client-epic-state 5.2.35-beta0MM → 5.2.35-beta10MM
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/entity/actualMatching/actualMatchingPayload.d.ts +7 -1
- package/lib/entity/actualMatching/actualMatchingPayload.js +19 -12
- package/lib/entity/actualMatching/actualMatchingReducer.d.ts +3 -2
- package/lib/entity/actualMatching/actualMatchingReducer.js +3 -3
- package/lib/entity/actualMatching/actualMatchingState.d.ts +3 -2
- package/lib/entity/aiCfo/aiCfoReducer.js +78 -4
- package/lib/entity/aiCfo/aiCfoState.d.ts +16 -0
- package/lib/entity/jeSchedules/jeSchedulesPayload.d.ts +2 -0
- package/lib/entity/jeSchedules/jeSchedulesPayload.js +4 -0
- package/lib/entity/jeSchedules/jeSchedulesState.d.ts +2 -0
- package/lib/entity/snackbar/snackbarTypes.d.ts +1 -1
- package/lib/entity/snackbar/snackbarTypes.js +2 -0
- package/lib/esm/entity/actualMatching/actualMatchingPayload.js +18 -12
- package/lib/esm/entity/actualMatching/actualMatchingReducer.js +3 -3
- package/lib/esm/entity/aiCfo/aiCfoReducer.js +79 -5
- package/lib/esm/entity/jeSchedules/jeSchedulesPayload.js +4 -0
- package/lib/esm/entity/snackbar/snackbarTypes.js +2 -0
- package/lib/esm/index.js +6 -6
- package/lib/esm/view/expenseAutomationView/epics/actualMatching/fetchActualMatchingEpic.js +3 -5
- package/lib/esm/view/expenseAutomationView/epics/actualMatching/fetchActualMatchingMatchEpic.js +3 -2
- package/lib/esm/view/expenseAutomationView/epics/actualMatching/matchAndReverseAccrualsEpic.js +80 -25
- package/lib/esm/view/expenseAutomationView/epics/actualMatching/refreshActualMatchingList.js +16 -0
- package/lib/esm/view/expenseAutomationView/epics/actualMatching/searchMatchAccrualsEpic.js +16 -6
- package/lib/esm/view/expenseAutomationView/epics/actualMatching/searchMatchTransactionsEpic.js +12 -11
- package/lib/esm/view/expenseAutomationView/epics/actualMatching/undoReversalAccrualsEpic.js +50 -26
- package/lib/esm/view/expenseAutomationView/epics/common/fetchAllExpenseAutomationTabsEpic.js +5 -9
- package/lib/esm/view/expenseAutomationView/epics/common/refreshExpenseAutomationCurrentTabEpic.js +1 -9
- package/lib/esm/view/expenseAutomationView/epics/jeSchedule/fetchJeSchedulePageEpic.js +13 -3
- package/lib/esm/view/expenseAutomationView/epics/jeSchedule/prefetchOtherJeSchedulesTabEpic.js +34 -16
- package/lib/esm/view/expenseAutomationView/epics/jeSchedule/switchJESchedulesTabEpic.js +8 -8
- package/lib/esm/view/expenseAutomationView/expenseAutomationViewReducer.js +2 -1
- package/lib/esm/view/expenseAutomationView/expenseAutomationViewSelector.js +5 -2
- package/lib/esm/view/expenseAutomationView/expenseAutomationViewState.js +4 -2
- package/lib/esm/view/expenseAutomationView/payload/actualMatchingPayload.js +10 -10
- package/lib/esm/view/expenseAutomationView/reducers/actualMatchingViewReducer.js +90 -20
- package/lib/esm/view/expenseAutomationView/reducers/jeSchedulesViewReducer.js +2 -1
- package/lib/esm/view/expenseAutomationView/selectors/actualMatchingViewSelector.js +206 -61
- package/lib/esm/view/expenseAutomationView/selectors/jeSchedulesViewSelector.js +1 -0
- package/lib/esm/view/expenseAutomationView/types/actualMatchingViewState.js +3 -0
- package/lib/esm/view/expenseAutomationView/types/jeSchedulesViewState.js +3 -0
- package/lib/esm/view/financeStatement/financeStatementReducer.js +17 -27
- package/lib/esm/view/spendManagement/billPay/billList/billListSelector.js +3 -3
- package/lib/index.d.ts +7 -6
- package/lib/index.js +52 -48
- package/lib/view/expenseAutomationView/epics/actualMatching/fetchActualMatchingEpic.js +3 -5
- package/lib/view/expenseAutomationView/epics/actualMatching/fetchActualMatchingMatchEpic.d.ts +1 -1
- package/lib/view/expenseAutomationView/epics/actualMatching/fetchActualMatchingMatchEpic.js +3 -2
- package/lib/view/expenseAutomationView/epics/actualMatching/matchAndReverseAccrualsEpic.d.ts +2 -1
- package/lib/view/expenseAutomationView/epics/actualMatching/matchAndReverseAccrualsEpic.js +77 -22
- package/lib/view/expenseAutomationView/epics/actualMatching/refreshActualMatchingList.d.ts +3 -0
- package/lib/view/expenseAutomationView/epics/actualMatching/refreshActualMatchingList.js +19 -0
- package/lib/view/expenseAutomationView/epics/actualMatching/searchMatchAccrualsEpic.d.ts +3 -2
- package/lib/view/expenseAutomationView/epics/actualMatching/searchMatchAccrualsEpic.js +15 -5
- package/lib/view/expenseAutomationView/epics/actualMatching/searchMatchTransactionsEpic.js +10 -9
- package/lib/view/expenseAutomationView/epics/actualMatching/undoReversalAccrualsEpic.d.ts +2 -1
- package/lib/view/expenseAutomationView/epics/actualMatching/undoReversalAccrualsEpic.js +48 -24
- package/lib/view/expenseAutomationView/epics/common/fetchAllExpenseAutomationTabsEpic.d.ts +1 -2
- package/lib/view/expenseAutomationView/epics/common/fetchAllExpenseAutomationTabsEpic.js +5 -9
- package/lib/view/expenseAutomationView/epics/common/refreshExpenseAutomationCurrentTabEpic.d.ts +1 -2
- package/lib/view/expenseAutomationView/epics/common/refreshExpenseAutomationCurrentTabEpic.js +1 -9
- package/lib/view/expenseAutomationView/epics/jeSchedule/fetchJeSchedulePageEpic.d.ts +2 -1
- package/lib/view/expenseAutomationView/epics/jeSchedule/fetchJeSchedulePageEpic.js +13 -3
- package/lib/view/expenseAutomationView/epics/jeSchedule/prefetchOtherJeSchedulesTabEpic.d.ts +5 -1
- package/lib/view/expenseAutomationView/epics/jeSchedule/prefetchOtherJeSchedulesTabEpic.js +33 -15
- package/lib/view/expenseAutomationView/epics/jeSchedule/switchJESchedulesTabEpic.d.ts +1 -2
- package/lib/view/expenseAutomationView/epics/jeSchedule/switchJESchedulesTabEpic.js +8 -8
- package/lib/view/expenseAutomationView/expenseAutomationViewReducer.d.ts +2 -1
- package/lib/view/expenseAutomationView/expenseAutomationViewReducer.js +2 -1
- package/lib/view/expenseAutomationView/expenseAutomationViewSelector.js +5 -2
- package/lib/view/expenseAutomationView/expenseAutomationViewState.d.ts +2 -1
- package/lib/view/expenseAutomationView/expenseAutomationViewState.js +6 -3
- package/lib/view/expenseAutomationView/helpers/transactionCategorizationLocalDataHelper.d.ts +1 -1
- package/lib/view/expenseAutomationView/payload/actualMatchingPayload.d.ts +3 -3
- package/lib/view/expenseAutomationView/payload/actualMatchingPayload.js +10 -10
- package/lib/view/expenseAutomationView/reducers/actualMatchingViewReducer.d.ts +21 -16
- package/lib/view/expenseAutomationView/reducers/actualMatchingViewReducer.js +90 -20
- package/lib/view/expenseAutomationView/reducers/jeSchedulesViewReducer.d.ts +2 -1
- package/lib/view/expenseAutomationView/reducers/jeSchedulesViewReducer.js +2 -1
- package/lib/view/expenseAutomationView/selectorTypes/actualMatchingViewSelectorTypes.d.ts +13 -9
- package/lib/view/expenseAutomationView/selectorTypes/expenseAutomationViewSelectorTypes.d.ts +9 -0
- package/lib/view/expenseAutomationView/selectors/actualMatchingViewSelector.d.ts +11 -2
- package/lib/view/expenseAutomationView/selectors/actualMatchingViewSelector.js +209 -60
- package/lib/view/expenseAutomationView/selectors/jeSchedulesViewSelector.js +1 -0
- package/lib/view/expenseAutomationView/types/actualMatchingViewState.d.ts +8 -2
- package/lib/view/expenseAutomationView/types/actualMatchingViewState.js +4 -0
- package/lib/view/expenseAutomationView/types/jeSchedulesViewState.js +3 -0
- package/lib/view/financeStatement/financeStatementReducer.js +17 -27
- package/lib/view/spendManagement/billPay/billList/billListSelector.js +2 -2
- package/package.json +1 -1
|
@@ -3,6 +3,8 @@ 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.getSelectedMonthYearForCurrentTenant = getSelectedMonthYearForCurrentTenant;
|
|
7
|
+
exports.getTenantDisplayCurrency = getTenantDisplayCurrency;
|
|
6
8
|
exports.parseActualMatchingMatchId = parseActualMatchingMatchId;
|
|
7
9
|
exports.isAutoReversedAccrual = isAutoReversedAccrual;
|
|
8
10
|
exports.isManuallyReversedAccrual = isManuallyReversedAccrual;
|
|
@@ -11,7 +13,9 @@ exports.isReversedMatchKind = isReversedMatchKind;
|
|
|
11
13
|
exports.getClearedMatchedAmount = getClearedMatchedAmount;
|
|
12
14
|
exports.getActualMatchingAmount = getActualMatchingAmount;
|
|
13
15
|
exports.getActualMatchingReversalDate = getActualMatchingReversalDate;
|
|
16
|
+
exports.buildActualMatchingKpiSummary = buildActualMatchingKpiSummary;
|
|
14
17
|
exports.getExpenseAutomationActualMatchingMatchView = getExpenseAutomationActualMatchingMatchView;
|
|
18
|
+
exports.getCloseMatchAfterUndo = getCloseMatchAfterUndo;
|
|
15
19
|
exports.getExpenseAutomationActualMatchingView = getExpenseAutomationActualMatchingView;
|
|
16
20
|
const orderBy_1 = __importDefault(require("lodash/orderBy"));
|
|
17
21
|
const timePeriod_1 = require("../../../commonStateTypes/timePeriod");
|
|
@@ -19,6 +23,27 @@ const actualMatchingSelector_1 = require("../../../entity/actualMatching/actualM
|
|
|
19
23
|
const jeScheduleHelper_1 = require("../../../entity/jeSchedules/jeScheduleHelper");
|
|
20
24
|
const jeSchedulesSelector_1 = require("../../../entity/jeSchedules/jeSchedulesSelector");
|
|
21
25
|
const tenantSelector_1 = require("../../../entity/tenant/tenantSelector");
|
|
26
|
+
const zeniDayJS_1 = require("../../../zeniDayJS");
|
|
27
|
+
const actualMatchingViewState_1 = require("../types/actualMatchingViewState");
|
|
28
|
+
function getSelectedMonthYearForCurrentTenant(state) {
|
|
29
|
+
const tenantId = (0, tenantSelector_1.getCurrentTenant)(state)?.tenantId;
|
|
30
|
+
if (tenantId == null) {
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
return state.expenseAutomationViewState.selectedPeriodByTenantId[tenantId];
|
|
34
|
+
}
|
|
35
|
+
function getTenantDisplayCurrency(state) {
|
|
36
|
+
const locale = (0, tenantSelector_1.getCurrentTenant)(state)?.company?.companyLocaleInfo;
|
|
37
|
+
if (locale?.currencyCode == null || locale.currencyCode === '') {
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
currencyCode: locale.currencyCode,
|
|
42
|
+
currencySymbol: locale.currencySymbol != null && locale.currencySymbol !== ''
|
|
43
|
+
? locale.currencySymbol
|
|
44
|
+
: locale.currencyCode,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
22
47
|
function toMatchModalAccrual(member) {
|
|
23
48
|
if ('jeOneTimeAccrualKey' in member && member.jeOneTimeAccrualKey != null) {
|
|
24
49
|
return member;
|
|
@@ -40,9 +65,9 @@ function uniqueMatchModalAccruals(pinned, searched) {
|
|
|
40
65
|
}
|
|
41
66
|
function uniqueTransactionSearchResults(pinned, searched) {
|
|
42
67
|
const byKey = new Map();
|
|
43
|
-
pinned.forEach((row) => byKey.set(
|
|
68
|
+
pinned.forEach((row) => byKey.set((0, actualMatchingViewState_1.actualMatchingTransactionLineKey)(row.transactionType, row.transactionIntegrationId, row.lineId), row));
|
|
44
69
|
searched.forEach((row) => {
|
|
45
|
-
const key =
|
|
70
|
+
const key = (0, actualMatchingViewState_1.actualMatchingTransactionLineKey)(row.transactionType, row.transactionIntegrationId, row.lineId);
|
|
46
71
|
if (!byKey.has(key)) {
|
|
47
72
|
byKey.set(key, row);
|
|
48
73
|
}
|
|
@@ -87,11 +112,18 @@ function getClearedMatchedAmount(accrual) {
|
|
|
87
112
|
function getActualMatchingAmount(row) {
|
|
88
113
|
return row.actualAmount?.amount ?? row.estimatedAmount.amount;
|
|
89
114
|
}
|
|
90
|
-
function
|
|
91
|
-
if (
|
|
92
|
-
return
|
|
115
|
+
function parseMatchTransactionDate(transactionDate) {
|
|
116
|
+
if (transactionDate == null || transactionDate === '') {
|
|
117
|
+
return undefined;
|
|
93
118
|
}
|
|
94
|
-
|
|
119
|
+
const parsed = (0, zeniDayJS_1.date)(transactionDate);
|
|
120
|
+
return Number.isFinite(parsed.valueOf()) ? parsed : undefined;
|
|
121
|
+
}
|
|
122
|
+
function getActualMatchingReversalDate(accrual, matchTransactionDate) {
|
|
123
|
+
const matchedDate = isPendingReviewAccrual(accrual)
|
|
124
|
+
? accrual.matchSuggestion.matchedTransactionDate
|
|
125
|
+
: accrual.cleared?.matchedTransactionDate;
|
|
126
|
+
return matchedDate ?? parseMatchTransactionDate(matchTransactionDate);
|
|
95
127
|
}
|
|
96
128
|
function collectGroupedMembers(members) {
|
|
97
129
|
if (members.length <= 1) {
|
|
@@ -105,12 +137,32 @@ function collectGroupedMembers(members) {
|
|
|
105
137
|
serviceMonth: member.serviceMonth,
|
|
106
138
|
}));
|
|
107
139
|
}
|
|
140
|
+
function collectLeftoverMembers(leftovers) {
|
|
141
|
+
if (leftovers == null || leftovers.length === 0) {
|
|
142
|
+
return undefined;
|
|
143
|
+
}
|
|
144
|
+
return leftovers.map((member) => ({
|
|
145
|
+
accrualId: member.accrualId,
|
|
146
|
+
estimatedAmount: member.estimatedAmount,
|
|
147
|
+
jeDebitAccountName: member.jeDebitAccountName,
|
|
148
|
+
memo: member.memo,
|
|
149
|
+
serviceMonth: member.serviceMonth,
|
|
150
|
+
}));
|
|
151
|
+
}
|
|
108
152
|
function toActualMatchingRow(match) {
|
|
109
153
|
const firstMember = match.members[0];
|
|
110
154
|
if (firstMember == null) {
|
|
111
155
|
throw new Error('Actual matching row requires at least one member');
|
|
112
156
|
}
|
|
113
157
|
const first = toMatchModalAccrual(firstMember);
|
|
158
|
+
const leftoverMembers = match.leftovers ?? [];
|
|
159
|
+
const fromCleared = firstMember.cleared?.intendedAccrualIds;
|
|
160
|
+
const intendedAccrualIds = fromCleared != null && fromCleared.length > 0
|
|
161
|
+
? fromCleared
|
|
162
|
+
: [
|
|
163
|
+
...match.members.map((member) => member.accrualId),
|
|
164
|
+
...leftoverMembers.map((leftover) => leftover.accrualId),
|
|
165
|
+
];
|
|
114
166
|
return {
|
|
115
167
|
...first,
|
|
116
168
|
matchId: match.matchId,
|
|
@@ -123,8 +175,12 @@ function toActualMatchingRow(match) {
|
|
|
123
175
|
transactionDate: match.transactionDate,
|
|
124
176
|
groupedAccrualIds: match.members.map((member) => member.accrualId),
|
|
125
177
|
groupedMembers: collectGroupedMembers(match.members),
|
|
126
|
-
|
|
127
|
-
|
|
178
|
+
intendedAccrualIds,
|
|
179
|
+
leftovers: collectLeftoverMembers(match.leftovers),
|
|
180
|
+
reversalDate: getActualMatchingReversalDate(first, match.transactionDate),
|
|
181
|
+
vendorName: match.vendorName != null && match.vendorName !== ''
|
|
182
|
+
? match.vendorName
|
|
183
|
+
: first.vendorName,
|
|
128
184
|
estimatedAmount: match.accruedAmount,
|
|
129
185
|
};
|
|
130
186
|
}
|
|
@@ -133,10 +189,10 @@ function matchesSearch(row, searchString) {
|
|
|
133
189
|
if (trimmed.length === 0) {
|
|
134
190
|
return true;
|
|
135
191
|
}
|
|
136
|
-
if (row.vendorName.toLowerCase().includes(trimmed)) {
|
|
192
|
+
if ((row.vendorName ?? '').toLowerCase().includes(trimmed)) {
|
|
137
193
|
return true;
|
|
138
194
|
}
|
|
139
|
-
if (row.memo.toLowerCase().includes(trimmed)) {
|
|
195
|
+
if ((row.memo ?? '').toLowerCase().includes(trimmed)) {
|
|
140
196
|
return true;
|
|
141
197
|
}
|
|
142
198
|
return (row.groupedMembers ?? []).some((member) => (member.memo ?? '').toLowerCase().includes(trimmed));
|
|
@@ -144,9 +200,14 @@ function matchesSearch(row, searchString) {
|
|
|
144
200
|
function getActualMatchingSortValue(row, sortKey) {
|
|
145
201
|
switch (sortKey) {
|
|
146
202
|
case 'vendor':
|
|
147
|
-
return row.vendorName.toLowerCase();
|
|
148
|
-
case 'date':
|
|
149
|
-
|
|
203
|
+
return (row.vendorName ?? '').toLowerCase();
|
|
204
|
+
case 'date': {
|
|
205
|
+
const millis = row.reversalDate?.valueOf() ??
|
|
206
|
+
(row.transactionDate != null && row.transactionDate !== ''
|
|
207
|
+
? (0, zeniDayJS_1.date)(row.transactionDate).valueOf()
|
|
208
|
+
: 0);
|
|
209
|
+
return Number.isFinite(millis) ? millis : 0;
|
|
210
|
+
}
|
|
150
211
|
case 'amount':
|
|
151
212
|
return getActualMatchingAmount(row);
|
|
152
213
|
case 'category':
|
|
@@ -179,17 +240,103 @@ function sortActualMatchingRows(rows, uiState) {
|
|
|
179
240
|
const lodashOrder = uiState.sortOrder === 'ascending' ? 'asc' : 'desc';
|
|
180
241
|
return (0, orderBy_1.default)(rows, [(row) => getActualMatchingSortValue(row, uiState.sortKey)], [lodashOrder]);
|
|
181
242
|
}
|
|
243
|
+
const ESTIMATED_MINUTES_PER_PENDING_MATCH = 4;
|
|
182
244
|
function emptyKpiSummary() {
|
|
183
245
|
return {
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
manual: { count: 0 },
|
|
246
|
+
actualsReceived: { count: 0, expectedCount: 0, vendorsPending: 0 },
|
|
247
|
+
autoReversed: { count: 0, percent: 0, clearedAmount: 0 },
|
|
248
|
+
pendingReview: { count: 0, amount: 0, estimatedMinutesToClear: 0 },
|
|
249
|
+
variance: { percent: 0, averagePercentPerMatch: 0, netAmount: 0 },
|
|
189
250
|
};
|
|
190
251
|
}
|
|
191
|
-
function sumRowAmounts(rows) {
|
|
192
|
-
return rows.reduce((sum, row) =>
|
|
252
|
+
function sumRowAmounts(rows, displayCurrencyCode) {
|
|
253
|
+
return rows.reduce((sum, row) => {
|
|
254
|
+
if (displayCurrencyCode != null &&
|
|
255
|
+
row.accruedAmount.currencyCode !== displayCurrencyCode) {
|
|
256
|
+
return sum;
|
|
257
|
+
}
|
|
258
|
+
return sum + getActualMatchingAmount(row);
|
|
259
|
+
}, 0);
|
|
260
|
+
}
|
|
261
|
+
function uniqueVendorCount(rows) {
|
|
262
|
+
const ids = new Set();
|
|
263
|
+
rows.forEach((row) => {
|
|
264
|
+
if (row.vendorId != null && row.vendorId !== '') {
|
|
265
|
+
ids.add(row.vendorId);
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
if (row.vendorName != null && row.vendorName !== '') {
|
|
269
|
+
ids.add(row.vendorName);
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
return ids.size;
|
|
273
|
+
}
|
|
274
|
+
function rowVariancePercent(row) {
|
|
275
|
+
const accrued = row.accruedAmount?.amount ?? row.estimatedAmount.amount;
|
|
276
|
+
const actual = row.actualAmount?.amount;
|
|
277
|
+
if (actual == null || accrued === 0) {
|
|
278
|
+
return undefined;
|
|
279
|
+
}
|
|
280
|
+
return ((actual - accrued) / Math.abs(accrued)) * 100;
|
|
281
|
+
}
|
|
282
|
+
function buildActualMatchingKpiSummary(rows, displayCurrencyCode) {
|
|
283
|
+
if (rows.length === 0) {
|
|
284
|
+
return emptyKpiSummary();
|
|
285
|
+
}
|
|
286
|
+
const autoReversed = rows.filter((row) => row.kind === 'auto_reversed');
|
|
287
|
+
const pendingReview = rows.filter((row) => row.kind === 'pending_review');
|
|
288
|
+
const receivedCount = rows.length;
|
|
289
|
+
const rowsWithActual = rows.filter((row) => {
|
|
290
|
+
if (row.actualAmount?.amount == null) {
|
|
291
|
+
return false;
|
|
292
|
+
}
|
|
293
|
+
if (displayCurrencyCode == null) {
|
|
294
|
+
return true;
|
|
295
|
+
}
|
|
296
|
+
const rowCurrency = row.accruedAmount?.currencyCode ?? row.estimatedAmount.currencyCode;
|
|
297
|
+
return rowCurrency === displayCurrencyCode;
|
|
298
|
+
});
|
|
299
|
+
const accruedTotal = rowsWithActual.reduce((sum, row) => sum + (row.accruedAmount?.amount ?? row.estimatedAmount.amount), 0);
|
|
300
|
+
const actualTotal = rowsWithActual.reduce((sum, row) => sum + (row.actualAmount?.amount ?? 0), 0);
|
|
301
|
+
const variancePercents = rows
|
|
302
|
+
.filter((row) => {
|
|
303
|
+
if (displayCurrencyCode == null) {
|
|
304
|
+
return true;
|
|
305
|
+
}
|
|
306
|
+
const rowCurrency = row.accruedAmount?.currencyCode ?? row.estimatedAmount.currencyCode;
|
|
307
|
+
return rowCurrency === displayCurrencyCode;
|
|
308
|
+
})
|
|
309
|
+
.map(rowVariancePercent)
|
|
310
|
+
.filter((percent) => percent != null);
|
|
311
|
+
return {
|
|
312
|
+
actualsReceived: {
|
|
313
|
+
count: receivedCount,
|
|
314
|
+
// List API does not return still-expected accruals; hide "of N expected"
|
|
315
|
+
// in the strip until expectedCount is greater than received.
|
|
316
|
+
expectedCount: receivedCount,
|
|
317
|
+
vendorsPending: uniqueVendorCount(pendingReview),
|
|
318
|
+
},
|
|
319
|
+
autoReversed: {
|
|
320
|
+
count: autoReversed.length,
|
|
321
|
+
percent: Math.round((autoReversed.length / receivedCount) * 100),
|
|
322
|
+
clearedAmount: sumRowAmounts(autoReversed, displayCurrencyCode),
|
|
323
|
+
},
|
|
324
|
+
pendingReview: {
|
|
325
|
+
count: pendingReview.length,
|
|
326
|
+
amount: sumRowAmounts(pendingReview, displayCurrencyCode),
|
|
327
|
+
estimatedMinutesToClear: pendingReview.length * ESTIMATED_MINUTES_PER_PENDING_MATCH,
|
|
328
|
+
},
|
|
329
|
+
variance: {
|
|
330
|
+
percent: accruedTotal === 0
|
|
331
|
+
? 0
|
|
332
|
+
: ((actualTotal - accruedTotal) / Math.abs(accruedTotal)) * 100,
|
|
333
|
+
averagePercentPerMatch: variancePercents.length === 0
|
|
334
|
+
? 0
|
|
335
|
+
: variancePercents.reduce((sum, percent) => sum + percent, 0) /
|
|
336
|
+
variancePercents.length,
|
|
337
|
+
netAmount: actualTotal - accruedTotal,
|
|
338
|
+
},
|
|
339
|
+
};
|
|
193
340
|
}
|
|
194
341
|
function getExpenseAutomationActualMatchingMatchView(state, matchId) {
|
|
195
342
|
const view = state.expenseAutomationActualMatchingViewState;
|
|
@@ -197,74 +344,76 @@ function getExpenseAutomationActualMatchingMatchView(state, matchId) {
|
|
|
197
344
|
match: (0, actualMatchingSelector_1.getActualMatchingMatchById)(state.actualMatchingState, matchId),
|
|
198
345
|
detailFetchState: view.detailFetchStateByMatchId[matchId] ?? NOT_STARTED,
|
|
199
346
|
refreshStatus: view.detailRefreshStatusByMatchId[matchId] ?? NOT_STARTED,
|
|
200
|
-
undoReversalStatus: view.
|
|
347
|
+
undoReversalStatus: view.undoReversalStatusByMatchId[matchId] ?? NOT_STARTED,
|
|
348
|
+
lastMatchResults: view.lastMatchResultsByMatchId?.[matchId],
|
|
349
|
+
lastUndoResults: view.lastUndoResultsByMatchId?.[matchId],
|
|
201
350
|
};
|
|
202
351
|
}
|
|
352
|
+
function getCloseMatchAfterUndo(state) {
|
|
353
|
+
return state.expenseAutomationActualMatchingViewState.closeMatchAfterUndo;
|
|
354
|
+
}
|
|
203
355
|
function getExpenseAutomationActualMatchingView(state) {
|
|
204
|
-
const { expenseAutomationActualMatchingViewState
|
|
205
|
-
const { matchIdsByPeriod, fetchState, error, refreshStatus, uiState, matchModal,
|
|
206
|
-
const
|
|
207
|
-
const selectedPeriod = expenseAutomationViewState.selectedPeriodByTenantId[currentTenant?.tenantId ?? ''];
|
|
356
|
+
const { expenseAutomationActualMatchingViewState } = state;
|
|
357
|
+
const { matchIdsByPeriod, fetchState, error, refreshStatus, uiState, matchModal, } = expenseAutomationActualMatchingViewState;
|
|
358
|
+
const selectedPeriod = getSelectedMonthYearForCurrentTenant(state);
|
|
208
359
|
const monthYearPeriodId = selectedPeriod != null ? (0, timePeriod_1.toMonthYearPeriodId)(selectedPeriod) : undefined;
|
|
209
360
|
const matchIds = monthYearPeriodId != null ? matchIdsByPeriod[monthYearPeriodId] : undefined;
|
|
210
|
-
const
|
|
361
|
+
const allRows = (matchIds ?? [])
|
|
211
362
|
.map((matchId) => (0, actualMatchingSelector_1.getActualMatchingMatchById)(state.actualMatchingState, matchId))
|
|
212
363
|
.filter((match) => match != null)
|
|
213
364
|
.filter((match) => match.members.length > 0)
|
|
214
|
-
.map(toActualMatchingRow)
|
|
215
|
-
|
|
365
|
+
.map(toActualMatchingRow);
|
|
366
|
+
const rows = allRows.filter((row) => matchesSearch(row, uiState.searchString));
|
|
216
367
|
const reversed = sortActualMatchingRows(rows.filter((row) => isReversedMatchKind(row.kind)), uiState);
|
|
217
368
|
const pendingReview = sortActualMatchingRows(rows.filter((row) => row.kind === 'pending_review'), uiState);
|
|
218
|
-
const
|
|
219
|
-
const listedRows = [...reversed, ...pendingReview];
|
|
220
|
-
const kpiSummary = listedRows.length === 0
|
|
221
|
-
? emptyKpiSummary()
|
|
222
|
-
: {
|
|
223
|
-
autoReversed: {
|
|
224
|
-
count: autoReversedForKpi.length,
|
|
225
|
-
amount: sumRowAmounts(autoReversedForKpi),
|
|
226
|
-
},
|
|
227
|
-
pendingReview: {
|
|
228
|
-
count: pendingReview.length,
|
|
229
|
-
amount: sumRowAmounts(pendingReview),
|
|
230
|
-
},
|
|
231
|
-
totalThisClose: {
|
|
232
|
-
count: listedRows.length,
|
|
233
|
-
amount: sumRowAmounts(listedRows),
|
|
234
|
-
},
|
|
235
|
-
ai: {
|
|
236
|
-
count: listedRows.filter((row) => row.origin === 'agent').length,
|
|
237
|
-
},
|
|
238
|
-
manual: {
|
|
239
|
-
count: listedRows.filter((row) => row.origin === 'human').length,
|
|
240
|
-
},
|
|
241
|
-
};
|
|
369
|
+
const kpiSummary = buildActualMatchingKpiSummary(allRows, getTenantDisplayCurrency(state)?.currencyCode);
|
|
242
370
|
const searchedAccruals = matchModal.accrualSearch.results
|
|
243
371
|
.map((key) => (0, jeSchedulesSelector_1.getJEOneTimeAccrualByKey)(key, state.jeSchedulesState))
|
|
244
372
|
.filter((accrual) => accrual != null);
|
|
245
373
|
const openedMatch = matchModal.openedFromMatchId != null
|
|
246
374
|
? (0, actualMatchingSelector_1.getActualMatchingMatchById)(state.actualMatchingState, matchModal.openedFromMatchId)
|
|
247
375
|
: undefined;
|
|
248
|
-
const pinnedAccruals =
|
|
249
|
-
|
|
376
|
+
const pinnedAccruals = [
|
|
377
|
+
...(openedMatch?.members ?? []),
|
|
378
|
+
...(openedMatch?.leftovers ?? []),
|
|
379
|
+
].map(toMatchModalAccrual);
|
|
380
|
+
const firstMember = openedMatch?.members[0];
|
|
381
|
+
const cleared = firstMember?.cleared;
|
|
382
|
+
const suggestion = firstMember?.matchSuggestion;
|
|
383
|
+
const stamp = cleared ?? suggestion;
|
|
384
|
+
const consumedLineIds = cleared?.consumedLineIds;
|
|
250
385
|
const prefill = openedMatch == null
|
|
251
386
|
? undefined
|
|
252
387
|
: {
|
|
253
|
-
selectedAccrualIds:
|
|
388
|
+
selectedAccrualIds: [
|
|
389
|
+
...openedMatch.members.map((member) => member.accrualId),
|
|
390
|
+
...(openedMatch.leftovers ?? []).map((leftover) => leftover.accrualId),
|
|
391
|
+
],
|
|
254
392
|
actualAmount: openedMatch.actualAmount.amount,
|
|
255
|
-
|
|
256
|
-
|
|
393
|
+
matchedTransactionId: stamp?.matchedTransactionId,
|
|
394
|
+
matchedLineId: cleared == null ? suggestion?.matchedLineId : undefined,
|
|
395
|
+
...(consumedLineIds != null && consumedLineIds.length > 0
|
|
396
|
+
? { consumedLineIds }
|
|
397
|
+
: {}),
|
|
257
398
|
};
|
|
399
|
+
// `fetchState` is a flat slice field that survives a period switch. Until this
|
|
400
|
+
// month's ids land, leftover Completed/Error is the previous month. Report
|
|
401
|
+
// Not-Started so chrome matches Review's "cache slot missing" skeleton, not
|
|
402
|
+
// empty-complete or a stale error. In-Progress is this month's fetch.
|
|
403
|
+
const fetchStatus = matchIds == null &&
|
|
404
|
+
fetchState !== 'In-Progress' &&
|
|
405
|
+
fetchState !== 'Not-Started'
|
|
406
|
+
? { fetchState: 'Not-Started', error: undefined }
|
|
407
|
+
: { fetchState, error };
|
|
258
408
|
return {
|
|
259
|
-
fetchState,
|
|
260
|
-
error,
|
|
409
|
+
fetchState: fetchStatus.fetchState,
|
|
410
|
+
error: fetchStatus.error,
|
|
261
411
|
reversed,
|
|
262
412
|
pendingReview,
|
|
263
413
|
kpiSummary,
|
|
264
414
|
uiState,
|
|
265
415
|
refreshStatus,
|
|
266
416
|
hasLoadedForSelectedPeriod: matchIds != null,
|
|
267
|
-
undoReversalStatus,
|
|
268
417
|
matchModal: {
|
|
269
418
|
isOpen: matchModal.isOpen,
|
|
270
419
|
openedFromMatchId: matchModal.openedFromMatchId,
|
|
@@ -381,6 +381,7 @@ function getExpenseAutomationJESchedulesView(state) {
|
|
|
381
381
|
// reports undefined rather than 0, so the UI can hide the badge instead of asserting "none".
|
|
382
382
|
const scheduleCountsByTab = {};
|
|
383
383
|
jeSchedulesViewState_1.ALL_JE_PAGE_TABS.forEach((pageTab) => {
|
|
384
|
+
// AM's badge is `actualMatching.kpiSummary`, not schedule row keys.
|
|
384
385
|
if (pageTab === 'actual_matching') {
|
|
385
386
|
return;
|
|
386
387
|
}
|
|
@@ -51,7 +51,7 @@ export interface MatchAndReverseResult {
|
|
|
51
51
|
}
|
|
52
52
|
export interface MatchAndReverseAccrualsParams {
|
|
53
53
|
accrualIds: ID[];
|
|
54
|
-
|
|
54
|
+
lineIds: ID[];
|
|
55
55
|
transactionId: ID;
|
|
56
56
|
transactionIntegrationId: string;
|
|
57
57
|
transactionSyncToken: string;
|
|
@@ -66,6 +66,7 @@ export interface UndoReversalResult {
|
|
|
66
66
|
}
|
|
67
67
|
export interface UndoReversalAccrualsParams {
|
|
68
68
|
accrualIds: ID[];
|
|
69
|
+
matchId: ActualMatchingMatchId;
|
|
69
70
|
}
|
|
70
71
|
export interface ActualMatchingTransactionSearchState extends FetchStateAndError {
|
|
71
72
|
isLoadingMore: boolean;
|
|
@@ -82,18 +83,23 @@ export interface ActualMatchingAccrualSearchState extends FetchStateAndError {
|
|
|
82
83
|
export interface ActualMatchingMatchModalState {
|
|
83
84
|
accrualSearch: ActualMatchingAccrualSearchState;
|
|
84
85
|
confirmStatus: FetchStateAndError;
|
|
86
|
+
didPinPrefillTransactions: boolean;
|
|
85
87
|
isOpen: boolean;
|
|
86
88
|
pinnedTransactionResults: ActualMatchingTransactionSearchResult[];
|
|
87
89
|
transactionSearch: ActualMatchingTransactionSearchState;
|
|
88
90
|
lastBatchResults?: MatchAndReverseResult[];
|
|
89
91
|
openedFromMatchId?: ActualMatchingMatchId;
|
|
90
92
|
}
|
|
93
|
+
export declare function actualMatchingTransactionLineKey(transactionType: string, transactionIntegrationId: string, lineId: string): string;
|
|
91
94
|
export interface ActualMatchingViewState extends FetchedState {
|
|
95
|
+
closeMatchAfterUndo: boolean;
|
|
92
96
|
detailFetchStateByMatchId: Record<ActualMatchingMatchId, FetchStateAndError>;
|
|
93
97
|
detailRefreshStatusByMatchId: Record<ActualMatchingMatchId, FetchStateAndError>;
|
|
98
|
+
lastMatchResultsByMatchId: Record<ActualMatchingMatchId, MatchAndReverseResult[]>;
|
|
99
|
+
lastUndoResultsByMatchId: Record<ActualMatchingMatchId, UndoReversalResult[]>;
|
|
94
100
|
matchIdsByPeriod: Record<MonthYearPeriodId, ActualMatchingMatchId[]>;
|
|
95
101
|
matchModal: ActualMatchingMatchModalState;
|
|
96
102
|
refreshStatus: FetchStateAndError;
|
|
97
103
|
uiState: ActualMatchingViewUIState;
|
|
98
|
-
|
|
104
|
+
undoReversalStatusByMatchId: Record<ActualMatchingMatchId, FetchStateAndError>;
|
|
99
105
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.toUndoReversalResultStatus = exports.toMatchAndReverseResultStatus = exports.MATCH_SEARCH_DEBOUNCE_MS = exports.MATCH_ACCRUAL_SEARCH_LIMIT = exports.MATCH_TRANSACTION_SEARCH_PAGE_SIZE = exports.toActualMatchingSortKey = void 0;
|
|
4
|
+
exports.actualMatchingTransactionLineKey = actualMatchingTransactionLineKey;
|
|
4
5
|
const stringToUnion_1 = require("../../../commonStateTypes/stringToUnion");
|
|
5
6
|
const ACTUAL_MATCHING_SORT_KEYS = [
|
|
6
7
|
'vendor',
|
|
@@ -36,3 +37,6 @@ const UNDO_REVERSAL_RESULT_STATUSES = [
|
|
|
36
37
|
];
|
|
37
38
|
const toUndoReversalResultStatus = (v) => (0, stringToUnion_1.stringToUnion)(v, UNDO_REVERSAL_RESULT_STATUSES);
|
|
38
39
|
exports.toUndoReversalResultStatus = toUndoReversalResultStatus;
|
|
40
|
+
function actualMatchingTransactionLineKey(transactionType, transactionIntegrationId, lineId) {
|
|
41
|
+
return `${transactionType}:${transactionIntegrationId}:${lineId}`;
|
|
42
|
+
}
|
|
@@ -20,6 +20,9 @@ const JE_SCHEDULE_SORT_KEYS = [
|
|
|
20
20
|
];
|
|
21
21
|
const toJEScheduleSortKey = (v) => (0, stringToUnion_1.stringToUnion)(v, JE_SCHEDULE_SORT_KEYS);
|
|
22
22
|
exports.toJEScheduleSortKey = toJEScheduleSortKey;
|
|
23
|
+
// `actual_matching` is chrome only: the JE tab bar includes it, but AM data is
|
|
24
|
+
// `expenseAutomationActualMatchingViewState`, not the schedules period-tab
|
|
25
|
+
// cache below. Walks of ALL_JE_PAGE_TABS that read schedule keys must skip it.
|
|
23
26
|
const JE_PAGE_TABS = [
|
|
24
27
|
'schedules',
|
|
25
28
|
'actual_matching',
|
|
@@ -37,9 +37,18 @@ const financeStatement = (0, toolkit_1.createSlice)({
|
|
|
37
37
|
},
|
|
38
38
|
},
|
|
39
39
|
updateFinanceStatementTimeframe(draft, action) {
|
|
40
|
+
// Unrelated UI re-dispatches the *current* timeframe as a side effect (the
|
|
41
|
+
// reports page hangs its table scroll reset off that callback); dropping the
|
|
42
|
+
// anchor there would silently reset the user's period to the latest one.
|
|
43
|
+
if (draft.timeframe === action.payload) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
40
46
|
draft.timeframe = action.payload;
|
|
47
|
+
// A month anchor is meaningless once the timeframe becomes quarter/year, so
|
|
48
|
+
// a real change drops it and returns the width to the default — 12 months
|
|
49
|
+
// must not become 12 quarters. Order is the user's, so it survives.
|
|
41
50
|
draft.selectedCOABalancesRange = {
|
|
42
|
-
numberOfPeriods:
|
|
51
|
+
numberOfPeriods: exports.initialFinanceStatementState.selectedCOABalancesRange.numberOfPeriods,
|
|
43
52
|
orderBy: draft.selectedCOABalancesRange.orderBy,
|
|
44
53
|
};
|
|
45
54
|
},
|
|
@@ -81,8 +90,7 @@ const financeStatement = (0, toolkit_1.createSlice)({
|
|
|
81
90
|
draft.selectedReportId = action.payload;
|
|
82
91
|
},
|
|
83
92
|
updateFinanceStatementAdditionalBalancesSelection(draft, action) {
|
|
84
|
-
const {
|
|
85
|
-
const safeCoaBalances = coaBalances != null ? coaBalances : [];
|
|
93
|
+
const { additionalBalances: additionalBalancesPayload, maxNumOfPeriodsToHighlight, } = action.payload;
|
|
86
94
|
const additionalBalances = additionalBalancesPayload ?? [];
|
|
87
95
|
let tempAdditionalBalances = [...additionalBalances];
|
|
88
96
|
if (additionalBalances.includes('this_period_vs_last_period') ||
|
|
@@ -98,30 +106,12 @@ const financeStatement = (0, toolkit_1.createSlice)({
|
|
|
98
106
|
draft.maxNumOfPeriodsToHighlight = maxNumOfPeriodsToHighlight;
|
|
99
107
|
draft.isAdditionalBalancesShown =
|
|
100
108
|
additionalBalances.length != 0 ? true : false;
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
thisPeriod,
|
|
108
|
-
};
|
|
109
|
-
const selectionRanges = (0, getSelectedAndHighlightedRanges_1.getSelectedAndHighlightedRangesForThisPeriod)({
|
|
110
|
-
firstMonthOfFY,
|
|
111
|
-
thisPeriod: thisPeriod,
|
|
112
|
-
coaBalances: safeCoaBalances,
|
|
113
|
-
timeframe,
|
|
114
|
-
maxNumOfPeriodsToHighlight: maxNumOfPeriodsToHighlight,
|
|
115
|
-
currentSelection: {
|
|
116
|
-
selectedCOABalancesRange: selectedCoaBalancesRangeWithThisPeriod,
|
|
117
|
-
},
|
|
118
|
-
orderBy: 'ascending_date',
|
|
119
|
-
maxNumOfPeriodsToSelect: maxNumOfPeriodsToHighlight,
|
|
120
|
-
});
|
|
121
|
-
draft.selectedCOABalancesRange =
|
|
122
|
-
selectionRanges.selectedCOABalancesRange;
|
|
123
|
-
}
|
|
124
|
-
}
|
|
109
|
+
// Column metadata only — must not touch selectedCOABalancesRange. The
|
|
110
|
+
// reports page fires this on load, on resize and on every fetch-state
|
|
111
|
+
// transition (so also on report switch and on remount after a drill-down)
|
|
112
|
+
// with a viewport-derived period count that knows nothing about the range
|
|
113
|
+
// the user picked. The width default comes from initial state, and from
|
|
114
|
+
// updateFinanceStatementTimeframe on a real timeframe change.
|
|
125
115
|
},
|
|
126
116
|
updateDownloadState(draft, action) {
|
|
127
117
|
draft.downloadState = action.payload;
|
|
@@ -440,7 +440,7 @@ const getBillListOnSort = (sortKey, sortOrder, currentTab, transactions) => {
|
|
|
440
440
|
}
|
|
441
441
|
}
|
|
442
442
|
else {
|
|
443
|
-
if (Boolean(transaction.billPayInfo
|
|
443
|
+
if (Boolean((0, billListFilterHelpers_1.getActualPaymentDate)(transaction.billPayInfo)) === true) {
|
|
444
444
|
transactionsWithSortKeyValue.push(transaction);
|
|
445
445
|
}
|
|
446
446
|
else {
|
|
@@ -525,7 +525,7 @@ const getBillListOnSort = (sortKey, sortOrder, currentTab, transactions) => {
|
|
|
525
525
|
case 'dueDate':
|
|
526
526
|
return currentTab === 'draft'
|
|
527
527
|
? transaction.dueDate
|
|
528
|
-
: transaction.billPayInfo
|
|
528
|
+
: (0, billListFilterHelpers_1.getActualPaymentDate)(transaction.billPayInfo);
|
|
529
529
|
case 'status':
|
|
530
530
|
return getStatusPerTab(transaction);
|
|
531
531
|
case 'amount':
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zeniai/client-epic-state",
|
|
3
|
-
"version": "5.2.35-
|
|
3
|
+
"version": "5.2.35-beta10MM",
|
|
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",
|