feat: dynamic tax thresholds from backend (#5342)

* feat: dynamic tax thresholds from backend

* fix: lint & i18n
This commit is contained in:
Calum H.
2026-02-09 14:42:38 +00:00
committed by GitHub
parent 45a397d52b
commit e962521492
10 changed files with 89 additions and 22 deletions

View File

@@ -12,10 +12,20 @@ import { type Component, computed, type ComputedRef, type Ref, ref } from 'vue'
import { getRailConfig } from '@/utils/muralpay-rails'
// Tax form is required when withdrawn_ytd >= $600
// Therefore, the maximum withdrawal without a tax form is $599.99
export const TAX_THRESHOLD_REQUIREMENT = 600
export const TAX_THRESHOLD_ACTUAL = 599.99
export function getTaxThreshold(thresholds: Record<string, number> | undefined): number {
if (!thresholds || Object.keys(thresholds).length === 0) return 600
const currentYear = new Date().getFullYear()
const years = Object.keys(thresholds)
.map(Number)
.sort((a, b) => a - b)
if (currentYear <= years[0]) return thresholds[String(years[0])]
if (currentYear >= years[years.length - 1]) return thresholds[String(years[years.length - 1])]
return thresholds[String(currentYear)] ?? thresholds[String(years[years.length - 1])]
}
export function getTaxThresholdActual(thresholds: Record<string, number> | undefined): number {
return getTaxThreshold(thresholds) - 0.01
}
export type WithdrawStage =
| 'tax-form'
@@ -379,6 +389,7 @@ const STATE_EXPIRY_MS = 15 * 60 * 1000 // 15 minutes
export function createWithdrawContext(
balance: any,
preloadedPaymentData?: { country: string; methods: PayoutMethod[] },
taxComplianceThresholds?: Record<string, number>,
): WithdrawContextValue {
const debug = useDebugLogger('CreatorWithdraw')
const currentStage = ref<WithdrawStage | undefined>()
@@ -422,14 +433,18 @@ export function createWithdrawContext(
const available = balance?.available ?? 0
const needsTaxForm =
balance?.form_completion_status !== 'complete' && usedLimit + available >= 600
balance?.form_completion_status !== 'complete' &&
usedLimit + available >= getTaxThreshold(taxComplianceThresholds)
const threshold = getTaxThreshold(taxComplianceThresholds)
debug('Tax form check:', {
usedLimit,
available,
total: usedLimit + available,
status: balance?.form_completion_status,
needsTaxForm,
taxThreshold: threshold,
taxComplianceFilled: `${((usedLimit / threshold) * 100).toFixed(1)}%`,
})
if (needsTaxForm) {
@@ -462,7 +477,7 @@ export function createWithdrawContext(
}
const usedLimit = balance?.withdrawn_ytd ?? 0
const remainingLimit = Math.max(0, TAX_THRESHOLD_ACTUAL - usedLimit)
const remainingLimit = Math.max(0, getTaxThresholdActual(taxComplianceThresholds) - usedLimit)
return Math.max(0, Math.min(remainingLimit, availableBalance))
})
@@ -627,9 +642,9 @@ export function createWithdrawContext(
case 'tax-form': {
if (!balanceRef.value) return true
const ytd = balanceRef.value.withdrawn_ytd ?? 0
const remainingLimit = Math.max(0, TAX_THRESHOLD_ACTUAL - ytd)
const remainingLimit = Math.max(0, getTaxThresholdActual(taxComplianceThresholds) - ytd)
const form_completion_status = balanceRef.value.form_completion_status
if (ytd < 600) return true
if (ytd < getTaxThreshold(taxComplianceThresholds)) return true
if (withdrawData.value.tax.skipped && remainingLimit > 0) return true
return form_completion_status === 'complete'
}