@zeniai/client-epic-state 5.2.35-beta0MM → 5.2.35-beta2MM

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 (34) hide show
  1. package/lib/entity/actualMatching/actualMatchingPayload.js +6 -4
  2. package/lib/esm/entity/actualMatching/actualMatchingPayload.js +6 -4
  3. package/lib/esm/index.js +2 -2
  4. package/lib/esm/view/expenseAutomationView/epics/actualMatching/fetchActualMatchingEpic.js +2 -4
  5. package/lib/esm/view/expenseAutomationView/epics/actualMatching/matchAndReverseAccrualsEpic.js +2 -3
  6. package/lib/esm/view/expenseAutomationView/epics/actualMatching/searchMatchAccrualsEpic.js +6 -2
  7. package/lib/esm/view/expenseAutomationView/epics/actualMatching/searchMatchTransactionsEpic.js +6 -2
  8. package/lib/esm/view/expenseAutomationView/epics/actualMatching/undoReversalAccrualsEpic.js +2 -3
  9. package/lib/esm/view/expenseAutomationView/expenseAutomationViewSelector.js +1 -0
  10. package/lib/esm/view/expenseAutomationView/expenseAutomationViewState.js +4 -2
  11. package/lib/esm/view/expenseAutomationView/reducers/actualMatchingViewReducer.js +13 -6
  12. package/lib/esm/view/expenseAutomationView/selectors/actualMatchingViewSelector.js +83 -36
  13. package/lib/esm/view/expenseAutomationView/selectors/jeSchedulesViewSelector.js +1 -0
  14. package/lib/esm/view/expenseAutomationView/types/jeSchedulesViewState.js +3 -0
  15. package/lib/index.d.ts +2 -2
  16. package/lib/index.js +49 -48
  17. package/lib/view/expenseAutomationView/epics/actualMatching/fetchActualMatchingEpic.js +2 -4
  18. package/lib/view/expenseAutomationView/epics/actualMatching/matchAndReverseAccrualsEpic.js +2 -3
  19. package/lib/view/expenseAutomationView/epics/actualMatching/searchMatchAccrualsEpic.d.ts +2 -1
  20. package/lib/view/expenseAutomationView/epics/actualMatching/searchMatchAccrualsEpic.js +5 -1
  21. package/lib/view/expenseAutomationView/epics/actualMatching/searchMatchTransactionsEpic.js +5 -1
  22. package/lib/view/expenseAutomationView/epics/actualMatching/undoReversalAccrualsEpic.js +2 -3
  23. package/lib/view/expenseAutomationView/expenseAutomationViewSelector.js +1 -0
  24. package/lib/view/expenseAutomationView/expenseAutomationViewState.d.ts +2 -1
  25. package/lib/view/expenseAutomationView/expenseAutomationViewState.js +6 -3
  26. package/lib/view/expenseAutomationView/reducers/actualMatchingViewReducer.d.ts +4 -2
  27. package/lib/view/expenseAutomationView/reducers/actualMatchingViewReducer.js +13 -6
  28. package/lib/view/expenseAutomationView/selectorTypes/actualMatchingViewSelectorTypes.d.ts +10 -8
  29. package/lib/view/expenseAutomationView/selectorTypes/expenseAutomationViewSelectorTypes.d.ts +9 -0
  30. package/lib/view/expenseAutomationView/selectors/actualMatchingViewSelector.d.ts +4 -1
  31. package/lib/view/expenseAutomationView/selectors/actualMatchingViewSelector.js +84 -35
  32. package/lib/view/expenseAutomationView/selectors/jeSchedulesViewSelector.js +1 -0
  33. package/lib/view/expenseAutomationView/types/jeSchedulesViewState.js +3 -0
  34. package/package.json +1 -1
@@ -5,6 +5,7 @@ const rxjs_1 = require("rxjs");
5
5
  const operators_1 = require("rxjs/operators");
