@zeniai/client-epic-state 5.1.90 → 5.1.91

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 (145) hide show
  1. package/lib/entity/aiCfo/aiCfoPayload.d.ts +7 -0
  2. package/lib/entity/aiCfo/aiCfoReducer.d.ts +3 -2
  3. package/lib/entity/aiCfo/aiCfoReducer.js +25 -2
  4. package/lib/entity/aiCfo/aiCfoState.d.ts +7 -0
  5. package/lib/entity/company/companyPayload.d.ts +4 -0
  6. package/lib/entity/company/companyPayload.js +18 -8
  7. package/lib/entity/company/companyStateTypes.d.ts +4 -0
  8. package/lib/entity/customerSatisfaction/customerSatisfactionReducer.d.ts +1 -1
  9. package/lib/entity/customerSatisfaction/customerSatisfactionReducer.js +6 -2
  10. package/lib/entity/snackbar/snackbarTypes.d.ts +1 -1
  11. package/lib/entity/snackbar/snackbarTypes.js +31 -0
  12. package/lib/entity/tenant/tenantPayload.d.ts +1 -0
  13. package/lib/entity/tenant/tenantReducer.d.ts +5 -1
  14. package/lib/entity/tenant/tenantReducer.js +22 -2
  15. package/lib/entity/tenant/tenantSelector.d.ts +2 -0
  16. package/lib/entity/tenant/tenantSelector.js +11 -1
  17. package/lib/entity/tenant/tenantState.d.ts +1 -0
  18. package/lib/epic.d.ts +3 -1
  19. package/lib/epic.js +5 -1
  20. package/lib/esm/entity/aiCfo/aiCfoReducer.js +23 -1
  21. package/lib/esm/entity/company/companyPayload.js +18 -8
  22. package/lib/esm/entity/customerSatisfaction/customerSatisfactionReducer.js +4 -1
  23. package/lib/esm/entity/snackbar/snackbarTypes.js +31 -0
  24. package/lib/esm/entity/tenant/tenantReducer.js +21 -1
  25. package/lib/esm/entity/tenant/tenantSelector.js +9 -0
  26. package/lib/esm/epic.js +5 -1
  27. package/lib/esm/index.js +10 -9
  28. package/lib/esm/view/companyHealthMetricView/companyHealthMetricConfigReducer.js +4 -1
  29. package/lib/esm/view/companyView/companyViewReducer.js +58 -1
  30. package/lib/esm/view/companyView/epic/updateCompanyProductServicesEpic.js +91 -0
  31. package/lib/esm/view/companyView/epic/updateCompanyQboRealmIdEpic.js +58 -0
  32. package/lib/esm/view/companyView/selector/companyManagementViewSelector.js +52 -0
  33. package/lib/esm/view/companyView/types/cockpitTypes.js +13 -1
  34. package/lib/esm/view/companyView/types/companyManagementViewState.js +2 -0
  35. package/lib/esm/view/invoicing/createCreditNote/createCreditNoteEpic.js +24 -4
  36. package/lib/esm/view/invoicing/createInvoice/createInvoiceEpic.js +24 -4
  37. package/lib/esm/view/invoicing/createInvoice/createInvoiceSelector.js +37 -10
  38. package/lib/esm/view/invoicing/discountAction/discountActionEpic.js +33 -9
  39. package/lib/esm/view/invoicing/dunningAction/dunningActionEpic.js +35 -9
  40. package/lib/esm/view/invoicing/editInvoicingCatalogItemDetailView/catalogDetailEditPayload.js +5 -1
  41. package/lib/esm/view/invoicing/editInvoicingCatalogItemDetailView/saveInvoicingCatalogItemEpic.js +32 -5
  42. package/lib/esm/view/invoicing/editInvoicingCouponDetailView/saveInvoicingCouponEpic.js +23 -3
  43. package/lib/esm/view/invoicing/editInvoicingCustomerDetailView/saveInvoicingCustomerEpic.js +24 -4
  44. package/lib/esm/view/invoicing/editInvoicingSubscriptionDetailView/saveInvoicingSubscriptionEpic.js +24 -4
  45. package/lib/esm/view/invoicing/editInvoicingSubscriptionDetailView/syncInvoicingSubscriptionCouponsEpic.js +20 -3
  46. package/lib/esm/view/invoicing/invoiceAction/invoiceActionEpic.js +60 -17
  47. package/lib/esm/view/invoicing/invoicingConfigView/invoicingConfigViewReducer.js +30 -0
  48. package/lib/esm/view/invoicing/invoicingConfigView/invoicingConfigViewSelector.js +4 -29
  49. package/lib/esm/view/invoicing/invoicingDunningCaseDetailView/fetchInvoicingDunningEmailPreviewEpic.js +26 -0
  50. package/lib/esm/view/invoicing/invoicingDunningCaseDetailView/fetchInvoicingDunningEmailPreviewPageEpic.js +16 -0
  51. package/lib/esm/view/invoicing/invoicingDunningCaseDetailView/invoicingDunningCaseDetailViewPayload.js +9 -1
  52. package/lib/esm/view/invoicing/invoicingDunningCaseDetailView/invoicingDunningCaseDetailViewReducer.js +50 -1
  53. package/lib/esm/view/invoicing/invoicingDunningCaseDetailView/invoicingDunningCaseDetailViewSelector.js +14 -1
  54. package/lib/esm/view/invoicing/invoicingQboView/epics/retryInvoicingQboSyncTaskEpic.js +29 -10
  55. package/lib/esm/view/invoicing/invoicingQboView/epics/startInvoicingQboBackfillEpic.js +28 -5
  56. package/lib/esm/view/invoicing/issueCreditNote/issueCreditNoteEpic.js +24 -4
  57. package/lib/esm/view/invoicing/issueCreditNote/issueCreditNoteSelector.js +22 -14
  58. package/lib/esm/view/invoicing/recordPayment/recordPaymentEpic.js +46 -7
  59. package/lib/esm/view/invoicing/settingsView/settingsViewEpics.js +26 -8
  60. package/lib/esm/view/invoicing/settingsView/settingsViewReducer.js +1 -1
  61. package/lib/esm/view/invoicing/settingsView/settingsViewSelector.js +4 -1
  62. package/lib/esm/view/invoicing/subscriptionAction/subscriptionActionEpic.js +41 -9
  63. package/lib/esm/view/vendorFiling1099/vendorFiling1099UploadDetails/vendorFiling1099UploadDetailsReducer.js +4 -1
  64. package/lib/esm/view/vendorTabView/vendorTabViewReducer.js +4 -1
  65. package/lib/index.d.ts +16 -12
  66. package/lib/index.js +63 -50
  67. package/lib/view/companyHealthMetricView/companyHealthMetricConfigReducer.d.ts +1 -1
  68. package/lib/view/companyHealthMetricView/companyHealthMetricConfigReducer.js +5 -2
  69. package/lib/view/companyView/companyViewReducer.d.ts +22 -1
  70. package/lib/view/companyView/companyViewReducer.js +60 -3
  71. package/lib/view/companyView/epic/updateCompanyProductServicesEpic.d.ts +10 -0
  72. package/lib/view/companyView/epic/updateCompanyProductServicesEpic.js +95 -0
  73. package/lib/view/companyView/epic/updateCompanyQboRealmIdEpic.d.ts +8 -0
  74. package/lib/view/companyView/epic/updateCompanyQboRealmIdEpic.js +62 -0
  75. package/lib/view/companyView/selector/companyManagementViewSelector.d.ts +3 -0
  76. package/lib/view/companyView/selector/companyManagementViewSelector.js +55 -0
  77. package/lib/view/companyView/types/cockpitTypes.d.ts +7 -3
  78. package/lib/view/companyView/types/cockpitTypes.js +15 -1
  79. package/lib/view/companyView/types/companyManagementViewState.d.ts +2 -0
  80. package/lib/view/companyView/types/companyManagementViewState.js +2 -0
  81. package/lib/view/expenseAutomationView/helpers/transactionCategorizationLocalDataHelper.d.ts +1 -1
  82. package/lib/view/invoicing/createCreditNote/createCreditNoteEpic.d.ts +2 -1
  83. package/lib/view/invoicing/createCreditNote/createCreditNoteEpic.js +23 -3
  84. package/lib/view/invoicing/createInvoice/createInvoiceEpic.d.ts +2 -1
  85. package/lib/view/invoicing/createInvoice/createInvoiceEpic.js +23 -3
  86. package/lib/view/invoicing/createInvoice/createInvoiceSelector.d.ts +22 -8
  87. package/lib/view/invoicing/createInvoice/createInvoiceSelector.js +37 -10
  88. package/lib/view/invoicing/discountAction/discountActionEpic.d.ts +2 -1
  89. package/lib/view/invoicing/discountAction/discountActionEpic.js +32 -8
  90. package/lib/view/invoicing/dunningAction/dunningActionEpic.d.ts +2 -1
  91. package/lib/view/invoicing/dunningAction/dunningActionEpic.js +34 -8
  92. package/lib/view/invoicing/editInvoicingCatalogItemDetailView/catalogDetailEditPayload.d.ts +3 -1
  93. package/lib/view/invoicing/editInvoicingCatalogItemDetailView/catalogDetailEditPayload.js +5 -1
  94. package/lib/view/invoicing/editInvoicingCatalogItemDetailView/saveInvoicingCatalogItemEpic.d.ts +2 -1
  95. package/lib/view/invoicing/editInvoicingCatalogItemDetailView/saveInvoicingCatalogItemEpic.js +31 -4
  96. package/lib/view/invoicing/editInvoicingCouponDetailView/saveInvoicingCouponEpic.d.ts +2 -1
  97. package/lib/view/invoicing/editInvoicingCouponDetailView/saveInvoicingCouponEpic.js +22 -2
  98. package/lib/view/invoicing/editInvoicingCustomerDetailView/saveInvoicingCustomerEpic.d.ts +2 -1
  99. package/lib/view/invoicing/editInvoicingCustomerDetailView/saveInvoicingCustomerEpic.js +23 -3
  100. package/lib/view/invoicing/editInvoicingSubscriptionDetailView/saveInvoicingSubscriptionEpic.d.ts +2 -1
  101. package/lib/view/invoicing/editInvoicingSubscriptionDetailView/saveInvoicingSubscriptionEpic.js +23 -3
  102. package/lib/view/invoicing/editInvoicingSubscriptionDetailView/syncInvoicingSubscriptionCouponsEpic.d.ts +2 -1
  103. package/lib/view/invoicing/editInvoicingSubscriptionDetailView/syncInvoicingSubscriptionCouponsEpic.js +19 -2
  104. package/lib/view/invoicing/invoiceAction/invoiceActionEpic.d.ts +2 -1
  105. package/lib/view/invoicing/invoiceAction/invoiceActionEpic.js +59 -16
  106. package/lib/view/invoicing/invoicingConfigView/invoicingConfigViewPayload.d.ts +13 -0
  107. package/lib/view/invoicing/invoicingConfigView/invoicingConfigViewReducer.d.ts +1 -1
  108. package/lib/view/invoicing/invoicingConfigView/invoicingConfigViewReducer.js +30 -0
  109. package/lib/view/invoicing/invoicingConfigView/invoicingConfigViewSelector.d.ts +11 -31
  110. package/lib/view/invoicing/invoicingConfigView/invoicingConfigViewSelector.js +4 -29
  111. package/lib/view/invoicing/invoicingConfigView/invoicingConfigViewState.d.ts +15 -0
  112. package/lib/view/invoicing/invoicingDunningCaseDetailView/fetchInvoicingDunningEmailPreviewEpic.d.ts +7 -0
  113. package/lib/view/invoicing/invoicingDunningCaseDetailView/fetchInvoicingDunningEmailPreviewEpic.js +30 -0
  114. package/lib/view/invoicing/invoicingDunningCaseDetailView/fetchInvoicingDunningEmailPreviewPageEpic.d.ts +6 -0
  115. package/lib/view/invoicing/invoicingDunningCaseDetailView/fetchInvoicingDunningEmailPreviewPageEpic.js +20 -0
  116. package/lib/view/invoicing/invoicingDunningCaseDetailView/invoicingDunningCaseDetailViewEpics.d.ts +2 -2
  117. package/lib/view/invoicing/invoicingDunningCaseDetailView/invoicingDunningCaseDetailViewPayload.d.ts +12 -0
  118. package/lib/view/invoicing/invoicingDunningCaseDetailView/invoicingDunningCaseDetailViewPayload.js +11 -0
  119. package/lib/view/invoicing/invoicingDunningCaseDetailView/invoicingDunningCaseDetailViewReducer.d.ts +21 -4
  120. package/lib/view/invoicing/invoicingDunningCaseDetailView/invoicingDunningCaseDetailViewReducer.js +51 -2
  121. package/lib/view/invoicing/invoicingDunningCaseDetailView/invoicingDunningCaseDetailViewSelector.d.ts +3 -0
  122. package/lib/view/invoicing/invoicingDunningCaseDetailView/invoicingDunningCaseDetailViewSelector.js +16 -2
  123. package/lib/view/invoicing/invoicingDunningCaseDetailView/invoicingDunningCaseDetailViewState.d.ts +13 -0
  124. package/lib/view/invoicing/invoicingQboView/epics/invoicingQboViewActionType.d.ts +2 -1
  125. package/lib/view/invoicing/invoicingQboView/epics/retryInvoicingQboSyncTaskEpic.js +28 -9
  126. package/lib/view/invoicing/invoicingQboView/epics/startInvoicingQboBackfillEpic.js +27 -4
  127. package/lib/view/invoicing/issueCreditNote/issueCreditNoteEpic.d.ts +2 -1
  128. package/lib/view/invoicing/issueCreditNote/issueCreditNoteEpic.js +23 -3
  129. package/lib/view/invoicing/issueCreditNote/issueCreditNoteSelector.d.ts +24 -7
  130. package/lib/view/invoicing/issueCreditNote/issueCreditNoteSelector.js +23 -16
  131. package/lib/view/invoicing/recordPayment/recordPaymentEpic.d.ts +2 -1
  132. package/lib/view/invoicing/recordPayment/recordPaymentEpic.js +45 -6
  133. package/lib/view/invoicing/settingsView/settingsViewEpics.d.ts +2 -1
  134. package/lib/view/invoicing/settingsView/settingsViewEpics.js +25 -7
  135. package/lib/view/invoicing/settingsView/settingsViewReducer.d.ts +10 -1
  136. package/lib/view/invoicing/settingsView/settingsViewReducer.js +1 -1
  137. package/lib/view/invoicing/settingsView/settingsViewSelector.d.ts +4 -2
  138. package/lib/view/invoicing/settingsView/settingsViewSelector.js +4 -1
  139. package/lib/view/invoicing/subscriptionAction/subscriptionActionEpic.d.ts +2 -1
  140. package/lib/view/invoicing/subscriptionAction/subscriptionActionEpic.js +40 -8
  141. package/lib/view/vendorFiling1099/vendorFiling1099UploadDetails/vendorFiling1099UploadDetailsReducer.d.ts +1 -1
  142. package/lib/view/vendorFiling1099/vendorFiling1099UploadDetails/vendorFiling1099UploadDetailsReducer.js +5 -2
  143. package/lib/view/vendorTabView/vendorTabViewReducer.d.ts +1 -1
  144. package/lib/view/vendorTabView/vendorTabViewReducer.js +5 -2
  145. package/package.json +2 -2
