@zeniai/client-epic-state 5.0.53-betaML4 → 5.0.54

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 (29) hide show
  1. package/lib/entity/tenant/epic/deleteConnectionEpic.d.ts +1 -1
  2. package/lib/entity/tenant/epic/saveAPIKeyConnectionEpic.d.ts +1 -1
  3. package/lib/entity/tenant/epic/saveConnectorCredentialsEpic.d.ts +21 -0
  4. package/lib/entity/tenant/epic/saveConnectorCredentialsEpic.js +42 -0
  5. package/lib/entity/tenant/epic/saveOAuthConnectionEpic.d.ts +1 -1
  6. package/lib/entity/tenant/tenantReducer.d.ts +64 -16
  7. package/lib/entity/tenant/tenantReducer.js +86 -7
  8. package/lib/entity/tenant/tenantState.d.ts +3 -1
  9. package/lib/epic.js +2 -2
  10. package/lib/esm/entity/tenant/epic/saveConnectorCredentialsEpic.js +38 -0
  11. package/lib/esm/entity/tenant/tenantReducer.js +84 -5
  12. package/lib/esm/epic.js +2 -2
  13. package/lib/esm/index.js +4 -4
  14. package/lib/esm/view/expenseAutomationView/reducers/transactionsViewReducer.js +27 -114
  15. package/lib/esm/view/expenseAutomationView/selectors/transactionCategorizationSelector.js +0 -6
  16. package/lib/esm/view/recommendation/recommendationReducer.js +1 -52
  17. package/lib/index.d.ts +5 -4
  18. package/lib/index.js +32 -33
  19. package/lib/tsconfig.typecheck.tsbuildinfo +1 -1
  20. package/lib/view/expenseAutomationView/reducers/transactionsViewReducer.js +27 -114
  21. package/lib/view/expenseAutomationView/selectorTypes/transactionsViewSelectorTypes.d.ts +0 -1
  22. package/lib/view/expenseAutomationView/selectors/transactionCategorizationSelector.js +0 -6
  23. package/lib/view/expenseAutomationView/types/transactionsViewState.d.ts +0 -1
  24. package/lib/view/recommendation/recommendationReducer.d.ts +1 -21
  25. package/lib/view/recommendation/recommendationReducer.js +2 -53
  26. package/package.json +1 -1
  27. package/lib/esm/view/recommendation/fetchEntityRecommendationsForCategorizationEpic.js +0 -83
  28. package/lib/view/recommendation/fetchEntityRecommendationsForCategorizationEpic.d.ts +0 -8
  29. package/lib/view/recommendation/fetchEntityRecommendationsForCategorizationEpic.js +0 -87
@@ -8,7 +8,6 @@ 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");
12
11
  const timePeriod_1 = require("../../../commonStateTypes/timePeriod");
13
12
  const vendorTransaction_1 = require("../../../entity/transaction/stateTypes/vendorTransaction");
14
13
  const transactionCategorizationLocalDataHelper_1 = require("../helpers/transactionCategorizationLocalDataHelper");
