Skip to content

Commit a4f964e

Browse files
fix: in-arrears transition in upcoming invoice (supabase#45765)
## Show notice when plan fee is prepaid for the upcoming invoice When a subscription's plan fee has already been billed for the current period (e.g. transitioning to in-arrears billing), the upcoming invoice no longer contains a plan line item. Previously this rendered as an empty plan row with a `-`, which was confusing. <img width="2178" height="538" alt="image" src="https://github.com/user-attachments/assets/1fa289d9-60ae-48b1-b779-34770bc2c242" /> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Billing breakdown now detects when the plan fee was already paid upfront, hides the redundant plan line, and notes only usage will be invoiced; shows the organization plan name when available. * Backup restoration: added an optional recovery time target for physical backups. * Expanded supported AWS instance types for deployments. * **UI** * Compute and Replica Compute docs links now use inline linking for a smoother in-app experience. [![Review Change Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/supabase/supabase/pull/45765) <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
1 parent 10ce3b6 commit a4f964e

2 files changed

Lines changed: 68 additions & 24 deletions

File tree

apps/studio/components/interfaces/Organization/BillingSettings/BillingBreakdown/UpcomingInvoice.tsx

Lines changed: 38 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import Link from 'next/link'
21
import React from 'react'
32
import { Table, TableBody, TableCell, TableFooter, TableRow } from 'ui'
43
import { InfoTooltip } from 'ui-patterns/info-tooltip'
@@ -12,6 +11,7 @@ import {
1211
UpcomingInvoiceResponse,
1312
useOrgUpcomingInvoiceQuery,
1413
} from '@/data/invoices/org-invoice-upcoming-query'
14+
import { useOrganizationQuery } from '@/data/organizations/organization-query'
1515
import { DOCS_URL } from '@/lib/constants'
1616
import { formatCurrency } from '@/lib/helpers'
1717

@@ -58,6 +58,8 @@ export const UpcomingInvoice = ({ slug }: UpcomingInvoiceProps) => {
5858
isSuccess,
5959
} = useOrgUpcomingInvoiceQuery({ orgSlug: slug })
6060

61+
const { data: organization } = useOrganizationQuery({ slug })
62+
6163
// For non-platform customers, compute is broken down per project and contains a breakdown array
6264
const computeItems =
6365
upcomingInvoice?.lines?.filter(
@@ -100,6 +102,9 @@ export const UpcomingInvoice = ({ slug }: UpcomingInvoiceProps) => {
100102
upcomingInvoice?.tax_status === 'calculated' && (upcomingInvoice?.tax?.tax_amount ?? 0) > 0
101103
const taxFailed = upcomingInvoice?.tax_status === 'failed'
102104

105+
const planFeePaidInAdvance =
106+
!planItem && upcomingInvoice?.fixed_fees_billing_mode === 'in_arrears'
107+
103108
return (
104109
<>
105110
{isLoading && (
@@ -117,19 +122,21 @@ export const UpcomingInvoice = ({ slug }: UpcomingInvoiceProps) => {
117122
<div>
118123
<Table className="w-full text-sm">
119124
<TableBody>
120-
<TableRow>
121-
<TableCell className="py-2! px-0">{planItem?.description}</TableCell>
122-
<TableCell className="text-right py-2 px-0">
123-
{planItem == null ? (
124-
'-'
125-
) : (
126-
<InvoiceLineItemAmount
127-
amount={planItem.amount}
128-
amountBeforeDiscount={planItem.amount_before_discount}
129-
/>
130-
)}
131-
</TableCell>
132-
</TableRow>
125+
{!planFeePaidInAdvance && (
126+
<TableRow>
127+
<TableCell className="py-2! px-0">{planItem?.description}</TableCell>
128+
<TableCell className="text-right py-2 px-0">
129+
{!planItem ? (
130+
'-'
131+
) : (
132+
<InvoiceLineItemAmount
133+
amount={planItem.amount}
134+
amountBeforeDiscount={planItem.amount_before_discount}
135+
/>
136+
)}
137+
</TableCell>
138+
</TableRow>
139+
)}
133140

134141
{/* Compute section */}
135142
<ComputeLineItem
@@ -141,12 +148,9 @@ export const UpcomingInvoice = ({ slug }: UpcomingInvoiceProps) => {
141148
The first project is covered by Compute Credits. Additional projects incur
142149
compute costs starting at <span translate="no">$10</span>/month, independent
143150
of activity. See{' '}
144-
<Link
145-
href={`${DOCS_URL}/guides/platform/manage-your-usage/compute`}
146-
target="_blank"
147-
>
151+
<InlineLink href={`${DOCS_URL}/guides/platform/manage-your-usage/compute`}>
148152
docs
149-
</Link>
153+
</InlineLink>
150154
.
151155
</p>
152156
}
@@ -161,12 +165,11 @@ export const UpcomingInvoice = ({ slug }: UpcomingInvoiceProps) => {
161165
Each Read Replica is a dedicated database. You are charged for its resources:
162166
Compute, Disk Size, provisioned Disk IOPS, provisioned Disk Throughput, and
163167
IPv4. See{' '}
164-
<Link
168+
<InlineLink
165169
href={`${DOCS_URL}/guides/platform/manage-your-usage/read-replicas`}
166-
target="_blank"
167170
>
168171
docs
169-
</Link>
172+
</InlineLink>
170173
.
171174
</p>
172175
}
@@ -369,6 +372,19 @@ export const UpcomingInvoice = ({ slug }: UpcomingInvoiceProps) => {
369372
</TableCell>
370373
</TableRow>
371374
)}
375+
376+
{planFeePaidInAdvance && (
377+
<TableRow className="border-0 hover:bg-transparent">
378+
<TableCell
379+
className="pt-2! pb-0! px-0 text-foreground-light text-xs text-right"
380+
colSpan={2}
381+
>
382+
Your {organization?.plan?.name && `${organization.plan.name} `}Plan fee for
383+
this period has already been paid. This invoice will only reflect usage
384+
charges.
385+
</TableCell>
386+
</TableRow>
387+
)}
372388
</TableFooter>
373389
</Table>
374390
</div>

packages/api-types/types/platform.d.ts

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5444,6 +5444,33 @@ export interface components {
54445444
cloud_provider: 'AWS' | 'FLY' | 'AWS_K8S' | 'AWS_NIMBUS'
54455445
custom_supabase_internal_requests?: {
54465446
ami: {
5447+
/**
5448+
* @description Exact AWS instance type to provision (e.g. `t3.nano`, `t4g.nano`). When omitted the default for `desired_instance_size` is used. Only for internal use; rejected for user-facing requests in production.
5449+
* @enum {string}
5450+
*/
5451+
instance_type?:
5452+
| 't4g.nano'
5453+
| 't3.nano'
5454+
| 't3a.nano'
5455+
| 't4g.micro'
5456+
| 't4g.small'
5457+
| 't4g.medium'
5458+
| 'm6g.medium'
5459+
| 'm6g.large'
5460+
| 'm6g.xlarge'
5461+
| 'm6g.2xlarge'
5462+
| 'm6g.4xlarge'
5463+
| 'm6g.8xlarge'
5464+
| 'm6g.12xlarge'
5465+
| 'm6g.16xlarge'
5466+
| 'm8g.24xlarge'
5467+
| 'c8g.24xlarge'
5468+
| 'r8g.24xlarge'
5469+
| 'x8g.24xlarge'
5470+
| 'm8g.48xlarge'
5471+
| 'c8g.48xlarge'
5472+
| 'r8g.48xlarge'
5473+
| 'x8g.48xlarge'
54475474
search_tags?: {
54485475
[key: string]: string
54495476
}
@@ -9914,6 +9941,7 @@ export interface components {
99149941
}
99159942
RestorePhysicalBackupBody: {
99169943
id: number
9944+
recovery_time_target?: string
99179945
}
99189946
RevokeAuthorizedOAuthAppResponse: {
99199947
authorized_at?: string
@@ -10390,6 +10418,8 @@ export interface components {
1039010418
billing_cycle_start: string
1039110419
currency: string
1039210420
customer_balance: number
10421+
/** @enum {string} */
10422+
fixed_fees_billing_mode: 'in_advance' | 'in_arrears'
1039310423
lines: {
1039410424
amount: number
1039510425
amount_before_discount: number
@@ -15222,7 +15252,6 @@ export interface operations {
1522215252
OrgAuditLogsController_getAuditLogs: {
1522315253
parameters: {
1522415254
query: {
15225-
format?: 'legacy' | 'v2'
1522615255
/** @description End timestamp */
1522715256
iso_timestamp_end: string
1522815257
/** @description Start timestamp */
@@ -19446,7 +19475,6 @@ export interface operations {
1944619475
UserAuditLogsController_getAuditLogs: {
1944719476
parameters: {
1944819477
query: {
19449-
format?: 'legacy' | 'v2'
1945019478
/** @description End timestamp */
1945119479
iso_timestamp_end: string
1945219480
/** @description Start timestamp */

0 commit comments

Comments
 (0)