@zeniai/client-epic-state 5.1.59 → 5.1.60-beta0ND

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 (59) hide show
  1. package/lib/epic.d.ts +2 -1
  2. package/lib/epic.js +2 -1
  3. package/lib/esm/epic.js +2 -1
  4. package/lib/esm/index.js +2 -2
  5. package/lib/esm/view/commonVendorView/transactionVendorView/epics/saveTransactionVendorEpic.js +0 -1
  6. package/lib/esm/view/commonVendorView/transactionVendorView/transactionVendorViewReducer.js +1 -2
  7. package/lib/esm/view/expenseAutomationView/epics/common/fetchAllExpenseAutomationTabsEpic.js +3 -3
  8. package/lib/esm/view/expenseAutomationView/epics/common/refreshExpenseAutomationCurrentTabEpic.js +2 -3
  9. package/lib/esm/view/expenseAutomationView/epics/transactionCategorization/backgroundRefetchReviewTabEpic.js +2 -2
  10. package/lib/esm/view/expenseAutomationView/epics/transactionCategorization/fetchTransactionCategorizationEpic.js +1 -1
  11. package/lib/esm/view/expenseAutomationView/epics/transactionCategorization/fetchTransactionCategorizationViewEpic.js +4 -4
  12. package/lib/esm/view/expenseAutomationView/epics/transactionCategorization/initializeTransactionCategorizationLocalDataEpic.js +2 -4
  13. package/lib/esm/view/expenseAutomationView/epics/transactionCategorization/markTransactionAsNotMiscategorizedEpic.js +2 -2
  14. package/lib/esm/view/expenseAutomationView/epics/transactionCategorization/saveTransactionCategorizationEpic.js +2 -2
  15. package/lib/esm/view/expenseAutomationView/epics/transactionCategorization/triggerReviewTabRefetchEpic.js +2 -2
  16. package/lib/esm/view/expenseAutomationView/epics/transactionCategorization/updateTransactionCategorizationEpic.js +2 -2
  17. package/lib/esm/view/expenseAutomationView/expenseAutomationViewReducer.js +3 -4
  18. package/lib/esm/view/expenseAutomationView/helpers/transactionCategorizationLocalDataHelper.js +14 -25
  19. package/lib/esm/view/expenseAutomationView/reducers/transactionsViewReducer.js +21 -32
  20. package/lib/esm/view/recommendation/fetchEntityRecommendationsByTransactionIdEpic.js +2 -2
  21. package/lib/esm/view/recommendation/fetchEntityRecommendationsForLineUpdateEpic.js +2 -2
  22. package/lib/esm/view/recommendation/recommendationReducer.js +18 -3
  23. package/lib/esm/view/recommendation/submitRecommendationFeedbackEpic.js +51 -0
  24. package/lib/esm/view/transactionDetail/epics/saveTransactionDetailEpic.js +2 -2
  25. package/lib/esm/view/transactionDetail/epics/updateTransactionDetailEpic.js +2 -2
  26. package/lib/esm/view/transactionDetail/transactionDetailReducer.js +1 -2
  27. package/lib/index.d.ts +2 -2
  28. package/lib/index.js +18 -17
  29. package/lib/view/commonVendorView/transactionVendorView/epics/saveTransactionVendorEpic.js +0 -1
  30. package/lib/view/commonVendorView/transactionVendorView/transactionVendorViewReducer.d.ts +1 -2
  31. package/lib/view/commonVendorView/transactionVendorView/transactionVendorViewReducer.js +1 -2
  32. package/lib/view/expenseAutomationView/epics/common/fetchAllExpenseAutomationTabsEpic.js +3 -3
  33. package/lib/view/expenseAutomationView/epics/common/refreshExpenseAutomationCurrentTabEpic.js +2 -3
  34. package/lib/view/expenseAutomationView/epics/transactionCategorization/backgroundRefetchReviewTabEpic.js +2 -2
  35. package/lib/view/expenseAutomationView/epics/transactionCategorization/fetchTransactionCategorizationEpic.js +1 -1
  36. package/lib/view/expenseAutomationView/epics/transactionCategorization/fetchTransactionCategorizationViewEpic.js +4 -4
  37. package/lib/view/expenseAutomationView/epics/transactionCategorization/initializeTransactionCategorizationLocalDataEpic.js +2 -4
  38. package/lib/view/expenseAutomationView/epics/transactionCategorization/markTransactionAsNotMiscategorizedEpic.js +2 -2
  39. package/lib/view/expenseAutomationView/epics/transactionCategorization/saveTransactionCategorizationEpic.js +2 -2
  40. package/lib/view/expenseAutomationView/epics/transactionCategorization/triggerReviewTabRefetchEpic.js +2 -2
  41. package/lib/view/expenseAutomationView/epics/transactionCategorization/updateTransactionCategorizationEpic.js +2 -2
  42. package/lib/view/expenseAutomationView/expenseAutomationViewReducer.d.ts +2 -5
  43. package/lib/view/expenseAutomationView/expenseAutomationViewReducer.js +3 -4
  44. package/lib/view/expenseAutomationView/helpers/transactionCategorizationLocalDataHelper.d.ts +5 -5
  45. package/lib/view/expenseAutomationView/helpers/transactionCategorizationLocalDataHelper.js +14 -25
  46. package/lib/view/expenseAutomationView/reducers/transactionsViewReducer.d.ts +10 -23
  47. package/lib/view/expenseAutomationView/reducers/transactionsViewReducer.js +21 -32
  48. package/lib/view/recommendation/fetchEntityRecommendationsByTransactionIdEpic.js +2 -2
  49. package/lib/view/recommendation/fetchEntityRecommendationsForLineUpdateEpic.js +2 -2
  50. package/lib/view/recommendation/recommendationPayload.d.ts +33 -0
  51. package/lib/view/recommendation/recommendationReducer.d.ts +3 -4
  52. package/lib/view/recommendation/recommendationReducer.js +19 -4
  53. package/lib/view/recommendation/submitRecommendationFeedbackEpic.d.ts +7 -0
  54. package/lib/view/recommendation/submitRecommendationFeedbackEpic.js +55 -0
  55. package/lib/view/transactionDetail/epics/saveTransactionDetailEpic.js +2 -2
  56. package/lib/view/transactionDetail/epics/updateTransactionDetailEpic.js +2 -2
  57. package/lib/view/transactionDetail/transactionDetailReducer.d.ts +1 -3
  58. package/lib/view/transactionDetail/transactionDetailReducer.js +1 -2
  59. package/package.json +1 -1
@@ -2,12 +2,11 @@ import { from } from 'rxjs';
2
2
  import { filter, mergeMap } from 'rxjs/operators';
3
3
  import { getTransactionWithCOT } from '../../../../entity/transaction/transactionHelper';
4
4
  import { getSupportedTransactionsByIds } from '../../../../entity/transaction/transactionSelector';