package/lib/esm/index.js CHANGED
@@ -71,7 +71,7 @@ import { getSnackbar } from './entity/snackbar/snackbarSelector';
71
71
  import { ALL_WEEK_DAYS, toDayOfWeek, toPriorityCodeType, toTaskStatusCodeType, } from './entity/task/taskState';
72
72
  import { getTaskGroupById } from './entity/taskGroup/taskGroupSelector';
73
73
  import { deleteConnection, doMagicLinkSignIn, doSignIn, doSignOut, fetchActiveTenant, fetchAllTenants, fetchExcludedResources as fetchExcludedResourcesForTenant, fetchExternalConnections as fetchExternalConnectionsForTenant, fetchSubscriptionSummaryForTenant, initEmailConnectOAuth, resendVerifyDeviceOTP, resetSignInState, saveAPIKeyConnection, saveConnectorCredentials, saveExternalConnection as saveExternalConnectionForTenant, saveOAuthConnection, sendEmailMagicLinkToUser, sendSessionHeartbeat, toExternalIntegrationType, toExternalSupportedTool, updateCurrentTenant, updateSignInState, verifyDeviceWithTwoFA, } from './entity/tenant/tenantReducer';
74
- import { getCurrentTenant, getIsAccountingClassesEnabled, getIsAccountingProjectsEnabled, getTenantBaseById, getTenantBaseViewForTenantId, getTenantBaseViewForTenantView, getTenantsBaseByCheckInDateSelector, getTenantsByCheckInDateSelector, getTenantsCoreDetailsByCheckInDateSelector, isOtherProductsEnabledForTenant, isTenantBankingOnly, isTenantBookkeepingEnabled, isTenantCardsOnly, isTenantUsingZeniCOA, isZeniDomainTenant, toTenantCoreDetailsView, } from './entity/tenant/tenantSelector';
74
+ import { DEFAULT_RECEIPTS_EMAIL, getCurrentTenant, getIsAccountingClassesEnabled, getIsAccountingProjectsEnabled, getReceiptsEmail, getTenantBaseById, getTenantBaseViewForTenantId, getTenantBaseViewForTenantView, getTenantsBaseByCheckInDateSelector, getTenantsByCheckInDateSelector, getTenantsCoreDetailsByCheckInDateSelector, isOtherProductsEnabledForTenant, isTenantBankingOnly, isTenantBookkeepingEnabled, isTenantCardsOnly, isTenantUsingZeniCOA, isZeniDomainTenant, toTenantCoreDetailsView, } from './entity/tenant/tenantSelector';
75
75
  import { pushToastNotification } from './entity/toastNotification/toastNotificationReducer';
