@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.
- package/lib/epic.d.ts +2 -1
- package/lib/epic.js +2 -1
- package/lib/esm/epic.js +2 -1
- package/lib/esm/index.js +2 -2
- package/lib/esm/view/commonVendorView/transactionVendorView/epics/saveTransactionVendorEpic.js +0 -1
- package/lib/esm/view/commonVendorView/transactionVendorView/transactionVendorViewReducer.js +1 -2
- package/lib/esm/view/expenseAutomationView/epics/common/fetchAllExpenseAutomationTabsEpic.js +3 -3
- package/lib/esm/view/expenseAutomationView/epics/common/refreshExpenseAutomationCurrentTabEpic.js +2 -3
- package/lib/esm/view/expenseAutomationView/epics/transactionCategorization/backgroundRefetchReviewTabEpic.js +2 -2
- package/lib/esm/view/expenseAutomationView/epics/transactionCategorization/fetchTransactionCategorizationEpic.js +1 -1
- package/lib/esm/view/expenseAutomationView/epics/transactionCategorization/fetchTransactionCategorizationViewEpic.js +4 -4
- package/lib/esm/view/expenseAutomationView/epics/transactionCategorization/initializeTransactionCategorizationLocalDataEpic.js +2 -4
- package/lib/esm/view/expenseAutomationView/epics/transactionCategorization/markTransactionAsNotMiscategorizedEpic.js +2 -2
- package/lib/esm/view/expenseAutomationView/epics/transactionCategorization/saveTransactionCategorizationEpic.js +2 -2
- package/lib/esm/view/expenseAutomationView/epics/transactionCategorization/triggerReviewTabRefetchEpic.js +2 -2
- package/lib/esm/view/expenseAutomationView/epics/transactionCategorization/updateTransactionCategorizationEpic.js +2 -2
- package/lib/esm/view/expenseAutomationView/expenseAutomationViewReducer.js +3 -4
- package/lib/esm/view/expenseAutomationView/helpers/transactionCategorizationLocalDataHelper.js +14 -25
- package/lib/esm/view/expenseAutomationView/reducers/transactionsViewReducer.js +21 -32
- package/lib/esm/view/recommendation/fetchEntityRecommendationsByTransactionIdEpic.js +2 -2
- package/lib/esm/view/recommendation/fetchEntityRecommendationsForLineUpdateEpic.js +2 -2
- package/lib/esm/view/recommendation/recommendationReducer.js +18 -3
- package/lib/esm/view/recommendation/submitRecommendationFeedbackEpic.js +51 -0
- package/lib/esm/view/transactionDetail/epics/saveTransactionDetailEpic.js +2 -2
- package/lib/esm/view/transactionDetail/epics/updateTransactionDetailEpic.js +2 -2
- package/lib/esm/view/transactionDetail/transactionDetailReducer.js +1 -2
- package/lib/index.d.ts +2 -2
- package/lib/index.js +18 -17
- package/lib/view/commonVendorView/transactionVendorView/epics/saveTransactionVendorEpic.js +0 -1
- package/lib/view/commonVendorView/transactionVendorView/transactionVendorViewReducer.d.ts +1 -2
- package/lib/view/commonVendorView/transactionVendorView/transactionVendorViewReducer.js +1 -2
- package/lib/view/expenseAutomationView/epics/common/fetchAllExpenseAutomationTabsEpic.js +3 -3
- package/lib/view/expenseAutomationView/epics/common/refreshExpenseAutomationCurrentTabEpic.js +2 -3
- package/lib/view/expenseAutomationView/epics/transactionCategorization/backgroundRefetchReviewTabEpic.js +2 -2
- package/lib/view/expenseAutomationView/epics/transactionCategorization/fetchTransactionCategorizationEpic.js +1 -1
- package/lib/view/expenseAutomationView/epics/transactionCategorization/fetchTransactionCategorizationViewEpic.js +4 -4
- package/lib/view/expenseAutomationView/epics/transactionCategorization/initializeTransactionCategorizationLocalDataEpic.js +2 -4
- package/lib/view/expenseAutomationView/epics/transactionCategorization/markTransactionAsNotMiscategorizedEpic.js +2 -2
- package/lib/view/expenseAutomationView/epics/transactionCategorization/saveTransactionCategorizationEpic.js +2 -2
- package/lib/view/expenseAutomationView/epics/transactionCategorization/triggerReviewTabRefetchEpic.js +2 -2
- package/lib/view/expenseAutomationView/epics/transactionCategorization/updateTransactionCategorizationEpic.js +2 -2
- package/lib/view/expenseAutomationView/expenseAutomationViewReducer.d.ts +2 -5
- package/lib/view/expenseAutomationView/expenseAutomationViewReducer.js +3 -4
- package/lib/view/expenseAutomationView/helpers/transactionCategorizationLocalDataHelper.d.ts +5 -5
- package/lib/view/expenseAutomationView/helpers/transactionCategorizationLocalDataHelper.js +14 -25
- package/lib/view/expenseAutomationView/reducers/transactionsViewReducer.d.ts +10 -23
- package/lib/view/expenseAutomationView/reducers/transactionsViewReducer.js +21 -32
- package/lib/view/recommendation/fetchEntityRecommendationsByTransactionIdEpic.js +2 -2
- package/lib/view/recommendation/fetchEntityRecommendationsForLineUpdateEpic.js +2 -2
- package/lib/view/recommendation/recommendationPayload.d.ts +33 -0
- package/lib/view/recommendation/recommendationReducer.d.ts +3 -4
- package/lib/view/recommendation/recommendationReducer.js +19 -4
- package/lib/view/recommendation/submitRecommendationFeedbackEpic.d.ts +7 -0
- package/lib/view/recommendation/submitRecommendationFeedbackEpic.js +55 -0
- package/lib/view/transactionDetail/epics/saveTransactionDetailEpic.js +2 -2
- package/lib/view/transactionDetail/epics/updateTransactionDetailEpic.js +2 -2
- package/lib/view/transactionDetail/transactionDetailReducer.d.ts +1 -3
- package/lib/view/transactionDetail/transactionDetailReducer.js +1 -2
- 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,
|
|
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]
|
|
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,
|
|
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
|
|
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,
|
|
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
|
|
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,
|
|
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 }
|
|
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,
|
|
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
|
|
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
|
|
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(
|
|
31
|
+
prepare() {
|
|
33
32
|
return {
|
|
34
|
-
payload: {
|
|
33
|
+
payload: {},
|
|
35
34
|
};
|
|
36
35
|
},
|
|
37
36
|
reducer() {
|
package/lib/esm/view/expenseAutomationView/helpers/transactionCategorizationLocalDataHelper.js
CHANGED
|
@@ -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,
|
|
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,
|
|
76
|
+
return checkIfAllLineItemsAreCategoryClassFilled(transaction, localData, isAccountingClassesEnabled);
|
|
77
77
|
};
|
|
78
|
-
export const toTransactionDetailLocalData = (transaction, selectedTab, lineItemsByTransactionIds
|
|
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,
|
|
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,
|
|
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 (
|
|
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,
|
|
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
|
-
|
|
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,
|
|
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,
|
|
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
|
-
|
|
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,
|
|
558
|
+
if (!checkIfAllLineItemsAreCategoryClassFilled(transaction, newLocalData, isAccountingClassesEnabled)) {
|
|
568
559
|
selectedCheckBoxTransactionIds = selectedCheckBoxTransactionIds.filter((id) => id !== transaction.id);
|
|
569
560
|
}
|
|
570
|
-
else if (shouldAutoSelectAndAdd(transaction, newLocalData, selectedCheckBoxTransactionIds,
|
|
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,
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
|
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
|
|
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,
|
|
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
|
|
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
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
|
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
|
+
}));
|