5
- import { getUncategorizedAccounts } from '../../../accountList/accountListSelector';
6
5
  import { mergeTabSpecificLineItems, toTransactionDetailLocalData, } from '../../helpers/transactionCategorizationLocalDataHelper';
7
6
  import { initializeTransactionCategorizationViewLocalData, saveTransactionCategorizationLocalData, } from '../../reducers/transactionsViewReducer';
8
7
  import { initialSupportedTransactionCategorization, } from '../../types/transactionsViewState';
9
8
  export const initializeTransactionCategorizationViewLocalDataEpic = (actions$, state$) => actions$.pipe(filter(initializeTransactionCategorizationViewLocalData.match), mergeMap((action) => {
10
- const { transactionIds, selectedPeriod, keepExistingListItems, selectedTab, pageToken, totalCount, lineItemsByTransactionIds, refreshViewInBackground, isUncategorizedExpenseCategoryEnabled, } = action.payload;
9
+ const { transactionIds, selectedPeriod, keepExistingListItems, selectedTab, pageToken, totalCount, lineItemsByTransactionIds, refreshViewInBackground, } = action.payload;
11
10
  const fetchActions = [];
12
11
  const { transactionState, expenseAutomationTransactionsViewState } = state$.value;
13
12
  const { transactionReviewLocalDataById } = expenseAutomationTransactionsViewState.transactionCategorizationView[selectedTab];
@@ -15,10 +14,9 @@ export const initializeTransactionCategorizationViewLocalDataEpic = (actions$, s
15
14
  ? { ...transactionReviewLocalDataById }
16
15
  : {};
17
16
  const transactions = getSupportedTransactionsByIds(transactionState, transactionIds);
18
- const uncategorizedIncomeExpense = getUncategorizedAccounts(state$.value.accountState, state$.value.accountListState, 'accountList');
19
17
  transactions.forEach((transaction) => {
20
18
  const transactionWithCOT = getTransactionWithCOT(transaction);
21
- const newTransactionsLocalData = toTransactionDetailLocalData(transactionWithCOT, selectedTab, lineItemsByTransactionIds[transaction.id], uncategorizedIncomeExpense, isUncategorizedExpenseCategoryEnabled);
19
+ const newTransactionsLocalData = toTransactionDetailLocalData(transactionWithCOT, selectedTab, lineItemsByTransactionIds[transaction.id]);
22
20
  if (transactionLocalData[transaction.id] == null) {
23
21
  transactionLocalData[transaction.id] = {
24
22
  ...initialSupportedTransactionCategorization,
@@ -10,7 +10,7 @@ import { getSnackbarMessageForTransactionReview } from '../../helpers/transactio
10
10
  import { fetchTransactionCategorizationView, markTransactionAsNotMiscategorized, updateStatusForTransactionNotMiscategorizedUpdateForCategorization, updateTotalCountForTransactionCategorization, } from '../../reducers/transactionsViewReducer';
11
11
  export const markTransactionAsNotMiscategorizedEpic = (actions$, state$, zeniAPI) => actions$.pipe(filter(markTransactionAsNotMiscategorized.match), mergeMap((action) => {
12
12
  const { expenseAutomationViewState, expenseAutomationTransactionsViewState, } = state$.value;
13
- const { transactionsInReview, selectedTab, isUncategorizedExpenseCategoryEnabled, } = action.payload;
13
+ const { transactionsInReview, selectedTab, } = action.payload;
14
14
  const transactionsSuccess = transactionsInReview.transactions_update_success;
15
15
  const transactionsFailed = transactionsInReview.transactions_update_failed;
16
16
  const currentTenant = getCurrentTenant(state$.value);
@@ -107,7 +107,7 @@ export const markTransactionAsNotMiscategorizedEpic = (actions$, state$, zeniAPI
107
107
  if (timePeriodStart != null && timePeriodEnd != null) {
108
108
  actions.push(fetchTransactionCategorizationView('review', { start: timePeriodStart, end: timePeriodEnd }, true, // cacheOverride = true to force refresh
109
109
  false, undefined, undefined, true, // refreshViewInBackground
110
- false, isUncategorizedExpenseCategoryEnabled));
110
+ false));
111
111
  }
112
112
  }
113
113
  actions.push(updateTotalCountForTransactionCategorization({
@@ -6,7 +6,7 @@ import { getUncategorizedAccounts } from '../../../accountList/accountListSelect
6
6
  import { toTransactionUpdatesFromTransactionCategorizationLocalData } from '../../helpers/saveTransactionHelper';
7
7
  import { saveTransactionCategorization, updateTransactionCategorization, } from '../../reducers/transactionsViewReducer';
8
8
  export const saveTransactionCategorizationEpic = (actions$, state$) => actions$.pipe(filter(saveTransactionCategorization.match), mergeMap((action) => {
9
- const { transactionIds, cotTrackingByTransactionId, selectedTab, isUncategorizedExpenseCategoryEnabled, } = action.payload;
9
+ const { transactionIds, cotTrackingByTransactionId, selectedTab, } = action.payload;
10
10
  const fetchActions = [];
11
11
  const { transactionState, expenseAutomationTransactionsViewState } = state$.value;
12
12
  const transactionSaveUpdates = [];
@@ -19,6 +19,6 @@ export const saveTransactionCategorizationEpic = (actions$, state$) => actions$.
19
19
  transactionSaveUpdates.push(toTransactionUpdatesFromTransactionCategorizationLocalData(localData, transaction, uncategorizedIncomeExpense));
20
20
  }
21
21
  });
22
- fetchActions.push(updateTransactionCategorization(selectedTab, transactionIds, transactionSaveUpdates, cotTrackingByTransactionId, isUncategorizedExpenseCategoryEnabled));
22
+ fetchActions.push(updateTransactionCategorization(selectedTab, transactionIds, transactionSaveUpdates, cotTrackingByTransactionId));
23
23
  return from(fetchActions);
24
24
  }));
@@ -4,7 +4,7 @@ import { convertToPeriod, toAbsoluteDay, } from '../../../../commonStateTypes/ti
4
4
  import { getCurrentTenant } from '../../../../entity/tenant/tenantSelector';
5
5
  import { backgroundRefetchReviewTab, syncTransactionCategorizationFromDetailSave, } from '../../reducers/transactionsViewReducer';
6
6
  export const triggerReviewTabRefetchEpic = (actions$, state$) => actions$.pipe(filter(syncTransactionCategorizationFromDetailSave.match), mergeMap((action) => {
7
- const { pendingReviewLineIds, transactionId, isUncategorizedExpenseCategoryEnabled, } = action.payload;
7
+ const { pendingReviewLineIds, transactionId, } = action.payload;
8
8
  if (pendingReviewLineIds.length === 0) {
9
9
  return EMPTY;
10
10
  }
@@ -23,5 +23,5 @@ export const triggerReviewTabRefetchEpic = (actions$, state$) => actions$.pipe(f
23
23
  if (timePeriodStart == null || timePeriodEnd == null) {
24
24
  return EMPTY;
25
25
  }
26
- return of(backgroundRefetchReviewTab({ start: timePeriodStart, end: timePeriodEnd }, isUncategorizedExpenseCategoryEnabled));
26
+ return of(backgroundRefetchReviewTab({ start: timePeriodStart, end: timePeriodEnd }));
27
27
  }));
@@ -15,7 +15,7 @@ import { getVendorByVendorId } from '../../../../entity/vendor/vendorSelector';
15
15
  import { createZeniAPIStatus, isSuccessResponse, } from '../../../../responsePayload';
16
16
  import { markTransactionAsNotMiscategorized, updateTransactionCategorization, updateTransactionCategorizationSaveStatus, } from '../../reducers/transactionsViewReducer';
17
17
  export const updateTransactionCategorizationEpic = (actions$, state$, zeniAPI) => actions$.pipe(filter(updateTransactionCategorization.match), mergeMap((action) => {
18
- const { selectedTab, transactionIds, transactionSaveUpdates, cotTrackingByTransactionId, isUncategorizedExpenseCategoryEnabled, } = action.payload;
18
+ const { selectedTab, transactionIds, transactionSaveUpdates, cotTrackingByTransactionId, } = action.payload;
19
19
  const { transactionState, vendorState, expenseAutomationTransactionsViewState, } = state$.value;
20
20
  const payloadArray = preparePayload(selectedTab, transactionState, vendorState, expenseAutomationTransactionsViewState, transactionIds, transactionSaveUpdates, cotTrackingByTransactionId).filter((payload) => payload != null);
21
21
  return zeniAPI
@@ -24,7 +24,7 @@ export const updateTransactionCategorizationEpic = (actions$, state$, zeniAPI) =
24
24
  if (isSuccessResponse(response) && response.data != null) {
25
25
  const transactionsInReview = response.data;
26
26
  const actions = [];
27
- actions.push(markTransactionAsNotMiscategorized(selectedTab, transactionsInReview, isUncategorizedExpenseCategoryEnabled), updateTransactionCategorizationSaveStatus(selectedTab, 'Completed', createZeniAPIStatus('Completed', 'Transaction categorization updated successfully')));
27
+ actions.push(markTransactionAsNotMiscategorized(selectedTab, transactionsInReview), updateTransactionCategorizationSaveStatus(selectedTab, 'Completed', createZeniAPIStatus('Completed', 'Transaction categorization updated successfully')));
28
28
  return from(actions);
29
29
  }
30
30
  else {
@@ -18,20 +18,19 @@ const expenseAutomationView = createSlice({
18
18
  reducer() {
19
19
  //do nothing
20
20
  },
21
- prepare(cacheOverride = false, refreshViewInBackground = false, isUncategorizedExpenseCategoryEnabled = false) {
21
+ prepare(cacheOverride = false, refreshViewInBackground = false) {
22
22
  return {
23
23
  payload: {
24
24
  cacheOverride,
25
25
  refreshViewInBackground,
26
- isUncategorizedExpenseCategoryEnabled,
27
26
  },
28
27
  };
29
28
  },
30
29
  },
31
30
  refreshExpenseAutomationCurrentTab: {
32
- prepare(isUncategorizedExpenseCategoryEnabled) {
31
+ prepare() {
33
32
  return {
34
- payload: { isUncategorizedExpenseCategoryEnabled },
33
+ payload: {},
35
34
  };
36
35
  },
37
36
  reducer() {
@@ -66,16 +66,16 @@ export const MAX_SELECTION_LIMIT = 5;
66
66
  // triplet that was inlined at four sites (updateSelectedCustomer/Vendor,
67
67
  // setAllItemsToCategoryClassInLocalDataForCategorization, and
68
68
  // setEntityRecommendationForLineIdInLocalData below).
69
- export const shouldAutoSelectAndAdd = (transaction, localData, selectedCheckBoxTransactionIds, uncategorizedAccounts, isUncategorizedExpenseCategoryEnabled, isAccountingClassesEnabled) => {
69
+ export const shouldAutoSelectAndAdd = (transaction, localData, selectedCheckBoxTransactionIds, isAccountingClassesEnabled) => {
70
70
  if (selectedCheckBoxTransactionIds.includes(transaction.id)) {
71
71
  return false;
72
72
  }
73
73
  if (selectedCheckBoxTransactionIds.length >= MAX_SELECTION_LIMIT) {
74
74
  return false;
75
75
  }
76
- return checkIfAllLineItemsAreCategoryClassFilled(transaction, localData, uncategorizedAccounts, isUncategorizedExpenseCategoryEnabled, isAccountingClassesEnabled);
76
+ return checkIfAllLineItemsAreCategoryClassFilled(transaction, localData, isAccountingClassesEnabled);
77
77
  };
78
- export const toTransactionDetailLocalData = (transaction, selectedTab, lineItemsByTransactionIds, uncategorizedIncomeExpense, isUncategorizedExpenseCategoryEnabled) => {
78
+ export const toTransactionDetailLocalData = (transaction, selectedTab, lineItemsByTransactionIds) => {
79
79
  const transactionLocal = {
80
80
  ...initialSupportedTransactionCategorization.transactionReviewLocalData,
81
81
  };
@@ -162,7 +162,7 @@ export const toTransactionDetailLocalData = (transaction, selectedTab, lineItems
162
162
  }
163
163
  transactionLocal.lineItemById =
164
164
  transaction.lines != null && transaction.lines.length > 0
165
- ? buildLineItemById(transaction, uncategorizedIncomeExpense, transaction.recommendationsWithCOT, isUncategorizedExpenseCategoryEnabled)
165
+ ? buildLineItemById(transaction, transaction.recommendationsWithCOT)
166
166
  : {};
167
167
  transactionLocal.sortedLineItems =
168
168
  transaction.lines != null && transaction.lines.length > 0
@@ -177,7 +177,7 @@ export const toTransactionDetailLocalData = (transaction, selectedTab, lineItems
177
177
  };
178
178
  return transactionLocal;
179
179
  };
180
- const buildLineItemById = (transaction, uncategorizedIncomeExpense, recommendations, isUncategorizedExpenseCategoryEnabled) => {
180
+ const buildLineItemById = (transaction, recommendations) => {
181
181
  const lineItemData = {};
182
182
  const { lines } = transaction;
183
183
  lines?.forEach((line) => {
@@ -241,10 +241,7 @@ const buildLineItemById = (transaction, uncategorizedIncomeExpense, recommendati
241
241
  if (recommendations != null) {
242
242
  record.recommendationsWithCOT = recommendations[lineId];
243
243
  const higherPreferenceRecommendation = recommendations[lineId]?.[0];
244
- if ((Boolean(accountLine.account.accountId) === false ||
245
- isAccountUncategorized(uncategorizedIncomeExpense, accountLine.account.qboId, isUncategorizedExpenseCategoryEnabled === true
246
- ? ['income']
247
- : undefined)) &&
244
+ if (Boolean(accountLine.account.accountId) === false &&
248
245
  higherPreferenceRecommendation?.account != null) {
249
246
  record.account = {
250
247
  ...higherPreferenceRecommendation.account,
@@ -322,7 +319,7 @@ const buildLineItemById = (transaction, uncategorizedIncomeExpense, recommendati
322
319
  });
323
320
  return lineItemData;
324
321
  };
325
- export const toSetAllLineItemsToCategoryClass = (draft, transaction, transactionId, transactionLineId, transactionDetailLocalData, updatedItem, uncategorizedAccounts, selectedTab, vendorId, customerId, selectedAccount, selectedClassBase, selectedProjectBase, isUncategorizedExpenseCategoryEnabled, isAccountingClassesEnabled) => {
322
+ export const toSetAllLineItemsToCategoryClass = (draft, transaction, transactionId, transactionLineId, transactionDetailLocalData, updatedItem, selectedTab, vendorId, customerId, selectedAccount, selectedClassBase, selectedProjectBase, isAccountingClassesEnabled) => {
326
323
  let newLineItems = {};
327
324
  let selectedCheckBoxTransactionIds = draft.selectedCheckBoxTransactionIds;
328
325
  let similarTransactionUpdated = false;
@@ -379,10 +376,7 @@ export const toSetAllLineItemsToCategoryClass = (draft, transaction, transaction
379
376
  // if the vendor is same, then set the account and class with the selected ones and add the transaction to the selected list
380
377
  if (selectedAccount != null &&
381
378
  updatedItem === 'category' &&
382
- (record.account?.qboId == null ||
383
- isAccountUncategorized(uncategorizedAccounts, record.account?.qboId, isUncategorizedExpenseCategoryEnabled === true
384
- ? ['income']
385
- : undefined) === true)) {
379
+ record.account?.qboId == null) {
386
380
  record.account = selectedAccount;
387
381
  similarTransactionUpdated = true;
388
382
  if ((isAccountingClassesEnabled === false ||
@@ -451,13 +445,13 @@ export const toSetAllLineItemsToCategoryClass = (draft, transaction, transaction
451
445
  selectedEntity: transactionDetailLocalData.selectedEntity,
452
446
  tabSpecificLineItems: transactionDetailLocalData.tabSpecificLineItems,
453
447
  };
454
- if (!checkIfAllLineItemsAreCategoryClassFilled(transaction, newLocalData, uncategorizedAccounts, isUncategorizedExpenseCategoryEnabled, isAccountingClassesEnabled)) {
448
+ if (!checkIfAllLineItemsAreCategoryClassFilled(transaction, newLocalData, isAccountingClassesEnabled)) {
455
449
  selectedCheckBoxTransactionIds = selectedCheckBoxTransactionIds.filter((id) => id !== transaction.id);
456
450
  }
457
451
  draft.selectedCheckBoxTransactionIds = selectedCheckBoxTransactionIds;
458
452
  return { localData: newLocalData, similarTransactionUpdated };
459
453
  };
460
- export const setEntityRecommendationForLineIdInLocalData = (draft, transaction, transactionDetailLocalData, entity, recommendationLineIds, uncategorizedAccounts, recommendationWithCOTByLineId, isUncategorizedExpenseCategoryEnabled, isAccountingClassesEnabled) => {
454
+ export const setEntityRecommendationForLineIdInLocalData = (draft, transaction, transactionDetailLocalData, entity, recommendationLineIds, recommendationWithCOTByLineId, isAccountingClassesEnabled) => {
461
455
  const newLineItems = {};
462
456
  let matchingEntityLineId = undefined;
463
457
  const checkIfNotJEAndDeposit = !isVendorTransaction(transaction);
@@ -477,11 +471,8 @@ export const setEntityRecommendationForLineIdInLocalData = (draft, transaction,
477
471
  };
478
472
  const recommendations = mergeTransactionAndEntityRecommendationsWithCOT(oldRecord.recommendationsWithCOT, recommendationWithCOTByLineId?.[lineId]);
479
473
  const accountLine = line;
480
- const isUncategorizedAccount = isAccountUncategorized(uncategorizedAccounts, accountLine.account.qboId, isUncategorizedExpenseCategoryEnabled === true
481
- ? ['income']
482
- : undefined);
483
474
  record.account =
484
- isUncategorizedAccount === false && accountLine.account.qboId !== ''
475
+ accountLine.account.qboId !== ''
485
476
  ? accountLine.account
486
477
  : recommendations?.[0]?.account
487
478
  ? {
@@ -564,23 +555,21 @@ export const setEntityRecommendationForLineIdInLocalData = (draft, transaction,
564
555
  // category/class became stale after the entity update). Otherwise
565
556
  // route through shouldAutoSelectAndAdd so the MAX_SELECTION_LIMIT
566
557
  // gate is consistently applied across all auto-add sites.
567
- if (!checkIfAllLineItemsAreCategoryClassFilled(transaction, newLocalData, uncategorizedAccounts, isUncategorizedExpenseCategoryEnabled, isAccountingClassesEnabled)) {
558
+ if (!checkIfAllLineItemsAreCategoryClassFilled(transaction, newLocalData, isAccountingClassesEnabled)) {
568
559
  selectedCheckBoxTransactionIds = selectedCheckBoxTransactionIds.filter((id) => id !== transaction.id);
569
560
  }
570
- else if (shouldAutoSelectAndAdd(transaction, newLocalData, selectedCheckBoxTransactionIds, uncategorizedAccounts, isUncategorizedExpenseCategoryEnabled, isAccountingClassesEnabled)) {
561
+ else if (shouldAutoSelectAndAdd(transaction, newLocalData, selectedCheckBoxTransactionIds, isAccountingClassesEnabled)) {
571
562
  selectedCheckBoxTransactionIds.push(transaction.id);
572
563
  }
573
564
  draft.selectedCheckBoxTransactionIds = selectedCheckBoxTransactionIds;
574
565
  return newLocalData;
575
566
  };
576
- export const checkIfAllLineItemsAreCategoryClassFilled = (transaction, transactionDetailLocalData, uncategorizedAccounts, isUncategorizedExpenseCategoryEnabled, isAccountingClassesEnabled) => {
567
+ export const checkIfAllLineItemsAreCategoryClassFilled = (transaction, transactionDetailLocalData, isAccountingClassesEnabled) => {
577
568
  const lineItems = Object.values(transactionDetailLocalData.lineItemById);
578
569
  const isVendorTransactionAvailable = isVendorTransaction(transaction);
579
- const uncategorizedAccountTypes = isUncategorizedExpenseCategoryEnabled === true ? ['income'] : undefined;
580
570
  return lineItems.every((lineItem) => Boolean(lineItem?.account?.qboId) &&
581
571
  (isAccountingClassesEnabled === false ||
582
572
  Boolean(lineItem?.class?.qboId)) &&
583
- isAccountUncategorized(uncategorizedAccounts, lineItem?.account?.qboId, uncategorizedAccountTypes) === false &&
584
573
  ((isVendorTransactionAvailable
585
574
  ? Boolean(transactionDetailLocalData?.vendor?.id)
586
575
  : Boolean(lineItem?.vendor?.id)) ||
@@ -121,7 +121,7 @@ const expenseAutomationTransactionsView = createSlice({
121
121
  reducers: {
122
122
  fetchTransactionCategorizationView: {
123
123
  prepare(selectedTab, period, cacheOverride = false, keepExistingListItems = false, searchString, // when this is defined the output should go to search result IDs
124
- pageToken, refreshViewInBackground = false, resetListItems = false, isUncategorizedExpenseCategoryEnabled) {
124
+ pageToken, refreshViewInBackground = false, resetListItems = false) {
125
125
  return {
126
126
  payload: {
127
127
  selectedTab,
@@ -132,7 +132,6 @@ const expenseAutomationTransactionsView = createSlice({
132
132
  cacheOverride,
133
133
  refreshViewInBackground,
134
134
  resetListItems,
135
- isUncategorizedExpenseCategoryEnabled,
136
135
  },
137
136
  };
138
137
  },
@@ -200,7 +199,7 @@ const expenseAutomationTransactionsView = createSlice({
200
199
  }
201
200
  },
202
201
  prepare(selectedTab, period, cacheOverride = false, keepExistingListItems = false, searchString, // when this is defined the output should go to search result IDs
203
- pageToken, refreshViewInBackground = false, resetListItems, isUncategorizedExpenseCategoryEnabled) {
202
+ pageToken, refreshViewInBackground = false, resetListItems) {
204
203
  return {
205
204
  payload: {
206
205
  selectedTab,
@@ -211,13 +210,12 @@ const expenseAutomationTransactionsView = createSlice({
211
210
  cacheOverride,
212
211
  refreshViewInBackground,
213
212
  resetListItems,
214
- isUncategorizedExpenseCategoryEnabled,
215
213
  },
216
214
  };
217
215
  },
218
216
  },
219
217
  initializeTransactionCategorizationViewLocalData: {
220
- prepare(selectedTab, transactionIds, selectedPeriod, keepExistingListItems = false, pageToken, totalCount, lineItemsByTransactionIds, refreshViewInBackground = false, isUncategorizedExpenseCategoryEnabled) {
218
+ prepare(selectedTab, transactionIds, selectedPeriod, keepExistingListItems = false, pageToken, totalCount, lineItemsByTransactionIds, refreshViewInBackground = false) {
221
219
  return {
222
220
  payload: {
223
221
  selectedTab,
@@ -228,7 +226,6 @@ const expenseAutomationTransactionsView = createSlice({
228
226
  totalCount,
229
227
  lineItemsByTransactionIds,
230
228
  refreshViewInBackground,
231
- isUncategorizedExpenseCategoryEnabled,
232
229
  },
233
230
  };
234
231
  },
@@ -332,13 +329,12 @@ const expenseAutomationTransactionsView = createSlice({
332
329
  });
333
330
  },
334
331
  saveTransactionCategorization: {
335
- prepare(selectedTab, transactionIds, cotTrackingByTransactionId, isUncategorizedExpenseCategoryEnabled) {
332
+ prepare(selectedTab, transactionIds, cotTrackingByTransactionId) {
336
333
  return {
337
334
  payload: {
338
335
  selectedTab,
339
336
  transactionIds,
340
337
  cotTrackingByTransactionId,
341
- isUncategorizedExpenseCategoryEnabled,
342
338
  },
343
339
  };
344
340
  },
@@ -412,21 +408,20 @@ const expenseAutomationTransactionsView = createSlice({
412
408
  fetchState: 'In-Progress',
413
409
  };
414
410
  },
415
- prepare(selectedTab, transactionIds, transactionSaveUpdates, cotTrackingByTransactionId, isUncategorizedExpenseCategoryEnabled) {
411
+ prepare(selectedTab, transactionIds, transactionSaveUpdates, cotTrackingByTransactionId) {
416
412
  return {
417
413
  payload: {
418
414
  selectedTab,
419
415
  transactionIds,
420
416
  transactionSaveUpdates,
421
417
  cotTrackingByTransactionId,
422
- isUncategorizedExpenseCategoryEnabled,
423
418
  },
424
419
  };
425
420
  },
426
421
  },
427
422
  setAllItemsToCategoryClassInLocalDataForCategorization: {
428
423
  reducer(draft, action) {
429
- const { vendorId, customerId, selectedAccount, selectedClassBase, selectedProjectBase, transactions, transactionId, lineId, selectedTab, uncategorizedAccounts, isAccountingClassesEnabled, isUncategorizedExpenseCategoryEnabled, } = action.payload;
424
+ const { vendorId, customerId, selectedAccount, selectedClassBase, selectedProjectBase, transactions, transactionId, lineId, selectedTab, isAccountingClassesEnabled, } = action.payload;
430
425
  const transactionLocalData = recordGet(draft.transactionCategorizationView[selectedTab]
431
426
  .transactionReviewLocalDataById, transactionId, undefined);
432
427
  const existingAccount = transactionLocalData?.transactionReviewLocalData.lineItemById[lineId]
@@ -452,7 +447,7 @@ const expenseAutomationTransactionsView = createSlice({
452
447
  const selectedCheckBoxTransactionIds = draft.transactionCategorizationView[selectedTab]
453
448
  .selectedCheckBoxTransactionIds;
454
449
  if (selectedTransaction != null &&
455
- shouldAutoSelectAndAdd(selectedTransaction, newTransactionLocalData, selectedCheckBoxTransactionIds, uncategorizedAccounts, isUncategorizedExpenseCategoryEnabled, isAccountingClassesEnabled)) {
450
+ shouldAutoSelectAndAdd(selectedTransaction, newTransactionLocalData, selectedCheckBoxTransactionIds, isAccountingClassesEnabled)) {
456
451
  draft.transactionCategorizationView[selectedTab].selectedCheckBoxTransactionIds = [
457
452
  ...selectedCheckBoxTransactionIds,
458
453
  transactionId,
@@ -475,7 +470,7 @@ const expenseAutomationTransactionsView = createSlice({
475
470
  : existingProjectId !== selectedProjectBase?.projectId
476
471
  ? 'project'
477
472
  : 'class';
478
- const { localData, similarTransactionUpdated } = toSetAllLineItemsToCategoryClass(draft.transactionCategorizationView[selectedTab], transaction, transactionId, lineId, transactionLocalData.transactionReviewLocalData, updatedItem, uncategorizedAccounts, selectedTab, vendorId, customerId, selectedAccount, selectedClassBase, selectedProjectBase, isUncategorizedExpenseCategoryEnabled, isAccountingClassesEnabled);
473
+ const { localData, similarTransactionUpdated } = toSetAllLineItemsToCategoryClass(draft.transactionCategorizationView[selectedTab], transaction, transactionId, lineId, transactionLocalData.transactionReviewLocalData, updatedItem, selectedTab, vendorId, customerId, selectedAccount, selectedClassBase, selectedProjectBase, isAccountingClassesEnabled);
479
474
  draft.transactionCategorizationView[selectedTab].transactionReviewLocalDataById[transaction.id] = {
480
475
  transactionId: transaction.id,
481
476
  transactionReviewLocalData: localData,
@@ -494,7 +489,7 @@ const expenseAutomationTransactionsView = createSlice({
494
489
  ...dirtyIds,
495
490
  ]);
496
491
  },
497
- prepare(selectedTab, transactionId, lineId, transactions, uncategorizedAccounts, vendorId, customerId, selectedAccount, selectedClassBase, lineEntity, recommendations, isUncategorizedExpenseCategoryEnabled, isAccountingClassesEnabled, selectedProjectBase) {
492
+ prepare(selectedTab, transactionId, lineId, transactions, uncategorizedAccounts, vendorId, customerId, selectedAccount, selectedClassBase, lineEntity, recommendations, isAccountingClassesEnabled, selectedProjectBase) {
498
493
  return {
499
494
  payload: {
500
495
  selectedTab,
@@ -510,13 +505,12 @@ const expenseAutomationTransactionsView = createSlice({
510
505
  recommendations,
511
506
  uncategorizedAccounts,
512
507
  isAccountingClassesEnabled,
513
- isUncategorizedExpenseCategoryEnabled,
514
508
  },
515
509
  };
516
510
  },
517
511
  },
518
512
  setEntityRecommendationForLineIdsForCategorization: {
519
- prepare(selectedTab, transactionId, transaction, lineIds, entity, amount, recommendationWithCOTByLineId, uncategorizedAccounts, isUncategorizedExpenseCategoryEnabled, isAccountingClassesEnabled) {
513
+ prepare(selectedTab, transactionId, transaction, lineIds, entity, amount, recommendationWithCOTByLineId, uncategorizedAccounts, isAccountingClassesEnabled) {
520
514
  return {
521
515
  payload: {
522
516
  selectedTab,
@@ -528,16 +522,15 @@ const expenseAutomationTransactionsView = createSlice({
528
522
  recommendationWithCOTByLineId,
529
523
  uncategorizedAccounts,
530
524
  isAccountingClassesEnabled,
531
- isUncategorizedExpenseCategoryEnabled,
532
525
  },
533
526
  };
534
527
  },
535
528
  reducer(draft, action) {
536
- const { entity, transaction, transactionId, selectedTab, lineIds, uncategorizedAccounts, isAccountingClassesEnabled, isUncategorizedExpenseCategoryEnabled, } = action.payload;
529
+ const { entity, transaction, transactionId, selectedTab, lineIds, isAccountingClassesEnabled, } = action.payload;
537
530
  const transactionLocalData = recordGet(draft.transactionCategorizationView[selectedTab]
538
531
  .transactionReviewLocalDataById, transactionId.id, undefined);
539
532
  if (transactionLocalData != null) {
540
- const newLocalData = setEntityRecommendationForLineIdInLocalData(draft.transactionCategorizationView[selectedTab], transaction, transactionLocalData.transactionReviewLocalData, entity, lineIds, uncategorizedAccounts, action.payload.recommendationWithCOTByLineId, isUncategorizedExpenseCategoryEnabled, isAccountingClassesEnabled);
533
+ const newLocalData = setEntityRecommendationForLineIdInLocalData(draft.transactionCategorizationView[selectedTab], transaction, transactionLocalData.transactionReviewLocalData, entity, lineIds, action.payload.recommendationWithCOTByLineId, isAccountingClassesEnabled);
541
534
  // Release the per-line fetch flag for every line this fetch
542
535
  // covered. Done in-place on the freshly returned local data
543
536
  // (the helper produces a new mutable object), before we write
@@ -710,7 +703,7 @@ const expenseAutomationTransactionsView = createSlice({
710
703
  action.payload.status;
711
704
  },
712
705
  updateSelectedCustomerForTransaction(draft, action) {
713
- const { transactionId, lineId, updatedCustomer, selectedTransaction, selectedTab, uncategorizedAccounts, isAccountingClassesEnabled, isUncategorizedExpenseCategoryEnabled, } = action.payload;
706
+ const { transactionId, lineId, updatedCustomer, selectedTransaction, selectedTab, isAccountingClassesEnabled, } = action.payload;
714
707
  const transactionLocalData = recordGet(draft.transactionCategorizationView[selectedTab]
715
708
  .transactionReviewLocalDataById, transactionId, undefined);
716
709
  if (transactionLocalData != null) {
@@ -763,7 +756,7 @@ const expenseAutomationTransactionsView = createSlice({
763
756
  .selectedCheckBoxTransactionIds;
764
757
  if (newTransactionLocalData.customer != null &&
765
758
  newTransactionLocalData.customer.id != null &&
766
- shouldAutoSelectAndAdd(selectedTransaction, newTransactionLocalData, selectedCheckBoxTransactionIds, uncategorizedAccounts, isUncategorizedExpenseCategoryEnabled, isAccountingClassesEnabled)) {
759
+ shouldAutoSelectAndAdd(selectedTransaction, newTransactionLocalData, selectedCheckBoxTransactionIds, isAccountingClassesEnabled)) {
767
760
  draft.transactionCategorizationView[selectedTab].selectedCheckBoxTransactionIds = [
768
761
  ...selectedCheckBoxTransactionIds,
769
762
  transactionId,
@@ -780,7 +773,7 @@ const expenseAutomationTransactionsView = createSlice({
780
773
  }
781
774
  },
782
775
  updateSelectedVendorForTransaction(draft, action) {
783
- const { transactionId, lineId, updatedVendor, selectedTransaction, selectedTab, uncategorizedAccounts, isAccountingClassesEnabled, isUncategorizedExpenseCategoryEnabled, } = action.payload;
776
+ const { transactionId, lineId, updatedVendor, selectedTransaction, selectedTab, isAccountingClassesEnabled, } = action.payload;
784
777
  const transactionLocalData = recordGet(draft.transactionCategorizationView[selectedTab]
785
778
  .transactionReviewLocalDataById, transactionId, undefined);
786
779
  if (transactionLocalData != null) {
@@ -837,7 +830,7 @@ const expenseAutomationTransactionsView = createSlice({
837
830
  .selectedCheckBoxTransactionIds;
838
831
  if (newTransactionLocalData.vendor != null &&
839
832
  newTransactionLocalData.vendor.id != null &&
840
- shouldAutoSelectAndAdd(selectedTransaction, newTransactionLocalData, selectedCheckBoxTransactionIds, uncategorizedAccounts, isUncategorizedExpenseCategoryEnabled, isAccountingClassesEnabled)) {
833
+ shouldAutoSelectAndAdd(selectedTransaction, newTransactionLocalData, selectedCheckBoxTransactionIds, isAccountingClassesEnabled)) {
841
834
  draft.transactionCategorizationView[selectedTab].selectedCheckBoxTransactionIds = [
842
835
  ...selectedCheckBoxTransactionIds,
843
836
  transactionId,
@@ -885,12 +878,11 @@ const expenseAutomationTransactionsView = createSlice({
885
878
  error: undefined,
886
879
  };
887
880
  },
888
- prepare(selectedTab, transactionsInReview, isUncategorizedExpenseCategoryEnabled) {
881
+ prepare(selectedTab, transactionsInReview) {
889
882
  return {
890
883
  payload: {
891
884
  selectedTab,
892
885
  transactionsInReview,
893
- isUncategorizedExpenseCategoryEnabled,
894
886
  },
895
887
  };
896
888
  },
@@ -1000,19 +992,17 @@ const expenseAutomationTransactionsView = createSlice({
1000
992
  },
1001
993
  },
1002
994
  syncTransactionCategorizationFromDetailSave: {
1003
- prepare(transactionId, updatedTransaction, pendingReviewLineIds, uncategorizedIncomeExpense, isUncategorizedExpenseCategoryEnabled) {
995
+ prepare(transactionId, updatedTransaction, pendingReviewLineIds) {
1004
996
  return {
1005
997
  payload: {
1006
998
  transactionId,
1007
999
  updatedTransaction,
1008
1000
  pendingReviewLineIds,
1009
- uncategorizedIncomeExpense,
1010
- isUncategorizedExpenseCategoryEnabled,
1011
1001
  },
1012
1002
  };
1013
1003
  },
1014
1004
  reducer(draft, action) {
1015
- const { transactionId, updatedTransaction, pendingReviewLineIds, uncategorizedIncomeExpense, isUncategorizedExpenseCategoryEnabled, } = action.payload;
1005
+ const { transactionId, updatedTransaction, pendingReviewLineIds, } = action.payload;
1016
1006
  const txnId = transactionId.id;
1017
1007
  const reviewTabView = draft.transactionCategorizationView.review;
1018
1008
  const autoTabView = draft.transactionCategorizationView.autoCategorized;
@@ -1034,7 +1024,7 @@ const expenseAutomationTransactionsView = createSlice({
1034
1024
  }
1035
1025
  // Partially reviewed: rebuild review tab local data with pending review lines
1036
1026
  if (existsInReview) {
1037
- const newLocalData = toTransactionDetailLocalData(updatedTransaction, 'review', pendingReviewLineIds, uncategorizedIncomeExpense, isUncategorizedExpenseCategoryEnabled);
1027
+ const newLocalData = toTransactionDetailLocalData(updatedTransaction, 'review', pendingReviewLineIds);
1038
1028
  reviewTabView.transactionReviewLocalDataById[txnId] = {
1039
1029
  transactionId: txnId,
1040
1030
  transactionReviewLocalData: {
@@ -1053,11 +1043,10 @@ const expenseAutomationTransactionsView = createSlice({
1053
1043
  },
1054
1044
  },
1055
1045
  backgroundRefetchReviewTab: {
1056
- prepare(period, isUncategorizedExpenseCategoryEnabled) {
1046
+ prepare(period) {
1057
1047
  return {
1058
1048
  payload: {
1059
1049
  period,
1060
- isUncategorizedExpenseCategoryEnabled,
1061
1050
  },
1062
1051
  };
1063
1052
  },
@@ -10,7 +10,7 @@ import { getActualTransactionID } from '../transactionDetail/transactionDetailSt
10
10
  import { getRecommendationByLineIdWithCOTFromRecommendationPayloadWithCOT, getRecommendationFromRecommendationPayload, } from './recommendationHelper';
11
11
  import { fetchEntityRecommendationsByTransactionId, updateRecommendationForEntity, updateRecommendationForEntityFailure, } from './recommendationReducer';
12
12
  export const fetchEntityRecommendationsByTransactionIdEpic = (actions$, state$, zeniAPI) => actions$.pipe(filter(fetchEntityRecommendationsByTransactionId.match), switchMap((action) => {
13
- const { entity, amount, transactionDetails, isBulkFetch, isTransactionCategorizationFlow, isFetchCOT, isUncategorizedExpenseCategoryEnabled, } = action.payload;
13
+ const { entity, amount, transactionDetails, isBulkFetch, isTransactionCategorizationFlow, isFetchCOT, } = action.payload;
14
14
  const { expenseAutomationTransactionsViewState: { selectedTransactionCategorizationTab, }, } = state$.value;
15
15
  const query = {
16
16
  amount,
@@ -52,7 +52,7 @@ export const fetchEntityRecommendationsByTransactionIdEpic = (actions$, state$,
52
52
  if (recommendationWithCOTByLineId != null) {
53
53
  const uncategorizedIncomeExpense = getUncategorizedAccounts(state$.value.accountState, state$.value.accountListState, 'accountList');
54
54
  if (isTransactionCategorizationFlow === true) {
55
- actions.push(setEntityRecommendationForLineIdsForCategorization(selectedTransactionCategorizationTab, transactionId, transactionWithCOT, lineIds, entity, amount, recommendationWithCOTByLineId, uncategorizedIncomeExpense, isUncategorizedExpenseCategoryEnabled, getIsAccountingClassesEnabled(state$.value)));
55
+ actions.push(setEntityRecommendationForLineIdsForCategorization(selectedTransactionCategorizationTab, transactionId, transactionWithCOT, lineIds, entity, amount, recommendationWithCOTByLineId, uncategorizedIncomeExpense, getIsAccountingClassesEnabled(state$.value)));
56
56
  }
57
57
  else {
58
58
  actions.push(setEntityRecommendationForLineIdsForTransactionDetail(transactionId, transactionWithCOT, lineIds, entity, amount, recommendationWithCOTByLineId, uncategorizedIncomeExpense));
@@ -37,7 +37,7 @@ const toGroupKey = (flowType, transactionId, lineIds) => `${flowType}${SEPARATOR
37
37
  export const fetchEntityRecommendationsForLineUpdateEpic = (actions$, state$, zeniAPI) => actions$.pipe(filter(fetchEntityRecommendationsForLineUpdate.match), groupBy((action) => toGroupKey(action.payload.flowType, action.payload.transactionDetails.transactionId.id, action.payload.transactionDetails.lineIds), undefined, (group$) => group$.pipe(debounce(() => timer(GROUP_INACTIVITY_TIMEOUT_MS)))), mergeMap((group$) => group$.pipe(switchMap((action) => {
38
38
  const payload = action.payload;
39
39
  const { transactionId, transaction, lineIds } = payload.transactionDetails;
40
- const { entity, amount, isFetchCOT, isUncategorizedExpenseCategoryEnabled, } = payload;
40
+ const { entity, amount, isFetchCOT, } = payload;
41
41
  // The vendor "Update all lines" popup on the detail page watches
42
42
  // its own transaction-level fetch status, separate from the
43
43
  // per-line statuses below — surface completion/failure there too
@@ -84,7 +84,7 @@ export const fetchEntityRecommendationsForLineUpdateEpic = (actions$, state$, ze
84
84
  : getTransactionWithCOT(transaction);
85
85
  const uncategorizedIncomeExpense = getUncategorizedAccounts(state$.value.accountState, state$.value.accountListState, 'accountList');
86
86
  if (payload.flowType === 'categorization') {
87
- return of(setEntityRecommendationForLineIdsForCategorization(payload.selectedTab, transactionId, transactionWithCOT, lineIds, entity, amount, recommendationWithCOTByLineId, uncategorizedIncomeExpense, isUncategorizedExpenseCategoryEnabled, getIsAccountingClassesEnabled(state$.value)));
87
+ return of(setEntityRecommendationForLineIdsForCategorization(payload.selectedTab, transactionId, transactionWithCOT, lineIds, entity, amount, recommendationWithCOTByLineId, uncategorizedIncomeExpense, getIsAccountingClassesEnabled(state$.value)));
88
88
  }
89
89
  return emitWithBulkStatus(setEntityRecommendationForLineIdsForTransactionDetail(transactionId, transactionWithCOT, lineIds, entity, amount, recommendationWithCOTByLineId, uncategorizedIncomeExpense), isBulkDetailFetch
90
90
  ? updateVendorBulkRecommendationsFetchStatus(transactionId, 'Completed')
@@ -19,7 +19,7 @@ const recommendation = createSlice({
19
19
  };
20
20
  }
21
21
  },
22
- prepare(entity, amount, transactionDetails, isBulkFetch, isTransactionCategorizationFlow = false, isFetchCOT = false, isUncategorizedExpenseCategoryEnabled) {
22
+ prepare(entity, amount, transactionDetails, isBulkFetch, isTransactionCategorizationFlow = false, isFetchCOT = false) {
23
23
  return {
24
24
  payload: {
25
25
  entity,
@@ -28,7 +28,6 @@ const recommendation = createSlice({
28
28
  isBulkFetch,
29
29
  isTransactionCategorizationFlow,
30
30
  isFetchCOT,
31
- isUncategorizedExpenseCategoryEnabled,
32
31
  },
33
32
  };
34
33
  },
@@ -134,9 +133,25 @@ const recommendation = createSlice({
134
133
  clearRecommendation(draft) {
135
134
  Object.assign(draft, initialState);
136
135
  },
136
+ // Fire-and-forget human feedback on a recommendation (accept, ranked
137
+ // override or outside override). Handled by submitRecommendationFeedbackEpic
138
+ // which POSTs to the accounting recommendation-feedback endpoint. No state
139
+ // mutation: the categorization UX must not depend on the feedback round-trip.
140
+ submitRecommendationFeedback: {
141
+ reducer(
142
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
143
+ _draft,
144
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
145
+ _action) {
146
+ // do nothing
147
+ },
148
+ prepare(args) {
149
+ return { payload: args };
150
+ },
151
+ },
137
152
  },
138
153
  });
139
- export const { fetchEntityRecommendationsByTransactionId, fetchEntityRecommendationsForLineUpdate, fetchRecommendationByEntityName, fetchRecommendationByEntityId, updateRecommendationForEntity, updateRecommendationForEntityFailure, indicateReadyToInitializeRecommendation, clearRecommendation, } = recommendation.actions;
154
+ export const { fetchEntityRecommendationsByTransactionId, fetchEntityRecommendationsForLineUpdate, fetchRecommendationByEntityName, fetchRecommendationByEntityId, updateRecommendationForEntity, updateRecommendationForEntityFailure, indicateReadyToInitializeRecommendation, clearRecommendation, submitRecommendationFeedback, } = recommendation.actions;
140
155
  export default recommendation.reducer;
141
156
  const doUpdateRecommendationForEntity = (draft, entityId, entityType, payload) => {
142
157
  const key = getEntityRecommendationKey(entityType, entityId);
@@ -0,0 +1,51 @@
1
+ import { EMPTY } from 'rxjs';
2
+ import { catchError, filter, mergeMap } from 'rxjs/operators';
3
+ import { isSuccessStatus } from '../../responsePayload';
4
+ import { submitRecommendationFeedback } from './recommendationReducer';
5
+ const toRequestBody = (payload) => ({
6
+ line_id: payload.lineId ?? null,
7
+ dimension: payload.dimension,
8
+ recommendation_set_id: payload.recommendationSetId ?? null,
9
+ top_pick_source: payload.topPickSource ?? null,
10
+ shown_recommendations: (payload.shownRecommendations ?? []).map((recommendation) => ({
11
+ source: recommendation.source,
12
+ rank: recommendation.rank,
13
+ value: {
14
+ name: recommendation.value.name,
15
+ id: recommendation.value.id ?? null,
16
+ integration_id: recommendation.value.integrationId ?? null,
17
+ },
18
+ confidence: recommendation.confidence ?? null,
19
+ reasoning: recommendation.reasoning ?? null,
20
+ cot: recommendation.cot ?? [],
21
+ })),
22
+ selected: {
23
+ name: payload.selected.name,
24
+ id: payload.selected.id ?? null,
25
+ integration_id: payload.selected.integrationId ?? null,
26
+ is_new_entity: payload.selected.isNewEntity ?? false,
27
+ },
28
+ matched_source: payload.matchedSource ?? null,
29
+ matched_rank: payload.matchedRank ?? null,
30
+ outcome: payload.outcome,
31
+ reason_text: payload.reasonText ?? null,
32
+ skipped: payload.skipped ?? false,
33
+ features: payload.features ?? null,
34
+ });
35
+ // Fire-and-forget: the epic never re-emits an action. Feedback delivery is a
36
+ // side-effect that must not disrupt the categorization UX, so both the
37
+ // success and error paths complete silently (errors are logged for debugging).
38
+ export const submitRecommendationFeedbackEpic = (actions$, _state$, zeniAPI) => actions$.pipe(filter(submitRecommendationFeedback.match), mergeMap((action) => {
39
+ const { transactionId } = action.payload;
40
+ return zeniAPI
41
+ .postAndGetJSON(`${zeniAPI.apiEndPoints.accountMicroServiceBaseUrl}/1.0/transactions/${transactionId}/recommendation-feedback`, toRequestBody(action.payload))
42
+ .pipe(mergeMap((response) => {
43
+ if (!isSuccessStatus(response)) {
44
+ console.error('submitRecommendationFeedbackEpic: recommendation feedback was not recorded', response.status);
45
+ }
46
+ return EMPTY;
47
+ }), catchError((error) => {
48
+ console.error('submitRecommendationFeedbackEpic: failed to submit recommendation feedback', error);
49
+ return EMPTY;
50
+ }));
51
+ }));