@zeniai/client-epic-state 5.1.52 → 5.1.53-betaRD2

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 (83) hide show
  1. package/lib/commonStateTypes/amount.d.ts +0 -1
  2. package/lib/commonStateTypes/amount.js +1 -9
  3. package/lib/commonStateTypes/workingDayHelper.d.ts +3 -4
  4. package/lib/commonStateTypes/workingDayHelper.js +3 -4
  5. package/lib/entity/aiCfo/aiCfoPayload.d.ts +1 -0
  6. package/lib/entity/aiCfo/aiCfoReducer.js +3 -1
  7. package/lib/entity/aiCfo/aiCfoState.d.ts +1 -0
  8. package/lib/entity/tenant/clearAllEpic.d.ts +1 -3
  9. package/lib/entity/tenant/clearAllEpic.js +0 -4
  10. package/lib/epic.d.ts +1 -7
  11. package/lib/epic.js +1 -7
  12. package/lib/esm/commonStateTypes/amount.js +0 -7
  13. package/lib/esm/commonStateTypes/workingDayHelper.js +3 -4
  14. package/lib/esm/entity/aiCfo/aiCfoReducer.js +3 -1
  15. package/lib/esm/entity/tenant/clearAllEpic.js +0 -4
  16. package/lib/esm/epic.js +1 -7
  17. package/lib/esm/index.js +0 -5
  18. package/lib/esm/reducer.js +0 -6
  19. package/lib/esm/view/recommendation/fetchEntityRecommendationsForLineUpdateEpic.js +18 -8
  20. package/lib/esm/view/spendManagement/autotransferRules/autoTransferRulesSelector.js +2 -22
  21. package/lib/esm/view/transactionDetail/epics/markTransactionAsNotMiscategorizedEpic.js +89 -39
  22. package/lib/esm/view/transactionDetail/epics/updateTransactionDetailEpic.js +57 -64
  23. package/lib/esm/view/transactionDetail/transactionDetailLocalDataHelper.js +18 -12
  24. package/lib/esm/view/transactionDetail/transactionDetailReducer.js +2 -1
  25. package/lib/index.d.ts +1 -10
  26. package/lib/index.js +1 -20
  27. package/lib/reducer.d.ts +0 -6
  28. package/lib/reducer.js +0 -6
  29. package/lib/view/recommendation/fetchEntityRecommendationsForLineUpdateEpic.d.ts +2 -2
  30. package/lib/view/recommendation/fetchEntityRecommendationsForLineUpdateEpic.js +17 -7
  31. package/lib/view/recommendation/recommendationReducer.d.ts +1 -0
  32. package/lib/view/spendManagement/autotransferRules/autoTransferRulesSelector.d.ts +1 -8
  33. package/lib/view/spendManagement/autotransferRules/autoTransferRulesSelector.js +1 -21
  34. package/lib/view/spendManagement/autotransferRules/autoTransferRulesState.d.ts +0 -22
  35. package/lib/view/transactionDetail/epics/markTransactionAsNotMiscategorizedEpic.js +88 -38
  36. package/lib/view/transactionDetail/epics/updateTransactionDetailEpic.js +55 -62
  37. package/lib/view/transactionDetail/transactionDetailLocalDataHelper.js +18 -12
  38. package/lib/view/transactionDetail/transactionDetailReducer.d.ts +2 -1
  39. package/lib/view/transactionDetail/transactionDetailReducer.js +2 -1
  40. package/lib/zeniAPI.d.ts +0 -1
  41. package/package.json +1 -1
  42. package/lib/esm/view/spendManagement/cashManagement/autoSweepFlow/autoSweepFlowPayload.js +0 -29
  43. package/lib/esm/view/spendManagement/cashManagement/autoSweepFlow/autoSweepFlowReducer.js +0 -78
  44. package/lib/esm/view/spendManagement/cashManagement/autoSweepFlow/autoSweepFlowSelector.js +0 -49
  45. package/lib/esm/view/spendManagement/cashManagement/autoSweepFlow/autoSweepFlowState.js +0 -47
  46. package/lib/esm/view/spendManagement/cashManagement/autoSweepFlow/epics/fetchCashManagementSettingsEpic.js +0 -32
  47. package/lib/esm/view/spendManagement/cashManagement/autoSweepFlow/epics/saveAutoSweepSettingsEpic.js +0 -32
  48. package/lib/esm/view/spendManagement/cashManagement/cashManagementOverview/cashManagementOverviewPayload.js +0 -54
  49. package/lib/esm/view/spendManagement/cashManagement/cashManagementOverview/cashManagementOverviewReducer.js +0 -92
  50. package/lib/esm/view/spendManagement/cashManagement/cashManagementOverview/cashManagementOverviewSelector.js +0 -212
  51. package/lib/esm/view/spendManagement/cashManagement/cashManagementOverview/cashManagementOverviewState.js +0 -10
  52. package/lib/esm/view/spendManagement/cashManagement/cashManagementOverview/epics/fetchCashManagementBannerEpic.js +0 -23
  53. package/lib/esm/view/spendManagement/cashManagement/cashManagementOverview/epics/fetchCashManagementOverviewPageEpic.js +0 -36
  54. package/lib/esm/view/spendManagement/cashManagement/cashManagementOverview/epics/fetchCashManagementRecommendationEpic.js +0 -27
  55. package/lib/esm/view/spendManagement/cashManagement/cashManagementOverview/epics/fetchRecentTransferEpic.js +0 -28
  56. package/lib/view/spendManagement/cashManagement/autoSweepFlow/autoSweepFlowPayload.d.ts +0 -47
  57. package/lib/view/spendManagement/cashManagement/autoSweepFlow/autoSweepFlowPayload.js +0 -34
  58. package/lib/view/spendManagement/cashManagement/autoSweepFlow/autoSweepFlowReducer.d.ts +0 -23
  59. package/lib/view/spendManagement/cashManagement/autoSweepFlow/autoSweepFlowReducer.js +0 -82
  60. package/lib/view/spendManagement/cashManagement/autoSweepFlow/autoSweepFlowSelector.d.ts +0 -40
  61. package/lib/view/spendManagement/cashManagement/autoSweepFlow/autoSweepFlowSelector.js +0 -53
  62. package/lib/view/spendManagement/cashManagement/autoSweepFlow/autoSweepFlowState.d.ts +0 -37
  63. package/lib/view/spendManagement/cashManagement/autoSweepFlow/autoSweepFlowState.js +0 -53
  64. package/lib/view/spendManagement/cashManagement/autoSweepFlow/epics/fetchCashManagementSettingsEpic.d.ts +0 -14
  65. package/lib/view/spendManagement/cashManagement/autoSweepFlow/epics/fetchCashManagementSettingsEpic.js +0 -36
  66. package/lib/view/spendManagement/cashManagement/autoSweepFlow/epics/saveAutoSweepSettingsEpic.d.ts +0 -7
  67. package/lib/view/spendManagement/cashManagement/autoSweepFlow/epics/saveAutoSweepSettingsEpic.js +0 -36
  68. package/lib/view/spendManagement/cashManagement/cashManagementOverview/cashManagementOverviewPayload.d.ts +0 -56
  69. package/lib/view/spendManagement/cashManagement/cashManagementOverview/cashManagementOverviewPayload.js +0 -60
  70. package/lib/view/spendManagement/cashManagement/cashManagementOverview/cashManagementOverviewReducer.d.ts +0 -22
  71. package/lib/view/spendManagement/cashManagement/cashManagementOverview/cashManagementOverviewReducer.js +0 -96
  72. package/lib/view/spendManagement/cashManagement/cashManagementOverview/cashManagementOverviewSelector.d.ts +0 -59
  73. package/lib/view/spendManagement/cashManagement/cashManagementOverview/cashManagementOverviewSelector.js +0 -217
  74. package/lib/view/spendManagement/cashManagement/cashManagementOverview/cashManagementOverviewState.d.ts +0 -122
  75. package/lib/view/spendManagement/cashManagement/cashManagementOverview/cashManagementOverviewState.js +0 -13
  76. package/lib/view/spendManagement/cashManagement/cashManagementOverview/epics/fetchCashManagementBannerEpic.d.ts +0 -7
  77. package/lib/view/spendManagement/cashManagement/cashManagementOverview/epics/fetchCashManagementBannerEpic.js +0 -27
  78. package/lib/view/spendManagement/cashManagement/cashManagementOverview/epics/fetchCashManagementOverviewPageEpic.d.ts +0 -9
  79. package/lib/view/spendManagement/cashManagement/cashManagementOverview/epics/fetchCashManagementOverviewPageEpic.js +0 -40
  80. package/lib/view/spendManagement/cashManagement/cashManagementOverview/epics/fetchCashManagementRecommendationEpic.d.ts +0 -7
  81. package/lib/view/spendManagement/cashManagement/cashManagementOverview/epics/fetchCashManagementRecommendationEpic.js +0 -31
  82. package/lib/view/spendManagement/cashManagement/cashManagementOverview/epics/fetchRecentTransferEpic.d.ts +0 -15
  83. package/lib/view/spendManagement/cashManagement/cashManagementOverview/epics/fetchRecentTransferEpic.js +0 -32
