@zeniai/client-epic-state 5.2.70 → 5.2.71

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 (51) hide show
  1. package/lib/entity/actualMatching/actualMatchingReducer.d.ts +3 -1
  2. package/lib/entity/actualMatching/actualMatchingReducer.js +11 -2
  3. package/lib/entity/jeSchedules/jeSchedulesPayload.d.ts +2 -0
  4. package/lib/entity/jeSchedules/jeSchedulesPayload.js +2 -0
  5. package/lib/entity/jeSchedules/jeSchedulesState.d.ts +2 -0
  6. package/lib/entity/snackbar/snackbarTypes.d.ts +1 -1
  7. package/lib/entity/snackbar/snackbarTypes.js +2 -0
  8. package/lib/entity/transaction/payloadTypes/transactionLinePayload.js +2 -10
  9. package/lib/epic.d.ts +2 -1
  10. package/lib/epic.js +2 -0
  11. package/lib/esm/entity/actualMatching/actualMatchingReducer.js +9 -1
  12. package/lib/esm/entity/jeSchedules/jeSchedulesPayload.js +2 -0
  13. package/lib/esm/entity/snackbar/snackbarTypes.js +2 -0
  14. package/lib/esm/entity/transaction/payloadTypes/transactionLinePayload.js +2 -10
  15. package/lib/esm/epic.js +2 -0
  16. package/lib/esm/index.js +5 -5
  17. package/lib/esm/view/expenseAutomationView/epics/jeSchedule/actualMatching/fetchActualMatchingMatchEpic.js +4 -2
  18. package/lib/esm/view/expenseAutomationView/epics/jeSchedule/actualMatching/ignoreActualMatchingSuggestionEpic.js +50 -0
  19. package/lib/esm/view/expenseAutomationView/epics/jeSchedule/actualMatching/matchAndReverseAccrualsEpic.js +26 -13
  20. package/lib/esm/view/expenseAutomationView/epics/jeSchedule/actualMatching/searchMatchTransactionsEpic.js +2 -23
  21. package/lib/esm/view/expenseAutomationView/helpers/transactionCategorizationLocalDataHelper.js +1 -2
  22. package/lib/esm/view/expenseAutomationView/reducers/actualMatchingViewReducer.js +89 -38
  23. package/lib/esm/view/expenseAutomationView/selectors/actualMatchingViewSelector.js +68 -62
  24. package/lib/esm/view/expenseAutomationView/types/actualMatchingViewState.js +15 -0
  25. package/lib/esm/view/invoicing/settingsView/settingsViewSelector.js +18 -9
  26. package/lib/esm/view/invoicing/settingsView/submitInvoicingBrandingFormEpic.js +11 -2
  27. package/lib/esm/view/spendManagement/billPay/editBillView/editBillViewSelector.js +4 -30
  28. package/lib/index.d.ts +6 -6
  29. package/lib/index.js +57 -49
  30. package/lib/view/expenseAutomationView/epics/jeSchedule/actualMatching/fetchActualMatchingMatchEpic.d.ts +2 -2
  31. package/lib/view/expenseAutomationView/epics/jeSchedule/actualMatching/fetchActualMatchingMatchEpic.js +3 -1
  32. package/lib/view/expenseAutomationView/epics/jeSchedule/actualMatching/ignoreActualMatchingSuggestionEpic.d.ts +10 -0
  33. package/lib/view/expenseAutomationView/epics/jeSchedule/actualMatching/ignoreActualMatchingSuggestionEpic.js +54 -0
  34. package/lib/view/expenseAutomationView/epics/jeSchedule/actualMatching/matchAndReverseAccrualsEpic.js +24 -11
  35. package/lib/view/expenseAutomationView/epics/jeSchedule/actualMatching/searchMatchTransactionsEpic.js +1 -22
  36. package/lib/view/expenseAutomationView/helpers/transactionCategorizationLocalDataHelper.d.ts +1 -1
  37. package/lib/view/expenseAutomationView/helpers/transactionCategorizationLocalDataHelper.js +1 -2
  38. package/lib/view/expenseAutomationView/payload/actualMatchingPayload.d.ts +12 -1
  39. package/lib/view/expenseAutomationView/reducers/actualMatchingViewReducer.d.ts +19 -8
  40. package/lib/view/expenseAutomationView/reducers/actualMatchingViewReducer.js +90 -39
  41. package/lib/view/expenseAutomationView/selectorTypes/actualMatchingViewSelectorTypes.d.ts +24 -10
  42. package/lib/view/expenseAutomationView/selectors/actualMatchingViewSelector.d.ts +7 -4
  43. package/lib/view/expenseAutomationView/selectors/actualMatchingViewSelector.js +71 -62
  44. package/lib/view/expenseAutomationView/types/actualMatchingViewState.d.ts +19 -4
  45. package/lib/view/expenseAutomationView/types/actualMatchingViewState.js +18 -1
  46. package/lib/view/invoicing/settingsView/settingsViewSelector.d.ts +12 -3
  47. package/lib/view/invoicing/settingsView/settingsViewSelector.js +17 -8
  48. package/lib/view/invoicing/settingsView/submitInvoicingBrandingFormEpic.js +11 -2
  49. package/lib/view/spendManagement/billPay/editBillView/editBillViewSelector.d.ts +1 -1
  50. package/lib/view/spendManagement/billPay/editBillView/editBillViewSelector.js +4 -30
  51. package/package.json +1 -1
@@ -5,13 +5,15 @@ import { JEOneTimeAccrual } from '../../../entity/jeSchedules/jeSchedulesSelecto
5
5
  import { ZeniDate } from '../../../zeniDayJS';
6
6
  import { ActualMatchingTransactionSearchResult, ActualMatchingViewUIState, MatchAndReverseResult } from '../types/actualMatchingViewState';
7
7
  export type ActualMatchingRowKind = 'auto_reversed' | 'pending_review' | 'manually_reversed';