76
76
  import { getLastNotificationTime, getNotifications, } from './entity/toastNotification/toastNotificationSelector';
77
77
  import { mapFileEntityToAttachment, toContentType, toContentTypeStrict, } from './entity/transaction/stateTypes/attachment';
@@ -154,10 +154,10 @@ import { fetchCompanyMonthEndReportHistoricData, fetchCompanyMonthEndReportHisto
154
154
  import { getCompanyMonthEndReportHistoricData, getCompanyMonthEndReportSelectorView, } from './view/companyMonthEndReportView/companyMonthEndReportViewSelector';
155
155
  import { fetchCockpitContext, fetchCompanyTaskManagerView, fetchTaskManagerMetrics, updateCompanyTaskManagerViewFilters, } from './view/companyTaskManagerView/companyTaskManagerViewReducer';
156
156
  import { getCompanyTaskManagerView, } from './view/companyTaskManagerView/companyTaskManagerViewSelector';
157
- import { clearCompanyView, clearQboProjectsReconnectRequest, companyManagementSaveUpdates, companyPassportClearDataInLocalStore, companyPassportSaveDataInLocalStore, deleteCompanyOfficerInLocalStore, disableAccountingProjects, dismissCapitalizationOnboarding, enableAccountingProjects, fetchAllCockpitViews, fetchCompanyManagementView, fetchCompanyPassportView, fetchCompanyPortfolioView, fetchManagementView, fetchOnboardingView, fetchParentSubsidiaryManagementView, fetchPortfolioView, fetchSubscriptionView, fetchZeniUsers, resumeEnableAccountingProjects, saveCompanyPassportDetails, saveIndustryAndIncDateInCompanyPassportLocalStore, updateAccountingClassesEnabled, updateCapitalizationAccountThreshold, updateCompanyDownloadState, updateCompanyManagementUIState, updateCompanyPassportLocalStoreData, updateCompanyPortfolioUIState, } from './view/companyView/companyViewReducer';
157
+ import { clearCompanyView, clearQboProjectsReconnectRequest, companyManagementSaveUpdates, companyPassportClearDataInLocalStore, companyPassportSaveDataInLocalStore, deleteCompanyOfficerInLocalStore, disableAccountingProjects, dismissCapitalizationOnboarding, enableAccountingProjects, fetchAllCockpitViews, fetchCompanyManagementView, fetchCompanyPassportView, fetchCompanyPortfolioView, fetchManagementView, fetchOnboardingView, fetchParentSubsidiaryManagementView, fetchPortfolioView, fetchSubscriptionView, fetchZeniUsers, resumeEnableAccountingProjects, saveCompanyPassportDetails, saveIndustryAndIncDateInCompanyPassportLocalStore, updateAccountingClassesEnabled, updateCapitalizationAccountThreshold, updateCompanyDownloadState, updateCompanyManagementUIState, updateCompanyPassportLocalStoreData, updateCompanyPortfolioUIState, updateCompanyProductServices, updateCompanyQboRealmId, } from './view/companyView/companyViewReducer';
158
158
  import { canSendMonthEndEmailReport, shouldEnableCalendarPickerForLastReportSent, } from './view/companyView/helpers/cockpitHelpers';
159
159
  import { getParentSubsidiaryManagementView } from './view/companyView/parentSubsidiaryView/parentSubsidiaryViewSelector';
160
- import { getAddonListZeniSku, getCompanyManagementView, getPlanListZeniSku, } from './view/companyView/selector/companyManagementViewSelector';
160
+ import { getAddonListZeniSku, getCompanyManagementView, getCompanyProductServicesUpdateStatus, getCompanyQboRealmIdUpdateStatus, getPlanListZeniSku, } from './view/companyView/selector/companyManagementViewSelector';
161
161
  import { getCompanyPassportLocalStoreData, getCompanyPassportView, isCompanyPassportDataToBeSaved, } from './view/companyView/selector/companyPassportViewSelector';
162
162
  import { getCompanyPortfolioView, isInfiniteRunway, } from './view/companyView/selector/companyPortfolioViewSelector';
163
163
  import { getAllCockpitTabsFilterView, } from './view/companyView/selector/getAllCockpitTabsFilterView';
@@ -456,6 +456,7 @@ export { scheduleTenantCreditScoreCron, fetchCreditAgentMacro, saveCreditAgentMa
456
456
  export { toCardTenantProfileRow, toMacro, } from './entity/creditAgent/creditAgentPayload';
457
457
  export { getCreditAgentView, getCreditAgentEntity, getCreditAgentMacro, getCardProfilesData, } from './view/creditAgentView/creditAgentViewSelector';
458
458
  export { getCreditReportDownloadPayload } from './view/creditAgentView/buildCreditReportCsv';
459
+ export { ALL_PRODUCT_VERTICALS, toProductVertical, toProductVerticalStrict, } from './view/companyView/types/cockpitTypes';
459
460
  export { clearAllCreditAgent, updateCreditAgentMacro, updateCreditAgentRow, updateCreditAgentRows, } from './entity/creditAgent/creditAgentReducer';
460
461
  export { getCreditAgentRows, getCreditAgentMacroEntity, } from './entity/creditAgent/creditAgentSelector';
461
462
  export { saveJeAccountSettings, saveJeAccountSettingsLocalData };
@@ -481,7 +482,7 @@ export { toTimeframeTick, mapTimePeriodtoTimeframeTick, toTimePeriod, toAbsolute
481
482
  export { toVendorSpendTrendFilterTabsTypeStrict, } from './entity/vendorExpense/vendorExpenseSelector';
482
483
  export { getNumberOfPeriods };
483
484
  export { SCHEDULE_DAYS_OF_MONTH, toScheduleDaysOfMonth, toMonth, toMonthStrict, toQuarter, toQuarterStrict, };
484
- export { getTenantBaseById, getCurrentTenant, getIsAccountingClassesEnabled, getIsAccountingProjectsEnabled, getTenantsByCheckInDateSelector, getTenantsBaseByCheckInDateSelector, getTenantsCoreDetailsByCheckInDateSelector, toTenantCoreDetailsView, getTenantBaseViewForTenantView, getTenantBaseViewForTenantId, isZeniDomainTenant, isTenantUsingZeniCOA, fetchActiveTenant, fetchAllTenants, updateCurrentTenant, clearAll, doMagicLinkSignIn, verifyDeviceWithTwoFA, resendVerifyDeviceOTP, sendEmailMagicLinkToUser, doSignIn, updateSignInState, doSignOut, sendSessionHeartbeat, resetSignInState, fetchExcludedResourcesForTenant, fetchExternalConnectionsForTenant, saveExternalConnectionForTenant, deleteConnection, saveAPIKeyConnection, saveConnectorCredentials, saveOAuthConnection, initEmailConnectOAuth, toExternalIntegrationType, toExternalSupportedTool, fetchSubscriptionSummaryForTenant, isTenantBankingOnly, isTenantCardsOnly, isTenantBookkeepingEnabled, isOtherProductsEnabledForTenant, };
485
+ export { getTenantBaseById, getCurrentTenant, getIsAccountingClassesEnabled, getIsAccountingProjectsEnabled, getTenantsByCheckInDateSelector, getTenantsBaseByCheckInDateSelector, getTenantsCoreDetailsByCheckInDateSelector, toTenantCoreDetailsView, getTenantBaseViewForTenantView, getTenantBaseViewForTenantId, isZeniDomainTenant, isTenantUsingZeniCOA, fetchActiveTenant, fetchAllTenants, updateCurrentTenant, clearAll, doMagicLinkSignIn, verifyDeviceWithTwoFA, resendVerifyDeviceOTP, sendEmailMagicLinkToUser, doSignIn, updateSignInState, doSignOut, sendSessionHeartbeat, resetSignInState, fetchExcludedResourcesForTenant, fetchExternalConnectionsForTenant, saveExternalConnectionForTenant, deleteConnection, saveAPIKeyConnection, saveConnectorCredentials, saveOAuthConnection, initEmailConnectOAuth, toExternalIntegrationType, toExternalSupportedTool, fetchSubscriptionSummaryForTenant, isTenantBankingOnly, isTenantCardsOnly, isTenantBookkeepingEnabled, isOtherProductsEnabledForTenant, DEFAULT_RECEIPTS_EMAIL, getReceiptsEmail, };
485
486
  export { toAccountType, toAccountGroupType, getAccountGroupKey, getAllAccounts, getTransactionFilterAccountOptions, };
486
487
  export { getAllClasses, getClassById, getClassFilterOptions, } from './entity/class/classSelector';
487
488
  export { getForecast };
@@ -566,7 +567,7 @@ export { newAddressInLocalStore, getAddress, getNewAddress, getAllNewAddresses,
566
567
  export { toManagementSortKeyType, toOnboardingSortKeyType, toPortfolioSortKeyType, toHealthSortKeyType, toTaskManagerSortKeyType, updateCompanyTaskManagerViewFilters, };
567
568
  export { ALL_COCKPIT_TABS_FILE_TYPES, ALL_COCKPIT_TABS_IDS, toCockpitTabsIDStrict, toCockpitTabsFileTypeStrict, };
568
569
  export { isAllowedValueWithCode, isAllowedValueWithID, };
569
- export { fetchCompanyPassportView, getCompanyPassportView, getCompanyPassportLocalStoreData, isCompanyPassportDataToBeSaved, companyPassportSaveDataInLocalStore, saveIndustryAndIncDateInCompanyPassportLocalStore, deleteCompanyOfficerInLocalStore, companyPassportClearDataInLocalStore, updateCompanyPassportLocalStoreData, saveCompanyPassportDetails, updateAccountingClassesEnabled, enableAccountingProjects, resumeEnableAccountingProjects, disableAccountingProjects, clearQboProjectsReconnectRequest, updateCapitalizationAccountThreshold, dismissCapitalizationOnboarding, clearCompanyView, };
570
+ export { fetchCompanyPassportView, getCompanyPassportView, getCompanyPassportLocalStoreData, isCompanyPassportDataToBeSaved, companyPassportSaveDataInLocalStore, saveIndustryAndIncDateInCompanyPassportLocalStore, deleteCompanyOfficerInLocalStore, companyPassportClearDataInLocalStore, updateCompanyPassportLocalStoreData, saveCompanyPassportDetails, updateAccountingClassesEnabled, enableAccountingProjects, resumeEnableAccountingProjects, disableAccountingProjects, clearQboProjectsReconnectRequest, updateCompanyProductServices, getCompanyProductServicesUpdateStatus, updateCompanyQboRealmId, getCompanyQboRealmIdUpdateStatus, updateCapitalizationAccountThreshold, dismissCapitalizationOnboarding, clearCompanyView, };
570
571
  export { toCompanyPassportLocalData, };
571
572
  export { COMPANY_ONBOARDING_INDUSTRY_TYPE_CODES, COMPANY_ONBOARDING_SUB_INDUSTRY_CODES_BY_INDUSTRY, COMPANY_PURPOSE_OF_ACCOUNT_CODES, COMPANY_SOURCE_OF_FUNDS_CODES, COMPANY_TRANSACTION_VOLUME_CODES, COMPANY_US_NEXUS_TYPE_CODES, getCompanyOnboardingSubIndustryCodesForIndustry, };
572
573
  export { getUncategorizedAccounts, getNestedAccountListHierarchy, };
@@ -674,7 +675,7 @@ export { cancelAiAccountantOnboarding, clearAiAccountantView, fetchAiAccountantC
674
675
  export { getAiAccountantCockpitView, } from './view/aiAccountantView/aiAccountantViewSelector';
675
676
  export { fetchChatSessionsForUser, createSession, submitQuestion, updateAiCfoViewScrollPosition, updateCurrentInput, updateCotCollapsedState, clearInput, setSession, clearAiCfoView, clearCurrentSessionId, clearDeleteChatSessionStatus, clearLastContextMessage, createSessionAndSubmit, stopSubmit, stopSubmitQuestion, fetchChatHistory, acceptMasterTOS, deleteChatSession, submitFeedback, updateResponseState, fetchSuggestedQuestions, fetchSuggestedQuestionsSuccess, fetchSuggestedQuestionsFailure, fetchSkills, fetchSkillsSuccess, fetchSkillsFailure, applyAiCfoSidePanelHostPageTransition, clearAiCfoSidePanelHostPageContext, } from './view/aiCfoView/aiCfoViewReducer';
676
677
  export { getAiCfoView, getSuggestedQuestionsForPageContext, getSkills, } from './view/aiCfoView/aiCfoViewSelector';
677
- export { setNewSession, setSessions, updateAiCfoAnswerCardPolicyWizardPlan, upsertAnswerPayload, upsertOrAddQuestionAnswerPayload, addQuestionPayload, clearSession, clearAiCfo, toAiCfoVisualization, appendSyntheticAiCfoAnswer, updateSyntheticAiCfoAnswer, removeSyntheticAiCfoAnswer, clearSyntheticAiCfoAnswers, } from './entity/aiCfo/aiCfoReducer';
678
+ export { setNewSession, setSessions, updateAiCfoAnswerCardPolicyWizardPlan, upsertAnswerPayload, upsertOrAddQuestionAnswerPayload, addQuestionPayload, clearSession, clearAiCfo, toAiCfoExports, toAiCfoVisualization, appendSyntheticAiCfoAnswer, updateSyntheticAiCfoAnswer, removeSyntheticAiCfoAnswer, clearSyntheticAiCfoAnswers, } from './entity/aiCfo/aiCfoReducer';
678
679
  export { getAllQuestionAnswersForChatSession, getQuestionAnswerByIdForChatSession, getAllQuestionsForChatSession, getAiCfoSelectorView, getSyntheticAiCfoAnswersForChatSession, getSyntheticAiCfoAnswerByQuestionAnswerId, } from './entity/aiCfo/aiCfoSelector';
679
680
  export { ALL_AI_CFO_CHARTS_TYPES, ALL_AI_CFO_VISUALIZATION_TYPES, ALL_AI_CFO_ANSWER_STATE_TYPES, ALL_AI_CFO_ANSWER_RESPONSE_TYPES, ALL_INTERACTIVE_FORM_TYPES, toAiCfoVisualizationType, toAiCfoVisualizationTypeStrict, toAiCfoChartType, toAiCfoChartTypeStrict, toAiCfoAnswerStateType, toAiCfoAnswerStateTypeStrict, toAiCfoAnswerResponseType, toAiCfoAnswerResponseTypeStrict, toInteractiveFormType, toInteractiveFormTypeStrict, ALL_SYNTHETIC_AI_CFO_ANSWER_KINDS, toMessageSender, toMessageType, } from './entity/aiCfo/aiCfoState';
680
681
  // ── Card Policy (shared between manual + AI-driven flows) ────────────
@@ -735,7 +736,7 @@ export { resetRecordPayment, initializeRecordPaymentDraft, submitRecordPayment,
735
736
  export { initializeIssueCreditNoteDraft, issueCreditNoteFailure, issueCreditNoteSuccess, resetIssueCreditNote, submitIssueCreditNote, updateIssueCreditNoteFormDraft, } from './view/invoicing/issueCreditNote/issueCreditNoteReducer';
736
737
  export { computeIssueCreditNoteTotal, initializeIssueCreditNoteDraftFromInvoice, localDataToIssueCreditNotePayload, } from './view/invoicing/issueCreditNote/issueCreditNotePayload';
737
738
  export { INVOICING_CREDIT_NOTE_REASON_CODES, initialIssueCreditNoteFormLocalData, } from './view/invoicing/issueCreditNote/issueCreditNoteFormConfig';
738
- export { getIssueCreditNoteFormView, getIssueCreditNoteView, } from './view/invoicing/issueCreditNote/issueCreditNoteSelector';
739
+ export { getIssueCreditNoteView } from './view/invoicing/issueCreditNote/issueCreditNoteSelector';
739
740
  export { createCreditNoteFailure, createCreditNoteSuccess, resetCreateCreditNote, submitCreateCreditNote, updateCreateCreditNoteFormDraft, } from './view/invoicing/createCreditNote/createCreditNoteReducer';
740
741
  export { localDataToCreateCreditNotePayload } from './view/invoicing/createCreditNote/createCreditNotePayload';
741
742
  export { initialCreateCreditNoteFormLocalData } from './view/invoicing/createCreditNote/createCreditNoteFormConfig';
@@ -824,8 +825,8 @@ export { clearAllInvoicingDunningCases, removeInvoicingDunningCase, updateInvoic
824
825
  export { clearInvoicingDunningCaseListView, fetchInvoicingDunningCaseCounts, fetchInvoicingDunningCaseList, fetchInvoicingDunningCaseListPage, setInvoicingDunningCaseActiveTab, updateInvoicingDunningCaseFilters, } from './view/invoicing/invoicingDunningCaseListView/invoicingDunningCaseListViewReducer';
825
826
  export { ALL_INVOICING_DUNNING_CASE_SORT_KEYS, ALL_INVOICING_DUNNING_CASE_TAB_IDS, INVOICING_DUNNING_CASE_COLUMNS, INVOICING_DUNNING_CASE_TABS, } from './view/invoicing/invoicingDunningCaseListView/invoicingDunningCaseListViewState';
826
827
  export { getInvoicingDunningCaseListView } from './view/invoicing/invoicingDunningCaseListView/invoicingDunningCaseListViewSelector';
827
- export { clearInvoicingDunningCaseDetailView, fetchInvoicingDunningCaseDetail, } from './view/invoicing/invoicingDunningCaseDetailView/invoicingDunningCaseDetailViewReducer';
828
- export { getInvoicingDunningCaseById, getInvoicingDunningCaseDetail, } from './view/invoicing/invoicingDunningCaseDetailView/invoicingDunningCaseDetailViewSelector';
828
+ export { clearInvoicingDunningCaseDetailView, fetchInvoicingDunningCaseDetail, fetchInvoicingDunningEmailPreviewPage, invalidateInvoicingDunningEmailPreview, } from './view/invoicing/invoicingDunningCaseDetailView/invoicingDunningCaseDetailViewReducer';
829
+ export { getInvoicingDunningCaseById, getInvoicingDunningCaseDetail, getInvoicingDunningEmailPreview, } from './view/invoicing/invoicingDunningCaseDetailView/invoicingDunningCaseDetailViewSelector';
829
830
  // ── Invoicing: catalog (plans + products) ─────────────────────────────────────
830
831
  export { clearAllInvoicingPlans, mergeInvoicingPlanSubFamily, removeInvoicingPlan, updateInvoicingPlans, } from './entity/invoicing/plan/planReducer';
831
832
  export { clearAllInvoicingProducts, removeInvoicingProduct, updateInvoicingProducts, } from './entity/invoicing/product/productReducer';
@@ -31,10 +31,13 @@ const companyHealthMetricConfigView = createSlice({
31
31
  draft.fetchState = 'Error';
32
32
  draft.error = action.payload;
33
33
  },
34
+ clearCompanyHealthMetricConfigView() {
35
+ return initialState;
36
+ },
34
37
  },
35
38
  extraReducers: (builder) => {
36
39
  builder.addCase(clearAll, () => initialState);
37
40
  },
38
41
  });
39
- export const { fetchCompanyHealthMetricConfig, updateCompanyHealthMetricConfig, updateCompanyHealthMetricConfigFailure, } = companyHealthMetricConfigView.actions;
42
+ export const { clearCompanyHealthMetricConfigView, fetchCompanyHealthMetricConfig, updateCompanyHealthMetricConfig, updateCompanyHealthMetricConfigFailure, } = companyHealthMetricConfigView.actions;
40
43
  export default companyHealthMetricConfigView.reducer;
@@ -377,6 +377,63 @@ const companyView = createSlice({
377
377
  clearQboProjectsReconnectRequest(draft) {
378
378
  draft.passportView.needsQboProjectsReconnect = false;
379
379
  },
380
+ updateCompanyProductServices: {
381
+ reducer(draft, action) {
382
+ draft.managementView.productServicesUpdateStatusByCompanyId[action.payload.companyId] = {
383
+ fetchState: 'In-Progress',
384
+ error: undefined,
385
+ };
386
+ },
387
+ prepare(companyId, services) {
388
+ return { payload: { companyId, services } };
389
+ },
390
+ },
391
+ updateCompanyProductServicesSuccess(draft, action) {
392
+ draft.managementView.productServicesUpdateStatusByCompanyId[action.payload.companyId] = {
393
+ fetchState: 'Completed',
394
+ error: undefined,
395
+ };
396
+ },
397
+ updateCompanyProductServicesFailure: {
398
+ reducer(draft, action) {
399
+ draft.managementView.productServicesUpdateStatusByCompanyId[action.payload.companyId] = {
400
+ fetchState: 'Error',
401
+ error: action.payload.status,
402
+ };
403
+ },
404
+ prepare(companyId, status) {
405
+ return { payload: { companyId, status } };
406
+ },
407
+ },
408
+ updateCompanyQboRealmId: {
409
+ reducer(draft, action) {
410
+ draft.managementView.qboRealmIdUpdateStatusByCompanyId[action.payload.companyId] = {
411
+ fetchState: 'In-Progress',
412
+ error: undefined,
413
+ };
414
+ },
415
+ // qboRealmId omitted -> reset (the epic sends null); a value -> update.
416
+ prepare(companyId, qboRealmId) {
417
+ return { payload: { companyId, qboRealmId } };
418
+ },
419
+ },
420
+ updateCompanyQboRealmIdSuccess(draft, action) {
421
+ draft.managementView.qboRealmIdUpdateStatusByCompanyId[action.payload.companyId] = {
422
+ fetchState: 'Completed',
423
+ error: undefined,
424
+ };
425
+ },
426
+ updateCompanyQboRealmIdFailure: {
427
+ reducer(draft, action) {
428
+ draft.managementView.qboRealmIdUpdateStatusByCompanyId[action.payload.companyId] = {
429
+ fetchState: 'Error',
430
+ error: action.payload.status,
431
+ };
432
+ },
433
+ prepare(companyId, status) {
434
+ return { payload: { companyId, status } };
435
+ },
436
+ },
380
437
  updateCapitalizationAccountThreshold: {
381
438
  prepare(companyId, accountId, threshold, dismissOnboardingForAccount, allOverrides) {
382
439
  return {
@@ -700,5 +757,5 @@ const companyView = createSlice({
700
757
  });
701
758
  },
702
759
  });
703
- export const { fetchCompanyPassportView, fetchAllCockpitViews, fetchOnboardingView, fetchManagementView, fetchSubscriptionView, fetchPortfolioView, fetchCompanyManagementView, companyManagementSaveUpdates, companyManagementSavePendingUpdates, companyManagementDiscardUpdates, companyManagementSaveUpdatesSuccess, companyManagementSaveUpdatesFailure, updateCompanyViewOnSuccess, updateCompanyViewOnFailure, fetchCompanyPortfolioView, fetchZeniUsers, updateZeniUsersOnSuccess, updateZeniUsersOnFailure, clearCompanyView, updateCompanyPortfolioUIState, updateCompanyManagementUIState, companyPassportSaveDataInLocalStore, deleteCompanyOfficerInLocalStore, companyPassportClearDataInLocalStore, companyPassportUpdateCompanyDetails, companyPassportUpdateCompanyDetailsSuccess, companyPassportUpdateCompanyDetailsFailure, updateAccountingClassesEnabled, updateAccountingClassesEnabledSuccess, updateAccountingClassesEnabledFailure, enableAccountingProjects, resumeEnableAccountingProjects, disableAccountingProjects, updateAccountingProjectsEnabledSuccess, updateAccountingProjectsEnabledFailure, requestQboProjectsReconnect, clearQboProjectsReconnectRequest, updateCapitalizationAccountThreshold, updateCapitalizationAccountThresholdSuccess, updateCapitalizationAccountThresholdFailure, dismissCapitalizationOnboarding, companyPassportUpdatePrimaryContact, companyPassportUpdatePrimaryContactSuccess, companyPassportUpdatePrimaryContactFailure, companyPassportUpdateCompanyOfficer, companyPassportUpdateCompanyOfficerSuccess, companyPassportUpdateCompanyOfficerFailure, companyPassportCreateCompanyOfficers, companyPassportCreateCompanyOfficersSuccess, companyPassportCreateCompanyOfficersFailure, updateCompanyPassportLocalStoreData, saveIndustryAndIncDateInCompanyPassportLocalStore, saveCompanyPassportDetails, fetchParentSubsidiaryManagementView, updateParentSubsidiaryView, updateParentSubsidiaryViewOnFailure, updateCompanyDownloadState, fetchCompanyMetaData, updateCompanyMetaDataOnSuccess, updateCompanyMetaDataOnFailure, updateMetaDataOnSendMonthEndReport, } = companyView.actions;
760
+ export const { fetchCompanyPassportView, fetchAllCockpitViews, fetchOnboardingView, fetchManagementView, fetchSubscriptionView, fetchPortfolioView, fetchCompanyManagementView, companyManagementSaveUpdates, companyManagementSavePendingUpdates, companyManagementDiscardUpdates, companyManagementSaveUpdatesSuccess, companyManagementSaveUpdatesFailure, updateCompanyViewOnSuccess, updateCompanyViewOnFailure, fetchCompanyPortfolioView, fetchZeniUsers, updateZeniUsersOnSuccess, updateZeniUsersOnFailure, clearCompanyView, updateCompanyPortfolioUIState, updateCompanyManagementUIState, companyPassportSaveDataInLocalStore, deleteCompanyOfficerInLocalStore, companyPassportClearDataInLocalStore, companyPassportUpdateCompanyDetails, companyPassportUpdateCompanyDetailsSuccess, companyPassportUpdateCompanyDetailsFailure, updateAccountingClassesEnabled, updateAccountingClassesEnabledSuccess, updateAccountingClassesEnabledFailure, updateCompanyProductServices, updateCompanyProductServicesSuccess, updateCompanyProductServicesFailure, updateCompanyQboRealmId, updateCompanyQboRealmIdSuccess, updateCompanyQboRealmIdFailure, enableAccountingProjects, resumeEnableAccountingProjects, disableAccountingProjects, updateAccountingProjectsEnabledSuccess, updateAccountingProjectsEnabledFailure, requestQboProjectsReconnect, clearQboProjectsReconnectRequest, updateCapitalizationAccountThreshold, updateCapitalizationAccountThresholdSuccess, updateCapitalizationAccountThresholdFailure, dismissCapitalizationOnboarding, companyPassportUpdatePrimaryContact, companyPassportUpdatePrimaryContactSuccess, companyPassportUpdatePrimaryContactFailure, companyPassportUpdateCompanyOfficer, companyPassportUpdateCompanyOfficerSuccess, companyPassportUpdateCompanyOfficerFailure, companyPassportCreateCompanyOfficers, companyPassportCreateCompanyOfficersSuccess, companyPassportCreateCompanyOfficersFailure, updateCompanyPassportLocalStoreData, saveIndustryAndIncDateInCompanyPassportLocalStore, saveCompanyPassportDetails, fetchParentSubsidiaryManagementView, updateParentSubsidiaryView, updateParentSubsidiaryViewOnFailure, updateCompanyDownloadState, fetchCompanyMetaData, updateCompanyMetaDataOnSuccess, updateCompanyMetaDataOnFailure, updateMetaDataOnSendMonthEndReport, } = companyView.actions;
704
761
  export default companyView.reducer;
@@ -0,0 +1,91 @@
1
+ import { from } from 'rxjs';
2
+ import { catchError, filter, mergeMap } from 'rxjs/operators';
3
+ import { updateCompanies } from '../../../entity/company/companyReducer';
4
+ import { openSnackbar } from '../../../entity/snackbar/snackbarReducer';
5
+ import { updateTenantProductSettings } from '../../../entity/tenant/tenantReducer';
6
+ import { createZeniAPIStatus, isSuccessResponse } from '../../../responsePayload';
7
+ import { updateCompanyProductServices, updateCompanyProductServicesFailure, updateCompanyProductServicesSuccess, } from '../companyViewReducer';
8
+ import { resolveTenantIdByCompanyId } from '../selector/companyManagementViewSelector';
9
+ export const updateCompanyProductServicesEpic = (actions$, state$, zeniAPI) => actions$.pipe(filter(updateCompanyProductServices.match),
10
+ // Per-row admin action: an operator can enable services on several
11
+ // companies at once, so each trigger keeps its own request (matches
12
+ // updateCompanyQboRealmIdEpic).
13
+ mergeMap((action) => {
14
+ const { companyId, services } = action.payload;
15
+ const { isBookkeepingEnabled, isBillPayEnabled, isReimbursementEnabled } = services;
16
+ const tenantId = resolveTenantIdByCompanyId(state$.value, companyId);
17
+ // Send only the service-enable fields so the backend routes this through
18
+ // its isolated "enable services" path. Bookkeeping is a Tenant product
19
+ // flag and implies spend management; bill pay / reimbursement are Company
20
+ // feature flags.
21
+ const requestBody = {};
22
+ if (isBookkeepingEnabled != null) {
23
+ requestBody.is_book_keeping_enabled = isBookkeepingEnabled;
24
+ requestBody.is_spend_management_enabled = isBookkeepingEnabled;
25
+ }
26
+ if (isBillPayEnabled != null) {
27
+ requestBody.is_bill_pay_feature_enabled = isBillPayEnabled;
28
+ }
29
+ if (isReimbursementEnabled != null) {
30
+ requestBody.is_reimbursement_feature_enabled = isReimbursementEnabled;
31
+ }
32
+ // No fields set would fall through to the normal company PUT on the backend;
33
+ // treat it as an immediate no-op success so the In-Progress status resolves.
34
+ if (Object.keys(requestBody).length === 0) {
35
+ return from([updateCompanyProductServicesSuccess({ companyId })]);
36
+ }
37
+ return zeniAPI
38
+ .putAndGetJSON(`${zeniAPI.apiEndPoints.tenantMicroServiceBaseUrl}/1.0/companies/${companyId}`, requestBody)
39
+ .pipe(mergeMap((response) => {
40
+ if (isSuccessResponse(response)) {
41
+ const companies = response.data?.companies ?? [];
42
+ const actions = [];
43
+ // The response normally carries the updated company; only write
44
+ // company state when it does (a 200 is still a success even with
45
+ // an empty company array).
46
+ if (companies.length > 0) {
47
+ actions.push(updateCompanies({
48
+ payload: companies,
49
+ schema: {},
50
+ isUpdate: true,
51
+ }));
52
+ }
53
+ // Independent of the company array: the company PUT is the source
54
+ // of truth and this tenant-state patch is a best-effort optimistic
55
+ // sync, skipped when the tenant isn't loaded (UI relies on the
56
+ // next tenant fetch).
57
+ if (isBookkeepingEnabled != null && tenantId != null) {
58
+ actions.push(updateTenantProductSettings({
59
+ tenantId,
60
+ isBookkeepingEnabled,
61
+ isSpendManagementEnabled: isBookkeepingEnabled,
62
+ }));
63
+ }
64
+ actions.push(updateCompanyProductServicesSuccess({ companyId }));
65
+ actions.push(openSnackbar({
66
+ messageSection: 'product_services_update',
67
+ messageText: 'success',
68
+ type: 'success',
69
+ }));
70
+ return from(actions);
71
+ }
72
+ return from([
73
+ updateCompanyProductServicesFailure(companyId, response.status),
74
+ openSnackbar({
75
+ messageSection: 'product_services_update',
76
+ messageText: 'failed',
77
+ type: 'error',
78
+ }),
79
+ ]);
80
+ }), catchError((error) => from([
81
+ updateCompanyProductServicesFailure(companyId, createZeniAPIStatus('Unexpected error', 'Update Company Product Services errored out: ' +
82
+ (error instanceof Error
83
+ ? error.message
84
+ : JSON.stringify(error)))),
85
+ openSnackbar({
86
+ messageSection: 'product_services_update',
87
+ messageText: 'failed',
88
+ type: 'error',
89
+ }),
90
+ ])));
91
+ }));
@@ -0,0 +1,58 @@
1
+ import { from } from 'rxjs';
2
+ import { catchError, filter, mergeMap } from 'rxjs/operators';
3
+ import { openSnackbar } from '../../../entity/snackbar/snackbarReducer';
4
+ import { createZeniAPIStatus, isSuccessResponse, } from '../../../responsePayload';
5
+ import { updateCompanyQboRealmId, updateCompanyQboRealmIdFailure, updateCompanyQboRealmIdSuccess, } from '../companyViewReducer';
6
+ import { resolveTenantIdByCompanyId } from '../selector/companyManagementViewSelector';
7
+ export const updateCompanyQboRealmIdEpic = (actions$, state$, zeniAPI) => actions$.pipe(filter(updateCompanyQboRealmId.match),
8
+ // Per-row admin action: several rows can be in flight at once, so each
9
+ // trigger keeps its own request instead of cancelling the previous one.
10
+ mergeMap((action) => {
11
+ const { companyId, qboRealmId } = action.payload;
12
+ const tenantId = resolveTenantIdByCompanyId(state$.value, companyId);
13
+ if (tenantId == null) {
14
+ return from([
15
+ updateCompanyQboRealmIdFailure(companyId, createZeniAPIStatus('Unexpected error', `No tenant found for companyId: ${companyId}`)),
16
+ openSnackbar({
17
+ messageSection: 'qbo_realm_id_update',
18
+ messageText: 'failed',
19
+ type: 'error',
20
+ }),
21
+ ]);
22
+ }
23
+ // The FE route reads the target tenant from the zeni-tenant-id header,
24
+ // which overrides the operator's ambient tenant for this request only.
25
+ // qboRealmId omitted -> null clears the pin; a value overwrites it.
26
+ return zeniAPI
27
+ .putAndGetJSON(`${zeniAPI.apiEndPoints.tenantMicroServiceBaseUrl}/1.0/qbo-realm-id`, { qbo_realm_id: qboRealmId ?? null }, { 'zeni-tenant-id': tenantId })
28
+ .pipe(mergeMap((response) => {
29
+ if (isSuccessResponse(response)) {
30
+ return from([
31
+ updateCompanyQboRealmIdSuccess({ companyId }),
32
+ openSnackbar({
33
+ messageSection: 'qbo_realm_id_update',
34
+ messageText: 'success',
35
+ type: 'success',
36
+ }),
37
+ ]);
38
+ }
39
+ return from([
40
+ updateCompanyQboRealmIdFailure(companyId, response.status),
41
+ openSnackbar({
42
+ messageSection: 'qbo_realm_id_update',
43
+ messageText: 'failed',
44
+ type: 'error',
45
+ }),
46
+ ]);
47
+ }), catchError((error) => from([
48
+ updateCompanyQboRealmIdFailure(companyId, createZeniAPIStatus('Unexpected error', 'Update Company QBO realm id errored out: ' +
49
+ (error instanceof Error
50
+ ? error.message
51
+ : JSON.stringify(error)))),
52
+ openSnackbar({
53
+ messageSection: 'qbo_realm_id_update',
54
+ messageText: 'failed',
55
+ type: 'error',
56
+ }),
57
+ ])));
58
+ }));
@@ -2,6 +2,7 @@ import { createSelector } from '@reduxjs/toolkit';
2
2
  import recordGet from 'lodash/get';
3
3
  import orderBy from 'lodash/orderBy';
4
4
  import { getSortOrder } from '../../../commonPayloadTypes/sortOrderPayload';
5
+ import { getCompanyByCompanyId } from '../../../entity/company/companySelector';
5
6
  import { customerRatingValues, teamRatingValues, } from '../../../entity/customerSatisfaction/customerSatisfactionState';
6
7
  import { getUserByUserId, getUserName, getUserNameById, } from '../../../entity/user/userSelector';
7
8
  import { date as zeniDate } from '../../../zeniDayJS';
@@ -122,6 +123,29 @@ const getCategoryValueForCompany = (key, company) => {
122
123
  if (key === 'annualRecurringRevenue' || key === 'monthlyRecurringRevenue') {
123
124
  return company.primarySubscription?.summary.summary[key].amount.toString();
124
125
  }
126
+ if (key === 'productVerticals') {
127
+ const featuresActivationInfo = company.company.company.featuresActivationInfo;
128
+ const enabledVerticals = [];
129
+ if (featuresActivationInfo?.isBookkeepingEnabled === true) {
130
+ enabledVerticals.push('bookkeeping');
131
+ }
132
+ if (featuresActivationInfo?.isBillPayFeatureEnabled === true) {
133
+ enabledVerticals.push('billPay');
134
+ }
135
+ if (featuresActivationInfo?.isReimbursementFeatureEnabled === true) {
136
+ enabledVerticals.push('reimbursement');
137
+ }
138
+ if (featuresActivationInfo?.isTaxEnabled === true) {
139
+ enabledVerticals.push('tax');
140
+ }
141
+ if (featuresActivationInfo?.isPayrollEnabled === true) {
142
+ enabledVerticals.push('payroll');
143
+ }
144
+ if (featuresActivationInfo?.isCfoEnabled === true) {
145
+ enabledVerticals.push('cfo');
146
+ }
147
+ return enabledVerticals;
148
+ }
125
149
  return company.company.company.managementInfo[key];
126
150
  };
127
151
  const applyAdvancedFiltersOnCompaniesList = (allCompanies, filters, filteredCompanies = [], index = 0) => {
@@ -192,6 +216,8 @@ const sortAndFilterCompanies = (sortKey, sortOrder, filterText, companies, userS
192
216
  updatedCompanies.forEach((company) => {
193
217
  switch (sortKey) {
194
218
  case 'companyName':
219
+ case 'productVerticals':
220
+ case 'realmId':
195
221
  companiesWithSortKeyValue.push(company);
196
222
  break;
197
223
  case 'status':
@@ -427,6 +453,12 @@ const sortAndFilterCompanies = (sortKey, sortOrder, filterText, companies, userS
427
453
  primarySortValue = (company.company.company.companyInfo.bookCloseDate ??
428
454
  zeniDate('1970-01-01T00:00:00.000Z')).valueOf();
429
455
  break;
456
+ case 'productVerticals':
457
+ case 'realmId':
458
+ // Not a meaningful sort axis (both are cockpit action columns);
459
+ // keep stable order rather than falling through to the status/group
460
+ // default sort.
461
+ break;
430
462
  case 'status':
431
463
  default: {
432
464
  isMultiSortApplicable = true;
@@ -480,3 +512,23 @@ export const getTotalRecurringRevenue = (companies) => companies.reduce((prev, c
480
512
  currencySymbol: '',
481
513
  },
482
514
  });
515
+ export function getCompanyProductServicesUpdateStatus(state, companyId) {
516
+ return (state.companyViewState.managementView
517
+ .productServicesUpdateStatusByCompanyId[companyId] ?? {
518
+ fetchState: 'Not-Started',
519
+ error: undefined,
520
+ });
521
+ }
522
+ export function getCompanyQboRealmIdUpdateStatus(state, companyId) {
523
+ return (state.companyViewState.managementView.qboRealmIdUpdateStatusByCompanyId[companyId] ?? {
524
+ fetchState: 'Not-Started',
525
+ error: undefined,
526
+ });
527
+ }
528
+ // Cockpit populates companyState (with a companyId->tenantId mapping) but not
529
+ // tenantState, so resolve from company first and fall back to tenant.
530
+ export function resolveTenantIdByCompanyId(state, companyId) {
531
+ return (getCompanyByCompanyId(state.companyState, companyId)?.company.companyInfo
532
+ .tenantId ??
533
+ Object.values(state.tenantState.tenantsById).find((tenant) => tenant?.companyId === companyId)?.tenantId);
534
+ }
@@ -1,4 +1,4 @@
1
- import { stringToUnion } from '../../../commonStateTypes/stringToUnion';
1
+ import { stringToUnion, stringToUnionStrict, } from '../../../commonStateTypes/stringToUnion';
2
2
  const ALL_PORTFOLIO_SORT_KEYS = [
3
3
  'companyName',
4
4
  'cashBalance',
@@ -13,6 +13,8 @@ const ALL_MANAGEMENT_SORT_KEYS = [
13
13
  'status',
14
14
  'group',
15
15
  'plans',
16
+ 'productVerticals',
17
+ 'realmId',
16
18
  'controller',
17
19
  'customerSuccessManager',
18
20
  'reviewer',
@@ -83,3 +85,13 @@ export const MANAGEMENT_STATUS_CODES = [
83
85
  'terminated',
84
86
  ];
85
87
  export const toManagementStatusCodeType = (v) => stringToUnion(v, MANAGEMENT_STATUS_CODES);
88
+ export const ALL_PRODUCT_VERTICALS = [
89
+ 'bookkeeping',
90
+ 'billPay',
91
+ 'reimbursement',
92
+ 'tax',
93
+ 'payroll',
94
+ 'cfo',
95
+ ];
96
+ export const toProductVertical = (v) => stringToUnion(v, ALL_PRODUCT_VERTICALS);
97
+ export const toProductVerticalStrict = (v) => stringToUnionStrict(v ?? '', ALL_PRODUCT_VERTICALS);
@@ -19,6 +19,8 @@ export function getCompanyManagementPendingUpdates(companyId, state) {
19
19
  export const initialCompanyManagementView = {
20
20
  ...initialCommonState,
21
21
  updatesByCompanyId: {},
22
+ productServicesUpdateStatusByCompanyId: {},
23
+ qboRealmIdUpdateStatusByCompanyId: {},
22
24
  uiState: {
23
25
  sortKey: 'status',
24
26
  sortOrder: 'ascending',
@@ -1,6 +1,7 @@
1
- import { of } from 'rxjs';
1
+ import { from } from 'rxjs';
2
2
  import { catchError, filter, mergeMap } from 'rxjs/operators';
3
3
  import { updateInvoicingCreditNotes } from '../../../entity/invoicing/creditNote/creditNoteReducer';
4
+ import { openSnackbar } from '../../../entity/snackbar/snackbarReducer';
4
5
  import { createZeniAPIStatus, isSuccessResponse } from '../../../responsePayload';
5
6
  import { localDataToCreateCreditNotePayload, } from './createCreditNotePayload';
6
7
  import { createCreditNoteFailure, createCreditNoteSuccess, submitCreateCreditNote, } from './createCreditNoteReducer';
@@ -15,9 +16,28 @@ export const createCreditNoteEpic = (actions$, state$, zeniAPI) => actions$.pipe
15
16
  return [
16
17
  updateInvoicingCreditNotes([response.data], 'merge'),
17
18
  createCreditNoteSuccess({ creditNoteId: response.data.id }),
19
+ openSnackbar({
20
+ messageSection: 'invoicing_credit_note_saved',
21
+ messageText: 'success',
22
+ type: 'success',
23
+ }),
18
24
  ];
19
25
  }
20
- return of(createCreditNoteFailure(response.status));
21
- }), catchError((error) => of(createCreditNoteFailure(createZeniAPIStatus('Unexpected Error', 'Create credit note REST API call errored out ' +
22
- JSON.stringify(error))))));
26
+ return from([
27
+ createCreditNoteFailure(response.status),
28
+ openSnackbar({
29
+ messageSection: 'invoicing_credit_note_saved',
30
+ messageText: 'failed',
31
+ type: 'error',
32
+ }),
33
+ ]);
34
+ }), catchError((error) => from([
35
+ createCreditNoteFailure(createZeniAPIStatus('Unexpected Error', 'Create credit note REST API call errored out ' +
36
+ JSON.stringify(error))),
37
+ openSnackbar({
38
+ messageSection: 'invoicing_credit_note_saved',
39
+ messageText: 'failed',
40
+ type: 'error',
41
+ }),
42
+ ])));
23
43
  }));
@@ -1,8 +1,9 @@
1
- import { of } from 'rxjs';
1
+ import { from } from 'rxjs';
2
2
  import { catchError, filter, mergeMap } from 'rxjs/operators';
3
3
  import { updateAddresses } from '../../../entity/address/addressReducer';
4
4
  import { extractInvoicingInvoiceAddresses } from '../../../entity/invoicing/invoice/invoicePayload';
5
5
  import { updateInvoices } from '../../../entity/invoicing/invoice/invoiceReducer';
6
+ import { openSnackbar } from '../../../entity/snackbar/snackbarReducer';
6
7
  import { createZeniAPIStatus, isSuccessResponse } from '../../../responsePayload';
7
8
  import { addInvoiceToList } from '../invoiceList/invoiceListReducer';
8
9
  import { createInvoice, createInvoiceFailure, createInvoiceSuccess, } from './createInvoiceReducer';
@@ -22,9 +23,28 @@ export const createInvoiceEpic = (actions$, _state$, zeniAPI) => actions$.pipe(f
22
23
  status: response.data.status,
23
24
  }),
24
25
  createInvoiceSuccess({ invoiceId: response.data.id }),
26
+ openSnackbar({
27
+ messageSection: 'invoicing_invoice_saved',
28
+ messageText: 'success',
29
+ type: 'success',
30
+ }),
25
31
  ];
26
32
  }
27
- return of(createInvoiceFailure(response.status));
28
- }), catchError((error) => of(createInvoiceFailure(createZeniAPIStatus('Unexpected Error', 'Create invoice REST API call errored out ' +
29
- JSON.stringify(error))))));
33
+ return from([
34
+ createInvoiceFailure(response.status),
35
+ openSnackbar({
36
+ messageSection: 'invoicing_invoice_saved',
37
+ messageText: 'failed',
38
+ type: 'error',
39
+ }),
40
+ ]);
41
+ }), catchError((error) => from([
42
+ createInvoiceFailure(createZeniAPIStatus('Unexpected Error', 'Create invoice REST API call errored out ' +
43
+ JSON.stringify(error))),
44
+ openSnackbar({
45
+ messageSection: 'invoicing_invoice_saved',
46
+ messageText: 'failed',
47
+ type: 'error',
48
+ }),
49
+ ])));
30
50
  }));