@@ -9,6 +9,7 @@ const rxjs_1 = require("rxjs");
9
9
  const operators_1 = require("rxjs/operators");
10
10
  const recommendationPayload_1 = require("../../../commonPayloadTypes/recommendationPayload");
11
11
  const snackbarReducer_1 = require("../../../entity/snackbar/snackbarReducer");
12
+ const tenantSelector_1 = require("../../../entity/tenant/tenantSelector");
12
13
  const customerTransactionPayload_1 = require("../../../entity/transaction/payloadTypes/customerTransactionPayload");
13
14
  const otherTransactionPayload_1 = require("../../../entity/transaction/payloadTypes/otherTransactionPayload");
14
15
  const transactionIDPayload_1 = require("../../../entity/transaction/payloadTypes/transactionIDPayload");
@@ -25,13 +26,12 @@ const transactionSelector_1 = require("../../../entity/transaction/transactionSe
25
26
  const vendorReducer_1 = require("../../../entity/vendor/vendorReducer");
26
27
  const vendorSelector_1 = require("../../../entity/vendor/vendorSelector");
27
28
  const responsePayload_1 = require("../../../responsePayload");
28
- const tenantSelector_1 = require("../../../entity/tenant/tenantSelector");
29
29
  const accountListSelector_1 = require("../../accountList/accountListSelector");
30
30
  const transactionCategorizationLocalDataHelper_1 = require("../../expenseAutomationView/helpers/transactionCategorizationLocalDataHelper");
31
31
  const transactionsViewReducer_1 = require("../../expenseAutomationView/reducers/transactionsViewReducer");
32
+ const transactionDetailLocalDataHelper_1 = require("../transactionDetailLocalDataHelper");
32
33
  const transactionDetailReducer_1 = require("../transactionDetailReducer");
33
34
  const transactionDetailState_1 = require("../transactionDetailState");
34
- const saveTransactionHelper_1 = require("./saveTransactionHelper");
35
35
  const updateTransactionDetailEpic = (actions$, state$, zeniAPI) => actions$.pipe((0, operators_1.filter)(transactionDetailReducer_1.updateTransactionDetail.match), (0, operators_1.mergeMap)((action) => {
36
36
  const { transactionState, vendorState, transactionDetailState } = state$.value;
37
37
  const isAccountingProjectsEnabled = (0, tenantSelector_1.getIsAccountingProjectsEnabled)(state$.value);
@@ -42,24 +42,6 @@ const updateTransactionDetailEpic = (actions$, state$, zeniAPI) => actions$.pipe
42
42
  if ((0, responsePayload_1.isSuccessResponse)(response) && response.data != null) {
43
43
  const transactionIdResp = (0, transactionIDPayload_1.toTransactionID)(response.data.transaction);
44
44
  const transactionDetailKeyForResponse = (0, transactionDetailState_1.getTransactionDetailKey)(transactionIdResp);
45
- const tranDetail = (0, get_1.default)(transactionDetailState.transactionDetailById, transactionDetailKeyForResponse, undefined);
46
- const tranVendorUpdates = tranDetail?.transactionDetailLocalData.vendorUpdates;
47
- let vendorUpdates = undefined;
48
- if (tranVendorUpdates?.qboId != null) {
49
- const vendor = (0, vendorSelector_1.getVendorByVendorId)(vendorState, tranVendorUpdates.qboId);
50
- if (vendor != null) {
51
- vendorUpdates = {
52
- id: vendor?.qboId,
53
- name: vendor?.name,
54
- };
55
- }
56
- else if (tranVendorUpdates.name != null) {
57
- vendorUpdates = {
58
- id: tranVendorUpdates.qboId,
59
- name: tranVendorUpdates.name,
60
- };
61
- }
62
- }
63
45
  const actions = [];
64
46
  if (response.data.vendors != null) {
65
47
  actions.push((0, vendorReducer_1.updateVendors)(response.data.vendors, 'merge'));
@@ -72,30 +54,14 @@ const updateTransactionDetailEpic = (actions$, state$, zeniAPI) => actions$.pipe
72
54
  vendorId: response.data.vendors?.[0]?.vendor_id,
73
55
  }));
74
56
  actions.push((0, transactionDetailReducer_1.updateTransactionDetailSaveStatus)(transactionIdResp, 'Completed'));
75
- let callMarkTransactionAsNotMiscategorized = true;
76
- // NOTE: If tran is vendorType OR JE OR Deposit transaction and there is no vendor chosen on this tranaction while saving,
77
- // then we need to skip mark-miscategorized-as-correct api call
78
- if ((0, transactionType_1.toVendorTransactionTypeStrict)(response.data.transaction.transaction_type) != null ||
79
- (0, transactionType_1.toJEAndDepositTransactionTypeStrict)(response.data.transaction.transaction_type) != null) {
80
- const isAccountingClassesEnabled = (0, tenantSelector_1.getIsAccountingClassesEnabled)(state$.value);
81
- const isAnyClassMissing = isAccountingClassesEnabled &&
82
- checkIfClassMissingForAnyLineItem(response.data.transaction);
83
- const isVendorOrCustomerMissingInAnyLine = (0, transactionType_1.toJEAndDepositTransactionTypeStrict)(response.data.transaction.transaction_type) != null &&
84
- checkIfVendorOrCustomerMissingForAnyLineItem(response.data.transaction);
85
- if ((vendorUpdates?.id == null &&
86
- isVendorOrCustomerMissingInAnyLine == true) ||
87
- isAnyClassMissing == true) {
88
- callMarkTransactionAsNotMiscategorized = false;
89
- }
90
- }
91
57
  const uncategorizedIncomeExpense = (0, accountListSelector_1.getUncategorizedAccounts)(state$.value.accountState, state$.value.accountListState, 'accountList');
92
58
  const transaction = (0, toSupportedTransaction_1.default)(response.data.transaction);
93
- const isTransactionUnCategorized = (0, saveTransactionHelper_1.isTransactionUncategorized)(transaction, uncategorizedIncomeExpense);
59
+ const isAccountingClassesEnabled = (0, tenantSelector_1.getIsAccountingClassesEnabled)(state$.value);
60
+ const eligibleLineIds = getLineIdsEligibleForReview(transaction, uncategorizedIncomeExpense, isAccountingClassesEnabled, action.payload.isExpenseAutomationEnabled);
94
61
  if (action.payload.transactionSaveUpdates.markAsMiscatUpdates ===
95
62
  true &&
96
- callMarkTransactionAsNotMiscategorized === true &&
97
- isTransactionUnCategorized === false) {
98
- actions.push((0, transactionDetailReducer_1.markTransactionAsNotMiscategorized)(transactionIdResp, action.payload.transactionSaveUpdates, action.payload.isExpenseAutomationEnabled));
63
+ eligibleLineIds.length > 0) {
64
+ actions.push((0, transactionDetailReducer_1.markTransactionAsNotMiscategorized)(transactionIdResp, action.payload.transactionSaveUpdates, action.payload.isExpenseAutomationEnabled, eligibleLineIds));
99
65
  }
100
66
  else {
101
67
  actions.push((0, transactionDetailReducer_1.updateStatusConsolidatedSaveTransactionDetail)({
@@ -307,31 +273,58 @@ const toTransactionUpdatePayload = (updates, detail, cotTransactionTracking, isA
307
273
  return (0, transactionPayload_1.injectCOTFlagsIntoTransactionPayload)(basePayload, transaction);
308
274
  };
309
275
  exports.toTransactionUpdatePayload = toTransactionUpdatePayload;
310
- const checkIfClassMissingForAnyLineItem = (transactionPayload) => {
311
- let isAnyClassMissing = false;
312
- if (transactionPayload.lines != null) {
313
- transactionPayload.lines.forEach((line) => {
314
- if (line.line_detail?.class
315
- ?.accounting_class_id == null) {
316
- isAnyClassMissing = true;
276
+ const getLineIdsEligibleForReview = (transaction, uncategorizedIncomeExpense, isAccountingClassesEnabled, isExpenseAutomationEnabled) => {
277
+ // For non-EA, any line missing a required field (class when enabled, or entity on JE/Deposit)
278
+ // suppresses the entire call. EA keeps the per-line model, returning only eligible line IDs.
279
+ if (!isExpenseAutomationEnabled) {
280
+ const isApplicableType = (0, vendorTransaction_1.isVendorTransaction)(transaction) ||
281
+ (0, transactionType_1.toJEAndDepositTransactionTypeStrict)(transaction.type) != null;
282
+ if (isApplicableType) {
283
+ const lines = (transaction.lines ?? []).filter((line) => line.type === 'transaction_with_account_and_class_line' ||
284
+ line.type === 'journal_entry_transaction_line' ||
285
+ line.type === 'transaction_with_product_or_service_line');
286
+ if (isAccountingClassesEnabled) {
287
+ const anyLineMissingClass = lines.some((line) => (line.class?.classId ??
288
+ '') === '');
289
+ if (anyLineMissingClass) {
290
+ return [];
291
+ }
317
292
  }
318
- });
319
- }
320
- return isAnyClassMissing;
321
- };
322
- const checkIfVendorOrCustomerMissingForAnyLineItem = (transactionPayload) => {
323
- let isAnyVendorOrCustomerMissing = false;
324
- if (transactionPayload.lines != null) {
325
- transactionPayload.lines.forEach((line) => {
326
- if (line.line_detail
327
- ?.vendor == null &&
328
- line.line_detail
329
- ?.customer == null) {
330
- isAnyVendorOrCustomerMissing = true;
293
+ if ((0, transactionType_1.toJEAndDepositTransactionTypeStrict)(transaction.type) != null) {
294
+ const anyLineMissingEntity = lines.some((line) => {
295
+ const tl = line;
296
+ return tl.vendor == null && tl.customer == null;
297
+ });
298
+ if (anyLineMissingEntity) {
299
+ return [];
300
+ }
331
301
  }
332
- });
302
+ }
333
303
  }
334
- return isAnyVendorOrCustomerMissing;
304
+ return (transaction.lines ?? [])
305
+ .filter((line) => {
306
+ if (line.type !== 'transaction_with_account_and_class_line' &&
307
+ line.type !== 'journal_entry_transaction_line' &&
308
+ line.type !== 'transaction_with_product_or_service_line') {
309
+ return false;
310
+ }
311
+ const typedLine = line;
312
+ const hasCategory = !(0, transactionDetailLocalDataHelper_1.isAccountUncategorized)(uncategorizedIncomeExpense, typedLine.account?.qboId);
313
+ // class is always a ClassBase object (never null) because the payload mapper
314
+ // calls mapClassBasePayloadToClassBase(payload ?? {}) which returns {} with classId:''.
315
+ // A real class is indicated by a non-empty classId.
316
+ const hasClass = !isAccountingClassesEnabled || (typedLine.class?.classId ?? '') !== '';
317
+ const hasAmount = (typedLine.amount?.amount ?? 0) !== 0;
318
+ // JE lines and Deposit transactions require vendor or customer before being eligible for review.
319
+ const isJEOrDepositContext = line.type === 'journal_entry_transaction_line' ||
320
+ (0, transactionType_1.toJEAndDepositTransactionTypeStrict)(transaction.type) != null;
321
+ const hasEntity = !isJEOrDepositContext ||
322
+ typedLine.vendor != null ||
323
+ typedLine.customer != null;
324
+ return hasCategory && hasClass && hasAmount && hasEntity;
325
+ })
326
+ .map((line) => line.id)
327
+ .filter(Boolean);
335
328
  };
336
329
  // Converts a locally-created line item (from edit mode) into the wire shape
337
330
  // expected by the categorize endpoint. Sending id='' signals the backend to
@@ -261,7 +261,7 @@ const getRecommendedCategoryClass = (isCategoryMiscategorized, isClassMiscategor
261
261
  recommendedAccount = accountWithoutCOT;
262
262
  }
263
263
  if (rec.class != null &&
264
- rec.class.qboId !== classObj?.qboId &&
264
+ rec.class.classId !== classObj?.classId &&
265
265
  recommendedClass == undefined) {
266
266
  const { cot, reasoning, ...classWithoutCOT } = rec.class;
267
267
  void cot;
@@ -274,7 +274,7 @@ const getRecommendedCategoryClass = (isCategoryMiscategorized, isClassMiscategor
274
274
  (0, exports.isAccountUncategorized)(uncategorizedIncomeExpense, account?.qboId);
275
275
  if ((isClassMiscategorized === false || isUncategorizedAccount) &&
276
276
  rec.class != null &&
277
- rec.class.qboId === classObj?.qboId) {
277
+ rec.class.classId === classObj?.classId) {
278
278
  isLineItemClassCorrectlyCategorized = true;
279
279
  }
280
280
  // Note: In case of uncat transactions, if category is correctly categorized,
@@ -648,8 +648,11 @@ const setEntityRecommendationForLineIdInLocalData = (transaction, transactionDet
648
648
  record.billable = accountLine.billable;
649
649
  record.recommendationsWithCOT = recommendationsWithCOT;
650
650
  const { recommendedAccount, recommendedClass } = (0, exports.getRecommendedCategoryClass)(accountLine.isCategoryMiscategorized ?? false, accountLine.isClassMiscategorized ?? false, recommendationsWithCOT, accountLine.account, accountLine.class);
651
- // TODO: comparing with qboId because backend temporarily required us to compare
652
- // class and account based on qbo id. This should ideally be based on accountId/ClassId
651
+ // Account comparisons use qboId (backend guarantees every account
652
+ // recommendation carries one). Class comparisons below use classId
653
+ // instead: ClassBase.qboId is optional and frequently absent even
654
+ // when a real class recommendation exists, so qboId-based
655
+ // comparison silently dropped valid class recommendations.
653
656
  if (recommendedAccount?.qboId !== accountLine.account.qboId &&
654
657
  Boolean(recommendedAccount?.qboId)) {
655
658
  record.isRecommendedCategorySelected = false;
@@ -677,13 +680,13 @@ const setEntityRecommendationForLineIdInLocalData = (transaction, transactionDet
677
680
  record.isRetainedCategorySelected = false;
678
681
  }
679
682
  }
680
- if (recommendedClass?.qboId !== accountLine.class?.qboId &&
681
- Boolean(recommendedClass?.qboId)) {
683
+ if (recommendedClass?.classId !== accountLine.class?.classId &&
684
+ Boolean(recommendedClass?.classId)) {
682
685
  record.isRecommendedClassSelected = false;
683
686
  record.recommendedClass = recommendedClass;
684
687
  record.isClassUpdateSelected = false;
685
688
  if (accountLine.isClassMiscategorized &&
686
- Boolean(accountLine.class?.qboId)) {
689
+ Boolean(accountLine.class?.classId)) {
687
690
  record.retainedClass = accountLine.class;
688
691
  record.isRetainedClassSelected = false;
689
692
  }
@@ -691,12 +694,12 @@ const setEntityRecommendationForLineIdInLocalData = (transaction, transactionDet
691
694
  else {
692
695
  record.isRecommendedClassSelected = false;
693
696
  record.recommendedClass = recommendedClass;
694
- if (recommendedClass?.qboId === accountLine.class?.qboId) {
697
+ if (recommendedClass?.classId === accountLine.class?.classId) {
695
698
  record.recommendedClass = undefined;
696
699
  }
697
700
  record.isClassUpdateSelected = false;
698
701
  if (accountLine.isClassMiscategorized &&
699
- Boolean(accountLine.class?.qboId)) {
702
+ Boolean(accountLine.class?.classId)) {
700
703
  record.retainedClass = recommendedClass;
701
704
  record.isRetainedClassSelected = false;
702
705
  }
@@ -712,8 +715,8 @@ const setEntityRecommendationForLineIdInLocalData = (transaction, transactionDet
712
715
  Boolean(recommendedAccount?.qboId) !== false) {
713
716
  record.account = recommendedAccount;
714
717
  }
715
- if (Boolean(record.class?.qboId) === false &&
716
- Boolean(recommendedClass?.qboId) !== false) {
718
+ if (Boolean(record.class?.classId) === false &&
719
+ Boolean(recommendedClass?.classId) !== false) {
717
720
  record.class = recommendedClass;
718
721
  }
719
722
  newLineItems[line.id] = record;
@@ -764,6 +767,7 @@ const setEntityRecommendationForLineIdInLocalData = (transaction, transactionDet
764
767
  latestUpdatedLineWithEntity: transactionDetailLocalData.latestUpdatedLineWithEntity,
765
768
  vendorBulkUpdateCategoryClassRecommendationsFetchStatus: transactionDetailLocalData.vendorBulkUpdateCategoryClassRecommendationsFetchStatus,
766
769
  selectedEntity: transactionDetailLocalData.selectedEntity,
770
+ editMode: transactionDetailLocalData.editMode,
767
771
  };
768
772
  return newLocalData;
769
773
  };
@@ -818,6 +822,7 @@ const removeEntityRecommendationForLineIdInLocalData = (transaction, transaction
818
822
  latestSelectedEntityOnLineItem: transactionDetailLocalData.latestSelectedEntityOnLineItem,
819
823
  latestUpdatedLineWithEntity: transactionDetailLocalData.latestUpdatedLineWithEntity,
820
824
  selectedEntity: transactionDetailLocalData.selectedEntity,
825
+ editMode: transactionDetailLocalData.editMode,
821
826
  };
822
827
  return newLocalData;
823
828
  };
@@ -838,7 +843,8 @@ const getChangedLineItemCountFromLocalData = (transaction, transactionLocalData)
838
843
  const accountLine = line;
839
844
  hasCategoryChanged =
840
845
  accountLineLocal.account?.qboId !== accountLine.account.qboId &&
841
- accountLineLocal.account?.accountId !== accountLine.account.accountId;
846
+ accountLineLocal.account?.accountId !==
847
+ accountLine.account.accountId;
842
848
  hasClassChanged =
843
849
  (accountLineLocal.class?.qboId != null ||
844
850
  accountLine.class?.qboId != null) &&
@@ -38,10 +38,11 @@ export declare const downloadAccountingProviderAttachment: import("@reduxjs/tool
38
38
  error: ZeniAPIStatus<Record<string, unknown>> | undefined;
39
39
  }, "transactionDetail/updateTransactionDetailSaveStatus", never, never>, clearTransactionDetailSaveStatus: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[transactionId: TransactionID], {
40
40
  transactionId: TransactionID;
41
- }, "transactionDetail/clearTransactionDetailSaveStatus", never, never>, markTransactionAsNotMiscategorized: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[transactionId: TransactionID, transactionSaveUpdates: TransactionSaveUpdates, isExpenseAutomationEnabled: boolean], {
41
+ }, "transactionDetail/clearTransactionDetailSaveStatus", never, never>, markTransactionAsNotMiscategorized: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[transactionId: TransactionID, transactionSaveUpdates: TransactionSaveUpdates, isExpenseAutomationEnabled: boolean, lineIds?: string[] | undefined], {
42
42
  transactionId: TransactionID;
43
43
  transactionSaveUpdates: TransactionSaveUpdates;
44
44
  isExpenseAutomationEnabled: boolean;
45
+ lineIds: string[] | undefined;
45
46
  }, "transactionDetail/markTransactionAsNotMiscategorized", never, never>, deleteTransactionAttachment: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[attachmentFileName: string, transactionId: TransactionID], {
46
47
  attachmentFileName: string;
47
48
  transactionId: TransactionID;
@@ -197,12 +197,13 @@ const transactionDetail = (0, toolkit_1.createSlice)({
197
197
  };
198
198
  }
199
199
  },
200
- prepare(transactionId, transactionSaveUpdates, isExpenseAutomationEnabled) {
200
+ prepare(transactionId, transactionSaveUpdates, isExpenseAutomationEnabled, lineIds) {
201
201
  return {
202
202
  payload: {
203
203
  transactionId,
204
204
  transactionSaveUpdates,
205
205
  isExpenseAutomationEnabled,
206
+ lineIds,
206
207
  },
207
208
  };
208
209
  },
package/lib/zeniAPI.d.ts CHANGED
@@ -10,7 +10,6 @@ export interface RESTAPIEndpoints {
10
10
  readonly authMicroServiceBaseUrl: string;
11
11
  readonly bankingMicroServiceBaseUrl: string;
12
12
  readonly cardMicroServiceBaseUrl: string;
13
- readonly cashManagementAgentBaseUrl: string;
14
13
  readonly communicationAgentMicroServiceBaseUrl: string;
15
14
  readonly documentAIMicroServiceBaseUrl: string;
16
15
  readonly fileMicroServiceBaseUrl: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zeniai/client-epic-state",
3
- "version": "5.1.52",
3
+ "version": "5.1.53-betaRD2",
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,29 +0,0 @@
1
- import { toAmount } from '../../../../commonStateTypes/amount';
2
- import { date } from '../../../../zeniDayJS';
3
- import { toAutoSweepFrequency, } from './autoSweepFlowState';
4
- export const toSaveAutoSweepSettingsBody = (state) => ({
5
- frequency: state.frequency,
6
- memo: state.memo,
7
- minimum_buffer_usd: state.bufferAmount.amount,
8
- });
9
- /**
10
- * Map a `GET /cash-management/settings` payload into the slice's writable
11
- * fields. Defensive on the frequency (string-from-the-wire → union via
12
- * `toAutoSweepFrequency`, falling back to `'weekly'` if the server returns
13
- * something we don't model yet). Only the source account *id* is stored
14
- * here — the full deposit-account payload is published to the entity bucket
15
- * by the fetch epic.
16
- */
17
- export const toAutoSweepFlowStatePatch = (payload) => {
18
- const currencyCode = payload.source_bank_account.balances?.currency_code;
19
- const currencySymbol = payload.source_bank_account.balances?.currency_symbol;
20
- return {
21
- bufferAmount: toAmount(payload.minimum_buffer_usd, currencyCode ?? 'USD', currencySymbol ?? '$'),
22
- frequency: toAutoSweepFrequency(payload.frequency) ?? 'weekly',
23
- nextTransferDate: payload.next_run_date != null && payload.next_run_date !== ''
24
- ? date(payload.next_run_date)
25
- : undefined,
26
- primaryFundingAccountId: payload.source_bank_account.deposit_account_id ?? undefined,
27
- settingsId: payload.cash_management_settings_id ?? undefined,
28
- };
29
- };
@@ -1,78 +0,0 @@
1
- import { createSlice } from '@reduxjs/toolkit';
2
- import { toAutoSweepFlowStatePatch, } from './autoSweepFlowPayload';
3
- import { initialAutoSweepFlowState, } from './autoSweepFlowState';
4
- export const initialState = initialAutoSweepFlowState;
5
- const autoSweepFlow = createSlice({
6
- name: 'autoSweepFlow',
7
- initialState,
8
- reducers: {
9
- /**
10
- * Kicks off `GET /cash-management/settings`. Flips the top-level
11
- * fetchState to `In-Progress`; the fetch epic picks this up.
12
- */
13
- fetchCashManagementSettings(draft) {
14
- draft.fetchState = 'In-Progress';
15
- draft.error = undefined;
16
- },
17
- /**
18
- * Hydrates the form fields with the server-side settings (and stashes
19
- * the settings id for subsequent saves). Marks the GET as `Completed`.
20
- */
21
- updateCashManagementSettings(draft, action) {
22
- const patch = toAutoSweepFlowStatePatch(action.payload.data);
23
- draft.bufferAmount = patch.bufferAmount;
24
- draft.frequency = patch.frequency;
25
- draft.nextTransferDate = patch.nextTransferDate;
26
- draft.primaryFundingAccountId = patch.primaryFundingAccountId;
27
- draft.settingsId = patch.settingsId;
28
- draft.fetchState = 'Completed';
29
- draft.error = undefined;
30
- },
31
- updateCashManagementSettingsFetchStatus(draft, action) {
32
- draft.fetchState = action.payload.fetchState;
33
- draft.error = action.payload.error;
34
- },
35
- /**
36
- * Live-update the buffer (risk band) as the user toggles the risk picker
37
- * on the setup page, without staging frequency/memo. The selector derives
38
- * the risk-adjusted sweep amount off `bufferAmount`, so dispatching this on
39
- * every risk change keeps the setup and review screens in sync.
40
- */
41
- updateAutoSweepRisk(draft, action) {
42
- draft.bufferAmount = action.payload.bufferAmount;
43
- },
44
- /**
45
- * Stage the in-flight form values without submitting them — fired from
46
- * the setup→review transition so other selectors (and devtools) can see
47
- * what the user is about to confirm. Leaves `saveStatus` untouched; the
48
- * actual PUT only kicks off when the user clicks "Confirm Sweep" via
49
- * `saveAutoSweepSettings`.
50
- */
51
- updateAutoSweepDraft(draft, action) {
52
- draft.bufferAmount = action.payload.bufferAmount;
53
- draft.frequency = action.payload.frequency;
54
- draft.memo = action.payload.memo;
55
- },
56
- /**
57
- * Trigger the PUT for the auto-sweep settings already staged via
58
- * `updateAutoSweepDraft`. Carries no payload — flips `saveStatus` to
59
- * `In-Progress` and the save epic reads `bufferAmount` / `frequency` /
60
- * `memo` straight from the slice. Callers must ensure those fields
61
- * reflect the user's latest input before dispatching.
62
- */
63
- saveAutoSweepSettings(draft) {
64
- draft.saveStatus = { fetchState: 'In-Progress', error: undefined };
65
- },
66
- updateAutoSweepSettingsFetchStatus(draft, action) {
67
- draft.saveStatus = {
68
- fetchState: action.payload.fetchState,
69
- error: action.payload.error,
70
- };
71
- },
72
- clearAutoSweepFlow() {
73
- return initialAutoSweepFlowState;
74
- },
75
- },
76
- });
77
- export const { fetchCashManagementSettings, updateCashManagementSettings, updateCashManagementSettingsFetchStatus, updateAutoSweepRisk, updateAutoSweepDraft, saveAutoSweepSettings, updateAutoSweepSettingsFetchStatus, clearAutoSweepFlow, } = autoSweepFlow.actions;
78
- export default autoSweepFlow.reducer;
@@ -1,49 +0,0 @@
1
- import { getDepositAccountByDepositAccountId } from '../../../../entity/depositAccount/depositAccountSelector';
2
- import { mapDepositAccToFundingAccount, } from '../../commonSetup/setupViewSelector';
3
- import { getTreasuryDetail } from '../../treasury/treasuryList/treasuryDetailSelector';
4
- import { getCashManagementOverviewBanner } from '../cashManagementOverview/cashManagementOverviewSelector';
5
- import { RISK_BUFFER_AMOUNT, bufferAmountToRisk, } from './autoSweepFlowState';
6
- /**
7
- * Risk band the agent's recommended sweep amount is computed against. The
8
- * `sweepAmount` returned by the banner assumes this buffer, so the selector
9
- * adjusts the displayed sweep by the delta between the user's selected band
10
- * and this default.
11
- */
12
- const DEFAULT_RISK_LEVEL = 'moderate';
13
- export const getAutoSweepFlow = (state) => {
14
- const { bufferAmount, frequency, memo, saveStatus, settingsId, primaryFundingAccountId, fetchState, error, } = state.autoSweepFlowState;
15
- const depositAccount = primaryFundingAccountId != null
16
- ? getDepositAccountByDepositAccountId(state.depositAccountState, primaryFundingAccountId)
17
- : undefined;
18
- const risk = bufferAmountToRisk(bufferAmount.amount);
19
- // The banner's recommended sweep assumes the default buffer band; shift it
20
- // by the delta between the default buffer and the user's selected band so a
21
- // tighter buffer (higher risk → smaller buffer) sweeps more and a looser
22
- // buffer (lower risk → larger buffer) sweeps less. Clamp at zero so a buffer
23
- // larger than the available surplus never yields a negative sweep.
24
- const baseSweepAmount = getCashManagementOverviewBanner(state).amount;
25
- const sweepAmount = {
26
- ...baseSweepAmount,
27
- amount: Math.max(0, baseSweepAmount.amount +
28
- RISK_BUFFER_AMOUNT[DEFAULT_RISK_LEVEL] -
29
- RISK_BUFFER_AMOUNT[risk]),
30
- };
31
- return {
32
- fetchState,
33
- error,
34
- formData: {
35
- bufferAmount,
36
- frequency,
37
- memo,
38
- risk,
39
- },
40
- primaryFundingAccount: depositAccount != null
41
- ? mapDepositAccToFundingAccount(depositAccount)
42
- : undefined,
43
- saveStatus,
44
- settingsId,
45
- sweepAmount,
46
- treasurySummary: getTreasuryDetail(state).accountSummary,
47
- version: 0,
48
- };
49
- };
@@ -1,47 +0,0 @@
1
- import { toAmount } from '../../../../commonStateTypes/amount';
2
- import { stringToUnion } from '../../../../commonStateTypes/stringToUnion';
3
- /**
4
- * Cadence at which the auto-sweep transfer fires. Lowercase to match the
5
- * cash-management settings API contract (and the `SweepFrequency` literal
6
- * already used by `<AutoSweepSetupPage>` in web-components).
7
- */
8
- export const ALL_AUTO_SWEEP_FREQUENCIES = [
9
- 'daily',
10
- 'weekly',
11
- 'biweekly',
12
- 'monthly',
13
- ];
14
- export const toAutoSweepFrequency = (v) => stringToUnion(v ?? '', ALL_AUTO_SWEEP_FREQUENCIES);
15
- /**
16
- * Risk presets the auto-sweep setup form exposes. Each level maps to a
17
- * canonical minimum-buffer amount (USD). Selecting a level in the UI is
18
- * shorthand for choosing that buffer.
19
- */
20
- export const ALL_AUTO_SWEEP_RISK_LEVELS = ['low', 'moderate', 'high'];
21
- export const toAutoSweepRiskLevel = (v) => stringToUnion(v ?? '', ALL_AUTO_SWEEP_RISK_LEVELS);
22
- export const RISK_BUFFER_AMOUNT = {
23
- low: 15000,
24
- moderate: 10000,
25
- high: 5000,
26
- };
27
- /**
28
- * Maps a buffer amount back onto the closest preset risk level. Falls back
29
- * to `'moderate'` when the amount doesn't match a known preset (e.g. the
30
- * server returned a custom value). The dropdown in `<AutoSweepSetupPage>`
31
- * uses this so it can pre-select the user's existing band on edit.
32
- */
33
- export const bufferAmountToRisk = (amount) => {
34
- const match = Object.entries(RISK_BUFFER_AMOUNT).find(([, presetAmount]) => presetAmount === amount);
35
- return match != null ? match[0] : 'moderate';
36
- };
37
- export const initialAutoSweepFlowState = {
38
- bufferAmount: toAmount(10000, 'USD', '$'),
39
- frequency: 'weekly',
40
- memo: '',
41
- saveStatus: { fetchState: 'Not-Started', error: undefined },
42
- nextTransferDate: undefined,
43
- primaryFundingAccountId: undefined,
44
- settingsId: undefined,
45
- fetchState: 'Not-Started',
46
- error: undefined,
47
- };
@@ -1,32 +0,0 @@
1
- import { of } from 'rxjs';
2
- import { catchError, filter, mergeMap, switchMap } from 'rxjs/operators';
3
- import { updateDepositAccounts } from '../../../../../entity/depositAccount/depositAccountReducer';
4
- import { createZeniAPIStatus, isSuccessResponse, } from '../../../../../responsePayload';
5
- import { fetchCashManagementSettings, updateCashManagementSettings, updateCashManagementSettingsFetchStatus, } from '../autoSweepFlowReducer';
6
- /**
7
- * Hits `GET /cash-management/settings` and hydrates the auto-sweep slice
8
- * with the server's existing settings (frequency, minimum buffer, settings
9
- * id). Called from the cash management overview page-fetch epic alongside
10
- * the other top-level fetches.
11
- */
12
- export const fetchCashManagementSettingsEpic = (actions$, _state$, zeniAPI) => actions$.pipe(filter(fetchCashManagementSettings.match), switchMap(() => {
13
- const endpoint = `${zeniAPI.apiEndPoints.cashManagementAgentBaseUrl}/1.0/cash-management/settings`;
14
- return zeniAPI.getJSON(endpoint).pipe(mergeMap((response) => {
15
- if (isSuccessResponse(response) && response.data != null) {
16
- return of(
17
- // Publish the source deposit account into the shared entity
18
- // bucket so the selector can hydrate the full `FundingAccount`
19
- // from `getDepositAccountByDepositAccountId` rather than the
20
- // auto-sweep slice holding a duplicate copy.
21
- updateDepositAccounts([response.data.source_bank_account]), updateCashManagementSettings({ data: response.data }));
22
- }
23
- return of(updateCashManagementSettingsFetchStatus({
24
- fetchState: 'Error',
25
- error: response.status,
26
- }));
27
- }), catchError((error) => of(updateCashManagementSettingsFetchStatus({
28
- fetchState: 'Error',
29
- error: createZeniAPIStatus('Unexpected Error', 'Fetch cash management settings REST API call errored out: ' +
30
- (error?.message ?? String(error))),
31
- }))));
32
- }));
@@ -1,32 +0,0 @@
1
- import { of } from 'rxjs';
2
- import { catchError, filter, mergeMap, switchMap } from 'rxjs/operators';
3
- import { createZeniAPIStatus, isSuccessResponse, } from '../../../../../responsePayload';
4
- import { toSaveAutoSweepSettingsBody, } from '../autoSweepFlowPayload';
5
- import { saveAutoSweepSettings, updateAutoSweepSettingsFetchStatus, } from '../autoSweepFlowReducer';
6
- export const saveAutoSweepSettingsEpic = (actions$, state$, zeniAPI) => actions$.pipe(filter(saveAutoSweepSettings.match), switchMap(() => {
7
- const endpoint = `${zeniAPI.apiEndPoints.cashManagementAgentBaseUrl}/1.0/cash-management/settings`;
8
- const { bufferAmount, frequency, memo, settingsId } = state$.value.autoSweepFlowState;
9
- const payload = toSaveAutoSweepSettingsBody({
10
- bufferAmount,
11
- frequency,
12
- memo,
13
- });
14
- // First-time setup (no settings id from the GET yet) → POST a new
15
- // record. Subsequent edits update the existing one via PUT.
16
- const request$ = settingsId == null
17
- ? zeniAPI.postAndGetJSON(endpoint, payload)
18
- : zeniAPI.putAndGetJSON(endpoint, payload);
19
- return request$.pipe(mergeMap((response) => {
20
- if (isSuccessResponse(response) && response.data != null) {
21
- return of(updateAutoSweepSettingsFetchStatus({ fetchState: 'Completed' }));
22
- }
23
- return of(updateAutoSweepSettingsFetchStatus({
24
- fetchState: 'Error',
25
- error: response?.status,
26
- }));
27
- }), catchError((error) => of(updateAutoSweepSettingsFetchStatus({
28
- fetchState: 'Error',
29
- error: createZeniAPIStatus('Unexpected Error', 'Save auto-sweep settings REST API call errored out: ' +
30
- (error?.message ?? String(error))),
31
- }))));
32
- }));
@@ -1,54 +0,0 @@
1
- import { toAmount } from '../../../../commonStateTypes/amount';
2
- import { date as zeniDate } from '../../../../zeniDayJS';
3
- // ---------------------------------------------------------------------------
4
- // Converters
5
- // ---------------------------------------------------------------------------
6
- // The cash agent payload reports `currency_code` only; the v1 surface is
7
- // USD-only, so we pair it with the canonical `$` symbol here. If the agent
8
- // grows multi-currency support, this is the single place to widen.
9
- const CASH_AGENT_CURRENCY_SYMBOL = '$';
10
- export const toUpcomingPaymentEvent = (event) => ({
11
- accountId: event.account_id,
12
- accountType: event.account_type,
13
- amount: toAmount(event.amount_usd, event.currency_code, CASH_AGENT_CURRENCY_SYMBOL),
14
- label: event.label,
15
- paymentDate: zeniDate(event.payment_date),
16
- });
17
- /**
18
- * The agent reports a `null` account id whenever the "account" in question is
19
- * a singleton inferred from the account type (today: the treasury account —
20
- * there's only one per company, so the type alone identifies it). Callers
21
- * already branch on `*BankAccountType`, so collapsing the id to `""` keeps the
22
- * state type a plain `ID` (string) without losing information.
23
- */
24
- const toCashManagementMovement = (payload) => ({
25
- amount: toAmount(payload.amount, payload.currency_code, CASH_AGENT_CURRENCY_SYMBOL),
26
- destinationBankAccountId: payload.destination_bank_account_id ?? '',
27
- destinationBankAccountType: payload.destination_bank_account_type,
28
- reasoning: payload.reasoning,
29
- sourceBankAccountId: payload.source_bank_account_id ?? '',
30
- sourceBankAccountType: payload.source_bank_account_type,
31
- subMovementType: payload.sub_movement_type,
32
- });
33
- const toCashManagementMoneyMovementGroup = (payload) => ({
34
- movements: payload.movements.map(toCashManagementMovement),
35
- movementType: payload.movement_type,
36
- });
37
- export const toCashManagementBannerData = (payload) => ({
38
- inflows: payload.inflows.map(toUpcomingPaymentEvent),
39
- moneyMovements: payload.money_movements.map(toCashManagementMoneyMovementGroup),
40
- outflows: payload.outflows.map(toUpcomingPaymentEvent),
41
- });
42
- export const toCashManagementTransfer = (payload) => ({
43
- amount: toAmount(payload.amount, payload.currency_code, CASH_AGENT_CURRENCY_SYMBOL),
44
- atomicTransactionId: payload.atomic_transaction_id,
45
- cashManagementTransferId: payload.cash_management_transfer_id,
46
- createTime: zeniDate(payload.create_time),
47
- date: zeniDate(payload.date),
48
- depositAccountId: payload.deposit_account_id,
49
- reasoning: payload.reasoning,
50
- scenario: payload.scenario,
51
- status: payload.status,
52
- transactionType: payload.transaction_type,
53
- updateTime: zeniDate(payload.update_time),
54
- });