@zeniai/client-epic-state 5.1.18-betaDI11 → 5.1.18-betaDI12

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 (127) hide show
  1. package/README.md +3 -1
  2. package/lib/commonStateTypes/viewAndReport/agingReportStateTypes.d.ts +1 -1
  3. package/lib/entity/accountRecon/accountReconPayload.d.ts +22 -0
  4. package/lib/entity/accountRecon/accountReconPayload.js +26 -1
  5. package/lib/entity/accountRecon/accountReconReducer.d.ts +5 -2
  6. package/lib/entity/accountRecon/accountReconReducer.js +36 -2
  7. package/lib/entity/accountRecon/accountReconSelector.d.ts +6 -1
  8. package/lib/entity/accountRecon/accountReconSelector.js +5 -0
  9. package/lib/entity/accountRecon/accountReconState.d.ts +22 -0
  10. package/lib/entity/monthEndCloseChecks/monthEndCloseChecksPayload.d.ts +8 -0
  11. package/lib/entity/monthEndCloseChecks/monthEndCloseChecksPayload.js +10 -0
  12. package/lib/entity/monthEndCloseChecks/monthEndCloseChecksState.d.ts +8 -0
  13. package/lib/entity/task/taskReducer.js +3 -0
  14. package/lib/entity/task/taskState.d.ts +1 -1
  15. package/lib/entity/transaction/payloadTypes/transactionPayload.d.ts +1 -0
  16. package/lib/entity/transaction/payloadTypes/transactionPayload.js +5 -0
  17. package/lib/entity/transaction/stateTypes/transaction.d.ts +1 -0
  18. package/lib/epic.d.ts +7 -2
  19. package/lib/epic.js +7 -2
  20. package/lib/esm/entity/accountRecon/accountReconPayload.js +26 -1
  21. package/lib/esm/entity/accountRecon/accountReconReducer.js +35 -1
  22. package/lib/esm/entity/accountRecon/accountReconSelector.js +5 -0
  23. package/lib/esm/entity/monthEndCloseChecks/monthEndCloseChecksPayload.js +10 -0
  24. package/lib/esm/entity/task/taskReducer.js +3 -0
  25. package/lib/esm/entity/transaction/payloadTypes/transactionPayload.js +5 -0
  26. package/lib/esm/epic.js +7 -2
  27. package/lib/esm/index.js +9 -9
  28. package/lib/esm/view/aiCfoView/aiCfoViewReducer.js +29 -3
  29. package/lib/esm/view/aiCfoView/aiCfoViewSelector.js +11 -0
  30. package/lib/esm/view/aiCfoView/epics/createSessionAndSubmitEpic.js +2 -2
  31. package/lib/esm/view/aiCfoView/epics/fetchSkillsEpic.js +34 -0
  32. package/lib/esm/view/aiCfoView/epics/submitQuestionEpic.js +7 -1
  33. package/lib/esm/view/expenseAutomationView/epics/accountRecon/parseStatementEpic.js +34 -0
  34. package/lib/esm/view/expenseAutomationView/epics/accountRecon/reparseStatementEpic.js +31 -0
  35. package/lib/esm/view/expenseAutomationView/epics/accountRecon/updateStatementInfoEpic.js +57 -0
  36. package/lib/esm/view/expenseAutomationView/epics/fluxAnalysis/fetchFluxAnalysisViewEpic.js +1 -0
  37. package/lib/esm/view/expenseAutomationView/epics/transactionCategorization/fetchTransactionCategorizationEpic.js +49 -11
  38. package/lib/esm/view/expenseAutomationView/epics/transactionCategorization/fetchTransactionCategorizationViewEpic.js +30 -3
  39. package/lib/esm/view/expenseAutomationView/payload/reconciliationPayload.js +153 -0
  40. package/lib/esm/view/expenseAutomationView/reducers/fluxAnalysisViewReducer.js +6 -1
  41. package/lib/esm/view/expenseAutomationView/reducers/reconciliationViewReducer.js +188 -9
  42. package/lib/esm/view/expenseAutomationView/reducers/transactionsViewReducer.js +37 -17
  43. package/lib/esm/view/expenseAutomationView/selectors/fluxAnalysisViewSelector.js +2 -1
  44. package/lib/esm/view/expenseAutomationView/selectors/reconciliationViewSelector.js +27 -0
  45. package/lib/esm/view/expenseAutomationView/types/reconciliationViewState.js +29 -0
  46. package/lib/esm/view/onboardingView/cockpitView/epic/initializeOnboardingCustomerViewUpdateDataEpic.js +4 -16
  47. package/lib/esm/view/onboardingView/cockpitView/onboardingCockpitViewPayload.js +2 -2
  48. package/lib/esm/view/opExByVendor/opExByVendorReducer.js +28 -5
  49. package/lib/esm/view/opExByVendor/opExByVendorSelector.js +1 -0
  50. package/lib/esm/view/opExByVendor/refreshOpExByVendorReportEpic.js +21 -0
  51. package/lib/esm/view/referralView/epics/sendReferralInviteEpic.js +9 -1
  52. package/lib/esm/view/referralView/referralReducer.js +3 -2
  53. package/lib/esm/view/spendManagement/commonSetup/setupViewReducer.js +5 -1
  54. package/lib/esm/view/taskManager/taskDetailView/epics/saveTaskDetailEpic.js +2 -2
  55. package/lib/esm/view/taskManager/taskDetailView/epics/sendCommentMentionNotificationEpic.js +2 -2
  56. package/lib/esm/view/taskManager/taskDetailView/taskDetailSelector.js +1 -1
  57. package/lib/esm/view/taskManager/taskListView/taskListReducer.js +3 -1
  58. package/lib/esm/view/transactionDetail/epics/saveTransactionDetailEpic.js +3 -1
  59. package/lib/esm/view/transactionDetail/epics/saveTransactionHelper.js +11 -2
  60. package/lib/esm/view/transactionDetail/epics/updateTransactionDetailEpic.js +34 -5
  61. package/lib/esm/view/transactionDetail/transactionDetailLocalDataHelper.js +17 -0
  62. package/lib/index.d.ts +14 -14
  63. package/lib/index.js +57 -37
  64. package/lib/view/aiCfoView/aiCfoViewPayload.d.ts +14 -0
  65. package/lib/view/aiCfoView/aiCfoViewReducer.d.ts +8 -4
  66. package/lib/view/aiCfoView/aiCfoViewReducer.js +30 -4
  67. package/lib/view/aiCfoView/aiCfoViewSelector.d.ts +2 -1
  68. package/lib/view/aiCfoView/aiCfoViewSelector.js +12 -0
  69. package/lib/view/aiCfoView/aiCfoViewState.d.ts +15 -0
  70. package/lib/view/aiCfoView/epics/createSessionAndSubmitEpic.js +2 -2
  71. package/lib/view/aiCfoView/epics/fetchSkillsEpic.d.ts +8 -0
  72. package/lib/view/aiCfoView/epics/fetchSkillsEpic.js +38 -0
  73. package/lib/view/aiCfoView/epics/submitQuestionEpic.js +7 -1
  74. package/lib/view/expenseAutomationView/epics/accountRecon/parseStatementEpic.d.ts +6 -0
  75. package/lib/view/expenseAutomationView/epics/accountRecon/parseStatementEpic.js +38 -0
  76. package/lib/view/expenseAutomationView/epics/accountRecon/reparseStatementEpic.d.ts +26 -0
  77. package/lib/view/expenseAutomationView/epics/accountRecon/reparseStatementEpic.js +35 -0
  78. package/lib/view/expenseAutomationView/epics/accountRecon/updateStatementInfoEpic.d.ts +27 -0
  79. package/lib/view/expenseAutomationView/epics/accountRecon/updateStatementInfoEpic.js +61 -0
  80. package/lib/view/expenseAutomationView/epics/fluxAnalysis/fetchFluxAnalysisViewEpic.js +1 -0
  81. package/lib/view/expenseAutomationView/epics/transactionCategorization/fetchTransactionCategorizationEpic.js +47 -9
  82. package/lib/view/expenseAutomationView/epics/transactionCategorization/fetchTransactionCategorizationViewEpic.js +30 -3
  83. package/lib/view/expenseAutomationView/payload/fluxAnalysisPayload.d.ts +8 -0
  84. package/lib/view/expenseAutomationView/payload/reconciliationPayload.d.ts +176 -10
  85. package/lib/view/expenseAutomationView/payload/reconciliationPayload.js +159 -0
  86. package/lib/view/expenseAutomationView/payload/transactionCategorizationPayload.d.ts +7 -0
  87. package/lib/view/expenseAutomationView/reducers/fluxAnalysisViewReducer.d.ts +2 -1
  88. package/lib/view/expenseAutomationView/reducers/fluxAnalysisViewReducer.js +6 -1
  89. package/lib/view/expenseAutomationView/reducers/reconciliationViewReducer.d.ts +53 -3
  90. package/lib/view/expenseAutomationView/reducers/reconciliationViewReducer.js +190 -10
  91. package/lib/view/expenseAutomationView/reducers/transactionsViewReducer.js +37 -17
  92. package/lib/view/expenseAutomationView/selectorTypes/reconciliationViewSelectorTypes.d.ts +7 -1
  93. package/lib/view/expenseAutomationView/selectors/fluxAnalysisViewSelector.js +2 -1
  94. package/lib/view/expenseAutomationView/selectors/reconciliationViewSelector.d.ts +2 -0
  95. package/lib/view/expenseAutomationView/selectors/reconciliationViewSelector.js +29 -1
  96. package/lib/view/expenseAutomationView/types/fluxAnalysisViewState.d.ts +2 -0
  97. package/lib/view/expenseAutomationView/types/reconciliationViewState.d.ts +175 -0
  98. package/lib/view/expenseAutomationView/types/reconciliationViewState.js +30 -0
  99. package/lib/view/onboardingView/cockpitView/epic/initializeOnboardingCustomerViewUpdateDataEpic.js +3 -15
  100. package/lib/view/onboardingView/cockpitView/onboardingCockpitViewPayload.js +2 -2
  101. package/lib/view/opExByVendor/opExByVendorReducer.d.ts +6 -1
  102. package/lib/view/opExByVendor/opExByVendorReducer.js +29 -6
  103. package/lib/view/opExByVendor/opExByVendorSelector.d.ts +1 -0
  104. package/lib/view/opExByVendor/opExByVendorSelector.js +1 -0
  105. package/lib/view/opExByVendor/opExByVendorState.d.ts +1 -0
  106. package/lib/view/opExByVendor/refreshOpExByVendorReportEpic.d.ts +17 -0
  107. package/lib/view/opExByVendor/refreshOpExByVendorReportEpic.js +25 -0
  108. package/lib/view/referralView/epics/sendReferralInviteEpic.js +9 -1
  109. package/lib/view/referralView/referralReducer.d.ts +1 -0
  110. package/lib/view/referralView/referralReducer.js +4 -3
  111. package/lib/view/spendManagement/commonSetup/setupViewReducer.js +5 -1
  112. package/lib/view/taskManager/taskDetailView/epics/saveTaskDetailEpic.js +2 -2
  113. package/lib/view/taskManager/taskDetailView/epics/sendCommentMentionNotificationEpic.d.ts +1 -1
  114. package/lib/view/taskManager/taskDetailView/epics/sendCommentMentionNotificationEpic.js +1 -1
  115. package/lib/view/taskManager/taskDetailView/taskDetail.d.ts +1 -1
  116. package/lib/view/taskManager/taskDetailView/taskDetailSelector.js +1 -1
  117. package/lib/view/taskManager/taskListView/taskListReducer.js +3 -1
  118. package/lib/view/transactionDetail/epics/saveTransactionDetailEpic.js +3 -1
  119. package/lib/view/transactionDetail/epics/saveTransactionHelper.d.ts +1 -1
  120. package/lib/view/transactionDetail/epics/saveTransactionHelper.js +11 -2
  121. package/lib/view/transactionDetail/epics/updateTransactionDetailEpic.d.ts +8 -0
  122. package/lib/view/transactionDetail/epics/updateTransactionDetailEpic.js +36 -6
  123. package/lib/view/transactionDetail/transactionDetailLocalDataHelper.js +17 -0
  124. package/lib/view/transactionDetail/transactionDetailState.d.ts +1 -0
  125. package/lib/view/transactionDetail/transactionDetailTypes.d.ts +1 -0
  126. package/lib/view/vendorFiling1099/vendorFiling1099List/vendorFiling1099ListState.d.ts +1 -1
  127. package/package.json +5 -2