6
6
  const actualMatchingSelector_1 = require("../../../../entity/actualMatching/actualMatchingSelector");
7
7
  const responsePayload_1 = require("../../../../responsePayload");
8
+ const rootActions_1 = require("../../../../rootActions");
8
9
  const actualMatchingPayload_1 = require("../../payload/actualMatchingPayload");
9
10
  const actualMatchingViewReducer_1 = require("../../reducers/actualMatchingViewReducer");
10
11
  const actualMatchingViewSelector_1 = require("../../selectors/actualMatchingViewSelector");
@@ -70,7 +71,10 @@ const searchMatchTransactionsEpic = (actions$, state$, zeniAPI) => {
70
71
  }), (0, operators_1.catchError)((error) => (0, rxjs_1.of)((0, actualMatchingViewReducer_1.searchMatchTransactionsFailure)({
71
72
  error: (0, responsePayload_1.createZeniAPIStatus)('Unexpected Error', 'Search match transactions REST API call errored out' +
72
73
  JSON.stringify(error)),
73
- }))));
74
+ }))),
75
+ // On the HTTP stream only. Wrapping `merge(openModal$, search$)`
76
+ // would complete the epic on the first `clearAll` and drop later searches.
77
+ (0, operators_1.takeUntil)(actions$.pipe((0, operators_1.filter)(rootActions_1.clearAll.match))));
74
78
  }));
75
79
  return (0, rxjs_1.merge)(openModal$, search$);
76
80
  };
@@ -4,14 +4,13 @@ exports.undoReversalAccrualsEpic = void 0;
4
4
  const rxjs_1 = require("rxjs");
5
5
  const operators_1 = require("rxjs/operators");
6
6
  const timePeriod_1 = require("../../../../commonStateTypes/timePeriod");
7
- const tenantSelector_1 = require("../../../../entity/tenant/tenantSelector");
8
7
  const responsePayload_1 = require("../../../../responsePayload");
9
8
  const rootActions_1 = require("../../../../rootActions");
10
9
  const actualMatchingPayload_1 = require("../../payload/actualMatchingPayload");
11
10
  const actualMatchingViewReducer_1 = require("../../reducers/actualMatchingViewReducer");
