@zeniai/client-epic-state 5.0.50-betaAS3 → 5.0.50-betaML1
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.js +2 -1
- package/lib/esm/epic.js +2 -1
- package/lib/esm/index.js +2 -2
- package/lib/esm/view/expenseAutomationView/reducers/transactionsViewReducer.js +87 -27
- package/lib/esm/view/expenseAutomationView/selectors/transactionCategorizationSelector.js +6 -0
- package/lib/esm/view/recommendation/fetchEntityRecommendationsForCategorizationEpic.js +83 -0
- package/lib/esm/view/recommendation/recommendationReducer.js +52 -1
- package/lib/esm/view/spendManagement/zeniAccounts/depositAccountDetail/depositAccountDetailSelector.js +1 -1
- package/lib/esm/view/spendManagement/zeniAccounts/depositAccountDetail/fetchDepositAccountDetailEpic.js +2 -2
- package/lib/index.d.ts +2 -2
- package/lib/index.js +15 -13
- package/lib/tsconfig.typecheck.tsbuildinfo +1 -0
- package/lib/view/expenseAutomationView/reducers/transactionsViewReducer.js +87 -27
- package/lib/view/expenseAutomationView/selectorTypes/transactionsViewSelectorTypes.d.ts +1 -0
- package/lib/view/expenseAutomationView/selectors/transactionCategorizationSelector.js +6 -0
- package/lib/view/expenseAutomationView/types/transactionsViewState.d.ts +1 -0
- package/lib/view/recommendation/fetchEntityRecommendationsForCategorizationEpic.d.ts +8 -0
- package/lib/view/recommendation/fetchEntityRecommendationsForCategorizationEpic.js +87 -0
- package/lib/view/recommendation/recommendationReducer.d.ts +21 -1
- package/lib/view/recommendation/recommendationReducer.js +53 -2
- package/lib/view/spendManagement/zeniAccounts/depositAccountDetail/depositAccountDetailSelector.js +1 -1
- package/lib/view/spendManagement/zeniAccounts/depositAccountDetail/fetchDepositAccountDetailEpic.js +2 -2
- package/package.json +1 -1
|
@@ -8,6 +8,7 @@ exports.uploadTransactionCategorizationReceiptSuccess = exports.updateTransactio
|
|
|
8
8
|
const toolkit_1 = require("@reduxjs/toolkit");
|
|
9
9
|
const get_1 = __importDefault(require("lodash/get"));
|
|
10
10
|
const uniq_1 = __importDefault(require("lodash/uniq"));
|
|
11
|
+
const recommendationReducer_1 = require("../../recommendation/recommendationReducer");
|
|
11
12
|
const timePeriod_1 = require("../../../commonStateTypes/timePeriod");
|
|
12
13
|
const vendorTransaction_1 = require("../../../entity/transaction/stateTypes/vendorTransaction");
|
|
13
14
|
const transactionCategorizationLocalDataHelper_1 = require("../helpers/transactionCategorizationLocalDataHelper");
|
|
@@ -403,9 +404,25 @@ const expenseAutomationTransactionsView = (0, toolkit_1.createSlice)({
|
|
|
403
404
|
const transactionLocalData = (0, get_1.default)(draft.transactionCategorizationView[selectedTab]
|
|
404
405
|
.transactionReviewLocalDataById, transactionId.id, undefined);
|
|
405
406
|
if (transactionLocalData != null) {
|
|
407
|
+
const newLocalData = (0, transactionCategorizationLocalDataHelper_1.setEntityRecommendationForLineIdInLocalData)(draft.transactionCategorizationView[selectedTab], transaction, transactionLocalData.transactionReviewLocalData, entity, lineIds, uncategorizedAccounts, action.payload.recommendationWithCOTByLineId, isUncategorizedExpenseCategoryEnabled, isAccountingClassesEnabled);
|
|
408
|
+
// Flip the per-line "fetching" flag to Completed for the lines this
|
|
409
|
+
// fetch covered. Reads as the success arm of the
|
|
410
|
+
// fetchEntityRecommendationsForCategorization → epic → reducer cycle.
|
|
411
|
+
lineIds.forEach((lineId) => {
|
|
412
|
+
const lineItem = newLocalData.lineItemById[lineId];
|
|
413
|
+
if (lineItem != null) {
|
|
414
|
+
newLocalData.lineItemById[lineId] = {
|
|
415
|
+
...lineItem,
|
|
416
|
+
categoryClassRecommendationsFetchState: {
|
|
417
|
+
fetchState: 'Completed',
|
|
418
|
+
error: undefined,
|
|
419
|
+
},
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
});
|
|
406
423
|
draft.transactionCategorizationView[selectedTab].transactionReviewLocalDataById[transaction.id] = {
|
|
407
424
|
transactionId: transaction.id,
|
|
408
|
-
transactionReviewLocalData:
|
|
425
|
+
transactionReviewLocalData: newLocalData,
|
|
409
426
|
};
|
|
410
427
|
// Mark transaction as dirty (autofill happened)
|
|
411
428
|
if (!draft.transactionCategorizationView[selectedTab].transactionIdsWithUnsavedData.includes(transaction.id)) {
|
|
@@ -460,7 +477,13 @@ const expenseAutomationTransactionsView = (0, toolkit_1.createSlice)({
|
|
|
460
477
|
action.payload.status;
|
|
461
478
|
},
|
|
462
479
|
updateSelectedCustomerForTransaction(draft, action) {
|
|
463
|
-
const { transactionId, lineId, updatedCustomer, selectedTransaction, selectedTab,
|
|
480
|
+
const { transactionId, lineId, updatedCustomer, selectedTransaction, selectedTab,
|
|
481
|
+
// uncategorizedAccounts, isAccountingClassesEnabled, and
|
|
482
|
+
// isUncategorizedExpenseCategoryEnabled remain in the payload type for
|
|
483
|
+
// backward compatibility but are no longer read here — the post-fetch
|
|
484
|
+
// success reducer (setEntityRecommendationForLineIdsForCategorization)
|
|
485
|
+
// owns the all-fields-filled check that drives auto-selection.
|
|
486
|
+
} = action.payload;
|
|
464
487
|
const transactionLocalData = (0, get_1.default)(draft.transactionCategorizationView[selectedTab]
|
|
465
488
|
.transactionReviewLocalDataById, transactionId, undefined);
|
|
466
489
|
if (transactionLocalData != null) {
|
|
@@ -509,18 +532,13 @@ const expenseAutomationTransactionsView = (0, toolkit_1.createSlice)({
|
|
|
509
532
|
transactionReviewLocalData: newTransactionLocalData,
|
|
510
533
|
transactionId,
|
|
511
534
|
};
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
draft.transactionCategorizationView[selectedTab].selectedCheckBoxTransactionIds = [
|
|
520
|
-
...selectedCheckBoxTransactionIds,
|
|
521
|
-
transactionId,
|
|
522
|
-
];
|
|
523
|
-
}
|
|
535
|
+
// Auto-selection of the row checkbox is intentionally deferred to
|
|
536
|
+
// setEntityRecommendationForLineIdsForCategorization (success path of
|
|
537
|
+
// the recommendations fetch). On a payee change the existing
|
|
538
|
+
// category/class are stale, so auto-selecting against them would
|
|
539
|
+
// either trigger save flows on the wrong data or rely on a later
|
|
540
|
+
// recommendations write to fix it. Letting the success reducer do the
|
|
541
|
+
// auto-add keeps "checkbox flips on" aligned with "fresh recs are in".
|
|
524
542
|
// Mark transaction as dirty (customer updated)
|
|
525
543
|
if (!draft.transactionCategorizationView[selectedTab].transactionIdsWithUnsavedData.includes(transactionId)) {
|
|
526
544
|
draft.transactionCategorizationView[selectedTab].transactionIdsWithUnsavedData = [
|
|
@@ -532,7 +550,11 @@ const expenseAutomationTransactionsView = (0, toolkit_1.createSlice)({
|
|
|
532
550
|
}
|
|
533
551
|
},
|
|
534
552
|
updateSelectedVendorForTransaction(draft, action) {
|
|
535
|
-
const { transactionId, lineId, updatedVendor, selectedTransaction, selectedTab,
|
|
553
|
+
const { transactionId, lineId, updatedVendor, selectedTransaction, selectedTab,
|
|
554
|
+
// See updateSelectedCustomerForTransaction for why
|
|
555
|
+
// uncategorizedAccounts, isAccountingClassesEnabled, and
|
|
556
|
+
// isUncategorizedExpenseCategoryEnabled are no longer destructured.
|
|
557
|
+
} = action.payload;
|
|
536
558
|
const transactionLocalData = (0, get_1.default)(draft.transactionCategorizationView[selectedTab]
|
|
537
559
|
.transactionReviewLocalDataById, transactionId, undefined);
|
|
538
560
|
if (transactionLocalData != null) {
|
|
@@ -585,18 +607,8 @@ const expenseAutomationTransactionsView = (0, toolkit_1.createSlice)({
|
|
|
585
607
|
},
|
|
586
608
|
transactionId,
|
|
587
609
|
};
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
if (newTransactionLocalData.vendor != null &&
|
|
591
|
-
newTransactionLocalData.vendor.id != null &&
|
|
592
|
-
(0, transactionCategorizationLocalDataHelper_1.checkIfAllLineItemsAreCategoryClassFilled)(selectedTransaction, newTransactionLocalData, uncategorizedAccounts, isUncategorizedExpenseCategoryEnabled, isAccountingClassesEnabled) === true && // check if all line items are filled
|
|
593
|
-
selectedCheckBoxTransactionIds.length < transactionCategorizationLocalDataHelper_1.MAX_SELECTION_LIMIT &&
|
|
594
|
-
selectedCheckBoxTransactionIds.includes(transactionId) === false) {
|
|
595
|
-
draft.transactionCategorizationView[selectedTab].selectedCheckBoxTransactionIds = [
|
|
596
|
-
...selectedCheckBoxTransactionIds,
|
|
597
|
-
transactionId,
|
|
598
|
-
];
|
|
599
|
-
}
|
|
610
|
+
// See updateSelectedCustomerForTransaction above for why auto-select
|
|
611
|
+
// is deferred to setEntityRecommendationForLineIdsForCategorization.
|
|
600
612
|
// Mark transaction as dirty (vendor updated)
|
|
601
613
|
if (!draft.transactionCategorizationView[selectedTab].transactionIdsWithUnsavedData.includes(transactionId)) {
|
|
602
614
|
draft.transactionCategorizationView[selectedTab].transactionIdsWithUnsavedData = [
|
|
@@ -809,6 +821,54 @@ const expenseAutomationTransactionsView = (0, toolkit_1.createSlice)({
|
|
|
809
821
|
},
|
|
810
822
|
},
|
|
811
823
|
},
|
|
824
|
+
extraReducers: (builder) => {
|
|
825
|
+
// Start arm of the categorization-only recommendations fetch. Marks every
|
|
826
|
+
// affected line's per-line flag as In-Progress. The epic uses
|
|
827
|
+
// groupBy(transactionId+lineIds) + switchMap, so concurrent fetches for
|
|
828
|
+
// distinct (transaction, lineIds-set) targets do not cancel each other —
|
|
829
|
+
// there's no need for a global cleanup sweep here.
|
|
830
|
+
builder.addCase(recommendationReducer_1.fetchEntityRecommendationsForCategorization, (draft, action) => {
|
|
831
|
+
const { selectedTab, transactionDetails } = action.payload;
|
|
832
|
+
const { transactionId, lineIds } = transactionDetails;
|
|
833
|
+
const transactionLocalData = (0, get_1.default)(draft.transactionCategorizationView[selectedTab]
|
|
834
|
+
.transactionReviewLocalDataById, transactionId.id, undefined);
|
|
835
|
+
if (transactionLocalData != null) {
|
|
836
|
+
lineIds.forEach((lineId) => {
|
|
837
|
+
const lineItem = transactionLocalData.transactionReviewLocalData.lineItemById[lineId];
|
|
838
|
+
if (lineItem != null) {
|
|
839
|
+
transactionLocalData.transactionReviewLocalData.lineItemById[lineId] = {
|
|
840
|
+
...lineItem,
|
|
841
|
+
categoryClassRecommendationsFetchState: {
|
|
842
|
+
fetchState: 'In-Progress',
|
|
843
|
+
error: undefined,
|
|
844
|
+
},
|
|
845
|
+
};
|
|
846
|
+
}
|
|
847
|
+
});
|
|
848
|
+
}
|
|
849
|
+
});
|
|
850
|
+
// Failure arm. Flips the affected lines' per-line flag to Error so the
|
|
851
|
+
// skeleton resolves and the queued checkbox re-evaluation drops the entry.
|
|
852
|
+
builder.addCase(recommendationReducer_1.setCategoryClassRecommendationsForCategorizationFailure, (draft, action) => {
|
|
853
|
+
const { selectedTab, transactionId, lineIds, error } = action.payload;
|
|
854
|
+
const transactionLocalData = (0, get_1.default)(draft.transactionCategorizationView[selectedTab]
|
|
855
|
+
.transactionReviewLocalDataById, transactionId.id, undefined);
|
|
856
|
+
if (transactionLocalData != null) {
|
|
857
|
+
lineIds.forEach((lineId) => {
|
|
858
|
+
const lineItem = transactionLocalData.transactionReviewLocalData.lineItemById[lineId];
|
|
859
|
+
if (lineItem != null) {
|
|
860
|
+
transactionLocalData.transactionReviewLocalData.lineItemById[lineId] = {
|
|
861
|
+
...lineItem,
|
|
862
|
+
categoryClassRecommendationsFetchState: {
|
|
863
|
+
fetchState: 'Error',
|
|
864
|
+
error,
|
|
865
|
+
},
|
|
866
|
+
};
|
|
867
|
+
}
|
|
868
|
+
});
|
|
869
|
+
}
|
|
870
|
+
});
|
|
871
|
+
},
|
|
812
872
|
});
|
|
813
873
|
_a = expenseAutomationTransactionsView.actions, exports.fetchTransactionCategorization = _a.fetchTransactionCategorization, exports.updateTransactionCategorizationUIState = _a.updateTransactionCategorizationUIState, exports.initializeTransactionCategorizationViewLocalData = _a.initializeTransactionCategorizationViewLocalData, exports.saveTransactionCategorizationLocalData = _a.saveTransactionCategorizationLocalData, exports.fetchTransactionCategorizationFailure = _a.fetchTransactionCategorizationFailure, exports.saveTransactionCategorization = _a.saveTransactionCategorization, exports.updateTransactionCategorization = _a.updateTransactionCategorization, exports.updateCurrentSelectedTransactionCategorizationTab = _a.updateCurrentSelectedTransactionCategorizationTab, exports.updateTransactionCategorizationSaveStatus = _a.updateTransactionCategorizationSaveStatus, exports.markTransactionAsNotMiscategorized = _a.markTransactionAsNotMiscategorized, exports.updateStatusForTransactionNotMiscategorizedUpdateForCategorization = _a.updateStatusForTransactionNotMiscategorizedUpdateForCategorization, exports.updateSelectedVendorForTransaction = _a.updateSelectedVendorForTransaction, exports.updateSelectedCustomerForTransaction = _a.updateSelectedCustomerForTransaction, exports.setAllItemsToCategoryClassInLocalDataForCategorization = _a.setAllItemsToCategoryClassInLocalDataForCategorization, exports.updateTotalCountForTransactionCategorization = _a.updateTotalCountForTransactionCategorization, exports.fetchTransactionCategorizationSuccess = _a.fetchTransactionCategorizationSuccess, exports.resetOtherTabsFetchState = _a.resetOtherTabsFetchState, exports.fetchTransactionCategorizationView = _a.fetchTransactionCategorizationView, exports.updateSelectedCheckboxTransactionIds = _a.updateSelectedCheckboxTransactionIds, exports.clearExpenseAutomationTransactionsViewPerTabView = _a.clearExpenseAutomationTransactionsViewPerTabView, exports.clearExpenseAutomationTransactionsView = _a.clearExpenseAutomationTransactionsView, exports.setEntityRecommendationForLineIdsForCategorization = _a.setEntityRecommendationForLineIdsForCategorization, exports.updateSelectedTransactionId = _a.updateSelectedTransactionId, exports.syncTransactionCategorizationFromDetailSave = _a.syncTransactionCategorizationFromDetailSave, exports.backgroundRefetchReviewTab = _a.backgroundRefetchReviewTab, exports.updateTransactionCategorizationUploadReceiptState = _a.updateTransactionCategorizationUploadReceiptState, exports.uploadTransactionCategorizationReceiptSuccess = _a.uploadTransactionCategorizationReceiptSuccess;
|
|
814
874
|
exports.default = expenseAutomationTransactionsView.reducer;
|
|
@@ -19,6 +19,7 @@ export interface ExpenseAutomationTransactionViewSelector extends SelectorView {
|
|
|
19
19
|
classList: ClassBase[];
|
|
20
20
|
completionStatus: CompletionStatusType;
|
|
21
21
|
fetchStateByTransactionTabs: Record<TransactionsTab, FetchStateAndError>;
|
|
22
|
+
hasInFlightCategoryClassRecommendations: boolean;
|
|
22
23
|
isAccountingProjectsEnabled: boolean;
|
|
23
24
|
markAsNotMiscategorizedStatus: FetchStateAndError;
|
|
24
25
|
projectList: ProjectListSelectorView;
|
|
@@ -48,6 +48,11 @@ function getExpenseAutomationTransactionView(state) {
|
|
|
48
48
|
return null;
|
|
49
49
|
})
|
|
50
50
|
.filter((transaction) => transaction != null);
|
|
51
|
+
// True if any visible line is currently fetching category/class
|
|
52
|
+
// recommendations because the user changed the payee. Used to disable Save
|
|
53
|
+
// and to defer manual checkbox selection.
|
|
54
|
+
const hasInFlightCategoryClassRecommendations = transactionLocalData.some((transaction) => Object.values(transaction.transactionReviewLocalData.lineItemById).some((lineItem) => lineItem.categoryClassRecommendationsFetchState?.fetchState ===
|
|
55
|
+
'In-Progress'));
|
|
51
56
|
const transactions = (0, transactionSelector_1.getSupportedTransactionsByIds)(transactionState, transactionIds);
|
|
52
57
|
const transactionsWithCOT = transactions.map((transaction) => (0, transactionHelper_1.getTransactionWithCOT)(transaction));
|
|
53
58
|
const monthEndFetchState = monthYearPeriodId != null
|
|
@@ -118,5 +123,6 @@ function getExpenseAutomationTransactionView(state) {
|
|
|
118
123
|
uploadReceiptStatusById,
|
|
119
124
|
selectedTransactionId,
|
|
120
125
|
selectedTransactionLineId,
|
|
126
|
+
hasInFlightCategoryClassRecommendations,
|
|
121
127
|
};
|
|
122
128
|
}
|
|
@@ -46,6 +46,7 @@ export interface TransactionCategorizationLineItemData extends TransactionLineBa
|
|
|
46
46
|
transactionId: ID;
|
|
47
47
|
account?: AccountBase;
|
|
48
48
|
categorizationStatus?: CategorizationStatusType;
|
|
49
|
+
categoryClassRecommendationsFetchState?: FetchStateAndError;
|
|
49
50
|
class?: ClassBase;
|
|
50
51
|
customer?: CustomerBase;
|
|
51
52
|
item?: ProductOrService;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { ActionsObservable, StateObservable } from 'redux-observable';
|
|
2
|
+
import { Observable } from 'rxjs';
|
|
3
|
+
import { RootState } from '../../reducer';
|
|
4
|
+
import { setEntityRecommendationForLineIdsForCategorization } from '../../view/expenseAutomationView/reducers/transactionsViewReducer';
|
|
5
|
+
import { ZeniAPI } from '../../zeniAPI';
|
|
6
|
+
import { fetchEntityRecommendationsForCategorization, setCategoryClassRecommendationsForCategorizationFailure, updateRecommendationForEntity } from './recommendationReducer';
|
|
7
|
+
export type ActionType = ReturnType<typeof updateRecommendationForEntity> | ReturnType<typeof fetchEntityRecommendationsForCategorization> | ReturnType<typeof setCategoryClassRecommendationsForCategorizationFailure> | ReturnType<typeof setEntityRecommendationForLineIdsForCategorization>;
|
|
8
|
+
export declare const fetchEntityRecommendationsForCategorizationEpic: (actions$: ActionsObservable<ActionType>, state$: StateObservable<RootState>, zeniAPI: ZeniAPI) => Observable<ActionType>;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.fetchEntityRecommendationsForCategorizationEpic = void 0;
|
|
4
|
+
const rxjs_1 = require("rxjs");
|
|
5
|
+
const operators_1 = require("rxjs/operators");
|
|
6
|
+
const tenantSelector_1 = require("../../entity/tenant/tenantSelector");
|
|
7
|
+
const transactionHelper_1 = require("../../entity/transaction/transactionHelper");
|
|
8
|
+
const responsePayload_1 = require("../../responsePayload");
|
|
9
|
+
const transactionsViewReducer_1 = require("../../view/expenseAutomationView/reducers/transactionsViewReducer");
|
|
10
|
+
const accountListSelector_1 = require("../accountList/accountListSelector");
|
|
11
|
+
const transactionDetailState_1 = require("../transactionDetail/transactionDetailState");
|
|
12
|
+
const recommendationHelper_1 = require("./recommendationHelper");
|
|
13
|
+
const recommendationReducer_1 = require("./recommendationReducer");
|
|
14
|
+
// Categorization-list-only recommendations fetch.
|
|
15
|
+
//
|
|
16
|
+
// Concurrency model: groupBy on (transactionId.id, sorted lineIds) → switchMap
|
|
17
|
+
// inside each group → mergeMap to flatten groups. This means:
|
|
18
|
+
// • Same target (same transaction + same lineIds-set) — newer fetch cancels
|
|
19
|
+
// the prior in-flight fetch. Avoids the same-line stale-response race.
|
|
20
|
+
// • Different targets — run fully in parallel. Avoids the silent
|
|
21
|
+
// "stuck spinner" cancellation that the global-switchMap epic suffers
|
|
22
|
+
// from when the user moves between rows or JE lines mid-flight.
|
|
23
|
+
//
|
|
24
|
+
// The transactionsViewReducer's per-line categoryClassRecommendationsFetchState
|
|
25
|
+
// flag is set to In-Progress when the action enters this epic (start arm in
|
|
26
|
+
// extraReducers), Completed via setEntityRecommendationForLineIdsForCategorization,
|
|
27
|
+
// or Error via setCategoryClassRecommendationsForCategorizationFailure.
|
|
28
|
+
const fetchEntityRecommendationsForCategorizationEpic = (actions$, state$, zeniAPI) => actions$.pipe((0, operators_1.filter)(recommendationReducer_1.fetchEntityRecommendationsForCategorization.match), (0, operators_1.groupBy)((action) => {
|
|
29
|
+
const { transactionId, lineIds } = action.payload.transactionDetails;
|
|
30
|
+
return `${transactionId.id}|${[...lineIds].sort().join('|')}`;
|
|
31
|
+
}), (0, operators_1.mergeMap)((group$) => group$.pipe((0, operators_1.switchMap)((action) => {
|
|
32
|
+
const { entity, amount, transactionDetails, isFetchCOT, selectedTab, isUncategorizedExpenseCategoryEnabled, } = action.payload;
|
|
33
|
+
const query = {
|
|
34
|
+
amount,
|
|
35
|
+
transaction_id: (0, transactionDetailState_1.getActualTransactionID)(transactionDetails.transactionId.id),
|
|
36
|
+
transaction_type: transactionDetails.transaction.type,
|
|
37
|
+
line_ids: transactionDetails.lineIds,
|
|
38
|
+
};
|
|
39
|
+
if (isFetchCOT === true) {
|
|
40
|
+
query.include_cot = true;
|
|
41
|
+
}
|
|
42
|
+
const entityQuery = {
|
|
43
|
+
entity_name: entity.name,
|
|
44
|
+
entity_type: entity.type,
|
|
45
|
+
};
|
|
46
|
+
const recommendationUrl = entity.qboId === entity.name // entity id null case
|
|
47
|
+
? `${zeniAPI.apiEndPoints.accountMicroServiceBaseUrl}/1.0/recommendations?query=${encodeURIComponent(JSON.stringify(entityQuery))}`
|
|
48
|
+
: `${zeniAPI.apiEndPoints.accountMicroServiceBaseUrl}/1.0/${toEntityTypeURL(entity.type)}/${entity.qboId}/recommendations?query=${encodeURIComponent(JSON.stringify(query))}`;
|
|
49
|
+
return zeniAPI
|
|
50
|
+
.getJSON(recommendationUrl)
|
|
51
|
+
.pipe((0, operators_1.mergeMap)((response) => {
|
|
52
|
+
if ((0, responsePayload_1.isSuccessResponse)(response) && response.data != null) {
|
|
53
|
+
const actions = [];
|
|
54
|
+
const { transactionId, transaction, lineIds } = transactionDetails;
|
|
55
|
+
const recommendationWithCOTByLineId = (0, recommendationHelper_1.getRecommendationByLineIdWithCOTFromRecommendationPayloadWithCOT)(response.data.recommendations, lineIds);
|
|
56
|
+
const recommendations = (0, recommendationHelper_1.getRecommendationFromRecommendationPayload)(response.data.recommendations);
|
|
57
|
+
const transactionWithCOT = isFetchCOT === true &&
|
|
58
|
+
recommendationWithCOTByLineId != null
|
|
59
|
+
? (0, transactionHelper_1.getTransactionWithCOTFromRecommendations)(transaction, recommendationWithCOTByLineId)
|
|
60
|
+
: (0, transactionHelper_1.getTransactionWithCOT)(transaction);
|
|
61
|
+
actions.push((0, recommendationReducer_1.updateRecommendationForEntity)({
|
|
62
|
+
entityId: entity.id,
|
|
63
|
+
entityType: entity.type,
|
|
64
|
+
recommendations,
|
|
65
|
+
}));
|
|
66
|
+
if (recommendationWithCOTByLineId != null) {
|
|
67
|
+
const uncategorizedIncomeExpense = (0, accountListSelector_1.getUncategorizedAccounts)(state$.value.accountState, state$.value.accountListState, 'accountList');
|
|
68
|
+
actions.push((0, transactionsViewReducer_1.setEntityRecommendationForLineIdsForCategorization)(selectedTab, transactionId, transactionWithCOT, lineIds, entity, amount, recommendationWithCOTByLineId, uncategorizedIncomeExpense, isUncategorizedExpenseCategoryEnabled, (0, tenantSelector_1.getIsAccountingClassesEnabled)(state$.value)));
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
// Successful HTTP but no per-line recommendations — still
|
|
72
|
+
// need to clear the In-Progress flag, otherwise the
|
|
73
|
+
// skeleton sticks. Treat as a "no recommendations" success
|
|
74
|
+
// by firing the failure action with a benign error so the
|
|
75
|
+
// line flag flips out of In-Progress.
|
|
76
|
+
actions.push((0, recommendationReducer_1.setCategoryClassRecommendationsForCategorizationFailure)(selectedTab, transactionId, lineIds, (0, responsePayload_1.createZeniAPIStatus)('Unexpected Error', 'Recommendations response missing per-line data')));
|
|
77
|
+
}
|
|
78
|
+
return (0, rxjs_1.from)(actions);
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
return (0, rxjs_1.of)((0, recommendationReducer_1.setCategoryClassRecommendationsForCategorizationFailure)(selectedTab, transactionDetails.transactionId, transactionDetails.lineIds, response.status));
|
|
82
|
+
}
|
|
83
|
+
}), (0, operators_1.catchError)((error) => (0, rxjs_1.of)((0, recommendationReducer_1.setCategoryClassRecommendationsForCategorizationFailure)(selectedTab, transactionDetails.transactionId, transactionDetails.lineIds, (0, responsePayload_1.createZeniAPIStatus)('Unexpected Error', 'Fetch recommendations for categorization REST API call errored out' +
|
|
84
|
+
JSON.stringify(error))))));
|
|
85
|
+
}))));
|
|
86
|
+
exports.fetchEntityRecommendationsForCategorizationEpic = fetchEntityRecommendationsForCategorizationEpic;
|
|
87
|
+
const toEntityTypeURL = (entityType) => `${entityType}s`;
|
|
@@ -22,7 +22,27 @@ export declare const fetchEntityRecommendationsByTransactionId: import("@reduxjs
|
|
|
22
22
|
isTransactionCategorizationFlow: any;
|
|
23
23
|
isFetchCOT: any;
|
|
24
24
|
isUncategorizedExpenseCategoryEnabled: boolean | undefined;
|
|
25
|
-
}, "recommendation/fetchEntityRecommendationsByTransactionId", never, never>,
|
|
25
|
+
}, "recommendation/fetchEntityRecommendationsByTransactionId", never, never>, fetchEntityRecommendationsForCategorization: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[entity: Entity, amount: number, transactionDetails: {
|
|
26
|
+
lineIds: ID[];
|
|
27
|
+
transaction: SupportedTransaction;
|
|
28
|
+
transactionId: TransactionID;
|
|
29
|
+
}, selectedTab: "review" | "autoCategorized", isFetchCOT?: any, isUncategorizedExpenseCategoryEnabled?: boolean | undefined], {
|
|
30
|
+
entity: Entity;
|
|
31
|
+
amount: number;
|
|
32
|
+
transactionDetails: {
|
|
33
|
+
lineIds: ID[];
|
|
34
|
+
transaction: SupportedTransaction;
|
|
35
|
+
transactionId: TransactionID;
|
|
36
|
+
};
|
|
37
|
+
selectedTab: "review" | "autoCategorized";
|
|
38
|
+
isFetchCOT: any;
|
|
39
|
+
isUncategorizedExpenseCategoryEnabled: boolean | undefined;
|
|
40
|
+
}, "recommendation/fetchEntityRecommendationsForCategorization", never, never>, setCategoryClassRecommendationsForCategorizationFailure: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[selectedTab: "review" | "autoCategorized", transactionId: TransactionID, lineIds: string[], error: ZeniAPIStatus<Record<string, unknown>>], {
|
|
41
|
+
selectedTab: "review" | "autoCategorized";
|
|
42
|
+
transactionId: TransactionID;
|
|
43
|
+
lineIds: string[];
|
|
44
|
+
error: ZeniAPIStatus<Record<string, unknown>>;
|
|
45
|
+
}, "recommendation/setCategoryClassRecommendationsForCategorizationFailure", never, never>, fetchRecommendationByEntityName: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[entityName: string, entityType: EntityType], {
|
|
26
46
|
entityName: string;
|
|
27
47
|
entityType: EntityType;
|
|
28
48
|
}, "recommendation/fetchRecommendationByEntityName", never, never>, fetchRecommendationByEntityId: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[entityId: string, entityType: EntityType], {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
var _a;
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
exports.clearRecommendation = exports.indicateReadyToInitializeRecommendation = exports.updateRecommendationForEntityFailure = exports.updateRecommendationForEntity = exports.fetchRecommendationByEntityId = exports.fetchRecommendationByEntityName = exports.fetchEntityRecommendationsByTransactionId = exports.initialState = void 0;
|
|
4
|
+
exports.clearRecommendation = exports.indicateReadyToInitializeRecommendation = exports.updateRecommendationForEntityFailure = exports.updateRecommendationForEntity = exports.fetchRecommendationByEntityId = exports.fetchRecommendationByEntityName = exports.setCategoryClassRecommendationsForCategorizationFailure = exports.fetchEntityRecommendationsForCategorization = exports.fetchEntityRecommendationsByTransactionId = exports.initialState = void 0;
|
|
5
5
|
const toolkit_1 = require("@reduxjs/toolkit");
|
|
6
6
|
const recommendationState_1 = require("./recommendationState");
|
|
7
7
|
exports.initialState = {
|
|
@@ -110,12 +110,63 @@ const recommendation = (0, toolkit_1.createSlice)({
|
|
|
110
110
|
};
|
|
111
111
|
},
|
|
112
112
|
},
|
|
113
|
+
// Categorization-list-only fetch action. Mirrors the
|
|
114
|
+
// fetchEntityRecommendationsByTransactionId payload but is observed by a
|
|
115
|
+
// separate epic (with groupBy + switchMap, keyed on
|
|
116
|
+
// transactionId+lineIds-set) so concurrent fetches for distinct targets do
|
|
117
|
+
// not cancel each other. Reducer hooks in transactionsViewReducer use this
|
|
118
|
+
// to drive the per-line categoryClassRecommendationsFetchState flag.
|
|
119
|
+
fetchEntityRecommendationsForCategorization: {
|
|
120
|
+
reducer(draft, action) {
|
|
121
|
+
const { entity } = action.payload;
|
|
122
|
+
const key = (0, recommendationState_1.getEntityRecommendationKey)(entity.type, entity.id);
|
|
123
|
+
const existingRecommendation = draft.recommendationByEntity[key];
|
|
124
|
+
if (existingRecommendation == null) {
|
|
125
|
+
draft.recommendationByEntity[key] = {
|
|
126
|
+
...recommendationState_1.initialEntityRecommendationState,
|
|
127
|
+
fetchState: 'In-Progress',
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
prepare(entity, amount, transactionDetails, selectedTab, isFetchCOT = false, isUncategorizedExpenseCategoryEnabled) {
|
|
132
|
+
return {
|
|
133
|
+
payload: {
|
|
134
|
+
entity,
|
|
135
|
+
amount,
|
|
136
|
+
transactionDetails,
|
|
137
|
+
selectedTab,
|
|
138
|
+
isFetchCOT,
|
|
139
|
+
isUncategorizedExpenseCategoryEnabled,
|
|
140
|
+
},
|
|
141
|
+
};
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
setCategoryClassRecommendationsForCategorizationFailure: {
|
|
145
|
+
reducer(
|
|
146
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
147
|
+
_draft,
|
|
148
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
149
|
+
_action) {
|
|
150
|
+
// The transactionsViewReducer handles the per-line flag flip; this
|
|
151
|
+
// reducer slice has no recommendation cache to update for the failure.
|
|
152
|
+
},
|
|
153
|
+
prepare(selectedTab, transactionId, lineIds, error) {
|
|
154
|
+
return {
|
|
155
|
+
payload: {
|
|
156
|
+
selectedTab,
|
|
157
|
+
transactionId,
|
|
158
|
+
lineIds,
|
|
159
|
+
error,
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
},
|
|
163
|
+
},
|
|
113
164
|
clearRecommendation(draft) {
|
|
114
165
|
Object.assign(draft, exports.initialState);
|
|
115
166
|
},
|
|
116
167
|
},
|
|
117
168
|
});
|
|
118
|
-
_a = recommendation.actions, exports.fetchEntityRecommendationsByTransactionId = _a.fetchEntityRecommendationsByTransactionId, exports.fetchRecommendationByEntityName = _a.fetchRecommendationByEntityName, exports.fetchRecommendationByEntityId = _a.fetchRecommendationByEntityId, exports.updateRecommendationForEntity = _a.updateRecommendationForEntity, exports.updateRecommendationForEntityFailure = _a.updateRecommendationForEntityFailure, exports.indicateReadyToInitializeRecommendation = _a.indicateReadyToInitializeRecommendation, exports.clearRecommendation = _a.clearRecommendation;
|
|
169
|
+
_a = recommendation.actions, exports.fetchEntityRecommendationsByTransactionId = _a.fetchEntityRecommendationsByTransactionId, exports.fetchEntityRecommendationsForCategorization = _a.fetchEntityRecommendationsForCategorization, exports.setCategoryClassRecommendationsForCategorizationFailure = _a.setCategoryClassRecommendationsForCategorizationFailure, exports.fetchRecommendationByEntityName = _a.fetchRecommendationByEntityName, exports.fetchRecommendationByEntityId = _a.fetchRecommendationByEntityId, exports.updateRecommendationForEntity = _a.updateRecommendationForEntity, exports.updateRecommendationForEntityFailure = _a.updateRecommendationForEntityFailure, exports.indicateReadyToInitializeRecommendation = _a.indicateReadyToInitializeRecommendation, exports.clearRecommendation = _a.clearRecommendation;
|
|
119
170
|
exports.default = recommendation.reducer;
|
|
120
171
|
const doUpdateRecommendationForEntity = (draft, entityId, entityType, payload) => {
|
|
121
172
|
const key = (0, recommendationState_1.getEntityRecommendationKey)(entityType, entityId);
|
package/lib/view/spendManagement/zeniAccounts/depositAccountDetail/depositAccountDetailSelector.js
CHANGED
|
@@ -34,7 +34,7 @@ const getDepositAccountDetail = (state, depositAccountId) => {
|
|
|
34
34
|
const zeniAccountDetail = zeniAccountDetailState.depositAccountDetailById[depositAccountId];
|
|
35
35
|
const depositAccount = (0, depositAccountSelector_1.getDepositAccountByDepositAccountId)(depositAccountState, depositAccountId);
|
|
36
36
|
const zeniAccountsConfig = (0, zeniAccountsConfigSelector_1.getZeniAccountsConfigDetail)(state);
|
|
37
|
-
const transactionListView = (0, depositAccountTransactionListSelector_1.getDepositAccountTransactionList)(state, depositAccountId);
|
|
37
|
+
const transactionListView = (0, depositAccountTransactionListSelector_1.getDepositAccountTransactionList)(state, depositAccountId, 5);
|
|
38
38
|
let accountUpdateStatus = {
|
|
39
39
|
editName: {
|
|
40
40
|
fetchState: 'Not-Started',
|
package/lib/view/spendManagement/zeniAccounts/depositAccountDetail/fetchDepositAccountDetailEpic.js
CHANGED
|
@@ -19,7 +19,7 @@ const fetchDepositAccountDetailEpic = (actions$, state$) => actions$.pipe((0, op
|
|
|
19
19
|
zeniAccountActions.push((0, paymentAccountListReducer_1.fetchPaymentAccountList)());
|
|
20
20
|
zeniAccountActions.push((0, zeniAccountsConfigReducer_1.fetchZeniAccountsConfig)());
|
|
21
21
|
zeniAccountActions.push((0, depositAccountDetailReducer_1.fetchDepositAccount)(depositAccountId));
|
|
22
|
-
zeniAccountActions.push((0, depositAccountTransactionListReducer_1.fetchDepositAccountTransactionList)(depositAccountId, true));
|
|
22
|
+
zeniAccountActions.push((0, depositAccountTransactionListReducer_1.fetchDepositAccountTransactionList)(depositAccountId, true, 5));
|
|
23
23
|
zeniAccountActions.push((0, depositAccountDetailReducer_1.fetchAccountHistory)(depositAccountId));
|
|
24
24
|
}
|
|
25
25
|
else {
|
|
@@ -40,7 +40,7 @@ const fetchDepositAccountDetailEpic = (actions$, state$) => actions$.pipe((0, op
|
|
|
40
40
|
targetZeniAccountAccount.fetchState !== 'In-Progress')) {
|
|
41
41
|
zeniAccountActions.push((0, depositAccountDetailReducer_1.fetchDepositAccount)(depositAccountId));
|
|
42
42
|
}
|
|
43
|
-
zeniAccountActions.push((0, depositAccountTransactionListReducer_1.fetchDepositAccountTransactionList)(depositAccountId, false));
|
|
43
|
+
zeniAccountActions.push((0, depositAccountTransactionListReducer_1.fetchDepositAccountTransactionList)(depositAccountId, false, 5));
|
|
44
44
|
const zeniAccountsConfig = state.zeniAccountsConfigState;
|
|
45
45
|
if (zeniAccountsConfig.hasValidState() === false &&
|
|
46
46
|
zeniAccountsConfig.fetchState !== 'In-Progress') {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zeniai/client-epic-state",
|
|
3
|
-
"version": "5.0.50-
|
|
3
|
+
"version": "5.0.50-betaML1",
|
|
4
4
|
"description": "Shared module between Web & Mobile containing required abstractions for state management, async network communication. ",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"module": "lib/esm/index.js",
|