@@ -43,6 +43,17 @@ export const getExpenseAutomationReconciliationView = createSelector((state) =>
43
43
  error: undefined,
44
44
  })
45
45
  : { fetchState: 'Not-Started', error: undefined };
46
+ const monthEndChecksForPeriod = selectedPeriod != null
47
+ ? monthEndCloseChecksState.monthEndCloseChecksByTenantId[currentTenant.tenantId]?.[toMonthYearPeriodId(selectedPeriod)]
48
+ : undefined;
49
+ const reconCheckTimeSavedHours = monthEndChecksForPeriod?.monthEndCloseCheckList?.find((check) => check.id === 'reconciliation')?.progressJson?.timeSavedHours;
50
+ const summaryWithTimeSaved = reconciliationViewState.summary != null
51
+ ? {
52
+ ...reconciliationViewState.summary,
53
+ timeSavedHours: reconCheckTimeSavedHours ??
54
+ reconciliationViewState.summary.timeSavedHours,
55
+ }
56
+ : undefined;
46
57
  if (accountReconForMonthYearPeriod == null) {
47
58
  return {
48
59
  bankAccounts: [],
@@ -66,6 +77,7 @@ export const getExpenseAutomationReconciliationView = createSelector((state) =>
66
77
  selectedTab: reconciliationViewState.selectedTab,
67
78
  monthYearPeriod: selectedPeriod,
68
79
  selectedAccountId: reconciliationViewState.selectedAccountId,
80
+ statementProcessingFailed: reconciliationViewState.statementProcessingFailed,
69
81
  statementUploadChosen: reconciliationViewState.statementUploadChosen,
70
82
  accountReconciliationsByAccountID: {},
71
83
  reconciliationTabsState: initialReconciliationTabsState,
@@ -115,6 +127,7 @@ export const getExpenseAutomationReconciliationView = createSelector((state) =>
115
127
  excludeAccountFetchState: reconciliationViewState.actionFetchState.excludeAccountFetch,
116
128
  includeAccountFetchState: reconciliationViewState.actionFetchState.includeAccountFetch,
117
129
  excludedAccountIDs: reconciliationViewState.excludedAccountIDs,
130
+ summary: summaryWithTimeSaved,
118
131
  };
119
132
  }
120
133
  const allAccountIDs = accountReconForMonthYearPeriod.accountIDs;
@@ -239,6 +252,12 @@ export const getExpenseAutomationReconciliationView = createSelector((state) =>
239
252
  .refreshStatus,
240
253
  statementDeleteStatus: accountReconForMonthYearPeriod.reconciliationByAccountID[accountID]
241
254
  .statementDeleteStatus,
255
+ statementParseStatus: accountReconForMonthYearPeriod.reconciliationByAccountID[accountID]
256
+ .statementParseStatus,
257
+ reparseStatementStatus: accountReconForMonthYearPeriod.reconciliationByAccountID[accountID]
258
+ .reparseStatementStatus,
259
+ statementDateConflict: accountReconForMonthYearPeriod.reconciliationByAccountID[accountID]
260
+ .statementDateConflict,
242
261
  statementUpdateStatus: accountReconForMonthYearPeriod.reconciliationByAccountID[accountID]
243
262
  .statementUpdateStatus,
244
263
  statementUploadStatus: accountReconForMonthYearPeriod.reconciliationByAccountID[accountID]
@@ -247,6 +266,8 @@ export const getExpenseAutomationReconciliationView = createSelector((state) =>
247
266
  .localData,
248
267
  statementParseInProgress: accountReconForMonthYearPeriod.reconciliationByAccountID[accountID]
249
268
  .statementParseInProgress,
269
+ parsedStatementData: accountReconForMonthYearPeriod.reconciliationByAccountID[accountID]
270
+ .parsedStatementData,
250
271
  transactionFetchState: reduceAnyFetchState([
251
272
  accountReconForMonthYearPeriod.reconciliationByAccountID[accountID]
252
273
  .transactionFetchState,
@@ -315,6 +336,9 @@ export const getExpenseAutomationReconciliationView = createSelector((state) =>
315
336
  statementUpdateStatus: { fetchState: 'Not-Started' },
316
337
  statementUploadStatus: { fetchState: 'Not-Started' },
317
338
  statementDeleteStatus: { fetchState: 'Not-Started' },
339
+ statementParseStatus: { fetchState: 'Not-Started' },
340
+ reparseStatementStatus: { fetchState: 'Not-Started' },
341
+ statementDateConflict: null,
318
342
  localData: {},
319
343
  connectionInProgress: false,
320
344
  statementParseInProgress: false,
@@ -403,6 +427,7 @@ export const getExpenseAutomationReconciliationView = createSelector((state) =>
403
427
  refreshStatus: reconciliationViewState.refreshStatus,
404
428
  selectedTab: reconciliationViewState.selectedTab,
405
429
  selectedDrawerAccountId: reconciliationViewState.selectedDrawerAccountId,
430
+ statementProcessingFailed: reconciliationViewState.statementProcessingFailed,
406
431
  statementUploadChosen: reconciliationViewState.statementUploadChosen,
407
432
  monthYearPeriod: selectedPeriod,
408
433
  accountReconciliationBySectionID,
@@ -423,8 +448,10 @@ export const getExpenseAutomationReconciliationView = createSelector((state) =>
423
448
  excludeAccountFetchState: reconciliationViewState.actionFetchState.excludeAccountFetch,
424
449
  includeAccountFetchState: reconciliationViewState.actionFetchState.includeAccountFetch,
425
450
  excludedAccountIDs: reconciliationViewState.excludedAccountIDs,
451
+ summary: summaryWithTimeSaved,
426
452
  };
427
453
  });
454
+ export const getReparseStatementStatusByAccountId = (state, accountId) => getExpenseAutomationReconciliationView(state).accountReconciliationsByAccountID[accountId]?.reparseStatementStatus;
428
455
  export const isAccountReconReport = (reportId) => {
429
456
  return reportId.endsWith('reconciliation');
430
457
  };
@@ -1,4 +1,33 @@
1
1
  import { stringToUnion } from '../../../commonStateTypes/stringToUnion';
2
+ export function toReconciliationViewSummary(payload) {
3
+ return {
4
+ accounts: {
5
+ done: payload.accounts.done,
6
+ failed: payload.accounts.failed,
7
+ inProgress: payload.accounts.in_progress,
8
+ total: payload.accounts.total,
9
+ },
10
+ accountsToConnectCount: payload.accounts_to_connect_count,
11
+ autoMatched: {
12
+ matchedTxns: payload.auto_matched.matched_txns,
13
+ totalTxns: payload.auto_matched.total_txns,
14
+ },
15
+ needsReview: {
16
+ accountCount: payload.needs_review.account_count,
17
+ txnCount: payload.needs_review.txn_count,
18
+ },
19
+ timeSavedHours: 0,
20
+ timeSavedPercentage: payload.time_saved_percentage,
21
+ progressBar: payload.progress_bar != null
22
+ ? {
23
+ aiPercentageShare: payload.progress_bar.ai_percentage_share,
24
+ completePercentage: payload.progress_bar.complete_percentage,
25
+ humanPercentageShare: payload.progress_bar.human_percentage_share,
26
+ remainingPercentage: payload.progress_bar.remaining_percentage,
27
+ }
28
+ : undefined,
29
+ };
30
+ }
2
31
  export const RECONCILE_ACTIONS = [
3
32
  'reconcile',
4
33
  'save_reconcile_for_later',
@@ -4,7 +4,7 @@ import { getAddressByAddressId } from '../../../../entity/address/addressSelecto
4
4
  import { getOnboardingCompanyByCompanyId } from '../../../../entity/company/companySelector';
5
5
  import { toAccountingConnectionCreationModeStrict } from '../../../../entity/company/companyStateTypes';
6
6
  import { getOnboardingCustomersByCompanyId } from '../../../../entity/onboardingCustomer/onboardingCustomerSelector';
7
- import { getOnboardingTenantByTenantId, isOtherProductsEnabledForTenant, isTenantBankingOnly, isTenantBookkeepingEnabled, isTenantCardsOnly, isTenantTreasuryEnabled, } from '../../../../entity/tenant/tenantSelector';
7
+ import { getOnboardingTenantByTenantId, isOtherProductsEnabledForTenant, isTenantBankingOnly, isTenantBookkeepingEnabled, isTenantCardsOnly, } from '../../../../entity/tenant/tenantSelector';
8
8
  import { getUserByUserId } from '../../../../entity/user/userSelector';
9
9
  import { newAddressInLocalStore, resetNewAddressesInLocalStore, } from '../../../addressView/addressViewReducer';
10
10
  import { clearOnboardingCustomerViewUpdateData, initializeOnboardingCustomerViewUpdateData, saveOnboardingCustomerDataInLocalStore, saveOnboardingCustomerViewUpdateData, updateCustomerLocalStoreInitStateOnFailure, } from '../onboardingCockpitViewReducer';
@@ -112,32 +112,20 @@ const getSelectedProducts = (isDebitCardFlagEnabled, tenant) => {
112
112
  const isBookkeepingEnabled = isTenantBookkeepingEnabled(tenant);
113
113
  const isOtherProductsEnabled = isOtherProductsEnabledForTenant(tenant);
114
114
  const isCardsEnabled = isTenantCardsOnly(tenant);
115
- const isTreasuryEnabled = isTenantTreasuryEnabled(tenant);
116
115
  if (isCardsEnabled && isBankingOnly) {
117
- const selectedProducts = ['zeni_cards', 'zeni_checking'];
118
- if (isTreasuryEnabled) {
119
- selectedProducts.push('zeni_treasury');
120
- }
121
- return selectedProducts;
116
+ return [];
122
117
  }
123
118
  else if (isBankingOnly) {
124
- const selectedProducts = ['zeni_checking'];
125
- if (isTreasuryEnabled) {
126
- selectedProducts.push('zeni_treasury');
127
- }
128
- return selectedProducts;
119
+ return [];
129
120
  }
130
121
  else if (isCardsEnabled) {
131
- return ['zeni_cards'];
122
+ return [];
132
123
  }
133
124
  else if (isBookkeepingEnabled) {
134
125
  return [
135
126
  'bookkeeping',
136
127
  'zeni_bill_pay',
137
- 'zeni_checking',
138
128
  'zeni_reimbursements',
139
- 'zeni_treasury',
140
- 'zeni_cards',
141
129
  ];
142
130
  }
143
131
  else if (isOtherProductsEnabled) {
@@ -48,9 +48,9 @@ export const toOnboardingCustomerPayload = (onboardingCustomer, sendInvite, comp
48
48
  const isTreasury = selectedProducts.includes('zeni_treasury');
49
49
  if (isBookkeeping) {
50
50
  onboardingCustomerPayload.is_book_keeping_enabled = true;
51
- onboardingCustomerPayload.is_banking_enabled = true;
51
+ onboardingCustomerPayload.is_banking_enabled = isChecking;
52
52
  onboardingCustomerPayload.is_spend_management_enabled = true;
53
- onboardingCustomerPayload.is_card_enabled = true;
53
+ onboardingCustomerPayload.is_card_enabled = isCards;
54
54
  onboardingCustomerPayload.is_treasury_enabled = isTreasury;
55
55
  }
56
56
  else if (isChecking && isCards) {
@@ -22,6 +22,10 @@ export const initialState = {
22
22
  error: undefined,
23
23
  },
24
24
  },
25
+ refreshStatus: {
26
+ fetchState: 'Not-Started',
27
+ error: undefined,
28
+ },
25
29
  summaryFetchState: {
26
30
  fetchState: 'Not-Started',
27
31
  error: undefined,
@@ -259,12 +263,32 @@ const opexByVendor = createSlice({
259
263
  [timeframe]: [],
260
264
  };
261
265
  },
266
+ refreshOpExByVendorReport(draft) {
267
+ draft.refreshStatus = { fetchState: 'In-Progress', error: undefined };
268
+ },
269
+ refreshOpExByVendorReportSuccess(draft, action) {
270
+ draft.refreshStatus = { fetchState: 'Completed', error: undefined };
271
+ if (action.payload.lastRefreshSuccessTime != null) {
272
+ draft.lastRefreshSuccessTime = action.payload.lastRefreshSuccessTime;
273
+ }
274
+ },
275
+ refreshOpExByVendorReportFailure: {
276
+ prepare(error) {
277
+ return { payload: { error } };
278
+ },
279
+ reducer(draft, action) {
280
+ draft.refreshStatus = {
281
+ fetchState: 'Error',
282
+ error: action.payload.error,
283
+ };
284
+ },
285
+ },
262
286
  clearOpExByVendor(draft) {
263
287
  Object.assign(draft, initialState);
264
288
  },
265
289
  },
266
290
  });
267
- export const { fetchOpExByVendor, updateOpExByVendorReportGenerated, fetchOpExByVendorReportForTimeframe, updateOpExByVendorReportFailureForTimeframe, updateOpExByVendorReportSuccessForTimeframe, fetchOpExByVendorReportSummary, updateOpExByVendorReportSummaryFailure, updateOpExByVendorReportSummarySuccess, updateOpExByVendorTimeFrame, updateOpExByVendorUIState, clearOpExByVendor, updateOpExByVendorAdditionalBalancesSelection, updateOpExByVendorSort, updateOpExByVendorSortOrSearchInProgress, updateOpExByVendorPageFetchInProgress, updateOpExByVendorDownloadState, clearOpExByVendorSearch, updateOpExByVendorPageLimit, updateOpExByVendorSearchString, } = opexByVendor.actions;
291
+ export const { fetchOpExByVendor, updateOpExByVendorReportGenerated, fetchOpExByVendorReportForTimeframe, updateOpExByVendorReportFailureForTimeframe, updateOpExByVendorReportSuccessForTimeframe, fetchOpExByVendorReportSummary, updateOpExByVendorReportSummaryFailure, updateOpExByVendorReportSummarySuccess, updateOpExByVendorTimeFrame, updateOpExByVendorUIState, clearOpExByVendor, updateOpExByVendorAdditionalBalancesSelection, updateOpExByVendorSort, updateOpExByVendorSortOrSearchInProgress, updateOpExByVendorPageFetchInProgress, updateOpExByVendorDownloadState, clearOpExByVendorSearch, updateOpExByVendorPageLimit, updateOpExByVendorSearchString, refreshOpExByVendorReport, refreshOpExByVendorReportSuccess, refreshOpExByVendorReportFailure, } = opexByVendor.actions;
268
292
  export default opexByVendor.reducer;
269
293
  const doUpdateOpexByVendorReport = (draft, timeframe, reportPayload, queryProps) => {
270
294
  if (reportPayload.status.is_report_generated == null) {
@@ -317,10 +341,9 @@ const doUpdateOpexByVendorReportSummary = (draft, reportPayload) => {
317
341
  const report = mapReportPayloadToReport(reportPayload);
318
342
  draft.firstMonthOfFY = report.firstMonthOfFY;
319
343
  draft.bookCloseDate = report.bookCloseDate;
320
- draft.lastRefreshSuccessTime =
321
- reportPayload.status.last_refresh_success_time != null
322
- ? date(reportPayload.status.last_refresh_success_time)
323
- : undefined;
344
+ if (reportPayload.status.last_refresh_success_time != null) {
345
+ draft.lastRefreshSuccessTime = date(reportPayload.status.last_refresh_success_time);
346
+ }
324
347
  draft.totalOpexByVendor = combineCOABalanceGrouped(mapCOABalanceGroupedPayloadV2ToCOABalanceGrouped(reportPayload.operating_expenses, reportPayload.currency, 'month'), mapCOABalanceGroupedPayloadV2ToCOABalanceGrouped(reportPayload.operating_expenses, reportPayload.currency, 'quarter'));
325
348
  draft.summaryOpexByVendorKey = mapVendorSummaryBalances(reportPayload.operating_expenses, reportPayload.currency);
326
349
  }
@@ -67,6 +67,7 @@ export const getOpExByVendorReport = createSelector((state) => state.opExByVendo
67
67
  filter,
68
68
  isReportGenerated: opExByVendorState.opExByVendorReportGenerated,
69
69
  lastRefreshSuccessTime: opExByVendorState.lastRefreshSuccessTime,
70
+ refreshStatus: opExByVendorState.refreshStatus,
70
71
  opexBalances,
71
72
  summaryBalances,
72
73
  allTimeframeTicks,
@@ -0,0 +1,21 @@
1
+ import { of } from 'rxjs';
2
+ import { catchError, filter, mergeMap, switchMap } from 'rxjs/operators';
3
+ import { createZeniAPIStatus } from '../../responsePayload';
4
+ import { date } from '../../zeniDayJS';
5
+ import { refreshOpExByVendorReport, refreshOpExByVendorReportFailure, refreshOpExByVendorReportSuccess, } from './opExByVendorReducer';
6
+ export const refreshOpExByVendorReportEpic = (actions$, _state, zeniAPI) => actions$.pipe(filter(refreshOpExByVendorReport.match), switchMap(() => {
7
+ return zeniAPI
8
+ .postAndGetJSON(`${zeniAPI.apiEndPoints.accountMicroServiceBaseUrl}/1.0/reports/operating-expenses-by-vendor/refresh`)
9
+ .pipe(mergeMap((response) => {
10
+ if (response.status.code >= 200 && response.status.code < 300) {
11
+ const lastRefreshSuccessTime = response.status.last_refresh_success_time != null
12
+ ? date(response.status.last_refresh_success_time)
13
+ : undefined;
14
+ return of(refreshOpExByVendorReportSuccess({ lastRefreshSuccessTime }));
15
+ }
16
+ else {
17
+ return of(refreshOpExByVendorReportFailure(response.status));
18
+ }
19
+ }), catchError((error) => of(refreshOpExByVendorReportFailure(createZeniAPIStatus('Unexpected error', 'Refresh OpEx by Vendor REST API call errored out: ' +
20
+ JSON.stringify(error))))));
21
+ }));
@@ -1,5 +1,6 @@
1
1
  import { of } from 'rxjs';
2
2
  import { catchError, filter, mergeMap, switchMap } from 'rxjs/operators';
3
+ import { openSnackbar } from '../../../entity/snackbar/snackbarReducer';
3
4
  import { createZeniAPIStatus, isSuccessStatus } from '../../../responsePayload';
4
5
  import { sendReferralInvite, sendReferralInviteFailure, sendReferralInviteSuccess, } from '../referralReducer';
5
6
  export const sendReferralInviteEpic = (actions$, state$, zeniAPI) => actions$.pipe(filter(sendReferralInvite.match), switchMap(() => {
@@ -13,7 +14,14 @@ export const sendReferralInviteEpic = (actions$, state$, zeniAPI) => actions$.pi
13
14
  .postAndGetJSON(`${zeniAPI.apiEndPoints.tenantMicroServiceBaseUrl}/1.0/company-referrals`, invitePayload)
14
15
  .pipe(mergeMap((response) => {
15
16
  if (isSuccessStatus(response)) {
16
- const actions = [sendReferralInviteSuccess()];
17
+ const actions = [
18
+ sendReferralInviteSuccess(),
19
+ openSnackbar({
20
+ messageSection: 'referral_invite_sent',
21
+ messageText: 'success',
22
+ type: 'success',
23
+ }),
24
+ ];
17
25
  return actions;
18
26
  }
19
27
  else {
@@ -4,6 +4,7 @@ import { date } from '../../zeniDayJS';
4
4
  import { toZeniUrl } from '../../zeniUrl';
5
5
  import { toReferralInvitation, } from './referralPayload';
6
6
  import { toSupportedRewardPlansType, } from './referralState';
7
+ export const DEFAULT_REFERRER_AMOUNT = 4000;
7
8
  export const initialState = {
8
9
  fetchState: 'Not-Started',
9
10
  error: undefined,
@@ -44,7 +45,7 @@ export const initialState = {
44
45
  },
45
46
  rewardsPlan: {
46
47
  referrerAmount: {
47
- amount: 0,
48
+ amount: DEFAULT_REFERRER_AMOUNT,
48
49
  currencyCode: 'USD',
49
50
  currencySymbol: '$',
50
51
  },
@@ -53,7 +54,7 @@ export const initialState = {
53
54
  currencyCode: 'USD',
54
55
  currencySymbol: '$',
55
56
  },
56
- planName: 'PLAN_1000',
57
+ planName: 'PLAN_4000',
57
58
  planId: 'dummy',
58
59
  referralLink: toZeniUrl(''),
59
60
  },
@@ -111,7 +111,11 @@ const setupView = createSlice({
111
111
  saveSetupViewDataInLocalStore(draft, action) {
112
112
  const setupViewLocalData = action.payload;
113
113
  if (setupViewLocalData.companyDetails != null) {
114
- draft.companyDetails.localData = setupViewLocalData.companyDetails;
114
+ const syncToken = setupViewLocalData.companyDetails.syncToken ?? draft.companyDetails.localData?.syncToken;
115
+ draft.companyDetails.localData = {
116
+ ...setupViewLocalData.companyDetails,
117
+ ...(syncToken != null ? { syncToken } : {}),
118
+ };
115
119
  }
116
120
  if (setupViewLocalData.primaryContactDetails != null) {
117
121
  draft.primaryContactDetails.localData =
@@ -16,8 +16,8 @@ export const saveTaskDetailEpic = (actions$, state$, zeniAPI) => actions$.pipe(f
16
16
  // fall back to the entity's current group. For a create, use the
17
17
  // taskGroupId from the action payload.
18
18
  const localTaskGroupId = taskId != null
19
- ? state.taskDetailState.editTaskStateById[taskId]?.taskDetailLocalData
20
- .taskGroupId
19
+ ? state.taskDetailState.editTaskStateById[taskId]
20
+ ?.taskDetailLocalData?.taskGroupId
21
21
  : undefined;
22
22
  // Distinguish three cases (PR #3101 review):
23
23
  // - create (taskId == null): use the action payload group.
@@ -1,5 +1,5 @@
1
1
  import { createAction } from '@reduxjs/toolkit';
2
- import { EMPTY, of } from 'rxjs';
2
+ import { EMPTY } from 'rxjs';
3
3
  import { catchError, filter, mergeMap } from 'rxjs/operators';
4
4
  /**
5
5
  * Fire-and-forget action — dispatched by the task-comments UI when a comment
@@ -33,5 +33,5 @@ const handleSendCommentMentionNotification = ({ taskId, mentionedUserIds, perfor
33
33
  // non-success responses and network errors so a Slack outage
34
34
  // does not pollute the dev console for every @-mention.
35
35
  return EMPTY;
36
- }), catchError(() => of()));
36
+ }), catchError(() => EMPTY));
37
37
  };
@@ -32,7 +32,7 @@ export const getTaskDetail = (state, taskId) => {
32
32
  const taskEntity = getTaskById(taskState, taskId);
33
33
  subtasks =
34
34
  taskEntity != null
35
- ? getTasksByIds(taskState, taskEntity.subTasksIds)
35
+ ? getTasksByIds(taskState, taskEntity.subTasksIds ?? [])
36
36
  : [];
37
37
  const fileIdsInEntity = taskEntity?.fileIds ?? [];
38
38
  if (taskEntity != null) {
@@ -490,7 +490,9 @@ const taskList = createSlice({
490
490
  // never move between tabs on their own.
491
491
  builder.addCase(updateTasks, (draft, action) => {
492
492
  action.payload.forEach((taskPayload) => {
493
- if (taskPayload.parent_task_id) {
493
+ // Only re-bucket parents; subtasks (parent_task_id is a non-empty
494
+ // string) are nested under their parent row and don't move tabs.
495
+ if (taskPayload.parent_task_id !== null && taskPayload.parent_task_id !== undefined && taskPayload.parent_task_id !== '') {
494
496
  return;
495
497
  }
496
498
  const taskId = taskPayload.task_id;
@@ -2,6 +2,7 @@ import recordGet from 'lodash/get';
2
2
  import { from } from 'rxjs';
3
3
  import { filter, mergeMap } from 'rxjs/operators';
4
4
  import { getSupportedTransactionById } from '../../../entity/transaction/transactionSelector';
5
+ import { getIsAccountingProjectsEnabled } from '../../../entity/tenant/tenantSelector';
5
6
  import { saveTransactionDetail, updateTransactionDetail, } from '../transactionDetailReducer';
6
7
  import { getTransactionDetailKey } from '../transactionDetailState';
7
8
  import { toTransactionUpdatesFromTransactionDetailLocalData } from './saveTransactionHelper';
@@ -10,12 +11,13 @@ export const saveTransactionDetailEpic = (actions$, state$) => actions$.pipe(fil
10
11
  const fetchActions = [];
11
12
  const transactionDetailState = state$.value.transactionDetailState;
12
13
  const transactionState = state$.value.transactionState;
14
+ const isAccountingProjectsEnabled = getIsAccountingProjectsEnabled(state$.value);
13
15
  const key = getTransactionDetailKey(transactionId);
14
16
  const detail = recordGet(transactionDetailState.transactionDetailById, key, undefined);
15
17
  const transaction = getSupportedTransactionById(transactionState, key);
16
18
  if (detail != null && transaction != null) {
17
19
  const localData = detail.transactionDetailLocalData;
18
- const transactionSaveUpdates = toTransactionUpdatesFromTransactionDetailLocalData(localData, transaction, isExpenseAutomationEnabled);
20
+ const transactionSaveUpdates = toTransactionUpdatesFromTransactionDetailLocalData(localData, transaction, isExpenseAutomationEnabled, isAccountingProjectsEnabled);
19
21
  fetchActions.push(updateTransactionDetail(transactionId, transactionSaveUpdates, isExpenseAutomationEnabled, cotTransactionTracking, isUncategorizedExpenseCategoryEnabled));
20
22
  return from(fetchActions);
21
23
  }
@@ -21,7 +21,7 @@ import { isAccountUncategorized } from '../transactionDetailLocalDataHelper';
21
21
  * 4. Only difference between this and Miscategorized will be that we will
22
22
  * additionally fire “Mark as not miscat” for every point above.
23
23
  */
24
- export const toTransactionUpdatesFromTransactionDetailLocalData = (transactionDetailLocalData, transaction, isExpenseAutomationEnabled) => {
24
+ export const toTransactionUpdatesFromTransactionDetailLocalData = (transactionDetailLocalData, transaction, isExpenseAutomationEnabled, isAccountingProjectsEnabled = false) => {
25
25
  const updates = {
26
26
  updatesByLineId: {},
27
27
  };
@@ -30,6 +30,12 @@ export const toTransactionUpdatesFromTransactionDetailLocalData = (transactionDe
30
30
  const isTransactionMisCategorized = isTransactionMiscategorized(transaction, isExpenseAutomationEnabled);
31
31
  updates.memo = transactionDetailLocalData.memo;
32
32
  updates.documentId = transactionDetailLocalData.documentId;
33
+ // Project is at the transaction level only for invoice and credit_memo,
34
+ // and only when the projects feature is enabled for this tenant.
35
+ if (isAccountingProjectsEnabled &&
36
+ (transaction.type === 'invoice' || transaction.type === 'credit_memo')) {
37
+ updates.project = transactionDetailLocalData.project;
38
+ }
33
39
  const selectedEntity = transactionDetailLocalData.selectedEntity;
34
40
  const updatedEntityId = transactionDetailLocalData.updatePreviousTransactionsWithUpdatedVendor ===
35
41
  true
@@ -52,7 +58,10 @@ export const toTransactionUpdatesFromTransactionDetailLocalData = (transactionDe
52
58
  lineDescription: lineItem.lineDescription,
53
59
  class: lineItem.class,
54
60
  account: lineItem.account,
55
- project: lineItem.project,
61
+ // Project is at the transaction level for invoice/credit_memo; omit from lines.
62
+ ...(transaction.type !== 'invoice' && transaction.type !== 'credit_memo'
63
+ ? { project: lineItem.project }
64
+ : {}),
56
65
  recommendation: convertCOTRecommendationsToRegular(lineItem.recommendationsWithCOT),
57
66
  };
58
67
  if (line.type === 'journal_entry_transaction_line' ||
@@ -19,6 +19,7 @@ import { getSupportedTransactionById } from '../../../entity/transaction/transac
19
19
  import { updateVendors } from '../../../entity/vendor/vendorReducer';
20
20
  import { getVendorByVendorId } from '../../../entity/vendor/vendorSelector';
21
21
  import { createZeniAPIStatus, isActionNotAllowed, isSuccessResponse, } from '../../../responsePayload';
22
+ import { getIsAccountingClassesEnabled, getIsAccountingProjectsEnabled, } from '../../../entity/tenant/tenantSelector';
22
23
  import { getUncategorizedAccounts } from '../../accountList/accountListSelector';
23
24
  import { getPendingReviewLineIdsFromTransaction } from '../../expenseAutomationView/helpers/transactionCategorizationLocalDataHelper';
24
25
  import { syncTransactionCategorizationFromDetailSave } from '../../expenseAutomationView/reducers/transactionsViewReducer';
@@ -27,9 +28,10 @@ import { getTransactionDetailKey, } from '../transactionDetailState';
27
28
  import { isTransactionUncategorized } from './saveTransactionHelper';
28
29
  export const updateTransactionDetailEpic = (actions$, state$, zeniAPI) => actions$.pipe(filter(updateTransactionDetail.match), mergeMap((action) => {
29
30
  const { transactionState, vendorState, transactionDetailState } = state$.value;
31
+ const isAccountingProjectsEnabled = getIsAccountingProjectsEnabled(state$.value);
30
32
  const { cotTransactionTracking, transactionId, transactionSaveUpdates, isUncategorizedExpenseCategoryEnabled, } = action.payload;
31
33
  return zeniAPI
32
- .putAndGetJSON(`${zeniAPI.apiEndPoints.accountMicroServiceBaseUrl}/1.0/transactions/${action.payload.transactionId.id}/categorize`, preparePayload(transactionState, vendorState, transactionDetailState, transactionId, transactionSaveUpdates, cotTransactionTracking))
34
+ .putAndGetJSON(`${zeniAPI.apiEndPoints.accountMicroServiceBaseUrl}/1.0/transactions/${action.payload.transactionId.id}/categorize`, preparePayload(transactionState, vendorState, transactionDetailState, transactionId, transactionSaveUpdates, cotTransactionTracking, isAccountingProjectsEnabled))
33
35
  .pipe(mergeMap((response) => {
34
36
  if (isSuccessResponse(response) && response.data != null) {
35
37
  const transactionIdResp = toTransactionID(response.data.transaction);
@@ -67,7 +69,9 @@ export const updateTransactionDetailEpic = (actions$, state$, zeniAPI) => action
67
69
  // then we need to skip mark-miscategorized-as-correct api call
68
70
  if (toVendorTransactionTypeStrict(response.data.transaction.transaction_type) != null ||
69
71
  toJEAndDepositTransactionTypeStrict(response.data.transaction.transaction_type) != null) {
70
- const isAnyClassMissing = checkIfClassMissingForAnyLineItem(response.data.transaction);
72
+ const isAccountingClassesEnabled = getIsAccountingClassesEnabled(state$.value);
73
+ const isAnyClassMissing = isAccountingClassesEnabled &&
74
+ checkIfClassMissingForAnyLineItem(response.data.transaction);
71
75
  const isVendorOrCustomerMissingInAnyLine = toJEAndDepositTransactionTypeStrict(response.data.transaction.transaction_type) != null &&
72
76
  checkIfVendorOrCustomerMissingForAnyLineItem(response.data.transaction);
73
77
  if ((vendorUpdates?.id == null &&
@@ -123,7 +127,7 @@ export const updateTransactionDetailEpic = (actions$, state$, zeniAPI) => action
123
127
  }
124
128
  }), catchError((error) => of(updateTransactionDetailSaveStatus(action.payload.transactionId, 'Error', createZeniAPIStatus('Unexpected Error', `REST API call errored out ${error}`)))));
125
129
  }));
126
- function preparePayload(transactionState, vendorState, transactionDetailState, transactionId, saveUpdates, cotTransactionTracking) {
130
+ function preparePayload(transactionState, vendorState, transactionDetailState, transactionId, saveUpdates, cotTransactionTracking, isAccountingProjectsEnabled) {
127
131
  const transactionDetailKey = getTransactionDetailKey(transactionId);
128
132
  const detail = getSupportedTransactionById(transactionState, transactionDetailKey);
129
133
  const tranDetail = recordGet(transactionDetailState.transactionDetailById, transactionDetailKey, undefined);
@@ -155,7 +159,7 @@ function preparePayload(transactionState, vendorState, transactionDetailState, t
155
159
  };
156
160
  }
157
161
  if (detail != null) {
158
- const transUpdates = toTransactionUpdatePayload(saveUpdates.updates, detail, cotTransactionTracking, vendorUpdates, customerUpdates, currentSelectedEntity);
162
+ const transUpdates = toTransactionUpdatePayload(saveUpdates.updates, detail, cotTransactionTracking, isAccountingProjectsEnabled, vendorUpdates, customerUpdates, currentSelectedEntity);
159
163
  const entityUpdates = toEntityUpdatePayload(saveUpdates.entityRelatedUpdates);
160
164
  return {
161
165
  entity_related_updates: entityUpdates,
@@ -165,7 +169,7 @@ function preparePayload(transactionState, vendorState, transactionDetailState, t
165
169
  }
166
170
  return undefined;
167
171
  }
168
- const toTransactionUpdatePayload = (updates, detail, cotTransactionTracking, vendorUpdates, customerUpdates, tranSelectedEntity) => {
172
+ export const toTransactionUpdatePayload = (updates, detail, cotTransactionTracking, isAccountingProjectsEnabled, vendorUpdates, customerUpdates, tranSelectedEntity) => {
169
173
  const transaction = Object.assign({}, detail);
170
174
  transaction.memo = updates.memo ?? '';
171
175
  // Unlike `memo` (always reset to '' when absent), `documentId` falls back to
@@ -226,6 +230,31 @@ const toTransactionUpdatePayload = (updates, detail, cotTransactionTracking, ven
226
230
  let basePayload;
227
231
  if (isCustomerTransaction(transaction)) {
228
232
  basePayload = toCustomerTransactionPayload(transaction);
233
+ // For invoice/credit_memo only, project lives at the transaction level.
234
+ // Guard behind the projects feature flag to avoid touching the field when
235
+ // projects are disabled for this tenant. Clear-project (null injection) is
236
+ // intentionally out of scope for this change — the null write-back path will
237
+ // be added in a follow-up once the backend contract is confirmed.
238
+ if (isAccountingProjectsEnabled &&
239
+ (transaction.type === 'invoice' || transaction.type === 'credit_memo') &&
240
+ updates.project != null) {
241
+ basePayload = {
242
+ ...basePayload,
243
+ accounting_project_id: updates.project.projectId,
244
+ // Only spread integration_id / name when we have a concrete value; omitting
245
+ // them avoids accidentally nulling/blanking out the server-side value when
246
+ // the load path didn't surface the field (e.g. list-shaped data or pre-deploy
247
+ // store where the API returned projectId but not the name/integration_id).
248
+ ...(updates.project.accountingProviderProjectId != null
249
+ ? {
250
+ accounting_project_integration_id: updates.project.accountingProviderProjectId,
251
+ }
252
+ : {}),
253
+ ...(updates.project.projectName
254
+ ? { accounting_project_name: updates.project.projectName }
255
+ : {}),
256
+ };
257
+ }
229
258
  }
230
259
  else if (isVendorTransaction(transaction)) {
231
260
  if (tranSelectedEntity != null && tranSelectedEntity.type === 'customer') {
@@ -12,6 +12,17 @@ export const toTransactionDetailLocalData = (transaction, defaultVendorId, uncat
12
12
  const vendorId = defaultVendorId ?? transaction.vendor?.id;
13
13
  transactionLocal.memo = transaction.memo;
14
14
  transactionLocal.documentId = transaction.documentId;
15
+ // Project is at the transaction level only for invoice and credit_memo.
16
+ if ((transaction.type === 'invoice' || transaction.type === 'credit_memo') &&
17
+ transaction.projectId != null) {
18
+ transactionLocal.project = {
19
+ projectId: transaction.projectId,
20
+ projectName: transaction.projectName ?? '',
21
+ ...(transaction.accountingProviderProjectId != null
22
+ ? { accountingProviderProjectId: transaction.accountingProviderProjectId }
23
+ : {}),
24
+ };
25
+ }
15
26
  transactionLocal.vendorUpdates =
16
27
  isVendorTransaction(transaction) && vendorId != null
17
28
  ? {
@@ -423,6 +434,7 @@ export const toUpdateLineItemsOriginalCategoryClass = (transaction, transactionD
423
434
  ...initialSupportedTransactionDetail.transactionDetailLocalData,
424
435
  memo: transactionDetailLocalData.memo,
425
436
  documentId: transactionDetailLocalData.documentId,
437
+ project: transactionDetailLocalData.project,
426
438
  lineItemById: newLineItems,
427
439
  vendorDetails: transactionDetailLocalData.vendorDetails,
428
440
  vendorUpdates: transactionDetailLocalData.vendorUpdates,
@@ -527,6 +539,7 @@ export const toSetAllLineItemsToCategoryClass = (transaction, transactionDetailL
527
539
  ...initialSupportedTransactionDetail.transactionDetailLocalData,
528
540
  memo: transactionDetailLocalData.memo,
529
541
  documentId: transactionDetailLocalData.documentId,
542
+ project: transactionDetailLocalData.project,
530
543
  lineItemById: newLineItems,
531
544
  vendorUpdates: transactionDetailLocalData.vendorUpdates,
532
545
  vendorDetails: transactionDetailLocalData.vendorDetails,
@@ -572,6 +585,7 @@ export const toSetAllLineItemsLocalDataIsUpdateAllChecked = (transaction, transa
572
585
  ...initialSupportedTransactionDetail.transactionDetailLocalData,
573
586
  memo: transactionDetailLocalData.memo,
574
587
  documentId: transactionDetailLocalData.documentId,
588
+ project: transactionDetailLocalData.project,
575
589
  lineItemById: newLineItems,
576
590
  vendorDetails: transactionDetailLocalData.vendorDetails,
577
591
  vendorUpdates: transactionDetailLocalData.vendorUpdates,
@@ -729,6 +743,7 @@ export const setEntityRecommendationForLineIdInLocalData = (transaction, transac
729
743
  ...initialSupportedTransactionDetail.transactionDetailLocalData,
730
744
  memo: transactionDetailLocalData.memo,
731
745
  documentId: transactionDetailLocalData.documentId,
746
+ project: transactionDetailLocalData.project,
732
747
  lineItemById: newLineItems,
733
748
  vendorUpdates: transactionDetailLocalData.vendorUpdates,
734
749
  vendorDetails: transactionDetailLocalData.vendorDetails,
@@ -782,6 +797,7 @@ export const removeEntityRecommendationForLineIdInLocalData = (transaction, tran
782
797
  ...initialSupportedTransactionDetail.transactionDetailLocalData,
783
798
  memo: transactionDetailLocalData.memo,
784
799
  documentId: transactionDetailLocalData.documentId,
800
+ project: transactionDetailLocalData.project,
785
801
  lineItemById: newLineItems,
786
802
  vendorDetails: transactionDetailLocalData.vendorDetails,
787
803
  vendorUpdates: transactionDetailLocalData.vendorUpdates,
@@ -1002,6 +1018,7 @@ export const toUpdateLineItemsToOriginalVendor = (transaction, transactionDetail
1002
1018
  ...initialSupportedTransactionDetail.transactionDetailLocalData,
1003
1019
  memo: transactionDetailLocalData.memo,
1004
1020
  documentId: transactionDetailLocalData.documentId,
1021
+ project: transactionDetailLocalData.project,
1005
1022
  lineItemById: newLineItems,
1006
1023
  isVendorUpdateAllChecked: false,
1007
1024
  latestUpdatedLineWithEntity: transactionDetailLocalData.latestUpdatedLineWithEntity,