-
Notifications
You must be signed in to change notification settings - Fork 3.9k
Update policyOwnerAmountOwedOverdue billing banner #58910
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
1bc977b
196cf70
8252fec
dbaebec
6511817
730424f
243fe04
2005b3b
1675fdb
4d04b10
406ecf1
5f498fc
658dca8
09c2601
6544c0e
669c151
c7071a9
e65ad3f
196a5d0
2e7dc76
fd6043b
c07617a
5bc1914
e28cd6d
fa1e98a
c34e176
7670805
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -47,6 +47,7 @@ function CardSection() { | |
| const [authenticationLink] = useOnyx(ONYXKEYS.VERIFY_3DS_SUBSCRIPTION); | ||
| const [session] = useOnyx(ONYXKEYS.SESSION); | ||
| const [fundList] = useOnyx(ONYXKEYS.FUND_LIST); | ||
| const [purchaseList] = useOnyx(ONYXKEYS.PURCHASE_LIST); | ||
| const subscriptionPlan = useSubscriptionPlan(); | ||
| const [subscriptionRetryBillingStatusPending] = useOnyx(ONYXKEYS.SUBSCRIPTION_RETRY_BILLING_STATUS_PENDING); | ||
| const [subscriptionRetryBillingStatusSuccessful] = useOnyx(ONYXKEYS.SUBSCRIPTION_RETRY_BILLING_STATUS_SUCCESSFUL); | ||
|
|
@@ -66,15 +67,31 @@ function CardSection() { | |
| Navigation.navigate(ROUTES.SEARCH_ROOT.getRoute({query})); | ||
| }, []); | ||
|
|
||
| const [billingStatus, setBillingStatus] = useState<BillingStatusResult | undefined>(() => CardSectionUtils.getBillingStatus(translate, defaultCard?.accountData ?? {})); | ||
| const [billingStatus, setBillingStatus] = useState<BillingStatusResult | undefined>(() => | ||
| CardSectionUtils.getBillingStatus({translate, accountData: defaultCard?.accountData ?? {}, purchase: purchaseList?.[0]}), | ||
| ); | ||
|
|
||
| const nextPaymentDate = !isEmptyObject(privateSubscription) ? CardSectionUtils.getNextBillingDate() : undefined; | ||
|
|
||
| const sectionSubtitle = defaultCard && !!nextPaymentDate ? translate('subscription.cardSection.cardNextPayment', {nextPaymentDate}) : translate('subscription.cardSection.subtitle'); | ||
|
|
||
| useEffect(() => { | ||
| setBillingStatus(CardSectionUtils.getBillingStatus(translate, defaultCard?.accountData ?? {})); | ||
| }, [subscriptionRetryBillingStatusPending, subscriptionRetryBillingStatusSuccessful, subscriptionRetryBillingStatusFailed, translate, defaultCard?.accountData, privateStripeCustomerID]); | ||
| setBillingStatus( | ||
| CardSectionUtils.getBillingStatus({ | ||
| translate, | ||
| accountData: defaultCard?.accountData ?? {}, | ||
| purchase: purchaseList?.[0], | ||
| }), | ||
|
Comment on lines
+80
to
+84
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a reason we pass just the first purchase?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, accroding to #58067 (comment) we need only the first item for now |
||
| ); | ||
| }, [ | ||
| subscriptionRetryBillingStatusPending, | ||
| subscriptionRetryBillingStatusSuccessful, | ||
| subscriptionRetryBillingStatusFailed, | ||
| translate, | ||
| defaultCard?.accountData, | ||
| privateStripeCustomerID, | ||
| purchaseList, | ||
| ]); | ||
|
|
||
| const handleRetryPayment = () => { | ||
| clearOutstandingBalance(); | ||
|
|
||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Many fields are optional, here is sample {
"amount": 0,
"created": "2025-03-01 03:32:13",
"currency": "USD",
"message": {
"accountManagerAccountID": 0,
"approvedAccountantAccountIDs": [],
"approvedSpend": {
"USD": 0
},
"billableAmount": 2700,
"billablePolicies": {
"0680EF3C70411F57": {
"actorList": "admin+testc@abdelhafidh.com",
"approvedSpend": {
"USD": 0
},
"corporate": true,
"expensifyCardSpend": {
"USD": 0
},
"type": "corporate"
},
"8970B100A18A2CD6": {
"actorList": "admin+testc@abdelhafidh.com",
"approvedSpend": {
"USD": 0
},
"corporate": true,
"expensifyCardSpend": {
"USD": 0
},
"type": "corporate"
},
"8ECD8C2EBB39933F": {
"actorList": "admin+testc@abdelhafidh.com",
"approvedSpend": {
"USD": 0
},
"corporate": true,
"expensifyCardSpend": {
"USD": 0
},
"type": "corporate"
},
"978C2546B3751E81": {
"actorList": "admin+testc@abdelhafidh.com",
"approvedSpend": {
"USD": 0
},
"corporate": false,
"expensifyCardSpend": {
"USD": 0
},
"type": "team"
},
"CDD878B939EB631E": {
"actorList": "admin+testc@abdelhafidh.com",
"approvedSpend": {
"USD": 0
},
"corporate": true,
"expensifyCardSpend": {
"USD": 0
},
"type": "corporate"
},
"E666A63199B43042": {
"actorList": "admin+testc@abdelhafidh.com",
"approvedSpend": {
"USD": 0
},
"corporate": true,
"expensifyCardSpend": {
"USD": 0
},
"type": "corporate"
}
},
"billingType": "failed_2018",
"cardSpendSurchargePercent": 1,
"cashBackAmount": 0,
"cashBackPercentage": 0,
"chatOnlyActorList": "",
"corporateActorCount": 1,
"corporateRevenue": 3600,
"expensifyCardMonthlySpend": 0,
"expensifyCardSpend": {
"USD": 0
},
"freeToTeamMigrationDiscountAmount": 900,
"freeToTeamMigrationDiscountPercent": 25,
"freebieCreditsUsed": 0,
"guideAccountID": 12003194,
"isApprovedAccountant": false,
"isApprovedAccountantClient": false,
"monthlyCorpSubscriptionCost": 1800,
"monthlyTeamSubscriptionCost": 0,
"paidActorCount": 1,
"partnerManagerAccountID": 0,
"perPolicyTotalMembersCount": {
"05E4A0124E314E2D": 1,
"23B249B9FA2A78A3": 2,
"2499B1B92C375F8B": 1,
"4656350AB2539E7A": 1,
"5F5EB0E523187199": 1,
"6AEBBB88BBAF7048": 1,
"762A250743827663": 1,
"978C2546B3751E81": 3,
"C22CACBB604EED41": 1,
"CFD83323ED0FC6A9": 1,
"D15117903706B9B0": 4,
"E5405AABB0C65377": 2,
"FED1806CF392498F": 1
},
"potentialCashBackAmount": 1755,
"potentialCashBackPercentage": 0.01,
"subscription": {
"type": "monthly2018"
},
"teamActorCount": 0,
"teamRevenue": 0,
"totalActorCount": 1,
"totalFreebieCredits": 0,
"totalPlatformSpend": 175500,
"totalRevenue": 2700,
"totalUniqueMembersCount": 6,
"wasDomainBillingUsed": false
},
"purchaseID": 71976932
}cc @amyevans to confirm which fields are required or we can assume all are optional to avoid accessing a non existing field which may lead to a crash
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The top level fields are required (amount, created, currency, message, purchaseID). Anything within |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,171 @@ | ||
| import type CONST from '@src/CONST'; | ||
| import type PrivateSubscription from './PrivateSubscription'; | ||
|
|
||
| /** Subscription type for a purchase */ | ||
| type Subscription = Omit<PrivateSubscription, 'errors' | 'errorFields'>; | ||
|
|
||
| /** Type for a billable policy */ | ||
| type BillablePolicy = { | ||
| /** Comma separated list of emails for members in the policy */ | ||
| actorList?: string; | ||
|
|
||
| /** Amount spent, by currency */ | ||
| approvedSpend?: Record<string, number>; | ||
|
|
||
| /** Whether the policy is corporate */ | ||
| corporate?: boolean; | ||
|
|
||
| /** Expensify card spend by currency */ | ||
| expensifyCardSpend?: Record<string, number>; | ||
|
|
||
| /** Policy type */ | ||
| type?: typeof CONST.POLICY.TYPE; | ||
| }; | ||
|
|
||
| /** Message type for a purchase */ | ||
| type Message = { | ||
| /** Account manager account ID */ | ||
| accountManagerAccountID?: number; | ||
|
|
||
| /** List of Approved Accountant account IDs */ | ||
| approvedAccountantAccountIDs?: number[]; | ||
|
|
||
| /** Approved spend amounts by currency */ | ||
| approvedSpend?: Record<string, number>; | ||
|
|
||
| /** Billable amount */ | ||
| billableAmount?: number; | ||
|
|
||
| /** Billable amount before free trial discount */ | ||
| billableAmountBeforeFreeTrialDiscount?: number; | ||
|
|
||
| /** Record of billable policies with their details */ | ||
| billablePolicies?: Record<string, BillablePolicy>; | ||
|
|
||
| /** Billing type */ | ||
| billingType?: string; | ||
|
|
||
| /** Card spend surcharge percentage */ | ||
| cardSpendSurchargePercent?: number; | ||
|
|
||
| /** Cash back amount */ | ||
| cashBackAmount?: number; | ||
|
|
||
| /** Cash back percentage */ | ||
| cashBackPercentage?: number; | ||
|
|
||
| /** Chat only actor list */ | ||
| chatOnlyActorList?: string; | ||
|
|
||
| /** Actor count for Corporate policy type */ | ||
| corporateActorCount?: number; | ||
|
|
||
| /** Amount charged for Corporate policy type */ | ||
| corporateRevenue?: number; | ||
|
|
||
| /** Expensify Card monthly spend */ | ||
| expensifyCardMonthlySpend?: number; | ||
|
|
||
| /** Expensify Card spend by currency */ | ||
| expensifyCardSpend?: Record<string, number>; | ||
|
|
||
| /** Free trial days */ | ||
| freeTrialDays?: number; | ||
|
|
||
| /** Free trial discount amount */ | ||
| freeTrialDiscountAmount?: number; | ||
|
|
||
| /** Free trial discount percentage */ | ||
| freeTrialDiscountPercentage?: number; | ||
|
|
||
| /** Freebie credits used */ | ||
| freebieCreditsUsed?: number; | ||
|
|
||
| /** Guide account ID */ | ||
| guideAccountID?: number; | ||
|
|
||
| /** Whether the user is an Approved Accountant */ | ||
| isApprovedAccountant?: boolean; | ||
|
|
||
| /** Whether the user is an Approved Accountant client */ | ||
| isApprovedAccountantClient?: boolean; | ||
|
|
||
| /** Paid actor count */ | ||
| paidActorCount?: number; | ||
|
|
||
| /** Partner manager account ID */ | ||
| partnerManagerAccountID?: number; | ||
|
|
||
| /** Per policy total members count */ | ||
| perPolicyTotalMembersCount?: Record<string, number>; | ||
|
|
||
| /** Potential cash back amount */ | ||
| potentialCashBackAmount?: number; | ||
|
|
||
| /** Potential cash back percentage */ | ||
| potentialCashBackPercentage?: number; | ||
|
|
||
| /** Subscription details */ | ||
| subscription?: Subscription; | ||
|
|
||
| /** Actor count for Team policy type */ | ||
| teamActorCount?: number; | ||
|
|
||
| /** Amount charged for Team policy type */ | ||
| teamRevenue?: number; | ||
|
|
||
| /** Total actor count */ | ||
| totalActorCount?: number; | ||
|
|
||
| /** Total freebie credits */ | ||
| totalFreebieCredits?: number; | ||
|
|
||
| /** Total platform spend */ | ||
| totalPlatformSpend?: number; | ||
|
|
||
| /** Total revenue */ | ||
| totalRevenue?: number; | ||
|
|
||
| /** Total unique members count */ | ||
| totalUniqueMembersCount?: number; | ||
|
|
||
| /** Whether domain billing was used */ | ||
| wasDomainBillingUsed?: boolean; | ||
|
|
||
| /** Yearly overage surcharge */ | ||
| yearlyOverageSurcharge?: number; | ||
|
|
||
| /** Yearly subscription overage cost */ | ||
| yearlySubscriptionOverageCost?: number; | ||
|
|
||
| /** Yearly subscription surcharge */ | ||
| yearlySubscriptionSurcharge?: number; | ||
|
|
||
| /** Yearly subscription user count cost */ | ||
| yearlySubscriptionUserCountCost?: number; | ||
| }; | ||
|
|
||
| /** Purchase type */ | ||
| type Purchase = { | ||
| /** Amount of the purchase */ | ||
| amount: number; | ||
|
|
||
| /** Creation date of the purchase */ | ||
| created: string; | ||
|
|
||
| /** Currency of the purchase */ | ||
| currency: string; | ||
|
|
||
| /** Message containing purchase details */ | ||
| message: Message; | ||
|
|
||
| /** ID of the purchase */ | ||
| purchaseID: number; | ||
| }; | ||
|
|
||
| /** Array of purchases */ | ||
| type PurchaseList = Purchase[]; | ||
|
|
||
| export default PurchaseList; | ||
|
|
||
| export type {Purchase}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While QAing this on staging I realized this got changed at the last minute to be
typeFailed2018, when the correct value isfailed_2018. I'll open a PR to fix itThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#59762