@zeniai/client-epic-state 5.1.6 → 5.1.8-beta0ND

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 (25) hide show
  1. package/lib/entity/tenant/tenantSelector.d.ts +1 -0
  2. package/lib/entity/tenant/tenantSelector.js +10 -1
  3. package/lib/entity/transaction/payloadTypes/transactionPayload.d.ts +2 -0
  4. package/lib/entity/transaction/payloadTypes/transactionPayload.js +13 -0
  5. package/lib/entity/transaction/stateTypes/transaction.d.ts +2 -0
  6. package/lib/esm/entity/tenant/tenantSelector.js +8 -0
  7. package/lib/esm/entity/transaction/payloadTypes/transactionPayload.js +13 -0
  8. package/lib/esm/view/onboardingView/customerView/onboardingCustomerViewSelector.js +13 -7
  9. package/lib/esm/view/onboardingView/customerView/onboardingCustomerViewState.js +8 -0
  10. package/lib/esm/view/settingsView/settingsViewHelpers.js +13 -9
  11. package/lib/esm/view/spendManagement/commonSetup/kycKybParseMapper.js +15 -1
  12. package/lib/esm/view/transactionDetail/epics/saveTransactionHelper.js +1 -0
  13. package/lib/esm/view/transactionDetail/epics/updateTransactionDetailEpic.js +6 -0
  14. package/lib/esm/view/transactionDetail/transactionDetailLocalDataHelper.js +7 -1
  15. package/lib/view/onboardingView/customerView/onboardingCustomerViewSelector.js +12 -6
  16. package/lib/view/onboardingView/customerView/onboardingCustomerViewState.d.ts +1 -0
  17. package/lib/view/onboardingView/customerView/onboardingCustomerViewState.js +9 -1
  18. package/lib/view/settingsView/settingsViewHelpers.js +12 -8
  19. package/lib/view/spendManagement/commonSetup/kycKybParseMapper.js +15 -1
  20. package/lib/view/transactionDetail/epics/saveTransactionHelper.js +1 -0
  21. package/lib/view/transactionDetail/epics/updateTransactionDetailEpic.js +6 -0
  22. package/lib/view/transactionDetail/transactionDetailLocalDataHelper.js +7 -1
  23. package/lib/view/transactionDetail/transactionDetailState.d.ts +1 -0
  24. package/lib/view/transactionDetail/transactionDetailTypes.d.ts +1 -0
  25. package/package.json +6 -4