11
+ const actualMatchingViewSelector_1 = require("../../selectors/actualMatchingViewSelector");
12
12
  function refreshActualMatchingList(state) {
13
- const currentTenant = (0, tenantSelector_1.getCurrentTenant)(state);
14
- const selectedMonthYear = state.expenseAutomationViewState.selectedPeriodByTenantId[currentTenant?.tenantId ?? ''];
13
+ const selectedMonthYear = (0, actualMatchingViewSelector_1.getSelectedMonthYearForCurrentTenant)(state);
15
14
  if (selectedMonthYear == null) {
16
15
  return [];
17
16
  }
@@ -46,6 +46,7 @@ function getExpenseAutomationView(state) {
46
46
  const reconciliation = (0, reconciliationViewSelector_1.getExpenseAutomationReconciliationView)(state);
47
47
  const expenseAutomationTransactionsView = (0, transactionCategorizationSelector_1.getExpenseAutomationTransactionView)(state);
48
48
  const jeSchedules = (0, jeSchedulesViewSelector_1.getExpenseAutomationJESchedulesView)(state);
49
+ // Top-level sibling of `jeSchedules`: see ExpenseAutomationViewSelector.
49
50
  const actualMatching = (0, actualMatchingViewSelector_1.getExpenseAutomationActualMatchingView)(state);
50
51
  switch (currentSelectedView) {
51
52
  case 'reconciliation':
@@ -1,8 +1,9 @@
1
1
  import { FetchStateAndError, FetchedState, ID } from '../../commonStateTypes/common';
2
2
  import { MonthYearPeriod } from '../../commonStateTypes/timePeriod';
3
- export declare const EXPENSE_AUTOMATION_VIEW_TYPES: readonly ["transaction_categorization", "je_schedules", "reconciliation", "missing_receipts", "flux_analysis", "month_end_insights"];
4
3
  export declare const toExpenseAutomationViewType: (v: string) => "transaction_categorization" | "je_schedules" | "reconciliation" | "missing_receipts" | "flux_analysis" | "month_end_insights";
5
4
  export type ExpenseAutomationViewType = ReturnType<typeof toExpenseAutomationViewType>;
5
+ /** Unlike `toExpenseAutomationViewType`, unknown strings return `undefined`. */
6
+ export declare const toExpenseAutomationViewTypeStrict: (v: string) => ExpenseAutomationViewType | undefined;
6
7
  export interface ExpenseAutomationViewState extends FetchedState {
7
8
  currentSelectedView: ExpenseAutomationViewType;
8
9
  saveStatus: FetchStateAndError;
@@ -1,8 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.toExpenseAutomationViewType = exports.EXPENSE_AUTOMATION_VIEW_TYPES = void 0;
3
+ exports.toExpenseAutomationViewTypeStrict = exports.toExpenseAutomationViewType = void 0;
4
4
  const stringToUnion_1 = require("../../commonStateTypes/stringToUnion");
5
- exports.EXPENSE_AUTOMATION_VIEW_TYPES = [
5
+ const EXPENSE_AUTOMATION_VIEW_TYPES = [
6
6
  'transaction_categorization',
7
7
  'je_schedules',
8
8
  'reconciliation',
@@ -10,5 +10,8 @@ exports.EXPENSE_AUTOMATION_VIEW_TYPES = [
10
10
  'flux_analysis',
11
11
  'month_end_insights',
12
12
  ];
13
- const toExpenseAutomationViewType = (v) => (0, stringToUnion_1.stringToUnion)(v, exports.EXPENSE_AUTOMATION_VIEW_TYPES);
13
+ const toExpenseAutomationViewType = (v) => (0, stringToUnion_1.stringToUnion)(v, EXPENSE_AUTOMATION_VIEW_TYPES);
14
14
  exports.toExpenseAutomationViewType = toExpenseAutomationViewType;
15
+ /** Unlike `toExpenseAutomationViewType`, unknown strings return `undefined`. */
16
+ const toExpenseAutomationViewTypeStrict = (v) => (0, stringToUnion_1.stringToUnionStrict)(v, EXPENSE_AUTOMATION_VIEW_TYPES);
17
+ exports.toExpenseAutomationViewTypeStrict = toExpenseAutomationViewTypeStrict;
@@ -67,8 +67,10 @@ export declare const clearActualMatchingView: import("@reduxjs/toolkit").ActionC
67
67
  results: ActualMatchingTransactionSearchResult[];
68
68
  }, "expenseAutomationActualMatchingView/searchMatchTransactionsSuccess">, undoReversalAccruals: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[params: UndoReversalAccrualsParams], UndoReversalAccrualsParams, "expenseAutomationActualMatchingView/undoReversalAccruals", never, never>, undoReversalAccrualsFailure: import("@reduxjs/toolkit").ActionCreatorWithPayload<{
69
69
  status: ZeniAPIStatus;
70
- }, "expenseAutomationActualMatchingView/undoReversalAccrualsFailure">, undoReversalAccrualsSuccess: import("@reduxjs/toolkit").ActionCreatorWithPayload<{
70
+ }, "expenseAutomationActualMatchingView/undoReversalAccrualsFailure">, undoReversalAccrualsSuccess: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[payload: {
71
71
  results: UndoReversalResult[];
72
- }, "expenseAutomationActualMatchingView/undoReversalAccrualsSuccess">, updateActualMatchingUIState: import("@reduxjs/toolkit").ActionCreatorWithPayload<Partial<ActualMatchingViewUIState>, "expenseAutomationActualMatchingView/updateActualMatchingUIState">;
72
+ }], {
73
+ results: UndoReversalResult[];
74
+ }, "expenseAutomationActualMatchingView/undoReversalAccrualsSuccess", never, never>, updateActualMatchingUIState: import("@reduxjs/toolkit").ActionCreatorWithPayload<Partial<ActualMatchingViewUIState>, "expenseAutomationActualMatchingView/updateActualMatchingUIState">;
73
75
  declare const _default: import("redux").Reducer<ActualMatchingViewState>;
74
76
  export default _default;
@@ -201,10 +201,13 @@ const expenseAutomationActualMatchingView = (0, toolkit_1.createSlice)({
201
201
  }
202
202
  },
203
203
  },
204
+ // Dismiss (Cancel / overlay). Store reset matches `clearMatchNewModal`;
205
+ // the actions stay distinct so confirm-success vs user-dismiss stay observable.
204
206
  closeMatchNewModal(draft) {
205
207
  draft.matchModal.isOpen = false;
206
208
  resetMatchModalSearches(draft);
207
209
  },
210
+ // Confirm-success / host reset. Same slice write as close; different action type.
208
211
  clearMatchNewModal(draft) {
209
212
  draft.matchModal.isOpen = false;
210
213
  resetMatchModalSearches(draft);
@@ -341,12 +344,16 @@ const expenseAutomationActualMatchingView = (0, toolkit_1.createSlice)({
341
344
  return { payload: params };
342
345
  },
343
346
  },
344
- undoReversalAccrualsSuccess(draft, action) {
345
- draft.undoReversalStatus = {
346
- fetchState: 'Completed',
347
- error: undefined,
348
- };
349
- void action;
347
+ undoReversalAccrualsSuccess: {
348
+ reducer(draft) {
349
+ draft.undoReversalStatus = {
350
+ fetchState: 'Completed',
351
+ error: undefined,
352
+ };
353
+ },
354
+ prepare(payload) {
355
+ return { payload };
356
+ },
350
357
  },
351
358
  undoReversalAccrualsFailure(draft, action) {
352
359
  draft.undoReversalStatus = {
@@ -26,23 +26,25 @@ export interface ActualMatchingRow extends JEOneTimeAccrual {
26
26
  transactionType?: string;
27
27
  }
28
28
  export interface ActualMatchingKpiSummary {
29
- ai: {
29
+ actualsReceived: {
30
30
  count: number;
31
+ expectedCount: number;
32
+ vendorsPending: number;
31
33
  };
32
34
  autoReversed: {
33
- amount: number;
34
- count: number;
35
- };
36
- manual: {
35
+ clearedAmount: number;
37
36
  count: number;
37
+ percent: number;
38
38
  };
39
39
  pendingReview: {
40
40
  amount: number;
41
41
  count: number;
42
+ estimatedMinutesToClear: number;
42
43
  };
43
- totalThisClose: {
44
- amount: number;
45
- count: number;
44
+ variance: {
45
+ averagePercentPerMatch: number;
46
+ netAmount: number;
47
+ percent: number;
46
48
  };
47
49
  }
48
50
  export interface ActualMatchingMatchModalView {
@@ -22,6 +22,15 @@ export interface ExpenseAutomationStepDetails {
22
22
  title: string;
23
23
  }
24
24
  export interface ExpenseAutomationViewSelector extends SelectorView {
25
+ /**
26
+ * Sibling of `jeSchedules`, not nested under it. The Journal Entries navbar
27
+ * hosts Actual Matching (`selectedPageTab === 'actual_matching'`), but AM is
28
+ * a different GET, entity map (`byMatchId`), and period cache
29
+ * (`matchIdsByPeriod`) than Review / Existing. Nesting it on `jeSchedules`
30
+ * would hide a second store inside the schedules selector. Chrome coupling
31
+ * stays `jeSchedules.uiState.selectedPageTab`; AM rows, Match New, and undo
32
+ * live here.
33
+ */
25
34
  actualMatching: ExpenseAutomationActualMatchingViewSelector;
26
35
  allSteps: ExpenseAutomationStepDetails[];
27
36
  completionStatus: CompletionStatusType;
@@ -1,11 +1,13 @@
1
1
  import { FetchStateAndError } from '../../../commonStateTypes/common';
2
+ import { MonthYearPeriod } from '../../../commonStateTypes/timePeriod';
2
3
  import { ActualMatchingMatch } from '../../../entity/actualMatching/actualMatchingState';
3
4
  import { ActualMatchingMatchId } from '../../../entity/actualMatching/actualMatchingTypes';
4
5
  import { JEOneTimeAccrual } from '../../../entity/jeSchedules/jeSchedulesSelector';
5
6
  import { JEOneTimeAccrualMatchSuggestion } from '../../../entity/jeSchedules/jeSchedulesState';
6
7
  import { RootState } from '../../../rootStateTypes';
7
8
  import { ZeniDate } from '../../../zeniDayJS';
8
- import { ActualMatchingRow, ActualMatchingRowKind, ExpenseAutomationActualMatchingViewSelector } from '../selectorTypes/actualMatchingViewSelectorTypes';
9
+ import { ActualMatchingKpiSummary, ActualMatchingRow, ActualMatchingRowKind, ExpenseAutomationActualMatchingViewSelector } from '../selectorTypes/actualMatchingViewSelectorTypes';
10
+ export declare function getSelectedMonthYearForCurrentTenant(state: RootState): MonthYearPeriod | undefined;
9
11
  export declare function parseActualMatchingMatchId(matchId: string): {
10
12
  transactionIntegrationId: string;
11
13
  transactionType: string;
@@ -19,6 +21,7 @@ export declare function isReversedMatchKind(kind: ActualMatchingRowKind): boolea
19
21
  export declare function getClearedMatchedAmount(accrual: JEOneTimeAccrual): number | undefined;
20
22
  export declare function getActualMatchingAmount(row: ActualMatchingRow): number;
21
23
  export declare function getActualMatchingReversalDate(accrual: JEOneTimeAccrual): ZeniDate | undefined;
24
+ export declare function buildActualMatchingKpiSummary(rows: ActualMatchingRow[]): ActualMatchingKpiSummary;
22
25
  export interface ExpenseAutomationActualMatchingMatchView {
23
26
  detailFetchState: FetchStateAndError;
24
27
  refreshStatus: FetchStateAndError;
@@ -3,6 +3,7 @@ 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;
6
7
  exports.parseActualMatchingMatchId = parseActualMatchingMatchId;
7
8
  exports.isAutoReversedAccrual = isAutoReversedAccrual;
8
9
  exports.isManuallyReversedAccrual = isManuallyReversedAccrual;
@@ -11,6 +12,7 @@ exports.isReversedMatchKind = isReversedMatchKind;
11
12
  exports.getClearedMatchedAmount = getClearedMatchedAmount;
12
13
  exports.getActualMatchingAmount = getActualMatchingAmount;
13
14
  exports.getActualMatchingReversalDate = getActualMatchingReversalDate;
15
+ exports.buildActualMatchingKpiSummary = buildActualMatchingKpiSummary;
14
16
  exports.getExpenseAutomationActualMatchingMatchView = getExpenseAutomationActualMatchingMatchView;
15
17
  exports.getExpenseAutomationActualMatchingView = getExpenseAutomationActualMatchingView;
16
18
  const orderBy_1 = __importDefault(require("lodash/orderBy"));
@@ -19,6 +21,13 @@ const actualMatchingSelector_1 = require("../../../entity/actualMatching/actualM
19
21
  const jeScheduleHelper_1 = require("../../../entity/jeSchedules/jeScheduleHelper");
20
22
  const jeSchedulesSelector_1 = require("../../../entity/jeSchedules/jeSchedulesSelector");
21
23
  const tenantSelector_1 = require("../../../entity/tenant/tenantSelector");
24
+ function getSelectedMonthYearForCurrentTenant(state) {
25
+ const tenantId = (0, tenantSelector_1.getCurrentTenant)(state)?.tenantId;
26
+ if (tenantId == null) {
27
+ return undefined;
28
+ }
29
+ return state.expenseAutomationViewState.selectedPeriodByTenantId[tenantId];
30
+ }
22
31
  function toMatchModalAccrual(member) {
23
32
  if ('jeOneTimeAccrualKey' in member && member.jeOneTimeAccrualKey != null) {
24
33
  return member;
@@ -179,18 +188,82 @@ function sortActualMatchingRows(rows, uiState) {
179
188
  const lodashOrder = uiState.sortOrder === 'ascending' ? 'asc' : 'desc';
180
189
  return (0, orderBy_1.default)(rows, [(row) => getActualMatchingSortValue(row, uiState.sortKey)], [lodashOrder]);
181
190
  }
191
+ const ESTIMATED_MINUTES_PER_PENDING_MATCH = 4;
182
192
  function emptyKpiSummary() {
183
193
  return {
184
- autoReversed: { count: 0, amount: 0 },
185
- pendingReview: { count: 0, amount: 0 },
186
- totalThisClose: { count: 0, amount: 0 },
187
- ai: { count: 0 },
188
- manual: { count: 0 },
194
+ actualsReceived: { count: 0, expectedCount: 0, vendorsPending: 0 },
195
+ autoReversed: { count: 0, percent: 0, clearedAmount: 0 },
196
+ pendingReview: { count: 0, amount: 0, estimatedMinutesToClear: 0 },
197
+ variance: { percent: 0, averagePercentPerMatch: 0, netAmount: 0 },
189
198
  };
190
199
  }
191
200
  function sumRowAmounts(rows) {
192
201
  return rows.reduce((sum, row) => sum + getActualMatchingAmount(row), 0);
193
202
  }
203
+ function uniqueVendorCount(rows) {
204
+ const ids = new Set();
205
+ rows.forEach((row) => {
206
+ if (row.vendorId != null && row.vendorId !== '') {
207
+ ids.add(row.vendorId);
208
+ return;
209
+ }
210
+ if (row.vendorName !== '') {
211
+ ids.add(row.vendorName);
212
+ }
213
+ });
214
+ return ids.size;
215
+ }
216
+ function rowVariancePercent(row) {
217
+ const accrued = row.accruedAmount?.amount ?? row.estimatedAmount.amount;
218
+ const actual = row.actualAmount?.amount;
219
+ if (actual == null || accrued === 0) {
220
+ return undefined;
221
+ }
222
+ return ((actual - accrued) / accrued) * 100;
223
+ }
224
+ function buildActualMatchingKpiSummary(rows) {
225
+ if (rows.length === 0) {
226
+ return emptyKpiSummary();
227
+ }
228
+ const autoReversed = rows.filter((row) => row.kind === 'auto_reversed');
229
+ const pendingReview = rows.filter((row) => row.kind === 'pending_review');
230
+ const receivedCount = rows.length;
231
+ const rowsWithActual = rows.filter((row) => row.actualAmount?.amount != null);
232
+ const accruedTotal = rowsWithActual.reduce((sum, row) => sum + (row.accruedAmount?.amount ?? row.estimatedAmount.amount), 0);
233
+ const actualTotal = rowsWithActual.reduce((sum, row) => sum + (row.actualAmount?.amount ?? 0), 0);
234
+ const variancePercents = rows
235
+ .map(rowVariancePercent)
236
+ .filter((percent) => percent != null);
237
+ return {
238
+ actualsReceived: {
239
+ count: receivedCount,
240
+ // List API does not return still-expected accruals; hide "of N expected"
241
+ // in the strip until expectedCount is greater than received.
242
+ expectedCount: receivedCount,
243
+ vendorsPending: uniqueVendorCount(pendingReview),
244
+ },
245
+ autoReversed: {
246
+ count: autoReversed.length,
247
+ percent: Math.round((autoReversed.length / receivedCount) * 100),
248
+ clearedAmount: sumRowAmounts(autoReversed),
249
+ },
250
+ pendingReview: {
251
+ count: pendingReview.length,
252
+ amount: sumRowAmounts(pendingReview),
253
+ estimatedMinutesToClear: pendingReview.length * ESTIMATED_MINUTES_PER_PENDING_MATCH,
254
+ },
255
+ variance: {
256
+ percent: accruedTotal === 0
257
+ ? 0
258
+ : ((actualTotal - accruedTotal) / accruedTotal) * 100,
259
+ averagePercentPerMatch: variancePercents.length === 0
260
+ ? 0
261
+ : variancePercents.reduce((sum, percent) => sum + percent, 0) /
262
+ variancePercents.length,
263
+ netAmount: actualTotal - accruedTotal,
264
+ },
265
+ };
266
+ }
194
267
  function getExpenseAutomationActualMatchingMatchView(state, matchId) {
195
268
  const view = state.expenseAutomationActualMatchingViewState;
196
269
  return {
@@ -201,44 +274,20 @@ function getExpenseAutomationActualMatchingMatchView(state, matchId) {
201
274
  };
202
275
  }
203
276
  function getExpenseAutomationActualMatchingView(state) {
204
- const { expenseAutomationActualMatchingViewState, expenseAutomationViewState } = state;
277
+ const { expenseAutomationActualMatchingViewState } = state;
205
278
  const { matchIdsByPeriod, fetchState, error, refreshStatus, uiState, matchModal, undoReversalStatus, } = expenseAutomationActualMatchingViewState;
206
- const currentTenant = (0, tenantSelector_1.getCurrentTenant)(state);
207
- const selectedPeriod = expenseAutomationViewState.selectedPeriodByTenantId[currentTenant?.tenantId ?? ''];
279
+ const selectedPeriod = getSelectedMonthYearForCurrentTenant(state);
208
280
  const monthYearPeriodId = selectedPeriod != null ? (0, timePeriod_1.toMonthYearPeriodId)(selectedPeriod) : undefined;
209
281
  const matchIds = monthYearPeriodId != null ? matchIdsByPeriod[monthYearPeriodId] : undefined;
210
- const rows = (matchIds ?? [])
282
+ const allRows = (matchIds ?? [])
211
283
  .map((matchId) => (0, actualMatchingSelector_1.getActualMatchingMatchById)(state.actualMatchingState, matchId))
212
284
  .filter((match) => match != null)
213
285
  .filter((match) => match.members.length > 0)
214
- .map(toActualMatchingRow)
215
- .filter((row) => matchesSearch(row, uiState.searchString));
286
+ .map(toActualMatchingRow);
287
+ const rows = allRows.filter((row) => matchesSearch(row, uiState.searchString));
216
288
  const reversed = sortActualMatchingRows(rows.filter((row) => isReversedMatchKind(row.kind)), uiState);
217
289
  const pendingReview = sortActualMatchingRows(rows.filter((row) => row.kind === 'pending_review'), uiState);
218
- const autoReversedForKpi = reversed.filter((row) => row.kind === 'auto_reversed');
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
- };
290
+ const kpiSummary = buildActualMatchingKpiSummary(allRows);
242
291
  const searchedAccruals = matchModal.accrualSearch.results
243
292
  .map((key) => (0, jeSchedulesSelector_1.getJEOneTimeAccrualByKey)(key, state.jeSchedulesState))
244
293
  .filter((accrual) => accrual != null);
@@ -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
  }
@@ -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',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zeniai/client-epic-state",
3
- "version": "5.2.35-beta0MM",
3
+ "version": "5.2.35-beta2MM",
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",