diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 152fec8fbd875..64ca41bfdcf5f 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -289,9 +289,6 @@ const ONYXKEYS = { /** If the approver dismissed the reject or hold explanation */ NVP_DISMISSED_REJECT_USE_EXPLANATION: 'nvp_dismissedRejectUseExplanation', - /** Whether the user is grandfathered into the free plan */ - NVP_PRIVATE_GRANDFATHERED_FREE: 'nvp_private_grandfatheredFree', - /** Details on whether an account is locked or not */ NVP_PRIVATE_LOCK_ACCOUNT_DETAILS: 'nvp_private_lockAccountDetails', @@ -1406,7 +1403,6 @@ type OnyxValuesMapping = { [ONYXKEYS.NVP_RECONNECT_APP_IF_FULL_RECONNECT_BEFORE]: string; [ONYXKEYS.NVP_PRIVATE_FIRST_POLICY_CREATED_DATE]: string; [ONYXKEYS.NVP_PRIVATE_MANUAL_TEAM_2025_PRICING]: string; - [ONYXKEYS.NVP_PRIVATE_GRANDFATHERED_FREE]: boolean; [ONYXKEYS.NVP_PRIVATE_LOCK_ACCOUNT_DETAILS]: OnyxTypes.LockAccountDetails; [ONYXKEYS.NVP_PRIVATE_CANCELLATION_DETAILS]: OnyxTypes.CancellationDetails[]; [ONYXKEYS.ROOM_MEMBERS_USER_SEARCH_PHRASE]: string; diff --git a/src/libs/SubscriptionUtils.ts b/src/libs/SubscriptionUtils.ts index e8165b2feff17..7f26ce1cebb98 100644 --- a/src/libs/SubscriptionUtils.ts +++ b/src/libs/SubscriptionUtils.ts @@ -594,29 +594,6 @@ function getSubscriptionPlanInfo( }; } -function shouldShowTrialEndedUI( - lastDayFreeTrial: string | undefined, - userBillingFundID: number | undefined, - policies: OnyxCollection, - isGrandfatheredFree: boolean | undefined, - isFromInternalDomain: boolean | undefined, - privateSubscriptionType: SubscriptionType | undefined, -): boolean { - if (!getOwnedPaidPolicies(policies, currentUserAccountID)?.length) { - return false; - } - if (isGrandfatheredFree || isFromInternalDomain) { - return false; - } - if (isSubscriptionTypeOfInvoicing(privateSubscriptionType)) { - return false; - } - if (doesUserHavePaymentCardAdded(userBillingFundID)) { - return false; - } - return hasUserFreeTrialEnded(lastDayFreeTrial); -} - function isSubscriptionTypeOfInvoicing(privateSubscriptionType: SubscriptionType | undefined) { return privateSubscriptionType === CONST.SUBSCRIPTION.TYPE.INVOICING; } @@ -642,7 +619,6 @@ export { shouldCalculateBillNewDot, getSubscriptionPlanInfo, getSubscriptionPrice, - shouldShowTrialEndedUI, isSubscriptionTypeOfInvoicing, }; diff --git a/src/pages/home/TimeSensitiveSection/hooks/useTimeSensitiveOffers.ts b/src/pages/home/TimeSensitiveSection/hooks/useTimeSensitiveOffers.ts index e9d9fd0b5d586..37743c26cc8fd 100644 --- a/src/pages/home/TimeSensitiveSection/hooks/useTimeSensitiveOffers.ts +++ b/src/pages/home/TimeSensitiveSection/hooks/useTimeSensitiveOffers.ts @@ -1,8 +1,7 @@ -import {isFromInternalDomainSelector} from '@selectors/Account'; import useHasTeam2025Pricing from '@hooks/useHasTeam2025Pricing'; import useOnyx from '@hooks/useOnyx'; import useSubscriptionPlan from '@hooks/useSubscriptionPlan'; -import {getEarlyDiscountInfo, shouldShowDiscountBanner, shouldShowTrialEndedUI} from '@libs/SubscriptionUtils'; +import {doesUserHavePaymentCardAdded, getEarlyDiscountInfo, hasUserFreeTrialEnded, shouldShowDiscountBanner} from '@libs/SubscriptionUtils'; import ONYXKEYS from '@src/ONYXKEYS'; function useTimeSensitiveOffers() { @@ -12,9 +11,6 @@ function useTimeSensitiveOffers() { const hasTeam2025Pricing = useHasTeam2025Pricing(); const subscriptionPlan = useSubscriptionPlan(); const [allPolicies] = useOnyx(ONYXKEYS.COLLECTION.POLICY); - const [isGrandfatheredFree] = useOnyx(ONYXKEYS.NVP_PRIVATE_GRANDFATHERED_FREE); - const [isFromInternalDomain] = useOnyx(ONYXKEYS.ACCOUNT, {selector: isFromInternalDomainSelector}); - const [privateSubscription] = useOnyx(ONYXKEYS.NVP_PRIVATE_SUBSCRIPTION); // Use the same logic as the subscription page to determine if discount banner should be shown const shouldShowDiscount = shouldShowDiscountBanner(hasTeam2025Pricing, subscriptionPlan, firstDayFreeTrial, lastDayFreeTrial, userBillingFundID, allPolicies); @@ -25,8 +21,7 @@ function useTimeSensitiveOffers() { const shouldShow25off = shouldShowDiscount && discountInfo?.discountType === 25; // Show add payment card for users whose trial ended and haven't added a payment card - const shouldShowAddPaymentCard = - hasTeam2025Pricing && shouldShowTrialEndedUI(lastDayFreeTrial, userBillingFundID, allPolicies, isGrandfatheredFree, isFromInternalDomain, privateSubscription?.type); + const shouldShowAddPaymentCard = hasUserFreeTrialEnded(lastDayFreeTrial) && !doesUserHavePaymentCardAdded(userBillingFundID); return { shouldShow50off, diff --git a/src/pages/settings/Subscription/CardSection/CardSection.tsx b/src/pages/settings/Subscription/CardSection/CardSection.tsx index f1c2255e39fa8..69ff8cc4d00c2 100644 --- a/src/pages/settings/Subscription/CardSection/CardSection.tsx +++ b/src/pages/settings/Subscription/CardSection/CardSection.tsx @@ -21,7 +21,7 @@ import DateUtils from '@libs/DateUtils'; import Navigation from '@libs/Navigation/Navigation'; import {getPaymentMethodDescription} from '@libs/PaymentUtils'; import {buildQueryStringFromFilterFormValues} from '@libs/SearchQueryUtils'; -import {hasCardAuthenticatedError, isUserOnFreeTrial, shouldShowDiscountBanner, shouldShowPreTrialBillingBanner, shouldShowTrialEndedUI} from '@libs/SubscriptionUtils'; +import {hasCardAuthenticatedError, hasUserFreeTrialEnded, isUserOnFreeTrial, shouldShowDiscountBanner, shouldShowPreTrialBillingBanner} from '@libs/SubscriptionUtils'; import {verifySetupIntent} from '@userActions/PaymentMethods'; import {clearOutstandingBalance} from '@userActions/Subscription'; import CONST from '@src/CONST'; @@ -75,7 +75,6 @@ function CardSection() { const [amountOwed = 0] = useOnyx(ONYXKEYS.NVP_PRIVATE_AMOUNT_OWED); const [ownerBillingGraceEndPeriod] = useOnyx(ONYXKEYS.NVP_PRIVATE_OWNER_BILLING_GRACE_PERIOD_END); const [allPolicies] = useOnyx(ONYXKEYS.COLLECTION.POLICY); - const [isGrandfatheredFree] = useOnyx(ONYXKEYS.NVP_PRIVATE_GRANDFATHERED_FREE); const requestRefund = useCallback(() => { requestRefundByUser(); Navigation.goBackToHome(); @@ -194,7 +193,7 @@ function CardSection() { BillingBanner = ; } else if (isUserOnFreeTrial(firstDayFreeTrial, lastDayFreeTrial)) { BillingBanner = ; - } else if (shouldShowTrialEndedUI(lastDayFreeTrial, userBillingFundID, allPolicies, isGrandfatheredFree, account?.isFromInternalDomain, privateSubscription?.type)) { + } else if (hasUserFreeTrialEnded(lastDayFreeTrial)) { BillingBanner = ; } if (billingStatus) { diff --git a/src/selectors/Account.ts b/src/selectors/Account.ts index 7330906d9731a..808a83a1a0158 100644 --- a/src/selectors/Account.ts +++ b/src/selectors/Account.ts @@ -17,8 +17,6 @@ const requiresTwoFactorAuthSelector = (data: OnyxEntry) => data?.requir const accountGuideDetailsSelector = (account: OnyxEntry) => account?.guideDetails; -const isFromInternalDomainSelector = (account: OnyxEntry) => account?.isFromInternalDomain; - export { isActingAsDelegateSelector, isUserValidatedSelector, @@ -28,5 +26,4 @@ export { isAccountLoadingSelector, requiresTwoFactorAuthSelector, accountGuideDetailsSelector, - isFromInternalDomainSelector, }; diff --git a/src/types/onyx/Account.ts b/src/types/onyx/Account.ts index 6fc7f97405a8e..aee940c0c7403 100644 --- a/src/types/onyx/Account.ts +++ b/src/types/onyx/Account.ts @@ -208,9 +208,6 @@ type Account = { /** Whether or not the user is on a public domain email account or not */ isFromPublicDomain?: boolean; - /** Whether the user's email domain is an internal Expensify domain (e.g. expensify.com) */ - isFromInternalDomain?: boolean; - /** Whether or not the user uses expensify card */ isUsingExpensifyCard?: boolean; diff --git a/tests/unit/SubscriptionUtilsTest.ts b/tests/unit/SubscriptionUtilsTest.ts index fccb9aec5d448..bdc2a4e6c5e41 100644 --- a/tests/unit/SubscriptionUtilsTest.ts +++ b/tests/unit/SubscriptionUtilsTest.ts @@ -18,7 +18,6 @@ import { shouldRestrictUserBillableActions, shouldShowDiscountBanner, shouldShowPreTrialBillingBanner, - shouldShowTrialEndedUI, } from '@libs/SubscriptionUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -1167,57 +1166,4 @@ describe('SubscriptionUtils', () => { expect(shouldCalculateBillNewDot(true, {})).toBeFalsy(); }); }); - - describe('shouldShowTrialEndedUI', () => { - const ownerAccountID = 345; - const policyID = '200012'; - const lastDayFreeTrialEnded = formatDate(subDays(new Date(), 2), CONST.DATE.FNS_DATE_TIME_FORMAT_STRING); - const lastDayFreeTrialActive = formatDate(addDays(new Date(), 5), CONST.DATE.FNS_DATE_TIME_FORMAT_STRING); - - let policies: Record>; - - beforeEach(async () => { - await Onyx.clear(); - policies = { - [`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]: { - ...createRandomPolicy(Number(policyID)), - ownerAccountID, - type: CONST.POLICY.TYPE.CORPORATE, - }, - }; - await Onyx.set(ONYXKEYS.SESSION, {accountID: ownerAccountID}); - }); - - it('should return true for a regular user whose trial ended, no card, with owned workspace', () => { - expect(shouldShowTrialEndedUI(lastDayFreeTrialEnded, undefined, policies, undefined, undefined, undefined)).toBeTruthy(); - }); - - it('should return false if the user has no owned paid policies', () => { - expect(shouldShowTrialEndedUI(lastDayFreeTrialEnded, undefined, {}, undefined, undefined, undefined)).toBeFalsy(); - }); - - it('should return false if the user is grandfathered free', () => { - expect(shouldShowTrialEndedUI(lastDayFreeTrialEnded, undefined, policies, true, undefined, undefined)).toBeFalsy(); - }); - - it('should return false if the user is from an internal domain', () => { - expect(shouldShowTrialEndedUI(lastDayFreeTrialEnded, undefined, policies, undefined, true, undefined)).toBeFalsy(); - }); - - it('should return false if the user is on invoiced billing', () => { - expect(shouldShowTrialEndedUI(lastDayFreeTrialEnded, undefined, policies, undefined, undefined, CONST.SUBSCRIPTION.TYPE.INVOICING)).toBeFalsy(); - }); - - it('should return false if the user has a payment card added', () => { - expect(shouldShowTrialEndedUI(lastDayFreeTrialEnded, 8010, policies, undefined, undefined, undefined)).toBeFalsy(); - }); - - it('should return false if the trial has not ended yet', () => { - expect(shouldShowTrialEndedUI(lastDayFreeTrialActive, undefined, policies, undefined, undefined, undefined)).toBeFalsy(); - }); - - it('should return false if lastDayFreeTrial is undefined', () => { - expect(shouldShowTrialEndedUI(undefined, undefined, policies, undefined, undefined, undefined)).toBeFalsy(); - }); - }); }); diff --git a/tests/unit/hooks/useTimeSensitiveOffers.test.ts b/tests/unit/hooks/useTimeSensitiveOffers.test.ts index f21565ad1c376..6f42988f7b5c6 100644 --- a/tests/unit/hooks/useTimeSensitiveOffers.test.ts +++ b/tests/unit/hooks/useTimeSensitiveOffers.test.ts @@ -4,7 +4,7 @@ import Onyx from 'react-native-onyx'; // Import mocks after they're defined import useHasTeam2025Pricing from '@hooks/useHasTeam2025Pricing'; import useSubscriptionPlan from '@hooks/useSubscriptionPlan'; -import {getEarlyDiscountInfo, shouldShowDiscountBanner, shouldShowTrialEndedUI} from '@libs/SubscriptionUtils'; +import {doesUserHavePaymentCardAdded, getEarlyDiscountInfo, hasUserFreeTrialEnded, shouldShowDiscountBanner} from '@libs/SubscriptionUtils'; import useTimeSensitiveOffers from '@pages/home/TimeSensitiveSection/hooks/useTimeSensitiveOffers'; import ONYXKEYS from '@src/ONYXKEYS'; import waitForBatchedUpdates from '../../utils/waitForBatchedUpdates'; @@ -23,14 +23,16 @@ jest.mock('@hooks/useSubscriptionPlan', () => ({ jest.mock('@libs/SubscriptionUtils', () => ({ shouldShowDiscountBanner: jest.fn(() => false), getEarlyDiscountInfo: jest.fn(() => null), - shouldShowTrialEndedUI: jest.fn(() => false), + hasUserFreeTrialEnded: jest.fn(() => false), + doesUserHavePaymentCardAdded: jest.fn(() => true), })); const mockedUseHasTeam2025Pricing = useHasTeam2025Pricing as jest.Mock; const mockedUseSubscriptionPlan = useSubscriptionPlan as jest.Mock; const mockedShouldShowDiscountBanner = shouldShowDiscountBanner as jest.Mock; const mockedGetEarlyDiscountInfo = getEarlyDiscountInfo as jest.Mock; -const mockedShouldShowTrialEndedUI = shouldShowTrialEndedUI as jest.Mock; +const mockedHasUserFreeTrialEnded = hasUserFreeTrialEnded as jest.Mock; +const mockedDoesUserHavePaymentCardAdded = doesUserHavePaymentCardAdded as jest.Mock; describe('useTimeSensitiveOffers', () => { beforeAll(() => { @@ -214,18 +216,18 @@ describe('useTimeSensitiveOffers', () => { }); describe('when add payment card should not be shown', () => { - it('should return shouldShowAddPaymentCard as false when shouldShowTrialEndedUI returns false', () => { - mockedUseHasTeam2025Pricing.mockReturnValue(true); - mockedShouldShowTrialEndedUI.mockReturnValue(false); + it('should return shouldShowAddPaymentCard as false when trial has not ended', () => { + mockedHasUserFreeTrialEnded.mockReturnValue(false); + mockedDoesUserHavePaymentCardAdded.mockReturnValue(false); const {result} = renderHook(() => useTimeSensitiveOffers()); expect(result.current.shouldShowAddPaymentCard).toBe(false); }); - it('should return shouldShowAddPaymentCard as false when hasTeam2025Pricing is false', () => { - mockedUseHasTeam2025Pricing.mockReturnValue(false); - mockedShouldShowTrialEndedUI.mockReturnValue(true); + it('should return shouldShowAddPaymentCard as false when user already has payment card', () => { + mockedHasUserFreeTrialEnded.mockReturnValue(true); + mockedDoesUserHavePaymentCardAdded.mockReturnValue(true); const {result} = renderHook(() => useTimeSensitiveOffers()); @@ -234,9 +236,9 @@ describe('useTimeSensitiveOffers', () => { }); describe('when add payment card should be shown', () => { - it('should return shouldShowAddPaymentCard as true when hasTeam2025Pricing and shouldShowTrialEndedUI both return true', () => { - mockedUseHasTeam2025Pricing.mockReturnValue(true); - mockedShouldShowTrialEndedUI.mockReturnValue(true); + it('should return shouldShowAddPaymentCard as true when trial ended and user has no payment card', () => { + mockedHasUserFreeTrialEnded.mockReturnValue(true); + mockedDoesUserHavePaymentCardAdded.mockReturnValue(false); const {result} = renderHook(() => useTimeSensitiveOffers()); @@ -245,19 +247,30 @@ describe('useTimeSensitiveOffers', () => { }); describe('add payment card hook dependencies', () => { - it('should call shouldShowTrialEndedUI with Onyx data', async () => { + it('should call hasUserFreeTrialEnded with lastDayFreeTrial from Onyx', async () => { const lastDayFreeTrial = '2025-01-15'; - const userBillingFundID = 12345; await Onyx.merge(ONYXKEYS.NVP_LAST_DAY_FREE_TRIAL, lastDayFreeTrial); + await waitForBatchedUpdates(); + + mockedHasUserFreeTrialEnded.mockReturnValue(false); + mockedDoesUserHavePaymentCardAdded.mockReturnValue(true); + + renderHook(() => useTimeSensitiveOffers()); + + expect(mockedHasUserFreeTrialEnded).toHaveBeenCalledWith(lastDayFreeTrial); + }); + + it('should call doesUserHavePaymentCardAdded with userBillingFundID from Onyx', async () => { + const userBillingFundID = 12345; await Onyx.merge(ONYXKEYS.NVP_BILLING_FUND_ID, userBillingFundID); await waitForBatchedUpdates(); - mockedUseHasTeam2025Pricing.mockReturnValue(true); - mockedShouldShowTrialEndedUI.mockReturnValue(false); + mockedHasUserFreeTrialEnded.mockReturnValue(true); + mockedDoesUserHavePaymentCardAdded.mockReturnValue(true); renderHook(() => useTimeSensitiveOffers()); - expect(mockedShouldShowTrialEndedUI).toHaveBeenCalledWith(lastDayFreeTrial, userBillingFundID, {}, undefined, undefined, undefined); + expect(mockedDoesUserHavePaymentCardAdded).toHaveBeenCalledWith(userBillingFundID); }); }); });