8
- export type ActualMatchingGroupedAccrual = {
8
+ export type ActualMatchingJournalEntryIds = Pick<JEOneTimeAccrual, 'reversalTransactionId' | 'reversalTransactionIntegrationId' | 'transactionId' | 'transactionIntegrationId'>;
9
+ export type ActualMatchingLeftoverAccrual = {
9
10
  accrualId: ID;
10
11
  estimatedAmount?: Amount;
11
12
  jeDebitAccountName?: string;
12
13
  memo?: string;
13
14
  serviceMonth?: string;
14
15
  };
16
+ export type ActualMatchingGroupedAccrual = ActualMatchingLeftoverAccrual & ActualMatchingJournalEntryIds;
15
17
  export interface ActualMatchingRow extends JEOneTimeAccrual {
16
18
  accruedAmount: Amount;
17
19
  actualAmount: Amount;
@@ -20,7 +22,7 @@ export interface ActualMatchingRow extends JEOneTimeAccrual {
20
22
  groupedAccrualIds?: ID[];
21
23
  groupedMembers?: ActualMatchingGroupedAccrual[];
22
24
  intendedAccrualIds?: ID[];
23
- leftovers?: ActualMatchingGroupedAccrual[];
25
+ leftovers?: ActualMatchingLeftoverAccrual[];
24
26
  matchShape?: string;
25
27
  matchTier?: string;
26
28
  reversalDate?: ZeniDate;
@@ -43,6 +45,9 @@ export interface ActualMatchingKpiSummary {
43
45
  count: number;
44
46
  estimatedMinutesToClear: number;
45
47
  };
48
+ reversed: {
49
+ count: number;
50
+ };
46
51
  variance: {
47
52
  averagePercentPerMatch: number;
48
53
  netAmount: number;
@@ -67,21 +72,30 @@ export interface ActualMatchingMatchModalView {
67
72
  results: ActualMatchingTransactionSearchResult[];
68
73
  };
69
74
  lastBatchResults?: MatchAndReverseResult[];
70
- openedFromMatchId?: string;
71
- prefill?: {
72
- selectedAccrualIds: ID[];
73
- actualAmount?: number;
74
- consumedLineIds?: ID[];
75
- matchedLineId?: string;
76
- matchedTransactionId?: string;
77
- };
75
+ }
76
+ export interface ActualMatchingMatchView {
77
+ accruedAmount: Amount;
78
+ actualAmount: Amount;
79
+ kind: ActualMatchingRowKind;
80
+ matchId: string;
81
+ members: JEOneTimeAccrual[];
82
+ transactionIntegrationId: ID;
83
+ transactionType: string;
84
+ leftovers?: JEOneTimeAccrual[];
85
+ matchShape?: string;
86
+ matchTier?: string;
87
+ transactionDate?: string;
88
+ vendorId?: ID;
89
+ vendorName?: string;
78
90
  }
79
91
  export interface ExpenseAutomationActualMatchingViewSelector extends SelectorView {
80
92
  hasLoadedForSelectedPeriod: boolean;
93
+ ignoreStatusByMatchId: Record<string, FetchStateAndError>;
81
94
  kpiSummary: ActualMatchingKpiSummary;
82
95
  matchModal: ActualMatchingMatchModalView;
83
96
  pendingReview: ActualMatchingRow[];
84
97
  refreshStatus: FetchStateAndError;
85
98
  reversed: ActualMatchingRow[];
86
99
  uiState: ActualMatchingViewUIState;
100
+ undoReversalStatusByMatchId: Record<string, FetchStateAndError>;
87
101
  }
@@ -1,13 +1,12 @@
1
1
  import { FetchStateAndError } from '../../../commonStateTypes/common';
2
2
  import { MonthYearPeriod } from '../../../commonStateTypes/timePeriod';
3
3
  import { DisplayCurrencyFallback } from '../../../entity/actualMatching/actualMatchingPayload';
4
- import { ActualMatchingMatch } from '../../../entity/actualMatching/actualMatchingState';
5
4
  import { ActualMatchingMatchId } from '../../../entity/actualMatching/actualMatchingTypes';
6
5
  import { JEOneTimeAccrual } from '../../../entity/jeSchedules/jeSchedulesSelector';
7
6
  import { JEOneTimeAccrualMatchSuggestion } from '../../../entity/jeSchedules/jeSchedulesState';
8
7
  import { RootState } from '../../../rootStateTypes';
9
8
  import { ZeniDate } from '../../../zeniDayJS';
10
- import { ActualMatchingKpiSummary, ActualMatchingRow, ActualMatchingRowKind, ExpenseAutomationActualMatchingViewSelector } from '../selectorTypes/actualMatchingViewSelectorTypes';
9
+ import { ActualMatchingKpiSummary, ActualMatchingMatchView, ActualMatchingRow, ActualMatchingRowKind, ExpenseAutomationActualMatchingViewSelector } from '../selectorTypes/actualMatchingViewSelectorTypes';
11
10
  import { MatchAndReverseResult, UndoReversalResult } from '../types/actualMatchingViewState';
12
11
  export declare function getSelectedMonthYearForCurrentTenant(state: RootState): MonthYearPeriod | undefined;
13
12
  export declare function getTenantDisplayCurrency(state: RootState): DisplayCurrencyFallback | undefined;
@@ -27,12 +26,16 @@ export declare function getActualMatchingReversalDate(accrual: JEOneTimeAccrual,
27
26
  export declare function buildActualMatchingKpiSummary(rows: ActualMatchingRow[], displayCurrencyCode?: string): ActualMatchingKpiSummary;
28
27
  export interface ExpenseAutomationActualMatchingMatchView {
29
28
  detailFetchState: FetchStateAndError;
29
+ ignoreStatus: FetchStateAndError;
30
30
  refreshStatus: FetchStateAndError;
31
31
  undoReversalStatus: FetchStateAndError;
32
32
  lastMatchResults?: MatchAndReverseResult[];
33
33
  lastUndoResults?: UndoReversalResult[];
34
- match?: ActualMatchingMatch;
34
+ match?: ActualMatchingMatchView;
35
35
  }
36
36
  export declare function getExpenseAutomationActualMatchingMatchView(state: RootState, matchId: ActualMatchingMatchId): ExpenseAutomationActualMatchingMatchView;
37
- export declare function getCloseMatchAfterUndo(state: RootState): boolean;
37
+ export declare function getCloseMatchAfterUndo(state: RootState): ActualMatchingMatchId | undefined;
38
+ export declare function getCloseMatchAfterUndoMatchId(state: RootState): ActualMatchingMatchId | undefined;
39
+ export declare function getCloseMatchAfterIgnoreMatchId(state: RootState): ActualMatchingMatchId | undefined;
40
+ export declare function getActualMatchingMatchModalConfirmStatus(state: RootState, matchId?: ActualMatchingMatchId): FetchStateAndError;
38
41
  export declare function getExpenseAutomationActualMatchingView(state: RootState): ExpenseAutomationActualMatchingViewSelector;
@@ -16,7 +16,11 @@ exports.getActualMatchingReversalDate = getActualMatchingReversalDate;
16
16
  exports.buildActualMatchingKpiSummary = buildActualMatchingKpiSummary;
17
17
  exports.getExpenseAutomationActualMatchingMatchView = getExpenseAutomationActualMatchingMatchView;
18
18
  exports.getCloseMatchAfterUndo = getCloseMatchAfterUndo;
19
+ exports.getCloseMatchAfterUndoMatchId = getCloseMatchAfterUndoMatchId;
20
+ exports.getCloseMatchAfterIgnoreMatchId = getCloseMatchAfterIgnoreMatchId;
21
+ exports.getActualMatchingMatchModalConfirmStatus = getActualMatchingMatchModalConfirmStatus;
19
22
  exports.getExpenseAutomationActualMatchingView = getExpenseAutomationActualMatchingView;
23
+ const toolkit_1 = require("@reduxjs/toolkit");
20
24
  const orderBy_1 = __importDefault(require("lodash/orderBy"));
21
25
  const timePeriod_1 = require("../../../commonStateTypes/timePeriod");
22
26
  const actualMatchingSelector_1 = require("../../../entity/actualMatching/actualMatchingSelector");
@@ -24,7 +28,6 @@ const jeScheduleHelper_1 = require("../../../entity/jeSchedules/jeScheduleHelper
24
28
  const jeSchedulesSelector_1 = require("../../../entity/jeSchedules/jeSchedulesSelector");
25
29
  const tenantSelector_1 = require("../../../entity/tenant/tenantSelector");
26
30
  const zeniDayJS_1 = require("../../../zeniDayJS");
27
- const actualMatchingViewState_1 = require("../types/actualMatchingViewState");
28
31
  function getSelectedMonthYearForCurrentTenant(state) {
29
32
  const tenantId = (0, tenantSelector_1.getCurrentTenant)(state)?.tenantId;
30
33
  if (tenantId == null) {
@@ -53,27 +56,6 @@ function toMatchModalAccrual(member) {
53
56
  jeOneTimeAccrualKey: (0, jeScheduleHelper_1.generateJEOneTimeAccrualKey)(member.accrualId),
54
57
  };
55
58
  }
56
- function uniqueMatchModalAccruals(pinned, searched) {
57
- const byId = new Map();
58
- pinned.forEach((row) => byId.set(row.accrualId, row));
59
- searched.forEach((row) => {
60
- if (!byId.has(row.accrualId)) {
61
- byId.set(row.accrualId, row);
62
- }
63
- });
64
- return Array.from(byId.values());
65
- }
66
- function uniqueTransactionSearchResults(pinned, searched) {
67
- const byKey = new Map();
68
- pinned.forEach((row) => byKey.set((0, actualMatchingViewState_1.actualMatchingTransactionLineKey)(row.transactionType, row.transactionIntegrationId, row.lineId), row));
69
- searched.forEach((row) => {
70
- const key = (0, actualMatchingViewState_1.actualMatchingTransactionLineKey)(row.transactionType, row.transactionIntegrationId, row.lineId);
71
- if (!byKey.has(key)) {
72
- byKey.set(key, row);
73
- }
74
- });
75
- return Array.from(byKey.values());
76
- }
77
59
  function parseActualMatchingMatchId(matchId) {
78
60
  const separator = matchId.indexOf(':');
79
61
  if (separator <= 0 || separator === matchId.length - 1) {
@@ -134,13 +116,18 @@ function collectGroupedMembers(members) {
134
116
  estimatedAmount: member.estimatedAmount,
135
117
  jeDebitAccountName: member.jeDebitAccountName,
136
118
  memo: member.memo,
119
+ reversalTransactionId: member.reversalTransactionId,
120
+ reversalTransactionIntegrationId: member.reversalTransactionIntegrationId,
137
121
  serviceMonth: member.serviceMonth,
122
+ transactionId: member.transactionId,
123
+ transactionIntegrationId: member.transactionIntegrationId,
138
124
  }));
139
125
  }
140
126
  function collectLeftoverMembers(leftovers) {
141
127
  if (leftovers == null || leftovers.length === 0) {
142
128
  return undefined;
143
129
  }
130
+ // Leftover accruals did not post, so they have no journal-entry ids to open.
144
131
  return leftovers.map((member) => ({
145
132
  accrualId: member.accrualId,
146
133
  estimatedAmount: member.estimatedAmount,
@@ -246,6 +233,7 @@ function emptyKpiSummary() {
246
233
  actualsReceived: { count: 0, expectedCount: 0, vendorsPending: 0 },
247
234
  autoReversed: { count: 0, percent: 0, clearedAmount: 0 },
248
235
  pendingReview: { count: 0, amount: 0, estimatedMinutesToClear: 0 },
236
+ reversed: { count: 0 },
249
237
  variance: { percent: 0, averagePercentPerMatch: 0, netAmount: 0 },
250
238
  };
251
239
  }
@@ -301,6 +289,7 @@ function buildActualMatchingKpiSummary(rows, displayCurrencyCode) {
301
289
  }
302
290
  const autoReversed = kpiRows.filter((row) => row.kind === 'auto_reversed');
303
291
  const pendingReview = kpiRows.filter((row) => row.kind === 'pending_review');
292
+ const reversed = kpiRows.filter((row) => isReversedMatchKind(row.kind));
304
293
  const receivedCount = kpiRows.length;
305
294
  const rowsWithActual = kpiRows.filter((row) => row.actualAmount?.amount != null);
306
295
  const accruedTotal = rowsWithActual.reduce((sum, row) => sum + (row.accruedAmount?.amount ?? row.estimatedAmount.amount), 0);
@@ -326,6 +315,9 @@ function buildActualMatchingKpiSummary(rows, displayCurrencyCode) {
326
315
  amount: sumRowAmounts(pendingReview, displayCurrencyCode),
327
316
  estimatedMinutesToClear: pendingReview.length * ESTIMATED_MINUTES_PER_PENDING_MATCH,
328
317
  },
318
+ reversed: {
319
+ count: reversed.length,
320
+ },
329
321
  variance: {
330
322
  percent: accruedTotal === 0
331
323
  ? 0
@@ -338,23 +330,66 @@ function buildActualMatchingKpiSummary(rows, displayCurrencyCode) {
338
330
  },
339
331
  };
340
332
  }
341
- function getExpenseAutomationActualMatchingMatchView(state, matchId) {
342
- const view = state.expenseAutomationActualMatchingViewState;
333
+ function toActualMatchingMatchView(match) {
343
334
  return {
344
- match: (0, actualMatchingSelector_1.getActualMatchingMatchById)(state.actualMatchingState, matchId),
345
- detailFetchState: view.detailFetchStateByMatchId[matchId] ?? NOT_STARTED,
346
- refreshStatus: view.detailRefreshStatusByMatchId[matchId] ?? NOT_STARTED,
347
- undoReversalStatus: view.undoReversalStatusByMatchId[matchId] ?? NOT_STARTED,
348
- lastMatchResults: view.lastMatchResultsByMatchId?.[matchId],
349
- lastUndoResults: view.lastUndoResultsByMatchId?.[matchId],
335
+ ...match,
336
+ members: match.members.map(toMatchModalAccrual),
337
+ leftovers: match.leftovers == null
338
+ ? undefined
339
+ : match.leftovers.map(toMatchModalAccrual),
350
340
  };
351
341
  }
342
+ const matchViewSelectorById = new Map();
343
+ function getExpenseAutomationActualMatchingMatchView(state, matchId) {
344
+ let selector = matchViewSelectorById.get(matchId);
345
+ if (selector == null) {
346
+ selector = (0, toolkit_1.createSelector)([
347
+ (root) => root.expenseAutomationActualMatchingViewState
348
+ .detailFetchStateByMatchId[matchId] ?? NOT_STARTED,
349
+ (root) => root.expenseAutomationActualMatchingViewState.ignoreStatusByMatchId[matchId] ?? NOT_STARTED,
350
+ (root) => root.expenseAutomationActualMatchingViewState
351
+ .lastMatchResultsByMatchId?.[matchId],
352
+ (root) => root.expenseAutomationActualMatchingViewState
353
+ .lastUndoResultsByMatchId?.[matchId],
354
+ (root) => root.expenseAutomationActualMatchingViewState
355
+ .detailRefreshStatusByMatchId[matchId] ?? NOT_STARTED,
356
+ (root) => root.expenseAutomationActualMatchingViewState
357
+ .undoReversalStatusByMatchId[matchId] ?? NOT_STARTED,
358
+ (root) => (0, actualMatchingSelector_1.getActualMatchingMatchById)(root.actualMatchingState, matchId),
359
+ ], (detailFetchState, ignoreStatus, lastMatchResults, lastUndoResults, refreshStatus, undoReversalStatus, match) => ({
360
+ detailFetchState,
361
+ ignoreStatus,
362
+ lastMatchResults,
363
+ lastUndoResults,
364
+ match: match == null ? undefined : toActualMatchingMatchView(match),
365
+ refreshStatus,
366
+ undoReversalStatus,
367
+ }));
368
+ matchViewSelectorById.set(matchId, selector);
369
+ }
370
+ return selector(state);
371
+ }
352
372
  function getCloseMatchAfterUndo(state) {
353
- return state.expenseAutomationActualMatchingViewState.closeMatchAfterUndo;
373
+ return state.expenseAutomationActualMatchingViewState
374
+ .closeMatchAfterUndoMatchIds[0];
375
+ }
376
+ function getCloseMatchAfterUndoMatchId(state) {
377
+ return getCloseMatchAfterUndo(state);
378
+ }
379
+ function getCloseMatchAfterIgnoreMatchId(state) {
380
+ return state.expenseAutomationActualMatchingViewState
381
+ .closeMatchAfterIgnoreMatchIds[0];
382
+ }
383
+ function getActualMatchingMatchModalConfirmStatus(state, matchId) {
384
+ if (matchId != null && matchId !== '') {
385
+ return (state.expenseAutomationActualMatchingViewState.confirmStatusByMatchId[matchId] ?? NOT_STARTED);
386
+ }
387
+ return state.expenseAutomationActualMatchingViewState.matchModal
388
+ .confirmStatus;
354
389
  }
355
390
  function getExpenseAutomationActualMatchingView(state) {
356
391
  const { expenseAutomationActualMatchingViewState } = state;
357
- const { matchIdsByPeriod, fetchState, error, refreshStatus, uiState, matchModal, } = expenseAutomationActualMatchingViewState;
392
+ const { matchIdsByPeriod, fetchState, error, refreshStatus, uiState, matchModal, ignoreStatusByMatchId, undoReversalStatusByMatchId, } = expenseAutomationActualMatchingViewState;
358
393
  const selectedPeriod = getSelectedMonthYearForCurrentTenant(state);
359
394
  const monthYearPeriodId = selectedPeriod != null ? (0, timePeriod_1.toMonthYearPeriodId)(selectedPeriod) : undefined;
360
395
  const matchIds = monthYearPeriodId != null ? matchIdsByPeriod[monthYearPeriodId] : undefined;
@@ -370,32 +405,6 @@ function getExpenseAutomationActualMatchingView(state) {
370
405
  const searchedAccruals = matchModal.accrualSearch.results
371
406
  .map((key) => (0, jeSchedulesSelector_1.getJEOneTimeAccrualByKey)(key, state.jeSchedulesState))
372
407
  .filter((accrual) => accrual != null);
373
- const openedMatch = matchModal.openedFromMatchId != null
374
- ? (0, actualMatchingSelector_1.getActualMatchingMatchById)(state.actualMatchingState, matchModal.openedFromMatchId)
375
- : undefined;
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;
385
- const prefill = openedMatch == null
386
- ? undefined
387
- : {
388
- selectedAccrualIds: [
389
- ...openedMatch.members.map((member) => member.accrualId),
390
- ...(openedMatch.leftovers ?? []).map((leftover) => leftover.accrualId),
391
- ],
392
- actualAmount: openedMatch.actualAmount.amount,
393
- matchedTransactionId: stamp?.matchedTransactionId,
394
- matchedLineId: cleared == null ? suggestion?.matchedLineId : undefined,
395
- ...(consumedLineIds != null && consumedLineIds.length > 0
396
- ? { consumedLineIds }
397
- : {}),
398
- };
399
408
  // `fetchState` is a flat slice field that survives a period switch. Until this
400
409
  // month's ids land, leftover Completed/Error is the previous month. Report
401
410
  // Not-Started so chrome matches Review's "cache slot missing" skeleton, not
@@ -413,16 +422,16 @@ function getExpenseAutomationActualMatchingView(state) {
413
422
  kpiSummary,
414
423
  uiState,
415
424
  refreshStatus,
425
+ ignoreStatusByMatchId,
426
+ undoReversalStatusByMatchId,
416
427
  hasLoadedForSelectedPeriod: matchIds != null,
417
428
  matchModal: {
418
429
  isOpen: matchModal.isOpen,
419
- openedFromMatchId: matchModal.openedFromMatchId,
420
- prefill,
421
430
  confirmStatus: matchModal.confirmStatus,
422
431
  lastBatchResults: matchModal.lastBatchResults,
423
432
  transactionSearch: {
424
433
  query: matchModal.transactionSearch.query,
425
- results: uniqueTransactionSearchResults(matchModal.pinnedTransactionResults, matchModal.transactionSearch.results),
434
+ results: matchModal.transactionSearch.results,
426
435
  fetchState: {
427
436
  fetchState: matchModal.transactionSearch.fetchState,
428
437
  error: matchModal.transactionSearch.error,
@@ -432,7 +441,7 @@ function getExpenseAutomationActualMatchingView(state) {
432
441
  },
433
442
  accrualSearch: {
434
443
  query: matchModal.accrualSearch.query,
435
- results: uniqueMatchModalAccruals(pinnedAccruals, searchedAccruals),
444
+ results: searchedAccruals,
436
445
  fetchState: {
437
446
  fetchState: matchModal.accrualSearch.fetchState,
438
447
  error: matchModal.accrualSearch.error,
@@ -5,6 +5,8 @@ import { ActualMatchingMatchId } from '../../../entity/actualMatching/actualMatc
5
5
  import { JEOneTimeAccrualKey } from '../../../entity/jeSchedules/jeSchedulesTypes';
6
6
  export declare const toActualMatchingSortKey: (v: string) => "type" | "date" | "vendor" | "category" | "class" | "amount" | "variance" | "lifecycle" | "service" | "accrued" | "actual";
7
7
  export type ActualMatchingSortKey = ReturnType<typeof toActualMatchingSortKey>;
8
+ export declare const toActualMatchingStatusFilter: (v: string) => "pending_review" | "reversed" | "all";
9
+ export type ActualMatchingStatusFilter = ReturnType<typeof toActualMatchingStatusFilter>;
8
10
  export declare const MATCH_TRANSACTION_SEARCH_PAGE_SIZE = 50;
9
11
  export declare const MATCH_ACCRUAL_SEARCH_LIMIT = 50;
10
12
  export declare const MATCH_SEARCH_DEBOUNCE_MS = 300;
@@ -18,6 +20,11 @@ export interface ActualMatchingViewUIState {
18
20
  searchString: string;
19
21
  sortKey: ActualMatchingSortKey;
20
22
  sortOrder: SortOrder;
23
+ /**
24
+ * Unused by the list: Pending review and Reversed always render as separate
25
+ * tables. Kept so persisted UI state and older clients still hydrate.
26
+ */
27
+ statusFilter: ActualMatchingStatusFilter;
21
28
  }
22
29
  /**
23
30
  * Sibling of Missing Receipts `MatchCandidate`. Confirm needs `lineId` and
@@ -69,6 +76,14 @@ export interface UndoReversalAccrualsParams {
69
76
  accrualIds: ID[];
70
77
  matchId: ActualMatchingMatchId;
71
78
  }
79
+ export declare const toActualMatchingIgnoreReasonType: (v: string) => "other_ignore" | "already_handled" | "amount_off" | "wrong_period" | "wrong_bill";
80
+ export type ActualMatchingIgnoreReasonType = ReturnType<typeof toActualMatchingIgnoreReasonType>;
81
+ export declare const ALL_ACTUAL_MATCHING_IGNORE_REASONS: readonly ActualMatchingIgnoreReasonType[];
82
+ export interface IgnoreActualMatchingSuggestionParams {
83
+ matchId: ActualMatchingMatchId;
84
+ reason: ActualMatchingIgnoreReasonType;
85
+ note?: string;
86
+ }
72
87
  export interface ActualMatchingTransactionSearchState extends FetchStateAndError {
73
88
  isLoadingMore: boolean;
74
89
  nextPageToken: string | null;
@@ -84,18 +99,18 @@ export interface ActualMatchingAccrualSearchState extends FetchStateAndError {
84
99
  export interface ActualMatchingMatchModalState {
85
100
  accrualSearch: ActualMatchingAccrualSearchState;
86
101
  confirmStatus: FetchStateAndError;
87
- didPinPrefillTransactions: boolean;
88
102
  isOpen: boolean;
89
- pinnedTransactionResults: ActualMatchingTransactionSearchResult[];
90
103
  transactionSearch: ActualMatchingTransactionSearchState;
91
104
  lastBatchResults?: MatchAndReverseResult[];
92
- openedFromMatchId?: ActualMatchingMatchId;
93
105
  }
94
106
  export declare function actualMatchingTransactionLineKey(transactionType: string, transactionIntegrationId: string, lineId: string): string;
95
107
  export interface ActualMatchingViewState extends FetchedState {
96
- closeMatchAfterUndo: boolean;
108
+ closeMatchAfterIgnoreMatchIds: ActualMatchingMatchId[];
109
+ closeMatchAfterUndoMatchIds: ActualMatchingMatchId[];
110
+ confirmStatusByMatchId: Record<ActualMatchingMatchId, FetchStateAndError>;
97
111
  detailFetchStateByMatchId: Record<ActualMatchingMatchId, FetchStateAndError>;
98
112
  detailRefreshStatusByMatchId: Record<ActualMatchingMatchId, FetchStateAndError>;
113
+ ignoreStatusByMatchId: Record<ActualMatchingMatchId, FetchStateAndError>;
99
114
  lastMatchResultsByMatchId: Record<ActualMatchingMatchId, MatchAndReverseResult[]>;
100
115
  lastUndoResultsByMatchId: Record<ActualMatchingMatchId, UndoReversalResult[]>;
101
116
  matchIdsByPeriod: Record<MonthYearPeriodId, ActualMatchingMatchId[]>;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
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;
3
+ exports.ALL_ACTUAL_MATCHING_IGNORE_REASONS = exports.toActualMatchingIgnoreReasonType = exports.toUndoReversalResultStatus = exports.toMatchAndReverseResultStatus = exports.MATCH_SEARCH_DEBOUNCE_MS = exports.MATCH_ACCRUAL_SEARCH_LIMIT = exports.MATCH_TRANSACTION_SEARCH_PAGE_SIZE = exports.toActualMatchingStatusFilter = exports.toActualMatchingSortKey = void 0;
4
4
  exports.isMatchAndReverseCompleteStatus = isMatchAndReverseCompleteStatus;
5
5
  exports.actualMatchingTransactionLineKey = actualMatchingTransactionLineKey;
6
6
  const stringToUnion_1 = require("../../../commonStateTypes/stringToUnion");
@@ -19,6 +19,13 @@ const ACTUAL_MATCHING_SORT_KEYS = [
19
19
  ];
20
20
  const toActualMatchingSortKey = (v) => (0, stringToUnion_1.stringToUnion)(v, ACTUAL_MATCHING_SORT_KEYS);
21
21
  exports.toActualMatchingSortKey = toActualMatchingSortKey;
22
+ const ACTUAL_MATCHING_STATUS_FILTERS = [
23
+ 'all',
24
+ 'pending_review',
25
+ 'reversed',
26
+ ];
27
+ const toActualMatchingStatusFilter = (v) => (0, stringToUnion_1.stringToUnion)(v, ACTUAL_MATCHING_STATUS_FILTERS);
28
+ exports.toActualMatchingStatusFilter = toActualMatchingStatusFilter;
22
29
  exports.MATCH_TRANSACTION_SEARCH_PAGE_SIZE = 50;
23
30
  exports.MATCH_ACCRUAL_SEARCH_LIMIT = 50;
24
31
  exports.MATCH_SEARCH_DEBOUNCE_MS = 300;
@@ -41,6 +48,16 @@ const UNDO_REVERSAL_RESULT_STATUSES = [
41
48
  ];
42
49
  const toUndoReversalResultStatus = (v) => (0, stringToUnion_1.stringToUnion)(v, UNDO_REVERSAL_RESULT_STATUSES);
43
50
  exports.toUndoReversalResultStatus = toUndoReversalResultStatus;
51
+ const ACTUAL_MATCHING_IGNORE_REASONS = [
52
+ 'wrong_bill',
53
+ 'wrong_period',
54
+ 'amount_off',
55
+ 'already_handled',
56
+ 'other_ignore',
57
+ ];
58
+ const toActualMatchingIgnoreReasonType = (v) => (0, stringToUnion_1.stringToUnion)(v, ACTUAL_MATCHING_IGNORE_REASONS);
59
+ exports.toActualMatchingIgnoreReasonType = toActualMatchingIgnoreReasonType;
60
+ exports.ALL_ACTUAL_MATCHING_IGNORE_REASONS = ACTUAL_MATCHING_IGNORE_REASONS;
44
61
  function actualMatchingTransactionLineKey(transactionType, transactionIntegrationId, lineId) {
45
62
  return `${transactionType}:${transactionIntegrationId}:${lineId}`;
46
63
  }
@@ -4,6 +4,7 @@ import { InvoicingDunningActionOption, InvoicingFieldHelpByCode, InvoicingNamedO
4
4
  import { File } from '../../../entity/file/fileState';
5
5
  import { ZeniAPIStatus } from '../../../responsePayload';
6
6
  import { RootState } from '../../../rootStateTypes';
7
+ import { AddressUpdatableInfo } from '../../addressView/addressViewState';
7
8
  import { InvoicingQboConnection, InvoicingQboSyncHealth } from '../invoicingQboView/invoicingQboViewPayload';
8
9
  import { InvoicingAccountingOption } from '../invoicingQboView/invoicingQboViewSelector';
9
10
  import { InvoicingBrandingFormLocalData } from './invoicingBrandingFormConfig';
@@ -113,6 +114,8 @@ export interface InvoicingQboSettingsFormView {
113
114
  localData: InvoicingQboSettingsLocalData;
114
115
  nestedAccountHierarchy: NestedAccountHierarchyForReport[];
115
116
  retryFetchStateByTaskId: Record<string, FetchStateAndError>;
117
+ /** The saved values the draft is compared against. */
118
+ savedLocalData: InvoicingQboSettingsLocalData;
116
119
  saveState: FetchStateAndError;
117
120
  settingsFetchState: FetchState;
118
121
  syncStatusOptions: InvoicingNamedOption[];
@@ -134,14 +137,20 @@ export interface InvoicingBrandingFormView {
134
137
  fieldHelp: InvoicingFieldHelpByCode;
135
138
  localData: InvoicingBrandingFormLocalData;
136
139
  saveState: FetchStateAndError;
140
+ /** The business address to show: the in-progress edit, else what is saved. */
141
+ address?: AddressUpdatableInfo;
137
142
  addressLabel?: string;
138
143
  settings?: InvoicingSettingsPayload;
139
144
  }
140
145
  /**
141
146
  * Bundled view for the branding settings form: the draft `localData` (seeded
142
- * from the existing settings until the user edits), the business-address label
143
- * (from the shared Address screen, seeded on load from `address_id`), the raw
144
- * settings (for the logo/preview), the backend date-format choices and the
147
+ * from the existing settings until the user edits), the business address, the
148
+ * raw settings (for the logo/preview), the backend date-format choices and the
145
149
  * save fetch state.
150
+ *
151
+ * The address is resolved here rather than read from the Address screen's
152
+ * staging store alone: that store is only seeded when settings are fetched, and
153
+ * returning to this page does not refetch, so a saved address would otherwise
154
+ * have no way to reach the screen.
146
155
  */
147
156
  export declare const getInvoicingBrandingFormView: (state: RootState) => InvoicingBrandingFormView;
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getInvoicingBrandingFormView = exports.getInvoicingQboSettingsFormView = exports.getInvoicingSettingsHubView = exports.getInvoicingSettingsView = exports.getInvoicingSettingsFetchState = exports.getInvoicingSettings = void 0;
4
4
  exports.deriveInvoicingEnablePrerequisites = deriveInvoicingEnablePrerequisites;
5
5
  const invoicingCommonPayload_1 = require("../../../entity/invoicing/invoicingCommonPayload");
6
+ const addressSelector_1 = require("../../../entity/address/addressSelector");
6
7
  const fileSelector_1 = require("../../../entity/file/fileSelector");
7
8
  const addressViewSelector_1 = require("../../addressView/addressViewSelector");
8
9
  const invoicingConfigViewSelector_1 = require("../invoicingConfigView/invoicingConfigViewSelector");
@@ -126,6 +127,7 @@ const getInvoicingQboSettingsFormView = (state) => {
126
127
  fieldHelp: (0, invoicingConfigViewSelector_1.invoicingFieldHelpForGroup)(state, 'settings'),
127
128
  accountMappingKindOptions: qboAccountMappingKindOptions,
128
129
  accounts: qboView.accounts,
130
+ savedLocalData: (0, invoicingQboSettingsFormConfig_1.entityToInvoicingQboSettingsLocalData)(settings?.qbo),
129
131
  backfillState: qboView.backfillState,
130
132
  connection: qboView.connection,
131
133
  configFetchState,
@@ -152,20 +154,27 @@ const getInvoicingQboSettingsFormView = (state) => {
152
154
  exports.getInvoicingQboSettingsFormView = getInvoicingQboSettingsFormView;
153
155
  /**
154
156
  * Bundled view for the branding settings form: the draft `localData` (seeded
155
- * from the existing settings until the user edits), the business-address label
156
- * (from the shared Address screen, seeded on load from `address_id`), the raw
157
- * settings (for the logo/preview), the backend date-format choices and the
157
+ * from the existing settings until the user edits), the business address, the
158
+ * raw settings (for the logo/preview), the backend date-format choices and the
158
159
  * save fetch state.
160
+ *
161
+ * The address is resolved here rather than read from the Address screen's
162
+ * staging store alone: that store is only seeded when settings are fetched, and
163
+ * returning to this page does not refetch, so a saved address would otherwise
164
+ * have no way to reach the screen.
159
165
  */
160
166
  const getInvoicingBrandingFormView = (state) => {
161
167
  const { brandingDraft, saveFetchState, settings } = state.invoicingSettingsViewState;
162
- const newAddress = state.addressViewState.newAddressState?.[invoicingBrandingFormConfig_1.INVOICING_BUSINESS_ADDRESS_TYPE];
163
- const addressLabel = newAddress?.addressToCreate != null
164
- ? (0, addressViewSelector_1.getFormattedAddress)(newAddress.addressToCreate)
165
- : undefined;
168
+ const savedAddressId = settings?.address?.address_id;
169
+ const address = state.addressViewState.newAddressState?.[invoicingBrandingFormConfig_1.INVOICING_BUSINESS_ADDRESS_TYPE]
170
+ ?.addressToCreate ??
171
+ (savedAddressId != null
172
+ ? (0, addressSelector_1.getAddressByAddressId)(state.addressState, savedAddressId)
173
+ : undefined);
166
174
  return {
167
175
  fieldHelp: (0, invoicingConfigViewSelector_1.invoicingFieldHelpForGroup)(state, 'settings'),
168
- addressLabel,
176
+ address,
177
+ addressLabel: address != null ? (0, addressViewSelector_1.getFormattedAddress)(address) : undefined,
169
178
  dateFormatOptions: (0, invoicingConfigViewSelector_1.getInvoicingConfigView)(state).dateFormatOptions,
170
179
  localData: brandingDraft ?? (0, invoicingBrandingFormConfig_1.entityToInvoicingBrandingFormLocalData)(settings),
171
180
  saveState: saveFetchState,
@@ -4,6 +4,7 @@ exports.submitInvoicingBrandingFormEpic = void 0;
4
4
  const rxjs_1 = require("rxjs");
5
5
  const operators_1 = require("rxjs/operators");
6
6
  const addressReducer_1 = require("../../../entity/address/addressReducer");
7
+ const addressSelector_1 = require("../../../entity/address/addressSelector");
7
8
  const fileReducer_1 = require("../../../entity/file/fileReducer");
8
9
  const responsePayload_1 = require("../../../responsePayload");
9
10
  const addressViewReducer_1 = require("../../addressView/addressViewReducer");
@@ -19,7 +20,11 @@ const submitInvoicingBrandingFormEpic = (actions$, state$, zeniAPI) => actions$.
19
20
  const state = state$.value;
20
21
  const { brandingDraft, settings } = state.invoicingSettingsViewState;
21
22
  const localData = brandingDraft ?? (0, invoicingBrandingFormConfig_1.entityToInvoicingBrandingFormLocalData)(settings);
22
- const address = state.addressViewState.newAddressState?.[invoicingBrandingFormConfig_1.INVOICING_BUSINESS_ADDRESS_TYPE]?.addressToCreate;
23
+ const savedAddressId = settings?.address?.address_id;
24
+ const address = state.addressViewState.newAddressState?.[invoicingBrandingFormConfig_1.INVOICING_BUSINESS_ADDRESS_TYPE]?.addressToCreate ??
25
+ (savedAddressId != null
26
+ ? (0, addressSelector_1.getAddressByAddressId)(state.addressState, savedAddressId)
27
+ : undefined);
23
28
  const data = (0, invoicingBrandingFormConfig_1.brandingLocalDataToSettingsPayload)(localData, address);
24
29
  const apiUrl = `${zeniAPI.apiEndPoints.invoicingMicroServiceBaseUrl}/1.0/settings`;
25
30
  return zeniAPI
@@ -40,7 +45,11 @@ const submitInvoicingBrandingFormEpic = (actions$, state$, zeniAPI) => actions$.
40
45
  ? [(0, fileReducer_1.updateFiles)({ files: [savedLogoFile] })]
41
46
  : []),
42
47
  (0, settingsViewReducer_1.saveInvoicingSettingsSuccess)(response.data),
43
- (0, addressViewReducer_1.resetNewAddressDataInLocalStore)(invoicingBrandingFormConfig_1.INVOICING_BUSINESS_ADDRESS_TYPE),
48
+ ...(savedAddress == null
49
+ ? [
50
+ (0, addressViewReducer_1.resetNewAddressDataInLocalStore)(invoicingBrandingFormConfig_1.INVOICING_BUSINESS_ADDRESS_TYPE),
51
+ ]
52
+ : []),
44
53
  ];
45
54
  }
46
55
  return (0, rxjs_1.of)((0, settingsViewReducer_1.saveInvoicingSettingsFailure)(response.status));
@@ -141,7 +141,7 @@ export declare const getWithdrawFrom: (state: RootState, fromAccountId?: ID, fro
141
141
  accountId: ID;
142
142
  accountType: AccountTypeSubConfigCodeKeyType;
143
143
  }) => FundingAccount | undefined;
144
- export declare const getDefaultWithdrawFromAccount: (state: RootState, depositAccounts: ID[], companyId: ID, lastSelectedFromAccount?: {
144
+ export declare const getDefaultWithdrawFromAccount: (state: RootState, companyId: ID, lastSelectedFromAccount?: {
145
145
  accountId: ID;
146
146
  accountType: AccountTypeSubConfigCodeKeyType;
147
147
  }) => FundingAccount | undefined;
@@ -640,7 +640,7 @@ const getDueDate = (billStageCode, dueDate, invoiceDate, bookCloseDate) => {
640
640
  };
641
641
  exports.getDueDate = getDueDate;
642
642
  const getWithdrawFrom = (state, fromAccountId, fromAccountType, companyId, lastSelectedFromAccount) => {
643
- const { paymentAccountState, depositAccountState, depositAccountListState, paymentAccountListState, } = state;
643
+ const { paymentAccountState, depositAccountState, paymentAccountListState } = state;
644
644
  if (fromAccountId != null) {
645
645
  const withdrawFromAsDepositAcc = (0, depositAccountSelector_1.getDepositAccountByDepositAccountId)(depositAccountState, fromAccountId);
646
646
  const withdrawFromAsPaymentAcc = (0, paymentAccountSelector_1.getPaymentAccountByPaymentAccountId)(paymentAccountState, fromAccountId);
@@ -654,42 +654,16 @@ const getWithdrawFrom = (state, fromAccountId, fromAccountType, companyId, lastS
654
654
  }
655
655
  }
656
656
  else {
657
- return (0, exports.getDefaultWithdrawFromAccount)(state, depositAccountListState.accountIds, companyId ?? '', lastSelectedFromAccount);
657
+ return (0, exports.getDefaultWithdrawFromAccount)(state, companyId ?? '', lastSelectedFromAccount);
658
658
  }
659
659
  return undefined;
660
660
  };
661
661
  exports.getWithdrawFrom = getWithdrawFrom;
662
- const getDefaultWithdrawFromAccount = (state, depositAccounts, companyId, lastSelectedFromAccount) => {
662
+ const getDefaultWithdrawFromAccount = (state, companyId, lastSelectedFromAccount) => {
663
663
  const { depositAccountState, paymentAccountState, paymentAccountListState } = state;
664
664
  const companyView = (0, getCompanyView_1.getCompanyView)(state, companyId, []);
665
665
  const primaryAccount = companyView?.company.company.companyBillPayInfo.primaryFundingAccount;
666
- let defaultAccount = undefined;
667
- // User paying bill for first time or previously paid with external account
668
- if (lastSelectedFromAccount == null ||
669
- lastSelectedFromAccount?.accountType === 'paymentAccount') {
670
- if (depositAccounts.length === 0) {
671
- defaultAccount = primaryAccount;
672
- }
673
- else {
674
- if (primaryAccount?.accountType === 'depositAccount') {
675
- defaultAccount = primaryAccount;
676
- }
677
- }
678
- }
679
- else {
680
- // User paid with Zeni account previously
681
- if (depositAccounts.length === 1) {
682
- defaultAccount = lastSelectedFromAccount;
683
- }
684
- else {
685
- if (primaryAccount?.accountType === 'depositAccount') {
686
- defaultAccount = primaryAccount;
687
- }
688
- else {
689
- defaultAccount = lastSelectedFromAccount;
690
- }
691
- }
692
- }
666
+ const defaultAccount = primaryAccount ?? lastSelectedFromAccount;
693
667
  if (defaultAccount != null) {
694
668
  const defaultAccAsDepositAcc = (0, depositAccountSelector_1.getDepositAccountByDepositAccountId)(depositAccountState, defaultAccount?.accountId);
695
669
  const defaultAccAsPaymentAcc = (0, paymentAccountSelector_1.getPaymentAccountByPaymentAccountId)(paymentAccountState, defaultAccount?.accountId);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zeniai/client-epic-state",
3
- "version": "5.2.70",
3
+ "version": "5.2.71",
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",