@@ -1,25 +1,52 @@
1
+ import { getAddressByAddressId } from '../../../entity/address/addressSelector';
1
2
  import { getInvoicingCatalogListView, getInvoicingCatalogPrices, } from '../invoicingCatalogListView/invoicingCatalogListViewSelector';
2
3
  import { getInvoicingCustomerListView } from '../invoicingCustomerListView/invoicingCustomerListViewSelector';
3
4
  import { getInvoicingSettings } from '../settingsView/settingsViewSelector';
5
+ /**
6
+ * Display name for a customer option: company, else the full name, else the
7
+ * email, else the raw id.
8
+ */
9
+ const customerDisplayName = (customer) => {
10
+ const fullName = [customer.firstName, customer.lastName]
11
+ .filter((part) => part != null && part.trim() !== '')
12
+ .join(' ')
13
+ .trim();
14
+ const company = customer.company?.trim() ?? '';
15
+ const email = customer.email?.trim() ?? '';
16
+ return company !== ''
17
+ ? company
18
+ : fullName !== ''
19
+ ? fullName
20
+ : email !== ''
21
+ ? email
22
+ : customer.id;
23
+ };
4
24
  /**
5
25
  * Single bundled view for the create-invoice screen — web-ui consumes only this.
6
26
  * Carries the form draft (`localData`), the option sources the form needs
7
- * (customers, catalog plans/products, settings), the create-invoice fetch
8
- * state/error, and the id of the invoice last created (`savedInvoiceId`, to
9
- * navigate on success). Returns raw entity lists; the screen maps them to its
10
- * option shapes.
27
+ * (customer picker rows, catalog plan names, products, settings), the
28
+ * create-invoice fetch state/error, and the id of the invoice last created
29
+ * (`savedInvoiceId`, to navigate on success).
11
30
  */