@@ -404,53 +403,10 @@ const expenseAutomationTransactionsView = (0, toolkit_1.createSlice)({
404
403
  const transactionLocalData = (0, get_1.default)(draft.transactionCategorizationView[selectedTab]
405
404
  .transactionReviewLocalDataById, transactionId.id, undefined);
406
405
  if (transactionLocalData != null) {
407
- // Snapshot the pre-fetch selection. setEntityRecommendationForLineIdInLocalData
408
- // mutates selectedCheckBoxTransactionIds internally (auto-add when
409
- // all-filled, auto-remove otherwise) but does not enforce
410
- // MAX_SELECTION_LIMIT. We restore from this snapshot below and let
411
- // the reducer-level gate own the selection contract so the cap is
412
- // honored and the gate mirrors the original auto-add gate from
413
- // updateSelectedVendor/CustomerForTransaction.
414
- const selectionBeforeFetch = [
415
- ...draft.transactionCategorizationView[selectedTab]
416
- .selectedCheckBoxTransactionIds,
417
- ];
418
- const newLocalData = (0, transactionCategorizationLocalDataHelper_1.setEntityRecommendationForLineIdInLocalData)(draft.transactionCategorizationView[selectedTab], transaction, transactionLocalData.transactionReviewLocalData, entity, lineIds, uncategorizedAccounts, action.payload.recommendationWithCOTByLineId, isUncategorizedExpenseCategoryEnabled, isAccountingClassesEnabled);
419
- // Flip the per-line "fetching" flag to Completed for the lines this
420
- // fetch covered. Reads as the success arm of the
421
- // fetchEntityRecommendationsForCategorization → epic → reducer cycle.
422
- lineIds.forEach((lineId) => {
423
- const lineItem = newLocalData.lineItemById[lineId];
424
- if (lineItem != null) {
425
- newLocalData.lineItemById[lineId] = {
426
- ...lineItem,
427
- categoryClassRecommendationsFetchState: {
428
- fetchState: 'Completed',
429
- error: undefined,
430
- },
431
- };
432
- }
433
- });
434
406
  draft.transactionCategorizationView[selectedTab].transactionReviewLocalDataById[transaction.id] = {
435
407
  transactionId: transaction.id,
436
- transactionReviewLocalData: newLocalData,
408
+ transactionReviewLocalData: (0, transactionCategorizationLocalDataHelper_1.setEntityRecommendationForLineIdInLocalData)(draft.transactionCategorizationView[selectedTab], transaction, transactionLocalData.transactionReviewLocalData, entity, lineIds, uncategorizedAccounts, action.payload.recommendationWithCOTByLineId, isUncategorizedExpenseCategoryEnabled, isAccountingClassesEnabled),
437
409
  };
438
- // Auto-add the transaction to the bulk-save selection now that
439
- // fresh recs have landed and all required fields are filled. This
440
- // is the success-arm of the deferral noted in
441
- // updateSelectedVendor/CustomerForTransaction: their auto-add was
442
- // suppressed because pre-fetch category/class were stale; we run
443
- // the same gate here against newLocalData so the checkbox flips
444
- // on against fresh recs.
445
- draft.transactionCategorizationView[selectedTab].selectedCheckBoxTransactionIds = selectionBeforeFetch;
446
- if ((0, transactionCategorizationLocalDataHelper_1.checkIfAllLineItemsAreCategoryClassFilled)(transaction, newLocalData, uncategorizedAccounts, isUncategorizedExpenseCategoryEnabled, isAccountingClassesEnabled) === true &&
447
- selectionBeforeFetch.length < transactionCategorizationLocalDataHelper_1.MAX_SELECTION_LIMIT &&
448
- selectionBeforeFetch.includes(transaction.id) === false) {
449
- draft.transactionCategorizationView[selectedTab].selectedCheckBoxTransactionIds = [
450
- ...selectionBeforeFetch,
451
- transaction.id,
452
- ];
453
- }
454
410
  // Mark transaction as dirty (autofill happened)
455
411
  if (!draft.transactionCategorizationView[selectedTab].transactionIdsWithUnsavedData.includes(transaction.id)) {
456
412
  draft.transactionCategorizationView[selectedTab].transactionIdsWithUnsavedData = [
@@ -504,13 +460,7 @@ const expenseAutomationTransactionsView = (0, toolkit_1.createSlice)({
504
460
  action.payload.status;
505
461
  },
506
462
  updateSelectedCustomerForTransaction(draft, action) {
507
- const { transactionId, lineId, updatedCustomer, selectedTransaction, selectedTab,
508
- // uncategorizedAccounts, isAccountingClassesEnabled, and
509
- // isUncategorizedExpenseCategoryEnabled remain in the payload type for
510
- // backward compatibility but are no longer read here — the post-fetch
511
- // success reducer (setEntityRecommendationForLineIdsForCategorization)
512
- // owns the all-fields-filled check that drives auto-selection.
513
- } = action.payload;
463
+ const { transactionId, lineId, updatedCustomer, selectedTransaction, selectedTab, uncategorizedAccounts, isAccountingClassesEnabled, isUncategorizedExpenseCategoryEnabled, } = action.payload;
514
464
  const transactionLocalData = (0, get_1.default)(draft.transactionCategorizationView[selectedTab]
515
465
  .transactionReviewLocalDataById, transactionId, undefined);
516
466
  if (transactionLocalData != null) {
@@ -559,13 +509,18 @@ const expenseAutomationTransactionsView = (0, toolkit_1.createSlice)({
559
509
  transactionReviewLocalData: newTransactionLocalData,
560
510
  transactionId,
561
511
  };
562
- // Auto-selection of the row checkbox is intentionally deferred to
563
- // setEntityRecommendationForLineIdsForCategorization (success path of
564
- // the recommendations fetch). On a payee change the existing
565
- // category/class are stale, so auto-selecting against them would
566
- // either trigger save flows on the wrong data or rely on a later
567
- // recommendations write to fix it. Letting the success reducer do the
568
- // auto-add keeps "checkbox flips on" aligned with "fresh recs are in".
512
+ const selectedCheckBoxTransactionIds = draft.transactionCategorizationView[selectedTab]
513
+ .selectedCheckBoxTransactionIds;
514
+ if (newTransactionLocalData.customer != null &&
515
+ newTransactionLocalData.customer.id != null &&
516
+ (0, transactionCategorizationLocalDataHelper_1.checkIfAllLineItemsAreCategoryClassFilled)(selectedTransaction, newTransactionLocalData, uncategorizedAccounts, isUncategorizedExpenseCategoryEnabled, isAccountingClassesEnabled) === true && // check if all line items are filled
517
+ selectedCheckBoxTransactionIds.length < transactionCategorizationLocalDataHelper_1.MAX_SELECTION_LIMIT &&
518
+ selectedCheckBoxTransactionIds.includes(transactionId) === false) {
519
+ draft.transactionCategorizationView[selectedTab].selectedCheckBoxTransactionIds = [
520
+ ...selectedCheckBoxTransactionIds,
521
+ transactionId,
522
+ ];
523
+ }
569
524
  // Mark transaction as dirty (customer updated)
570
525
  if (!draft.transactionCategorizationView[selectedTab].transactionIdsWithUnsavedData.includes(transactionId)) {
571
526
  draft.transactionCategorizationView[selectedTab].transactionIdsWithUnsavedData = [
@@ -577,11 +532,7 @@ const expenseAutomationTransactionsView = (0, toolkit_1.createSlice)({
577
532
  }
578
533
  },
579
534
  updateSelectedVendorForTransaction(draft, action) {
580
- const { transactionId, lineId, updatedVendor, selectedTransaction, selectedTab,
581
- // See updateSelectedCustomerForTransaction for why
582
- // uncategorizedAccounts, isAccountingClassesEnabled, and
583
- // isUncategorizedExpenseCategoryEnabled are no longer destructured.
584
- } = action.payload;
535
+ const { transactionId, lineId, updatedVendor, selectedTransaction, selectedTab, uncategorizedAccounts, isAccountingClassesEnabled, isUncategorizedExpenseCategoryEnabled, } = action.payload;
585
536
  const transactionLocalData = (0, get_1.default)(draft.transactionCategorizationView[selectedTab]
586
537
  .transactionReviewLocalDataById, transactionId, undefined);
587
538
  if (transactionLocalData != null) {
@@ -634,8 +585,18 @@ const expenseAutomationTransactionsView = (0, toolkit_1.createSlice)({
634
585
  },
635
586
  transactionId,
636
587
  };
637
- // See updateSelectedCustomerForTransaction above for why auto-select
638
- // is deferred to setEntityRecommendationForLineIdsForCategorization.
588
+ const selectedCheckBoxTransactionIds = draft.transactionCategorizationView[selectedTab]
589
+ .selectedCheckBoxTransactionIds;
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
+ }
639
600
  // Mark transaction as dirty (vendor updated)
640
601
  if (!draft.transactionCategorizationView[selectedTab].transactionIdsWithUnsavedData.includes(transactionId)) {
641
602
  draft.transactionCategorizationView[selectedTab].transactionIdsWithUnsavedData = [
@@ -848,54 +809,6 @@ const expenseAutomationTransactionsView = (0, toolkit_1.createSlice)({
848
809
  },
849
810
  },
850
811
  },
851
- extraReducers: (builder) => {
852
- // Start arm of the categorization-only recommendations fetch. Marks every
853
- // affected line's per-line flag as In-Progress. The epic uses
854
- // groupBy(transactionId+lineIds) + switchMap, so concurrent fetches for
855
- // distinct (transaction, lineIds-set) targets do not cancel each other —
856
- // there's no need for a global cleanup sweep here.
857
- builder.addCase(recommendationReducer_1.fetchEntityRecommendationsForCategorization, (draft, action) => {
858
- const { selectedTab, transactionDetails } = action.payload;
859
- const { transactionId, lineIds } = transactionDetails;
860
- const transactionLocalData = (0, get_1.default)(draft.transactionCategorizationView[selectedTab]
861
- .transactionReviewLocalDataById, transactionId.id, undefined);
862
- if (transactionLocalData != null) {
863
- lineIds.forEach((lineId) => {
864
- const lineItem = transactionLocalData.transactionReviewLocalData.lineItemById[lineId];
865
- if (lineItem != null) {
866
- transactionLocalData.transactionReviewLocalData.lineItemById[lineId] = {
867
- ...lineItem,
868
- categoryClassRecommendationsFetchState: {
869
- fetchState: 'In-Progress',
870
- error: undefined,
871
- },
872
- };
873
- }
874
- });
875
- }
876
- });
877
- // Failure arm. Flips the affected lines' per-line flag to Error so the
878
- // skeleton resolves and the queued checkbox re-evaluation drops the entry.
879
- builder.addCase(recommendationReducer_1.setCategoryClassRecommendationsForCategorizationFailure, (draft, action) => {
880
- const { selectedTab, transactionId, lineIds, error } = action.payload;
881
- const transactionLocalData = (0, get_1.default)(draft.transactionCategorizationView[selectedTab]
882
- .transactionReviewLocalDataById, transactionId.id, undefined);
883
- if (transactionLocalData != null) {
884
- lineIds.forEach((lineId) => {
885
- const lineItem = transactionLocalData.transactionReviewLocalData.lineItemById[lineId];
886
- if (lineItem != null) {
887
- transactionLocalData.transactionReviewLocalData.lineItemById[lineId] = {
888
- ...lineItem,
889
- categoryClassRecommendationsFetchState: {
890
- fetchState: 'Error',
891
- error,
892
- },
893
- };
894
- }
895
- });
896
- }
897
- });
898
- },
899
812
  });
900
813
  _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;
901
814
  exports.default = expenseAutomationTransactionsView.reducer;
@@ -19,7 +19,6 @@ export interface ExpenseAutomationTransactionViewSelector extends SelectorView {
19
19
  classList: ClassBase[];
20
20
  completionStatus: CompletionStatusType;
21
21
  fetchStateByTransactionTabs: Record<TransactionsTab, FetchStateAndError>;
22
- hasInFlightCategoryClassRecommendations: boolean;
23
22
  isAccountingProjectsEnabled: boolean;
24
23
  markAsNotMiscategorizedStatus: FetchStateAndError;
25
24
  projectList: ProjectListSelectorView;
@@ -48,11 +48,6 @@ 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'));
56
51
  const transactions = (0, transactionSelector_1.getSupportedTransactionsByIds)(transactionState, transactionIds);
57
52
  const transactionsWithCOT = transactions.map((transaction) => (0, transactionHelper_1.getTransactionWithCOT)(transaction));
58
53
  const monthEndFetchState = monthYearPeriodId != null
@@ -123,6 +118,5 @@ function getExpenseAutomationTransactionView(state) {
123
118
  uploadReceiptStatusById,
124
119
  selectedTransactionId,
125
120
  selectedTransactionLineId,
126
- hasInFlightCategoryClassRecommendations,
127
121
  };
128
122
  }
@@ -46,7 +46,6 @@ export interface TransactionCategorizationLineItemData extends TransactionLineBa
46
46
  transactionId: ID;
47
47
  account?: AccountBase;
48
48
  categorizationStatus?: CategorizationStatusType;
49
- categoryClassRecommendationsFetchState?: FetchStateAndError;
50
49
  class?: ClassBase;
51
50
  customer?: CustomerBase;
52
51
  item?: ProductOrService;
@@ -22,27 +22,7 @@ export declare const fetchEntityRecommendationsByTransactionId: import("@reduxjs
22
22
  isTransactionCategorizationFlow: any;
23
23
  isFetchCOT: any;
24
24
  isUncategorizedExpenseCategoryEnabled: boolean | undefined;
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], {
25
+ }, "recommendation/fetchEntityRecommendationsByTransactionId", never, never>, fetchRecommendationByEntityName: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[entityName: string, entityType: EntityType], {
46
26
  entityName: string;
47
27
  entityType: EntityType;
48
28
  }, "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.setCategoryClassRecommendationsForCategorizationFailure = exports.fetchEntityRecommendationsForCategorization = exports.fetchEntityRecommendationsByTransactionId = exports.initialState = void 0;
4
+ exports.clearRecommendation = exports.indicateReadyToInitializeRecommendation = exports.updateRecommendationForEntityFailure = exports.updateRecommendationForEntity = exports.fetchRecommendationByEntityId = exports.fetchRecommendationByEntityName = 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,63 +110,12 @@ 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
- },
164
113
  clearRecommendation(draft) {
165
114
  Object.assign(draft, exports.initialState);
166
115
  },
167
116
  },
168
117
  });
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;
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;
170
119
  exports.default = recommendation.reducer;
171
120
  const doUpdateRecommendationForEntity = (draft, entityId, entityType, payload) => {
172
121
  const key = (0, recommendationState_1.getEntityRecommendationKey)(entityType, entityId);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zeniai/client-epic-state",
3
- "version": "5.0.53-betaML4",
3
+ "version": "5.0.54",
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",
@@ -1,83 +0,0 @@
1
- import { from, of } from 'rxjs';
2
- import { catchError, filter, groupBy, mergeMap, switchMap } from 'rxjs/operators';
3
- import { getIsAccountingClassesEnabled } from '../../entity/tenant/tenantSelector';
4
- import { getTransactionWithCOT, getTransactionWithCOTFromRecommendations, } from '../../entity/transaction/transactionHelper';
5
- import { createZeniAPIStatus, isSuccessResponse } from '../../responsePayload';
6
- import { setEntityRecommendationForLineIdsForCategorization } from '../../view/expenseAutomationView/reducers/transactionsViewReducer';
7
- import { getUncategorizedAccounts } from '../accountList/accountListSelector';
8
- import { getActualTransactionID } from '../transactionDetail/transactionDetailState';
9
- import { getRecommendationByLineIdWithCOTFromRecommendationPayloadWithCOT, getRecommendationFromRecommendationPayload, } from './recommendationHelper';
10
- import { fetchEntityRecommendationsForCategorization, setCategoryClassRecommendationsForCategorizationFailure, updateRecommendationForEntity, } from './recommendationReducer';
11
- // Categorization-list-only recommendations fetch.
12
- //
13
- // Concurrency model: groupBy on (transactionId.id, sorted lineIds) → switchMap
14
- // inside each group → mergeMap to flatten groups. This means:
15
- // • Same target (same transaction + same lineIds-set) — newer fetch cancels
16
- // the prior in-flight fetch. Avoids the same-line stale-response race.
17
- // • Different targets — run fully in parallel. Avoids the silent
18
- // "stuck spinner" cancellation that the global-switchMap epic suffers
19
- // from when the user moves between rows or JE lines mid-flight.
20
- //
21
- // The transactionsViewReducer's per-line categoryClassRecommendationsFetchState
22
- // flag is set to In-Progress when the action enters this epic (start arm in
23
- // extraReducers), Completed via setEntityRecommendationForLineIdsForCategorization,
24
- // or Error via setCategoryClassRecommendationsForCategorizationFailure.
25
- export const fetchEntityRecommendationsForCategorizationEpic = (actions$, state$, zeniAPI) => actions$.pipe(filter(fetchEntityRecommendationsForCategorization.match), groupBy((action) => {
26
- const { transactionId, lineIds } = action.payload.transactionDetails;
27
- return `${transactionId.id}|${[...lineIds].sort().join('|')}`;
28
- }), mergeMap((group$) => group$.pipe(switchMap((action) => {
29
- const { entity, amount, transactionDetails, isFetchCOT, selectedTab, isUncategorizedExpenseCategoryEnabled, } = action.payload;
30
- const query = {
31
- amount,
32
- transaction_id: getActualTransactionID(transactionDetails.transactionId.id),
33
- transaction_type: transactionDetails.transaction.type,
34
- line_ids: transactionDetails.lineIds,
35
- };
36
- if (isFetchCOT === true) {
37
- query.include_cot = true;
38
- }
39
- const entityQuery = {
40
- entity_name: entity.name,
41
- entity_type: entity.type,
42
- };
43
- const recommendationUrl = entity.qboId === entity.name // entity id null case
44
- ? `${zeniAPI.apiEndPoints.accountMicroServiceBaseUrl}/1.0/recommendations?query=${encodeURIComponent(JSON.stringify(entityQuery))}`
45
- : `${zeniAPI.apiEndPoints.accountMicroServiceBaseUrl}/1.0/${toEntityTypeURL(entity.type)}/${entity.qboId}/recommendations?query=${encodeURIComponent(JSON.stringify(query))}`;
46
- return zeniAPI
47
- .getJSON(recommendationUrl)
48
- .pipe(mergeMap((response) => {
49
- if (isSuccessResponse(response) && response.data != null) {
50
- const actions = [];
51
- const { transactionId, transaction, lineIds } = transactionDetails;
52
- const recommendationWithCOTByLineId = getRecommendationByLineIdWithCOTFromRecommendationPayloadWithCOT(response.data.recommendations, lineIds);
53
- const recommendations = getRecommendationFromRecommendationPayload(response.data.recommendations);
54
- const transactionWithCOT = isFetchCOT === true &&
55
- recommendationWithCOTByLineId != null
56
- ? getTransactionWithCOTFromRecommendations(transaction, recommendationWithCOTByLineId)
57
- : getTransactionWithCOT(transaction);
58
- actions.push(updateRecommendationForEntity({
59
- entityId: entity.id,
60
- entityType: entity.type,
61
- recommendations,
62
- }));
63
- if (recommendationWithCOTByLineId != null) {
64
- const uncategorizedIncomeExpense = getUncategorizedAccounts(state$.value.accountState, state$.value.accountListState, 'accountList');
65
- actions.push(setEntityRecommendationForLineIdsForCategorization(selectedTab, transactionId, transactionWithCOT, lineIds, entity, amount, recommendationWithCOTByLineId, uncategorizedIncomeExpense, isUncategorizedExpenseCategoryEnabled, getIsAccountingClassesEnabled(state$.value)));
66
- }
67
- else {
68
- // Successful HTTP but no per-line recommendations — still
69
- // need to clear the In-Progress flag, otherwise the
70
- // skeleton sticks. Treat as a "no recommendations" success
71
- // by firing the failure action with a benign error so the
72
- // line flag flips out of In-Progress.
73
- actions.push(setCategoryClassRecommendationsForCategorizationFailure(selectedTab, transactionId, lineIds, createZeniAPIStatus('Unexpected Error', 'Recommendations response missing per-line data')));
74
- }
75
- return from(actions);
76
- }
77
- else {
78
- return of(setCategoryClassRecommendationsForCategorizationFailure(selectedTab, transactionDetails.transactionId, transactionDetails.lineIds, response.status));
79
- }
80
- }), catchError((error) => of(setCategoryClassRecommendationsForCategorizationFailure(selectedTab, transactionDetails.transactionId, transactionDetails.lineIds, createZeniAPIStatus('Unexpected Error', 'Fetch recommendations for categorization REST API call errored out' +
81
- JSON.stringify(error))))));
82
- }))));
83
- const toEntityTypeURL = (entityType) => `${entityType}s`;
@@ -1,8 +0,0 @@
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>;
@@ -1,87 +0,0 @@
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`;