@@ -85,6 +85,7 @@ export declare const getCurrentTenantExternalConnections: (tenantState: TenantSt
85
85
  export declare const toTenantCoreDetailsView: (tenantsBaseView: TenantBaseView[]) => TenantCoreDetailsView[];
86
86
  export declare const isTenantBankingOnly: (isDebitCardFlagEnabled: boolean, tenant: Tenant | undefined) => boolean;
87
87
  export declare const isTenantCardsOnly: (tenant: Tenant | undefined) => boolean;
88
+ export declare const isTenantAccountingServicesEnabled: (tenant: Tenant | undefined) => boolean;
88
89
  export declare const getIsAccountingClassesEnabled: (state: RootState) => boolean;
89
90
  export declare const getIsAccountingProjectsEnabled: (state: RootState) => boolean;
90
91
  export declare const isTenantBookkeepingEnabled: (tenant: Tenant | undefined) => boolean;
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.isTenantTreasuryEnabled = exports.isOtherProductsEnabledForTenant = exports.isTenantBookkeepingEnabled = exports.getIsAccountingProjectsEnabled = exports.getIsAccountingClassesEnabled = exports.isTenantCardsOnly = exports.isTenantBankingOnly = exports.toTenantCoreDetailsView = exports.getCurrentTenantExternalConnections = exports.isTenantUsingZeniCOA = exports.isZeniDomainTenant = exports.getTenantBaseViewForTenantView = exports.getTenantsCoreDetailsByCheckInDateSelector = exports.getTenantsBaseByCheckInDateSelector = exports.getTenantBaseById = exports.getTenantsByCheckInDateSelector = exports.getCurrentTenant = exports.getTenantBaseViewForTenantId = void 0;
6
+ exports.isTenantTreasuryEnabled = exports.isOtherProductsEnabledForTenant = exports.isTenantBookkeepingEnabled = exports.getIsAccountingProjectsEnabled = exports.getIsAccountingClassesEnabled = exports.isTenantAccountingServicesEnabled = exports.isTenantCardsOnly = exports.isTenantBankingOnly = exports.toTenantCoreDetailsView = exports.getCurrentTenantExternalConnections = exports.isTenantUsingZeniCOA = exports.isZeniDomainTenant = exports.getTenantBaseViewForTenantView = exports.getTenantsCoreDetailsByCheckInDateSelector = exports.getTenantsBaseByCheckInDateSelector = exports.getTenantBaseById = exports.getTenantsByCheckInDateSelector = exports.getCurrentTenant = exports.getTenantBaseViewForTenantId = void 0;
7
7
  exports.getTenant = getTenant;
8
8
  exports.getOnboardingTenantByTenantId = getOnboardingTenantByTenantId;
9
9
  const toolkit_1 = require("@reduxjs/toolkit");
@@ -280,6 +280,15 @@ const isTenantCardsOnly = (tenant) => {
280
280
  : false;
281
281
  };
282
282
  exports.isTenantCardsOnly = isTenantCardsOnly;
283
+ const isTenantAccountingServicesEnabled = (tenant) => {
284
+ return tenant != null && tenant.productSettings != null
285
+ ? tenant.productSettings.isCfoEnabled === true ||
286
+ tenant.productSettings.isTaxEnabled === true ||
287
+ tenant.productSettings.isPayrollEnabled === true ||
288
+ tenant.productSettings.isBookkeepingEnabled === true
289
+ : false;
290
+ };
291
+ exports.isTenantAccountingServicesEnabled = isTenantAccountingServicesEnabled;
283
292
  const getIsAccountingClassesEnabled = (state) => {
284
293
  const { currentTenantId, tenantsById } = state.tenantState;
285
294
  if (currentTenantId == null) {
@@ -51,6 +51,8 @@ export interface TransactionPayload extends TransactionIDPayload, AccountBasePay
51
51
  to_from_account_integration_id?: string | null;
52
52
  to_from_account_name?: string | null;
53
53
  total_amount?: number;
54
+ /** Transactions list API: count of categorizable lines across all statuses (not just the active tab) */
55
+ total_lines_count?: number;
54
56
  transaction_type_name?: string;
55
57
  transaction_update_time?: string;
56
58
  vendor_id?: string | null;
@@ -124,6 +124,9 @@ const toTransaction = (payload) => {
124
124
  : {}),
125
125
  linkedTransactions: payload.linked_transactions?.map((transactionIDPayload) => (0, transactionIDPayload_1.toTransactionID)(transactionIDPayload)),
126
126
  lines,
127
+ ...(payload.total_lines_count != null
128
+ ? { totalLinesCount: payload.total_lines_count }
129
+ : {}),
127
130
  account: payloadHasAccountInfo(payload)
128
131
  ? (0, accountPayload_1.mapAccountBasePayloadToAccountBase)(payload)
129
132
  : undefined,
@@ -157,6 +160,16 @@ const toTransactionPayload = (transaction) => {
157
160
  lines: transaction?.lines?.map((line) => (0, transactionLinePayload_1.toLinePayload)(line)) ?? [],
158
161
  recommendations: toRecommendationsPayload(transaction.lines ?? [], transaction.recommendations ?? {}),
159
162
  };
163
+ // Only carry `document_id` (the journal-entry title / document number) when
164
+ // the transaction actually has one, so transaction types that never expose
165
+ // the title don't start sending the key, and we never emit an empty string
166
+ // that the server could interpret as clearing the number.
167
+ if (transaction.documentId != null) {
168
+ transactionPayload = {
169
+ ...transactionPayload,
170
+ document_id: transaction.documentId,
171
+ };
172
+ }
160
173
  //note: for transaction type deposit and refund account is must for put call
161
174
  if (transaction.account != null &&
162
175
  (transaction.type === 'deposit' || transaction.type === 'refund')) {
@@ -56,6 +56,8 @@ export interface Transaction extends BaseTransaction {
56
56
  toFromAccountId?: ID;
57
57
  toFromAccountIntegrationId?: string;
58
58
  toFromAccountName?: string;
59
+ /** Count of categorizable lines across all statuses (transactions list API); used to gate single-line-only flows */
60
+ totalLinesCount?: number;
59
61
  updateTime?: ZeniDate;
60
62
  vendorId?: ID;
61
63
  vendorName?: string;
@@ -265,6 +265,14 @@ export const isTenantCardsOnly = (tenant) => {
265
265
  tenant.productSettings.isSpendManagementEnabled === false
266
266
  : false;
267
267
  };
268
+ export const isTenantAccountingServicesEnabled = (tenant) => {
269
+ return tenant != null && tenant.productSettings != null
270
+ ? tenant.productSettings.isCfoEnabled === true ||
271
+ tenant.productSettings.isTaxEnabled === true ||
272
+ tenant.productSettings.isPayrollEnabled === true ||
273
+ tenant.productSettings.isBookkeepingEnabled === true
274
+ : false;
275
+ };
268
276
  export const getIsAccountingClassesEnabled = (state) => {
269
277
  const { currentTenantId, tenantsById } = state.tenantState;
270
278
  if (currentTenantId == null) {
@@ -120,6 +120,9 @@ export const toTransaction = (payload) => {
120
120
  : {}),
121
121
  linkedTransactions: payload.linked_transactions?.map((transactionIDPayload) => toTransactionID(transactionIDPayload)),
122
122
  lines,
123
+ ...(payload.total_lines_count != null
124
+ ? { totalLinesCount: payload.total_lines_count }
125
+ : {}),
123
126
  account: payloadHasAccountInfo(payload)
124
127
  ? mapAccountBasePayloadToAccountBase(payload)
125
128
  : undefined,
@@ -152,6 +155,16 @@ export const toTransactionPayload = (transaction) => {
152
155
  lines: transaction?.lines?.map((line) => toLinePayload(line)) ?? [],
153
156
  recommendations: toRecommendationsPayload(transaction.lines ?? [], transaction.recommendations ?? {}),
154
157
  };
158
+ // Only carry `document_id` (the journal-entry title / document number) when
159
+ // the transaction actually has one, so transaction types that never expose
160
+ // the title don't start sending the key, and we never emit an empty string
161
+ // that the server could interpret as clearing the number.
162
+ if (transaction.documentId != null) {
163
+ transactionPayload = {
164
+ ...transactionPayload,
165
+ document_id: transaction.documentId,
166
+ };
167
+ }
155
168
  //note: for transaction type deposit and refund account is must for put call
156
169
  if (transaction.account != null &&
157
170
  (transaction.type === 'deposit' || transaction.type === 'refund')) {
@@ -8,7 +8,7 @@ import { getUserRoleByUserIds } from '../../../entity/userRole/userRoleSelector'
8
8
  import { getCompanyConfig } from '../../companyConfigView/companyConfigSelector';
9
9
  import { getCompanyView } from '../../companyView/selectorHelpers/getCompanyView';
10
10
  import { getCompanyAndIdentityDetails, } from '../../spendManagement/commonSetup/setupViewSelector';
11
- import { ALL_ONBOARDING_STEPS, ALL_ZENI_USER_ONBOARDING_STEPS, REQUIRED_ONBOARDING_STEPS, ZENI_USER_REQUIRED_ONBOARDING_STEPS, emptyAiAgentsActivationSubview, toOnboardingStepType, } from './onboardingCustomerViewState';
11
+ import { ALL_ONBOARDING_STEPS, ALL_ZENI_USER_BOOKKEEPING_ONBOARDING_STEPS, ALL_ZENI_USER_ONBOARDING_STEPS, REQUIRED_ONBOARDING_STEPS, ZENI_USER_REQUIRED_ONBOARDING_STEPS, emptyAiAgentsActivationSubview, toOnboardingStepType, } from './onboardingCustomerViewState';
12
12
  export function getOnboardingCustomerView(state, companyId) {
13
13
  const { fileState, paymentAccountState, onboardingCustomerViewState, userState, userRoleState, subscriptionPaymentAccountState, companyConfigState, cardPaymentViewState, countryListState, addressViewState, } = state;
14
14
  const { companyDetails, companyOfficerUpdateStatus, primaryContactDetails, subscriptionPaymentAccountIds, paymentAccountDetails, plaidConnectionDetails, parsingDocumentFileIds, updateStatus, saveStatus, currentStep, currentSubStep, uiState, fetchState, error, } = onboardingCustomerViewState;
@@ -207,14 +207,20 @@ export function isOfficerDetailsCompleted(companyOfficers, companyOfficerRoles,
207
207
  isPhoneVerified === true);
208
208
  }
209
209
  export const onboardingStepsData = (isValidConnection, productSettings, accountingConnectionCreationMode) => {
210
- const allStepsData = accountingConnectionCreationMode === 'zeni_user' ||
211
- ((productSettings.isBankingEnabled || productSettings.isCardsEnabled) &&
212
- !productSettings.isBookkeepingEnabled)
210
+ // 3 cohorts:
211
+ // - Core-only (banking/cards, no bookkeeping): no ledger, no integrations.
212
+ // - Zeni-team-connects-QBO bookkeeping: skip ledger but keep integrations
213
+ // so the customer can still wire up Plaid / Stripe / HubSpot / email.
214
+ // - Customer-connects-QBO bookkeeping: full 4-step flow.
215
+ const isCoreOnlyTenant = (productSettings.isBankingEnabled || productSettings.isCardsEnabled) &&
216
+ !productSettings.isBookkeepingEnabled;
217
+ const allStepsData = isCoreOnlyTenant
213
218
  ? ALL_ZENI_USER_ONBOARDING_STEPS
214
- : ALL_ONBOARDING_STEPS;
219
+ : accountingConnectionCreationMode === 'zeni_user'
220
+ ? ALL_ZENI_USER_BOOKKEEPING_ONBOARDING_STEPS
221
+ : ALL_ONBOARDING_STEPS;
215
222
  const requiredStepsData = (accountingConnectionCreationMode === 'zeni_user' && isValidConnection) ||
216
- ((productSettings.isBankingEnabled || productSettings.isCardsEnabled) &&
217
- !productSettings.isBookkeepingEnabled)
223
+ isCoreOnlyTenant
218
224
  ? ZENI_USER_REQUIRED_ONBOARDING_STEPS
219
225
  : REQUIRED_ONBOARDING_STEPS;
220
226
  return {
@@ -18,6 +18,14 @@ export const ALL_ZENI_USER_ONBOARDING_STEPS = [
18
18
  ...ZENI_USER_REQUIRED_ONBOARDING_STEPS,
19
19
  ...ALL_ZENI_USER_ONBOARDING_STEPS_WITHOUT_ACCOUNT,
20
20
  ];
21
+ // Zeni-team-connects-QBO bookkeeping tenants: skip the `connect_ledger` step
22
+ // (Zeni already did it) but keep `connect_data_source` so the customer can
23
+ // still wire up non-QBO integrations (Plaid, Stripe, HubSpot, email, etc.).
24
+ export const ALL_ZENI_USER_BOOKKEEPING_ONBOARDING_STEPS = [
25
+ ...ZENI_USER_REQUIRED_ONBOARDING_STEPS,
26
+ 'connect_data_source',
27
+ ...ALL_ZENI_USER_ONBOARDING_STEPS_WITHOUT_ACCOUNT,
28
+ ];
21
29
  export const ALL_CUSTOMER_ONBOARDING_STEPS_WITH_QBO = [
22
30
  ...CUSTOMER_REQUIRED_ONBOARDING_STEPS_WITH_QBO,
23
31
  'connect_data_source',
@@ -1,4 +1,4 @@
1
- import { isTenantBankingOnly, isTenantCardsOnly, } from '../../entity/tenant/tenantSelector';
1
+ import { isTenantAccountingServicesEnabled, isTenantBankingOnly, isTenantCardsOnly, } from '../../entity/tenant/tenantSelector';
2
2
  import { hasAnyAdminLevelAccess, hasBillPayAccessible, hasBookKeepingAdminLevelAccess, hasBookKeepingUserLevelAccess, hasChargeCardAccessible, hasFullOrAdminLevelAccess, hasInvestorBankerLevelAccess, hasReimbursementAccess, hasReimbursementAccessible, hasSuperAdminLevelAccess, hasZeniAccountsAccessible, isZeniSignedInUser, } from '../../entity/userRole/userRoleType';
3
3
  import { isVendorsTabVisible } from '../vendorList/vendorListSelector';
4
4
  export const isAccountingTabVisible = (userRole, isDebitCardFlagEnabled, currentTenant, userRoleMap) => {
@@ -10,8 +10,9 @@ export const isAccountingTabVisible = (userRole, isDebitCardFlagEnabled, current
10
10
  hasBillPayAccessible(currentTenant, userRoleMap) ||
11
11
  hasReimbursementAccessible(currentTenant, userRoleMap) ||
12
12
  hasFullOrAdminLevelAccess(userRole)) &&
13
- isTenantBankingOnly(isDebitCardFlagEnabled, currentTenant) === false &&
14
- isTenantCardsOnly(currentTenant) === false);
13
+ (isTenantAccountingServicesEnabled(currentTenant) ||
14
+ (isTenantBankingOnly(isDebitCardFlagEnabled, currentTenant) === false &&
15
+ isTenantCardsOnly(currentTenant) === false)));
15
16
  }
16
17
  else {
17
18
  return false;
@@ -29,8 +30,9 @@ export const isApprovalRulesTabVisible = (userRole, isDebitCardFlagEnabled, curr
29
30
  hasReimbursementAccessible(currentTenant, userRoleMap) ||
30
31
  ((isBillPayTOSAccepted || isReimbursementTOSAccepted) &&
31
32
  hasFullOrAdminLevelAccess(userRole))) &&
32
- isTenantBankingOnly(isDebitCardFlagEnabled, currentTenant) === false &&
33
- isTenantCardsOnly(currentTenant) === false);
33
+ (isTenantAccountingServicesEnabled(currentTenant) ||
34
+ (isTenantBankingOnly(isDebitCardFlagEnabled, currentTenant) === false &&
35
+ isTenantCardsOnly(currentTenant) === false)));
34
36
  }
35
37
  else {
36
38
  return false;
@@ -94,8 +96,9 @@ export const isIntegrationsTabVisible = (userRole, isDebitCardFlagEnabled, curre
94
96
  }
95
97
  if (isSettingsVisible(currentTenant)) {
96
98
  return (hasFullOrAdminLevelAccess(userRole) &&
97
- !(isTenantBankingOnly(isDebitCardFlagEnabled, currentTenant) ||
98
- isTenantCardsOnly(currentTenant)));
99
+ (isTenantAccountingServicesEnabled(currentTenant) ||
100
+ !(isTenantBankingOnly(isDebitCardFlagEnabled, currentTenant) ||
101
+ isTenantCardsOnly(currentTenant))));
99
102
  }
100
103
  else {
101
104
  return false;
@@ -146,6 +149,7 @@ export const isNotificationsTabVisible = (notificationSettings, signedInUseEmail
146
149
  const { reminderNotificationSettings, monthEndInsightsNotificationSettings } = notificationSettings;
147
150
  return ((isRemindersSectionVisibleOnSettingsPage(reminderNotificationSettings) ||
148
151
  isMonthEndNotificationsSectionVisibleOnSettingsPage(monthEndInsightsNotificationSettings, signedInUseEmail)) &&
149
- isTenantBankingOnly(isDebitCardFlagEnabled, currentTenant) === false &&
150
- isTenantCardsOnly(currentTenant) === false);
152
+ (isTenantAccountingServicesEnabled(currentTenant) ||
153
+ (isTenantBankingOnly(isDebitCardFlagEnabled, currentTenant) === false &&
154
+ isTenantCardsOnly(currentTenant) === false)));
151
155
  };
@@ -11,7 +11,21 @@ import { KYC_KYB_AUTOFILL_MIN_CONFIDENCE, } from './types/kycKybAutofill';
11
11
  export const OFFICER_ADDRESS_AUTOFILL_FIELD = 'officerAddress';
12
12
  // US driving licenses carry a US state in `address_state` but no country field;
13
13
  // default the composed address to the US.
14
- const DRIVING_LICENSE_DEFAULT_COUNTRY = 'United States';
14
+ // Extend's driving-license parser is US-focused — its response shape
15
+ // (`ExtendDrivingLicenseParsed`) only returns `issuing_state`, no
16
+ // `issuing_country` / `country_of_issue`. So we default the address country
17
+ // to US for autofill purposes.
18
+ //
19
+ // `country` is the alpha-3 ISO code (matches the rest of the codebase's
20
+ // address payloads — e.g. `subscriptionBillingAddress.country = 'USA'`).
21
+ // The Edit Address form's country dropdown looks up the option by alpha-3,
22
+ // so using the country name here would leave the dropdown blank.
23
+ //
24
+ // To support non-US driving licenses later: (1) extend the document-service
25
+ // Extend workflow to return an issuing-country field, (2) add it to
26
+ // `ExtendDrivingLicenseParsed`, (3) read it here with this US value as the
27
+ // fallback when the parser couldn't determine the country.
28
+ const DRIVING_LICENSE_DEFAULT_COUNTRY = 'USA';
15
29
  const DRIVING_LICENSE_DEFAULT_COUNTRY_CODE = 'US';
16
30
  const emptyResult = () => ({
17
31
  values: {},
@@ -29,6 +29,7 @@ export const toTransactionUpdatesFromTransactionDetailLocalData = (transactionDe
29
29
  const entityUpdates = {};
30
30
  const isTransactionMisCategorized = isTransactionMiscategorized(transaction, isExpenseAutomationEnabled);
31
31
  updates.memo = transactionDetailLocalData.memo;
32
+ updates.documentId = transactionDetailLocalData.documentId;
32
33
  const selectedEntity = transactionDetailLocalData.selectedEntity;
33
34
  const updatedEntityId = transactionDetailLocalData.updatePreviousTransactionsWithUpdatedVendor ===
34
35
  true
@@ -168,6 +168,12 @@ function preparePayload(transactionState, vendorState, transactionDetailState, t
168
168
  const toTransactionUpdatePayload = (updates, detail, cotTransactionTracking, vendorUpdates, customerUpdates, tranSelectedEntity) => {
169
169
  const transaction = Object.assign({}, detail);
170
170
  transaction.memo = updates.memo ?? '';
171
+ // Unlike `memo` (always reset to '' when absent), `documentId` falls back to
172
+ // the existing value rather than ''. Only the journal-entry title is
173
+ // user-editable; for every other transaction type `updates.documentId` is
174
+ // either the unchanged current value or undefined, and we must not send an
175
+ // empty `document_id` that the server would treat as clearing the number.
176
+ transaction.documentId = updates.documentId ?? transaction.documentId;
171
177
  const recommendationUpdates = {};
172
178
  for (const key in updates.updatesByLineId) {
173
179
  const lineUpdates = updates.updatesByLineId[key];
@@ -11,6 +11,7 @@ export const toTransactionDetailLocalData = (transaction, defaultVendorId, uncat
11
11
  ?.id;
12
12
  const vendorId = defaultVendorId ?? transaction.vendor?.id;
13
13
  transactionLocal.memo = transaction.memo;
14
+ transactionLocal.documentId = transaction.documentId;
14
15
  transactionLocal.vendorUpdates =
15
16
  isVendorTransaction(transaction) && vendorId != null
16
17
  ? {
@@ -421,6 +422,7 @@ export const toUpdateLineItemsOriginalCategoryClass = (transaction, transactionD
421
422
  const newLocalData = {
422
423
  ...initialSupportedTransactionDetail.transactionDetailLocalData,
423
424
  memo: transactionDetailLocalData.memo,
425
+ documentId: transactionDetailLocalData.documentId,
424
426
  lineItemById: newLineItems,
425
427
  vendorDetails: transactionDetailLocalData.vendorDetails,
426
428
  vendorUpdates: transactionDetailLocalData.vendorUpdates,
@@ -524,6 +526,7 @@ export const toSetAllLineItemsToCategoryClass = (transaction, transactionDetailL
524
526
  const newLocalData = {
525
527
  ...initialSupportedTransactionDetail.transactionDetailLocalData,
526
528
  memo: transactionDetailLocalData.memo,
529
+ documentId: transactionDetailLocalData.documentId,
527
530
  lineItemById: newLineItems,
528
531
  vendorUpdates: transactionDetailLocalData.vendorUpdates,
529
532
  vendorDetails: transactionDetailLocalData.vendorDetails,
@@ -568,6 +571,7 @@ export const toSetAllLineItemsLocalDataIsUpdateAllChecked = (transaction, transa
568
571
  const newLocalData = {
569
572
  ...initialSupportedTransactionDetail.transactionDetailLocalData,
570
573
  memo: transactionDetailLocalData.memo,
574
+ documentId: transactionDetailLocalData.documentId,
571
575
  lineItemById: newLineItems,
572
576
  vendorDetails: transactionDetailLocalData.vendorDetails,
573
577
  vendorUpdates: transactionDetailLocalData.vendorUpdates,
@@ -724,6 +728,7 @@ export const setEntityRecommendationForLineIdInLocalData = (transaction, transac
724
728
  const newLocalData = {
725
729
  ...initialSupportedTransactionDetail.transactionDetailLocalData,
726
730
  memo: transactionDetailLocalData.memo,
731
+ documentId: transactionDetailLocalData.documentId,
727
732
  lineItemById: newLineItems,
728
733
  vendorUpdates: transactionDetailLocalData.vendorUpdates,
729
734
  vendorDetails: transactionDetailLocalData.vendorDetails,
@@ -776,6 +781,7 @@ export const removeEntityRecommendationForLineIdInLocalData = (transaction, tran
776
781
  const newLocalData = {
777
782
  ...initialSupportedTransactionDetail.transactionDetailLocalData,
778
783
  memo: transactionDetailLocalData.memo,
784
+ documentId: transactionDetailLocalData.documentId,
779
785
  lineItemById: newLineItems,
780
786
  vendorDetails: transactionDetailLocalData.vendorDetails,
781
787
  vendorUpdates: transactionDetailLocalData.vendorUpdates,
@@ -881,7 +887,6 @@ export const toSetAllLineItemsToVendor = (transaction, transactionDetailLocalDat
881
887
  }
882
888
  const newLocalData = {
883
889
  ...transactionDetailLocalData,
884
- memo: transactionDetailLocalData.memo,
885
890
  lineItemById: newLineItems,
886
891
  isVendorUpdateAllChecked: true,
887
892
  };
@@ -996,6 +1001,7 @@ export const toUpdateLineItemsToOriginalVendor = (transaction, transactionDetail
996
1001
  const newLocalData = {
997
1002
  ...initialSupportedTransactionDetail.transactionDetailLocalData,
998
1003
  memo: transactionDetailLocalData.memo,
1004
+ documentId: transactionDetailLocalData.documentId,
999
1005
  lineItemById: newLineItems,
1000
1006
  isVendorUpdateAllChecked: false,
1001
1007
  latestUpdatedLineWithEntity: transactionDetailLocalData.latestUpdatedLineWithEntity,
@@ -213,14 +213,20 @@ function isOfficerDetailsCompleted(companyOfficers, companyOfficerRoles, officer
213
213
  isPhoneVerified === true);
214
214
  }
215
215
  const onboardingStepsData = (isValidConnection, productSettings, accountingConnectionCreationMode) => {
216
- const allStepsData = accountingConnectionCreationMode === 'zeni_user' ||
217
- ((productSettings.isBankingEnabled || productSettings.isCardsEnabled) &&
218
- !productSettings.isBookkeepingEnabled)
216
+ // 3 cohorts:
217
+ // - Core-only (banking/cards, no bookkeeping): no ledger, no integrations.
218
+ // - Zeni-team-connects-QBO bookkeeping: skip ledger but keep integrations
219
+ // so the customer can still wire up Plaid / Stripe / HubSpot / email.
220
+ // - Customer-connects-QBO bookkeeping: full 4-step flow.
221
+ const isCoreOnlyTenant = (productSettings.isBankingEnabled || productSettings.isCardsEnabled) &&
222
+ !productSettings.isBookkeepingEnabled;
223
+ const allStepsData = isCoreOnlyTenant
219
224
  ? onboardingCustomerViewState_1.ALL_ZENI_USER_ONBOARDING_STEPS
220
- : onboardingCustomerViewState_1.ALL_ONBOARDING_STEPS;
225
+ : accountingConnectionCreationMode === 'zeni_user'
226
+ ? onboardingCustomerViewState_1.ALL_ZENI_USER_BOOKKEEPING_ONBOARDING_STEPS
227
+ : onboardingCustomerViewState_1.ALL_ONBOARDING_STEPS;
221
228
  const requiredStepsData = (accountingConnectionCreationMode === 'zeni_user' && isValidConnection) ||
222
- ((productSettings.isBankingEnabled || productSettings.isCardsEnabled) &&
223
- !productSettings.isBookkeepingEnabled)
229
+ isCoreOnlyTenant
224
230
  ? onboardingCustomerViewState_1.ZENI_USER_REQUIRED_ONBOARDING_STEPS
225
231
  : onboardingCustomerViewState_1.REQUIRED_ONBOARDING_STEPS;
226
232
  return {
@@ -8,6 +8,7 @@ export declare const CUSTOMER_REQUIRED_ONBOARDING_STEPS_WITH_QBO: readonly ["con
8
8
  export declare const REQUIRED_ONBOARDING_STEPS: readonly ["setup_billing", "connect_ledger"];
9
9
  export declare const ALL_ZENI_USER_ONBOARDING_STEPS_WITHOUT_ACCOUNT: readonly ["verification"];
10
10
  export declare const ALL_ZENI_USER_ONBOARDING_STEPS: readonly ["setup_billing", "verification"];
11
+ export declare const ALL_ZENI_USER_BOOKKEEPING_ONBOARDING_STEPS: readonly ["setup_billing", "connect_data_source", "verification"];
11
12
  export declare const ALL_CUSTOMER_ONBOARDING_STEPS_WITH_QBO: readonly ["connect_ledger", "connect_data_source", "verification"];
12
13
  export declare const ALL_ONBOARDING_STEPS: readonly ["setup_billing", "connect_ledger", "connect_data_source", "verification"];
13
14
  export declare const toOnboardingStepType: (v: string) => "setup_billing" | "connect_ledger" | "verification" | "connect_data_source";
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.emptyAiAgentsActivationSubview = exports.emptyAiAgentsActivationCounts = exports.toOnboardingSubStepType = exports.ALL_ONBOARDING_SUB_STEPS = exports.toOnboardingStepType = exports.ALL_ONBOARDING_STEPS = exports.ALL_CUSTOMER_ONBOARDING_STEPS_WITH_QBO = exports.ALL_ZENI_USER_ONBOARDING_STEPS = exports.ALL_ZENI_USER_ONBOARDING_STEPS_WITHOUT_ACCOUNT = exports.REQUIRED_ONBOARDING_STEPS = exports.CUSTOMER_REQUIRED_ONBOARDING_STEPS_WITH_QBO = exports.ZENI_USER_REQUIRED_ONBOARDING_STEPS = void 0;
3
+ exports.emptyAiAgentsActivationSubview = exports.emptyAiAgentsActivationCounts = exports.toOnboardingSubStepType = exports.ALL_ONBOARDING_SUB_STEPS = exports.toOnboardingStepType = exports.ALL_ONBOARDING_STEPS = exports.ALL_CUSTOMER_ONBOARDING_STEPS_WITH_QBO = exports.ALL_ZENI_USER_BOOKKEEPING_ONBOARDING_STEPS = exports.ALL_ZENI_USER_ONBOARDING_STEPS = exports.ALL_ZENI_USER_ONBOARDING_STEPS_WITHOUT_ACCOUNT = exports.REQUIRED_ONBOARDING_STEPS = exports.CUSTOMER_REQUIRED_ONBOARDING_STEPS_WITH_QBO = exports.ZENI_USER_REQUIRED_ONBOARDING_STEPS = void 0;
4
4
  const stringToUnion_1 = require("../../../commonStateTypes/stringToUnion");
5
5
  // Onboarding steps are the 4 top-level surfaces. Officer + details verification
6
6
  // live as sub-steps under `verification` (state machine in CustomerOnboardingPage).
@@ -21,6 +21,14 @@ exports.ALL_ZENI_USER_ONBOARDING_STEPS = [
21
21
  ...exports.ZENI_USER_REQUIRED_ONBOARDING_STEPS,
22
22
  ...exports.ALL_ZENI_USER_ONBOARDING_STEPS_WITHOUT_ACCOUNT,
23
23
  ];
24
+ // Zeni-team-connects-QBO bookkeeping tenants: skip the `connect_ledger` step
25
+ // (Zeni already did it) but keep `connect_data_source` so the customer can
26
+ // still wire up non-QBO integrations (Plaid, Stripe, HubSpot, email, etc.).
27
+ exports.ALL_ZENI_USER_BOOKKEEPING_ONBOARDING_STEPS = [
28
+ ...exports.ZENI_USER_REQUIRED_ONBOARDING_STEPS,
29
+ 'connect_data_source',
30
+ ...exports.ALL_ZENI_USER_ONBOARDING_STEPS_WITHOUT_ACCOUNT,
31
+ ];
24
32
  exports.ALL_CUSTOMER_ONBOARDING_STEPS_WITH_QBO = [
25
33
  ...exports.CUSTOMER_REQUIRED_ONBOARDING_STEPS_WITH_QBO,
26
34
  'connect_data_source',
@@ -13,8 +13,9 @@ const isAccountingTabVisible = (userRole, isDebitCardFlagEnabled, currentTenant,
13
13
  (0, userRoleType_1.hasBillPayAccessible)(currentTenant, userRoleMap) ||
14
14
  (0, userRoleType_1.hasReimbursementAccessible)(currentTenant, userRoleMap) ||
15
15
  (0, userRoleType_1.hasFullOrAdminLevelAccess)(userRole)) &&
16
- (0, tenantSelector_1.isTenantBankingOnly)(isDebitCardFlagEnabled, currentTenant) === false &&
17
- (0, tenantSelector_1.isTenantCardsOnly)(currentTenant) === false);
16
+ ((0, tenantSelector_1.isTenantAccountingServicesEnabled)(currentTenant) ||
17
+ ((0, tenantSelector_1.isTenantBankingOnly)(isDebitCardFlagEnabled, currentTenant) === false &&
18
+ (0, tenantSelector_1.isTenantCardsOnly)(currentTenant) === false)));
18
19
  }
19
20
  else {
20
21
  return false;
@@ -33,8 +34,9 @@ const isApprovalRulesTabVisible = (userRole, isDebitCardFlagEnabled, currentTena
33
34
  (0, userRoleType_1.hasReimbursementAccessible)(currentTenant, userRoleMap) ||
34
35
  ((isBillPayTOSAccepted || isReimbursementTOSAccepted) &&
35
36
  (0, userRoleType_1.hasFullOrAdminLevelAccess)(userRole))) &&
36
- (0, tenantSelector_1.isTenantBankingOnly)(isDebitCardFlagEnabled, currentTenant) === false &&
37
- (0, tenantSelector_1.isTenantCardsOnly)(currentTenant) === false);
37
+ ((0, tenantSelector_1.isTenantAccountingServicesEnabled)(currentTenant) ||
38
+ ((0, tenantSelector_1.isTenantBankingOnly)(isDebitCardFlagEnabled, currentTenant) === false &&
39
+ (0, tenantSelector_1.isTenantCardsOnly)(currentTenant) === false)));
38
40
  }
39
41
  else {
40
42
  return false;
@@ -103,8 +105,9 @@ const isIntegrationsTabVisible = (userRole, isDebitCardFlagEnabled, currentTenan
103
105
  }
104
106
  if ((0, exports.isSettingsVisible)(currentTenant)) {
105
107
  return ((0, userRoleType_1.hasFullOrAdminLevelAccess)(userRole) &&
106
- !((0, tenantSelector_1.isTenantBankingOnly)(isDebitCardFlagEnabled, currentTenant) ||
107
- (0, tenantSelector_1.isTenantCardsOnly)(currentTenant)));
108
+ ((0, tenantSelector_1.isTenantAccountingServicesEnabled)(currentTenant) ||
109
+ !((0, tenantSelector_1.isTenantBankingOnly)(isDebitCardFlagEnabled, currentTenant) ||
110
+ (0, tenantSelector_1.isTenantCardsOnly)(currentTenant))));
108
111
  }
109
112
  else {
110
113
  return false;
@@ -161,7 +164,8 @@ const isNotificationsTabVisible = (notificationSettings, signedInUseEmail, isDeb
161
164
  const { reminderNotificationSettings, monthEndInsightsNotificationSettings } = notificationSettings;
162
165
  return (((0, exports.isRemindersSectionVisibleOnSettingsPage)(reminderNotificationSettings) ||
163
166
  (0, exports.isMonthEndNotificationsSectionVisibleOnSettingsPage)(monthEndInsightsNotificationSettings, signedInUseEmail)) &&
164
- (0, tenantSelector_1.isTenantBankingOnly)(isDebitCardFlagEnabled, currentTenant) === false &&
165
- (0, tenantSelector_1.isTenantCardsOnly)(currentTenant) === false);
167
+ ((0, tenantSelector_1.isTenantAccountingServicesEnabled)(currentTenant) ||
168
+ ((0, tenantSelector_1.isTenantBankingOnly)(isDebitCardFlagEnabled, currentTenant) === false &&
169
+ (0, tenantSelector_1.isTenantCardsOnly)(currentTenant) === false)));
166
170
  };
167
171
  exports.isNotificationsTabVisible = isNotificationsTabVisible;
@@ -14,7 +14,21 @@ const kycKybAutofill_1 = require("./types/kycKybAutofill");
14
14
  exports.OFFICER_ADDRESS_AUTOFILL_FIELD = 'officerAddress';
15
15
  // US driving licenses carry a US state in `address_state` but no country field;
16
16
  // default the composed address to the US.
17
- const DRIVING_LICENSE_DEFAULT_COUNTRY = 'United States';
17
+ // Extend's driving-license parser is US-focused — its response shape
18
+ // (`ExtendDrivingLicenseParsed`) only returns `issuing_state`, no
19
+ // `issuing_country` / `country_of_issue`. So we default the address country
20
+ // to US for autofill purposes.
21
+ //
22
+ // `country` is the alpha-3 ISO code (matches the rest of the codebase's
23
+ // address payloads — e.g. `subscriptionBillingAddress.country = 'USA'`).
24
+ // The Edit Address form's country dropdown looks up the option by alpha-3,
25
+ // so using the country name here would leave the dropdown blank.
26
+ //
27
+ // To support non-US driving licenses later: (1) extend the document-service
28
+ // Extend workflow to return an issuing-country field, (2) add it to
29
+ // `ExtendDrivingLicenseParsed`, (3) read it here with this US value as the
30
+ // fallback when the parser couldn't determine the country.
31
+ const DRIVING_LICENSE_DEFAULT_COUNTRY = 'USA';
18
32
  const DRIVING_LICENSE_DEFAULT_COUNTRY_CODE = 'US';
19
33
  const emptyResult = () => ({
20
34
  values: {},
@@ -32,6 +32,7 @@ const toTransactionUpdatesFromTransactionDetailLocalData = (transactionDetailLoc
32
32
  const entityUpdates = {};
33
33
  const isTransactionMisCategorized = (0, exports.isTransactionMiscategorized)(transaction, isExpenseAutomationEnabled);
34
34
  updates.memo = transactionDetailLocalData.memo;
35
+ updates.documentId = transactionDetailLocalData.documentId;
35
36
  const selectedEntity = transactionDetailLocalData.selectedEntity;
36
37
  const updatedEntityId = transactionDetailLocalData.updatePreviousTransactionsWithUpdatedVendor ===
37
38
  true
@@ -175,6 +175,12 @@ function preparePayload(transactionState, vendorState, transactionDetailState, t
175
175
  const toTransactionUpdatePayload = (updates, detail, cotTransactionTracking, vendorUpdates, customerUpdates, tranSelectedEntity) => {
176
176
  const transaction = Object.assign({}, detail);
177
177
  transaction.memo = updates.memo ?? '';
178
+ // Unlike `memo` (always reset to '' when absent), `documentId` falls back to
179
+ // the existing value rather than ''. Only the journal-entry title is
180
+ // user-editable; for every other transaction type `updates.documentId` is
181
+ // either the unchanged current value or undefined, and we must not send an
182
+ // empty `document_id` that the server would treat as clearing the number.
183
+ transaction.documentId = updates.documentId ?? transaction.documentId;
178
184
  const recommendationUpdates = {};
179
185
  for (const key in updates.updatesByLineId) {
180
186
  const lineUpdates = updates.updatesByLineId[key];
@@ -14,6 +14,7 @@ const toTransactionDetailLocalData = (transaction, defaultVendorId, uncategorize
14
14
  ?.id;
15
15
  const vendorId = defaultVendorId ?? transaction.vendor?.id;
16
16
  transactionLocal.memo = transaction.memo;
17
+ transactionLocal.documentId = transaction.documentId;
17
18
  transactionLocal.vendorUpdates =
18
19
  (0, vendorTransaction_1.isVendorTransaction)(transaction) && vendorId != null
19
20
  ? {
@@ -427,6 +428,7 @@ const toUpdateLineItemsOriginalCategoryClass = (transaction, transactionDetailLo
427
428
  const newLocalData = {
428
429
  ...transactionDetailState_1.initialSupportedTransactionDetail.transactionDetailLocalData,
429
430
  memo: transactionDetailLocalData.memo,
431
+ documentId: transactionDetailLocalData.documentId,
430
432
  lineItemById: newLineItems,
431
433
  vendorDetails: transactionDetailLocalData.vendorDetails,
432
434
  vendorUpdates: transactionDetailLocalData.vendorUpdates,
@@ -531,6 +533,7 @@ const toSetAllLineItemsToCategoryClass = (transaction, transactionDetailLocalDat
531
533
  const newLocalData = {
532
534
  ...transactionDetailState_1.initialSupportedTransactionDetail.transactionDetailLocalData,
533
535
  memo: transactionDetailLocalData.memo,
536
+ documentId: transactionDetailLocalData.documentId,
534
537
  lineItemById: newLineItems,
535
538
  vendorUpdates: transactionDetailLocalData.vendorUpdates,
536
539
  vendorDetails: transactionDetailLocalData.vendorDetails,
@@ -576,6 +579,7 @@ const toSetAllLineItemsLocalDataIsUpdateAllChecked = (transaction, transactionDe
576
579
  const newLocalData = {
577
580
  ...transactionDetailState_1.initialSupportedTransactionDetail.transactionDetailLocalData,
578
581
  memo: transactionDetailLocalData.memo,
582
+ documentId: transactionDetailLocalData.documentId,
579
583
  lineItemById: newLineItems,
580
584
  vendorDetails: transactionDetailLocalData.vendorDetails,
581
585
  vendorUpdates: transactionDetailLocalData.vendorUpdates,
@@ -733,6 +737,7 @@ const setEntityRecommendationForLineIdInLocalData = (transaction, transactionDet
733
737
  const newLocalData = {
734
738
  ...transactionDetailState_1.initialSupportedTransactionDetail.transactionDetailLocalData,
735
739
  memo: transactionDetailLocalData.memo,
740
+ documentId: transactionDetailLocalData.documentId,
736
741
  lineItemById: newLineItems,
737
742
  vendorUpdates: transactionDetailLocalData.vendorUpdates,
738
743
  vendorDetails: transactionDetailLocalData.vendorDetails,
@@ -786,6 +791,7 @@ const removeEntityRecommendationForLineIdInLocalData = (transaction, transaction
786
791
  const newLocalData = {
787
792
  ...transactionDetailState_1.initialSupportedTransactionDetail.transactionDetailLocalData,
788
793
  memo: transactionDetailLocalData.memo,
794
+ documentId: transactionDetailLocalData.documentId,
789
795
  lineItemById: newLineItems,
790
796
  vendorDetails: transactionDetailLocalData.vendorDetails,
791
797
  vendorUpdates: transactionDetailLocalData.vendorUpdates,
@@ -893,7 +899,6 @@ const toSetAllLineItemsToVendor = (transaction, transactionDetailLocalData, enti
893
899
  }
894
900
  const newLocalData = {
895
901
  ...transactionDetailLocalData,
896
- memo: transactionDetailLocalData.memo,
897
902
  lineItemById: newLineItems,
898
903
  isVendorUpdateAllChecked: true,
899
904
  };
@@ -1009,6 +1014,7 @@ const toUpdateLineItemsToOriginalVendor = (transaction, transactionDetailLocalDa
1009
1014
  const newLocalData = {
1010
1015
  ...transactionDetailState_1.initialSupportedTransactionDetail.transactionDetailLocalData,
1011
1016
  memo: transactionDetailLocalData.memo,
1017
+ documentId: transactionDetailLocalData.documentId,
1012
1018
  lineItemById: newLineItems,
1013
1019
  isVendorUpdateAllChecked: false,
1014
1020
  latestUpdatedLineWithEntity: transactionDetailLocalData.latestUpdatedLineWithEntity,
@@ -60,6 +60,7 @@ export interface TransactionLineUpdates {
60
60
  export interface TransactionUpdates {
61
61
  updatesByLineId: Record<ID, TransactionLineUpdates>;
62
62
  customerId?: ID;
63
+ documentId?: string;
63
64
  memo?: string;
64
65
  vendorId?: ID;
65
66
  }
@@ -52,6 +52,7 @@ export interface TransactionDetailLocalData {
52
52
  customerUpdates?: TransactionVendorUpdates;
53
53
  latestSelectedEntityOnLineItem?: Entity;
54
54
  latestUpdatedLineWithEntity?: ID;
55
+ documentId?: string;
55
56
  memo?: string;
56
57
  selectedEntity?: Entity;
57
58
  updatePreviousTransactionsWithUpdatedVendor?: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zeniai/client-epic-state",
3
- "version": "5.1.6",
3
+ "version": "5.1.8-beta0ND",
4
4
  "description": "Shared module between Web & Mobile containing required abstractions for state management, async network communication. ",
5
5
  "main": "lib/index.js",
6
6
  "module": "lib/esm/index.js",
@@ -106,9 +106,11 @@
106
106
  "scripts": {
107
107
  "clean": "rimraf lib",
108
108
  "test:typecheck": "echo 'Typechecking test files...' && NODE_OPTIONS=--max-old-space-size=4096 time tsc -p tsconfig.typecheck.json",
109
- "test:typecheck-modified-files": "chmod +x ./scripts/tsc-modified-files.sh && ./scripts/tsc-modified-files.sh master tsconfig.typecheck.json",
110
- "test": "pnpm lint-modified-files && pnpm test:typecheck-modified-files && vitest run --typecheck",
111
- "test:full": "vitest run --typecheck",
109
+ "test:typecheck-modified-files": "chmod +x ./scripts/tsc-modified-files.sh && SINCE_DAYS=2 ./scripts/tsc-modified-files.sh master tsconfig.typecheck.json",
110
+ "test:typecheck-mocks": "echo 'Typechecking mock files...' && NODE_OPTIONS=--max-old-space-size=4096 time tsc -p tsconfig.mocks.json",
111
+ "test:typecheck-mocks-modified-files": "chmod +x ./scripts/tsc-modified-files.sh && SINCE_DAYS=2 ./scripts/tsc-modified-files.sh master tsconfig.mocks.json",
112
+ "test": "pnpm lint-modified-files && pnpm test:typecheck-modified-files && pnpm test:typecheck-mocks-modified-files && vitest run --typecheck",
113
+ "test:full": "pnpm test:typecheck-modified-files && pnpm test:typecheck-mocks-modified-files && vitest run --typecheck",
112
114
  "lint": "eslint . --ext .js,.jsx,.ts,.tsx --max-warnings 0 --fix",
113
115
  "lint-modified-files": "chmod +x ./scripts/lint-modified-files.sh && ./scripts/lint-modified-files.sh",
114
116
  "lint:fix-modified-files": "chmod +x ./scripts/lint-modified-files.sh && LINT_FIX=true ./scripts/lint-modified-files.sh",