12
31
  export const getCreateInvoiceFormView = (state) => {
13
32
  const createInvoiceState = state.invoicingCreateInvoiceState;
14
33
  const catalogListView = getInvoicingCatalogListView(state);
15
- const plans = getInvoicingCatalogPrices(catalogListView.plans);
16
34
  return {
17
- customers: getInvoicingCustomerListView(state).customers,
35
+ customers: getInvoicingCustomerListView(state).customers.map((customer) => ({
36
+ billingAddress: customer.billingAddressId != null
37
+ ? getAddressByAddressId(state.addressState, customer.billingAddressId)
38
+ : undefined,
39
+ email: customer.email,
40
+ id: customer.id,
41
+ name: customerDisplayName(customer),
42
+ })),
18
43
  localData: createInvoiceState.formDraft,
19
- planNames: plans
20
- .map((plan) => plan.name ?? '')
21
- .filter((name) => name !== ''),
22
- plans,
44
+ planNames: catalogListView.plans.flatMap((plan) => (plan.subFamilies ?? [])
45
+ .filter((subFamily) => (subFamily.name ?? '') !== '')
46
+ .map((subFamily) => ({
47
+ planName: plan.name ?? '',
48
+ subFamilyName: subFamily.name ?? '',
49
+ }))),
23
50
  products: getInvoicingCatalogPrices(catalogListView.products),
24
51
  savedInvoiceId: createInvoiceState.createdInvoiceId,
25
52
  settings: getInvoicingSettings(state),