Commit cef22c70 authored by erio's avatar erio
Browse files

fix(notify): remove percentage threshold from balance notification

Balance low notification only supports fixed USD amount threshold.
Percentage threshold is a quota concept, not applicable to balance.
Reverted threshold_type from admin settings, user profile, and all
backend/frontend layers. DB fields (balance_notify_threshold_type,
total_recharged) retained for potential future quota use.
parent 9e33d0c4
...@@ -177,7 +177,6 @@ func (h *SettingHandler) GetSettings(c *gin.Context) { ...@@ -177,7 +177,6 @@ func (h *SettingHandler) GetSettings(c *gin.Context) {
EnableCCHSigning: settings.EnableCCHSigning, EnableCCHSigning: settings.EnableCCHSigning,
WebSearchEmulationEnabled: settings.WebSearchEmulationEnabled, WebSearchEmulationEnabled: settings.WebSearchEmulationEnabled,
BalanceLowNotifyEnabled: settings.BalanceLowNotifyEnabled, BalanceLowNotifyEnabled: settings.BalanceLowNotifyEnabled,
BalanceLowNotifyThresholdType: settings.BalanceLowNotifyThresholdType,
BalanceLowNotifyThreshold: settings.BalanceLowNotifyThreshold, BalanceLowNotifyThreshold: settings.BalanceLowNotifyThreshold,
AccountQuotaNotifyEmails: settings.AccountQuotaNotifyEmails, AccountQuotaNotifyEmails: settings.AccountQuotaNotifyEmails,
PaymentEnabled: paymentCfg.Enabled, PaymentEnabled: paymentCfg.Enabled,
...@@ -310,10 +309,9 @@ type UpdateSettingsRequest struct { ...@@ -310,10 +309,9 @@ type UpdateSettingsRequest struct {
EnableCCHSigning *bool `json:"enable_cch_signing"` EnableCCHSigning *bool `json:"enable_cch_signing"`
// Balance low notification // Balance low notification
BalanceLowNotifyEnabled *bool `json:"balance_low_notify_enabled"` BalanceLowNotifyEnabled *bool `json:"balance_low_notify_enabled"`
BalanceLowNotifyThresholdType *string `json:"balance_low_notify_threshold_type"` BalanceLowNotifyThreshold *float64 `json:"balance_low_notify_threshold"`
BalanceLowNotifyThreshold *float64 `json:"balance_low_notify_threshold"` AccountQuotaNotifyEmails *[]string `json:"account_quota_notify_emails"`
AccountQuotaNotifyEmails *[]string `json:"account_quota_notify_emails"`
// Payment configuration (integrated into settings, full replace) // Payment configuration (integrated into settings, full replace)
PaymentEnabled *bool `json:"payment_enabled"` PaymentEnabled *bool `json:"payment_enabled"`
...@@ -898,12 +896,6 @@ func (h *SettingHandler) UpdateSettings(c *gin.Context) { ...@@ -898,12 +896,6 @@ func (h *SettingHandler) UpdateSettings(c *gin.Context) {
} }
return previousSettings.BalanceLowNotifyEnabled return previousSettings.BalanceLowNotifyEnabled
}(), }(),
BalanceLowNotifyThresholdType: func() string {
if req.BalanceLowNotifyThresholdType != nil {
return *req.BalanceLowNotifyThresholdType
}
return previousSettings.BalanceLowNotifyThresholdType
}(),
BalanceLowNotifyThreshold: func() float64 { BalanceLowNotifyThreshold: func() float64 {
if req.BalanceLowNotifyThreshold != nil { if req.BalanceLowNotifyThreshold != nil {
return *req.BalanceLowNotifyThreshold return *req.BalanceLowNotifyThreshold
...@@ -1063,7 +1055,6 @@ func (h *SettingHandler) UpdateSettings(c *gin.Context) { ...@@ -1063,7 +1055,6 @@ func (h *SettingHandler) UpdateSettings(c *gin.Context) {
EnableMetadataPassthrough: updatedSettings.EnableMetadataPassthrough, EnableMetadataPassthrough: updatedSettings.EnableMetadataPassthrough,
EnableCCHSigning: updatedSettings.EnableCCHSigning, EnableCCHSigning: updatedSettings.EnableCCHSigning,
BalanceLowNotifyEnabled: updatedSettings.BalanceLowNotifyEnabled, BalanceLowNotifyEnabled: updatedSettings.BalanceLowNotifyEnabled,
BalanceLowNotifyThresholdType: updatedSettings.BalanceLowNotifyThresholdType,
BalanceLowNotifyThreshold: updatedSettings.BalanceLowNotifyThreshold, BalanceLowNotifyThreshold: updatedSettings.BalanceLowNotifyThreshold,
AccountQuotaNotifyEmails: updatedSettings.AccountQuotaNotifyEmails, AccountQuotaNotifyEmails: updatedSettings.AccountQuotaNotifyEmails,
PaymentEnabled: updatedPaymentCfg.Enabled, PaymentEnabled: updatedPaymentCfg.Enabled,
......
...@@ -150,10 +150,9 @@ type SystemSettings struct { ...@@ -150,10 +150,9 @@ type SystemSettings struct {
PaymentCancelRateLimitMode string `json:"payment_cancel_rate_limit_window_mode"` PaymentCancelRateLimitMode string `json:"payment_cancel_rate_limit_window_mode"`
// Balance low notification // Balance low notification
BalanceLowNotifyEnabled bool `json:"balance_low_notify_enabled"` BalanceLowNotifyEnabled bool `json:"balance_low_notify_enabled"`
BalanceLowNotifyThresholdType string `json:"balance_low_notify_threshold_type"` BalanceLowNotifyThreshold float64 `json:"balance_low_notify_threshold"`
BalanceLowNotifyThreshold float64 `json:"balance_low_notify_threshold"` AccountQuotaNotifyEmails []string `json:"account_quota_notify_emails"`
AccountQuotaNotifyEmails []string `json:"account_quota_notify_emails"`
} }
type DefaultSubscriptionSetting struct { type DefaultSubscriptionSetting struct {
......
...@@ -33,10 +33,9 @@ type ChangePasswordRequest struct { ...@@ -33,10 +33,9 @@ type ChangePasswordRequest struct {
// UpdateProfileRequest represents the update profile request payload // UpdateProfileRequest represents the update profile request payload
type UpdateProfileRequest struct { type UpdateProfileRequest struct {
Username *string `json:"username"` Username *string `json:"username"`
BalanceNotifyEnabled *bool `json:"balance_notify_enabled"` BalanceNotifyEnabled *bool `json:"balance_notify_enabled"`
BalanceNotifyThresholdType *string `json:"balance_notify_threshold_type"` BalanceNotifyThreshold *float64 `json:"balance_notify_threshold"`
BalanceNotifyThreshold *float64 `json:"balance_notify_threshold"`
} }
// GetProfile handles getting user profile // GetProfile handles getting user profile
...@@ -101,10 +100,9 @@ func (h *UserHandler) UpdateProfile(c *gin.Context) { ...@@ -101,10 +100,9 @@ func (h *UserHandler) UpdateProfile(c *gin.Context) {
} }
svcReq := service.UpdateProfileRequest{ svcReq := service.UpdateProfileRequest{
Username: req.Username, Username: req.Username,
BalanceNotifyEnabled: req.BalanceNotifyEnabled, BalanceNotifyEnabled: req.BalanceNotifyEnabled,
BalanceNotifyThresholdType: req.BalanceNotifyThresholdType, BalanceNotifyThreshold: req.BalanceNotifyThreshold,
BalanceNotifyThreshold: req.BalanceNotifyThreshold,
} }
updatedUser, err := h.userService.UpdateProfile(c.Request.Context(), subject.UserID, svcReq) updatedUser, err := h.userService.UpdateProfile(c.Request.Context(), subject.UserID, svcReq)
if err != nil { if err != nil {
......
...@@ -51,12 +51,16 @@ func (s *BalanceNotifyService) CheckBalanceAfterDeduction(ctx context.Context, u ...@@ -51,12 +51,16 @@ func (s *BalanceNotifyService) CheckBalanceAfterDeduction(ctx context.Context, u
return return
} }
globalEnabled, globalThresholdType, globalThresholdValue := s.getBalanceNotifyConfig(ctx) globalEnabled, globalThreshold := s.getBalanceNotifyConfig(ctx)
if !globalEnabled { if !globalEnabled {
return return
} }
threshold := s.resolveEffectiveThreshold(user, globalThresholdType, globalThresholdValue) // User custom threshold overrides system default
threshold := globalThreshold
if user.BalanceNotifyThreshold != nil {
threshold = *user.BalanceNotifyThreshold
}
if threshold <= 0 { if threshold <= 0 {
return return
} }
...@@ -76,30 +80,6 @@ func (s *BalanceNotifyService) CheckBalanceAfterDeduction(ctx context.Context, u ...@@ -76,30 +80,6 @@ func (s *BalanceNotifyService) CheckBalanceAfterDeduction(ctx context.Context, u
} }
} }
// resolveEffectiveThreshold computes the actual USD threshold based on type and user settings.
// When user sets a custom threshold, their type is used independently (defaults to "fixed" if unset).
func (s *BalanceNotifyService) resolveEffectiveThreshold(user *User, globalType string, globalValue float64) float64 {
if user.BalanceNotifyThreshold != nil {
thresholdType := user.BalanceNotifyThresholdType
if thresholdType == "" {
thresholdType = ThresholdTypeFixed // user custom value defaults to fixed, not inherited
}
return computeThreshold(thresholdType, *user.BalanceNotifyThreshold, user.TotalRecharged)
}
return computeThreshold(globalType, globalValue, user.TotalRecharged)
}
// computeThreshold converts a threshold value to USD based on type.
func computeThreshold(thresholdType string, value, totalRecharged float64) float64 {
if thresholdType == ThresholdTypePercentage {
if totalRecharged <= 0 {
return 0 // no recharge history → skip percentage check
}
return totalRecharged * value / 100
}
return value // fixed USD amount
}
// quotaDim describes one quota dimension for notification checking. // quotaDim describes one quota dimension for notification checking.
type quotaDim struct { type quotaDim struct {
name string name string
...@@ -154,21 +134,13 @@ func (s *BalanceNotifyService) asyncSendQuotaAlert(adminEmails []string, account ...@@ -154,21 +134,13 @@ func (s *BalanceNotifyService) asyncSendQuotaAlert(adminEmails []string, account
} }
// getBalanceNotifyConfig reads global balance notification settings. // getBalanceNotifyConfig reads global balance notification settings.
func (s *BalanceNotifyService) getBalanceNotifyConfig(ctx context.Context) (enabled bool, thresholdType string, threshold float64) { func (s *BalanceNotifyService) getBalanceNotifyConfig(ctx context.Context) (enabled bool, threshold float64) {
keys := []string{ keys := []string{SettingKeyBalanceLowNotifyEnabled, SettingKeyBalanceLowNotifyThreshold}
SettingKeyBalanceLowNotifyEnabled,
SettingKeyBalanceLowNotifyThresholdType,
SettingKeyBalanceLowNotifyThreshold,
}
settings, err := s.settingRepo.GetMultiple(ctx, keys) settings, err := s.settingRepo.GetMultiple(ctx, keys)
if err != nil { if err != nil {
return false, ThresholdTypeFixed, 0 return false, 0
} }
enabled = settings[SettingKeyBalanceLowNotifyEnabled] == "true" enabled = settings[SettingKeyBalanceLowNotifyEnabled] == "true"
thresholdType = settings[SettingKeyBalanceLowNotifyThresholdType]
if thresholdType == "" {
thresholdType = ThresholdTypeFixed
}
if v := settings[SettingKeyBalanceLowNotifyThreshold]; v != "" { if v := settings[SettingKeyBalanceLowNotifyThreshold]; v != "" {
if f, err := strconv.ParseFloat(v, 64); err == nil { if f, err := strconv.ParseFloat(v, 64); err == nil {
threshold = f threshold = f
......
...@@ -251,13 +251,8 @@ const ( ...@@ -251,13 +251,8 @@ const (
SettingKeyEnableCCHSigning = "enable_cch_signing" SettingKeyEnableCCHSigning = "enable_cch_signing"
// Balance Low Notification // Balance Low Notification
SettingKeyBalanceLowNotifyEnabled = "balance_low_notify_enabled" // 全局开关 SettingKeyBalanceLowNotifyEnabled = "balance_low_notify_enabled" // 全局开关
SettingKeyBalanceLowNotifyThresholdType = "balance_low_notify_threshold_type" // "fixed" | "percentage" SettingKeyBalanceLowNotifyThreshold = "balance_low_notify_threshold" // 默认阈值(USD)
SettingKeyBalanceLowNotifyThreshold = "balance_low_notify_threshold" // 默认阈值(USD 或百分比)
// Threshold type constants
ThresholdTypeFixed = "fixed"
ThresholdTypePercentage = "percentage"
// Account Quota Notification // Account Quota Notification
SettingKeyAccountQuotaNotifyEmails = "account_quota_notify_emails" // 管理员通知邮箱列表(JSON 数组) SettingKeyAccountQuotaNotifyEmails = "account_quota_notify_emails" // 管理员通知邮箱列表(JSON 数组)
......
...@@ -608,11 +608,6 @@ func (s *SettingService) UpdateSettings(ctx context.Context, settings *SystemSet ...@@ -608,11 +608,6 @@ func (s *SettingService) UpdateSettings(ctx context.Context, settings *SystemSet
// Balance low notification // Balance low notification
updates[SettingKeyBalanceLowNotifyEnabled] = strconv.FormatBool(settings.BalanceLowNotifyEnabled) updates[SettingKeyBalanceLowNotifyEnabled] = strconv.FormatBool(settings.BalanceLowNotifyEnabled)
thresholdType := settings.BalanceLowNotifyThresholdType
if thresholdType != ThresholdTypeFixed && thresholdType != ThresholdTypePercentage {
thresholdType = ThresholdTypeFixed
}
updates[SettingKeyBalanceLowNotifyThresholdType] = thresholdType
updates[SettingKeyBalanceLowNotifyThreshold] = strconv.FormatFloat(settings.BalanceLowNotifyThreshold, 'f', 8, 64) updates[SettingKeyBalanceLowNotifyThreshold] = strconv.FormatFloat(settings.BalanceLowNotifyThreshold, 'f', 8, 64)
accountQuotaNotifyEmailsJSON, err := json.Marshal(settings.AccountQuotaNotifyEmails) accountQuotaNotifyEmailsJSON, err := json.Marshal(settings.AccountQuotaNotifyEmails)
if err != nil { if err != nil {
...@@ -1252,10 +1247,6 @@ func (s *SettingService) parseSettings(settings map[string]string) *SystemSettin ...@@ -1252,10 +1247,6 @@ func (s *SettingService) parseSettings(settings map[string]string) *SystemSettin
// Balance low notification // Balance low notification
result.BalanceLowNotifyEnabled = settings[SettingKeyBalanceLowNotifyEnabled] == "true" result.BalanceLowNotifyEnabled = settings[SettingKeyBalanceLowNotifyEnabled] == "true"
result.BalanceLowNotifyThresholdType = settings[SettingKeyBalanceLowNotifyThresholdType]
if result.BalanceLowNotifyThresholdType == "" {
result.BalanceLowNotifyThresholdType = ThresholdTypeFixed
}
if v, err := strconv.ParseFloat(settings[SettingKeyBalanceLowNotifyThreshold], 64); err == nil && v >= 0 { if v, err := strconv.ParseFloat(settings[SettingKeyBalanceLowNotifyThreshold], 64); err == nil && v >= 0 {
result.BalanceLowNotifyThreshold = v result.BalanceLowNotifyThreshold = v
} }
......
...@@ -108,9 +108,8 @@ type SystemSettings struct { ...@@ -108,9 +108,8 @@ type SystemSettings struct {
EnableCCHSigning bool // 是否对 billing header cch 进行签名(默认 false) EnableCCHSigning bool // 是否对 billing header cch 进行签名(默认 false)
// Balance low notification // Balance low notification
BalanceLowNotifyEnabled bool BalanceLowNotifyEnabled bool
BalanceLowNotifyThresholdType string // "fixed" (default) | "percentage" BalanceLowNotifyThreshold float64
BalanceLowNotifyThreshold float64
// Account quota notification // Account quota notification
AccountQuotaNotifyEmails []string AccountQuotaNotifyEmails []string
......
...@@ -62,12 +62,11 @@ type UserRepository interface { ...@@ -62,12 +62,11 @@ type UserRepository interface {
// UpdateProfileRequest 更新用户资料请求 // UpdateProfileRequest 更新用户资料请求
type UpdateProfileRequest struct { type UpdateProfileRequest struct {
Email *string `json:"email"` Email *string `json:"email"`
Username *string `json:"username"` Username *string `json:"username"`
Concurrency *int `json:"concurrency"` Concurrency *int `json:"concurrency"`
BalanceNotifyEnabled *bool `json:"balance_notify_enabled"` BalanceNotifyEnabled *bool `json:"balance_notify_enabled"`
BalanceNotifyThresholdType *string `json:"balance_notify_threshold_type"` BalanceNotifyThreshold *float64 `json:"balance_notify_threshold"`
BalanceNotifyThreshold *float64 `json:"balance_notify_threshold"`
} }
// ChangePasswordRequest 修改密码请求 // ChangePasswordRequest 修改密码请求
...@@ -144,11 +143,6 @@ func (s *UserService) UpdateProfile(ctx context.Context, userID int64, req Updat ...@@ -144,11 +143,6 @@ func (s *UserService) UpdateProfile(ctx context.Context, userID int64, req Updat
if req.BalanceNotifyEnabled != nil { if req.BalanceNotifyEnabled != nil {
user.BalanceNotifyEnabled = *req.BalanceNotifyEnabled user.BalanceNotifyEnabled = *req.BalanceNotifyEnabled
} }
if req.BalanceNotifyThresholdType != nil {
if *req.BalanceNotifyThresholdType == ThresholdTypeFixed || *req.BalanceNotifyThresholdType == ThresholdTypePercentage {
user.BalanceNotifyThresholdType = *req.BalanceNotifyThresholdType
}
}
if req.BalanceNotifyThreshold != nil { if req.BalanceNotifyThreshold != nil {
if *req.BalanceNotifyThreshold <= 0 { if *req.BalanceNotifyThreshold <= 0 {
user.BalanceNotifyThreshold = nil // clear to system default user.BalanceNotifyThreshold = nil // clear to system default
......
...@@ -137,7 +137,6 @@ export interface SystemSettings { ...@@ -137,7 +137,6 @@ export interface SystemSettings {
// Balance & quota notification // Balance & quota notification
balance_low_notify_enabled: boolean balance_low_notify_enabled: boolean
balance_low_notify_threshold_type: 'fixed' | 'percentage'
balance_low_notify_threshold: number balance_low_notify_threshold: number
account_quota_notify_emails: string[] account_quota_notify_emails: string[]
} }
...@@ -241,7 +240,6 @@ export interface UpdateSettingsRequest { ...@@ -241,7 +240,6 @@ export interface UpdateSettingsRequest {
payment_cancel_rate_limit_window_mode?: string payment_cancel_rate_limit_window_mode?: string
// Balance & quota notification // Balance & quota notification
balance_low_notify_enabled?: boolean balance_low_notify_enabled?: boolean
balance_low_notify_threshold_type?: 'fixed' | 'percentage'
balance_low_notify_threshold?: number balance_low_notify_threshold?: number
account_quota_notify_emails?: string[] account_quota_notify_emails?: string[]
} }
......
...@@ -4633,12 +4633,8 @@ export default { ...@@ -4633,12 +4633,8 @@ export default {
title: 'Balance Low Notification', title: 'Balance Low Notification',
description: 'Send email notification when user balance falls below threshold', description: 'Send email notification when user balance falls below threshold',
enabled: 'Enable Balance Low Notification', enabled: 'Enable Balance Low Notification',
thresholdType: 'Threshold Type',
typeFixed: 'Fixed Amount',
typePercentage: 'Percentage of Recharged',
threshold: 'Default Threshold', threshold: 'Default Threshold',
thresholdHint: 'Used when user has not set a custom value', thresholdHint: 'Used when user has not set a custom value',
percentageHint: 'Notify when balance falls below this percentage of total recharged amount',
thresholdPlaceholder: 'Enter amount', thresholdPlaceholder: 'Enter amount',
}, },
quotaNotify: { quotaNotify: {
......
...@@ -4797,12 +4797,8 @@ export default { ...@@ -4797,12 +4797,8 @@ export default {
title: '余额不足提醒', title: '余额不足提醒',
description: '当用户余额低于阈值时发送邮件提醒', description: '当用户余额低于阈值时发送邮件提醒',
enabled: '启用余额不足提醒', enabled: '启用余额不足提醒',
thresholdType: '阈值类型', threshold: '默认提醒阈值',
typeFixed: '固定金额',
typePercentage: '充值百分比',
threshold: '提醒阈值',
thresholdHint: '用户未自定义时使用此值', thresholdHint: '用户未自定义时使用此值',
percentageHint: '当余额低于累计充值额的此百分比时提醒',
thresholdPlaceholder: '输入金额', thresholdPlaceholder: '输入金额',
}, },
quotaNotify: { quotaNotify: {
......
...@@ -2675,43 +2675,13 @@ ...@@ -2675,43 +2675,13 @@
<label class="mb-0 block text-sm font-medium text-gray-700 dark:text-gray-300">{{ t('admin.settings.balanceNotify.enabled') }}</label> <label class="mb-0 block text-sm font-medium text-gray-700 dark:text-gray-300">{{ t('admin.settings.balanceNotify.enabled') }}</label>
<Toggle v-model="form.balance_low_notify_enabled" /> <Toggle v-model="form.balance_low_notify_enabled" />
</div> </div>
<div v-if="form.balance_low_notify_enabled" class="space-y-3"> <div v-if="form.balance_low_notify_enabled">
<!-- Threshold type selector --> <label class="mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300">{{ t('admin.settings.balanceNotify.threshold') }}</label>
<div> <div class="relative">
<label class="mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300">{{ t('admin.settings.balanceNotify.thresholdType') }}</label> <span class="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400">$</span>
<div class="flex gap-4"> <input v-model.number="form.balance_low_notify_threshold" type="number" min="0" step="0.01" class="input pl-7" />
<label class="flex items-center gap-1.5 text-sm cursor-pointer">
<input type="radio" v-model="form.balance_low_notify_threshold_type" value="fixed" class="accent-primary-500" />
{{ t('admin.settings.balanceNotify.typeFixed') }}
</label>
<label class="flex items-center gap-1.5 text-sm cursor-pointer">
<input type="radio" v-model="form.balance_low_notify_threshold_type" value="percentage" class="accent-primary-500" />
{{ t('admin.settings.balanceNotify.typePercentage') }}
</label>
</div>
</div>
<!-- Threshold value -->
<div>
<label class="mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300">{{ t('admin.settings.balanceNotify.threshold') }}</label>
<div class="relative">
<span class="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400">
{{ form.balance_low_notify_threshold_type === 'percentage' ? '%' : '$' }}
</span>
<input
v-model.number="form.balance_low_notify_threshold"
type="number"
:min="0"
:max="form.balance_low_notify_threshold_type === 'percentage' ? 100 : undefined"
:step="form.balance_low_notify_threshold_type === 'percentage' ? 1 : 0.01"
class="input pl-7"
/>
</div>
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">
{{ form.balance_low_notify_threshold_type === 'percentage'
? t('admin.settings.balanceNotify.percentageHint')
: t('admin.settings.balanceNotify.thresholdHint') }}
</p>
</div> </div>
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">{{ t('admin.settings.balanceNotify.thresholdHint') }}</p>
</div> </div>
</div> </div>
</div> </div>
...@@ -3026,7 +2996,6 @@ const form = reactive<SettingsForm>({ ...@@ -3026,7 +2996,6 @@ const form = reactive<SettingsForm>({
enable_cch_signing: false, enable_cch_signing: false,
// Balance & quota notification // Balance & quota notification
balance_low_notify_enabled: false, balance_low_notify_enabled: false,
balance_low_notify_threshold_type: 'fixed' as 'fixed' | 'percentage',
balance_low_notify_threshold: 0, balance_low_notify_threshold: 0,
account_quota_notify_emails: [] as string[] account_quota_notify_emails: [] as string[]
}) })
...@@ -3591,7 +3560,6 @@ async function saveSettings() { ...@@ -3591,7 +3560,6 @@ async function saveSettings() {
payment_cancel_rate_limit_window_mode: form.payment_cancel_rate_limit_window_mode, payment_cancel_rate_limit_window_mode: form.payment_cancel_rate_limit_window_mode,
// Balance & quota notification // Balance & quota notification
balance_low_notify_enabled: form.balance_low_notify_enabled, balance_low_notify_enabled: form.balance_low_notify_enabled,
balance_low_notify_threshold_type: form.balance_low_notify_threshold_type,
balance_low_notify_threshold: Number(form.balance_low_notify_threshold) || 0, balance_low_notify_threshold: Number(form.balance_low_notify_threshold) || 0,
account_quota_notify_emails: (form.account_quota_notify_emails || []).filter((e: string) => e.trim() !== ''), account_quota_notify_emails: (form.account_quota_notify_emails || []).filter((e: string) => e.trim() !== ''),
} }
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment