diff --git a/apps/studio/components/interfaces/App/UpdateBillingAddressModal.tsx b/apps/studio/components/interfaces/App/UpdateBillingAddressModal.tsx index 2c49d389b6c..2bea198cc75 100644 --- a/apps/studio/components/interfaces/App/UpdateBillingAddressModal.tsx +++ b/apps/studio/components/interfaces/App/UpdateBillingAddressModal.tsx @@ -28,7 +28,6 @@ import { useBillingCustomerDataForm } from '@/components/interfaces/Organization import { useOrganizationCustomerProfileQuery } from '@/data/organizations/organization-customer-profile-query' import { useOrganizationCustomerProfileUpdateMutation } from '@/data/organizations/organization-customer-profile-update-mutation' import { useOrganizationTaxIdQuery } from '@/data/organizations/organization-tax-id-query' -import { useOrganizationTaxIdUpdateMutation } from '@/data/organizations/organization-tax-id-update-mutation' import { invalidateOrganizationsQuery } from '@/data/organizations/organizations-query' import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions' import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization' @@ -96,7 +95,6 @@ export function UpdateBillingAddressModal() { const { mutateAsync: updateCustomerProfile } = useOrganizationCustomerProfileUpdateMutation({ onError: () => {}, }) - const { mutateAsync: updateTaxId } = useOrganizationTaxIdUpdateMutation({ onError: () => {} }) const { form, @@ -116,27 +114,19 @@ export function UpdateBillingAddressModal() { setIsSubmitting(true) try { - try { - await updateCustomerProfile({ - slug, - address: data.address, - billing_name: data.billing_name, - }) - setDismissed(true) - await invalidateOrganizationsQuery(queryClient) - } catch (error: any) { - toast.error(`Failed to update billing address: ${error.message}`) - throw error - } - - try { - await updateTaxId({ slug, taxId: data.tax_id }) - } catch (error: any) { - toast.error(`Failed to update tax ID: ${error.message}`) - throw error - } + await updateCustomerProfile({ + slug, + address: data.address, + billing_name: data.billing_name, + tax_id: data.tax_id, + }) + setDismissed(true) + await invalidateOrganizationsQuery(queryClient) toast.success('Successfully updated billing address') + } catch (error: any) { + toast.error(`Failed to update billing address: ${error.message}`) + throw error } finally { setIsSubmitting(false) } diff --git a/apps/studio/components/interfaces/Organization/BillingSettings/BillingCustomerData/BillingCustomerData.tsx b/apps/studio/components/interfaces/Organization/BillingSettings/BillingCustomerData/BillingCustomerData.tsx index 36b1f50c7ba..696139b4fb8 100644 --- a/apps/studio/components/interfaces/Organization/BillingSettings/BillingCustomerData/BillingCustomerData.tsx +++ b/apps/studio/components/interfaces/Organization/BillingSettings/BillingCustomerData/BillingCustomerData.tsx @@ -27,7 +27,6 @@ import { organizationKeys } from '@/data/organizations/keys' import { useOrganizationCustomerProfileQuery } from '@/data/organizations/organization-customer-profile-query' import { useOrganizationCustomerProfileUpdateMutation } from '@/data/organizations/organization-customer-profile-update-mutation' import { useOrganizationTaxIdQuery } from '@/data/organizations/organization-tax-id-query' -import { useOrganizationTaxIdUpdateMutation } from '@/data/organizations/organization-tax-id-update-mutation' import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions' import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization' import { STRIPE_PUBLIC_KEY } from '@/lib/constants' @@ -64,7 +63,6 @@ export const BillingCustomerData = () => { const { mutateAsync: updateCustomerProfile } = useOrganizationCustomerProfileUpdateMutation({ onError: () => {}, }) - const { mutateAsync: updateTaxId } = useOrganizationTaxIdUpdateMutation({ onError: () => {} }) const [isSubmitting, setIsSubmitting] = useState(false) const addressElementRef = useRef(null) @@ -87,27 +85,12 @@ export const BillingCustomerData = () => { setIsSubmitting(true) try { - try { - await updateCustomerProfile({ - slug, - address: data.address, - billing_name: data.billing_name, - }) - } catch (error) { - toast.error( - `Failed updating billing address: ${error instanceof Error ? error.message : 'Unknown error'}` - ) - throw error - } - - try { - await updateTaxId({ slug, taxId: data.tax_id }) - } catch (error) { - toast.error( - `Failed updating tax ID: ${error instanceof Error ? error.message : 'Unknown error'}` - ) - throw error - } + await updateCustomerProfile({ + slug, + address: data.address, + billing_name: data.billing_name, + tax_id: data.tax_id, + }) toast.success('Successfully updated billing data') @@ -116,11 +99,22 @@ export const BillingCustomerData = () => { (prev) => { if (!prev) return prev return prev.map((org) => - org.slug === slug ? { ...org, organization_missing_tax_id: data.tax_id == null } : org + org.slug === slug + ? { + ...org, + ...(data.address !== undefined ? { organization_missing_address: false } : {}), + ...(data.tax_id !== undefined + ? { organization_missing_tax_id: data.tax_id == null } + : {}), + } + : org ) } ) } catch (error) { + toast.error( + `Failed updating billing data: ${error instanceof Error ? error.message : 'Unknown error'}` + ) throw error } finally { setIsSubmitting(false) diff --git a/apps/studio/data/organizations/organization-customer-profile-update-mutation.ts b/apps/studio/data/organizations/organization-customer-profile-update-mutation.ts index 0448d3b153a..21541271a41 100644 --- a/apps/studio/data/organizations/organization-customer-profile-update-mutation.ts +++ b/apps/studio/data/organizations/organization-customer-profile-update-mutation.ts @@ -2,7 +2,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query' import { toast } from 'sonner' import { organizationKeys } from './keys' -import type { CustomerAddress } from './types' +import type { CustomerAddress, CustomerTaxId } from './types' import { handleError, put } from '@/data/fetchers' import type { ResponseError, UseCustomMutationOptions } from '@/types' @@ -10,25 +10,33 @@ export type OrganizationCustomerProfileUpdateVariables = { slug?: string address?: CustomerAddress billing_name: string + /** Pass a tax ID object to set/update, `null` to clear, or `undefined` to leave unchanged */ + tax_id?: CustomerTaxId | null } export async function updateOrganizationCustomerProfile({ slug, address, billing_name, + tax_id, }: OrganizationCustomerProfileUpdateVariables) { if (!slug) return console.error('Slug is required') - const payload: any = {} - if (address) payload.address = address - const { data, error } = await put(`/platform/organizations/{slug}/customer`, { params: { path: { slug, }, }, - body: { address: address != null ? address : undefined, billing_name }, + body: { + address: address != null ? address : undefined, + billing_name, + ...(tax_id === null + ? { clear_tax_id: true as const } + : tax_id !== undefined + ? { tax_id } + : {}), + }, }) if (error) throw handleError(error) return data @@ -59,7 +67,7 @@ export const useOrganizationCustomerProfileUpdateMutation = ({ >({ mutationFn: (vars) => updateOrganizationCustomerProfile(vars), async onSuccess(data, variables, context) { - const { address, slug, billing_name } = variables + const { address, slug, billing_name, tax_id } = variables // We do not invalidate here as GET endpoint data is stale for 1-2 seconds, so we handle state manually queryClient.setQueriesData( @@ -69,11 +77,16 @@ export const useOrganizationCustomerProfileUpdateMutation = ({ return { ...prev, billing_name, - address, + ...(address !== undefined ? { address } : {}), } } ) + // Update tax ID cache if tax_id was part of this update + if (tax_id !== undefined) { + queryClient.setQueryData(organizationKeys.taxId(slug), tax_id) + } + await onSuccess?.(data, variables, context) }, async onError(data, variables, context) { diff --git a/apps/studio/tests/components/Billing/UpdateBillingAddressModal.test.tsx b/apps/studio/tests/components/Billing/UpdateBillingAddressModal.test.tsx index bc18daa018e..447979241a9 100644 --- a/apps/studio/tests/components/Billing/UpdateBillingAddressModal.test.tsx +++ b/apps/studio/tests/components/Billing/UpdateBillingAddressModal.test.tsx @@ -231,13 +231,6 @@ vi.mock('@/data/organizations/organization-customer-profile-update-mutation', () }), })) -const mockUpdateTaxId = vi.fn(() => Promise.resolve()) -vi.mock('@/data/organizations/organization-tax-id-update-mutation', () => ({ - useOrganizationTaxIdUpdateMutation: () => ({ - mutateAsync: mockUpdateTaxId, - }), -})) - vi.mock('@/data/organizations/organizations-query', () => ({ invalidateOrganizationsQuery: vi.fn(() => Promise.resolve()), })) @@ -403,7 +396,6 @@ describe('UpdateBillingAddressModal', () => { await waitFor(() => { expect(mockUpdateCustomerProfile).not.toHaveBeenCalled() - expect(mockUpdateTaxId).not.toHaveBeenCalled() }) }) })