Files
casdoor/object/transaction_validate.go

132 lines
4.4 KiB
Go

// Copyright 2025 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package object
import (
"errors"
"fmt"
"github.com/casdoor/casdoor/i18n"
)
func validateBalanceForTransaction(transaction *Transaction, amount float64, lang string) error {
currency := transaction.Currency
if currency == "" {
currency = "USD"
}
if transaction.Tag == "Organization" {
// Validate organization balance change
return validateOrganizationBalance("admin", transaction.Owner, amount, currency, true, lang)
} else if transaction.Tag == "User" {
// Validate user balance change
if transaction.User == "" {
return errors.New(i18n.Translate(lang, "general:User is required for User category transaction"))
}
if err := validateUserBalance(transaction.Owner, transaction.User, amount, currency, lang); err != nil {
return err
}
// Validate organization's user balance sum change
return validateOrganizationBalance("admin", transaction.Owner, amount, currency, false, lang)
}
return nil
}
func validateOrganizationBalance(owner string, name string, balance float64, currency string, isOrgBalance bool, lang string) error {
organization, err := getOrganization(owner, name)
if err != nil {
return err
}
if organization == nil {
return fmt.Errorf(i18n.Translate(lang, "auth:the organization: %s is not found"), fmt.Sprintf("%s/%s", owner, name))
}
// Convert the balance amount from transaction currency to organization's balance currency
balanceCurrency := organization.BalanceCurrency
if balanceCurrency == "" {
balanceCurrency = "USD"
}
convertedBalance := ConvertCurrency(balance, currency, balanceCurrency)
var newBalance float64
if isOrgBalance {
newBalance = AddPrices(organization.OrgBalance, convertedBalance)
// Check organization balance credit limit
if newBalance < organization.BalanceCredit {
return fmt.Errorf(i18n.Translate(lang, "general:Insufficient balance: new organization balance %v would be below credit limit %v"), newBalance, organization.BalanceCredit)
}
} else {
// User balance is just a sum of all users' balances, no credit limit check here
// Individual user credit limits are checked in validateUserBalance
newBalance = AddPrices(organization.UserBalance, convertedBalance)
}
// In validation mode, we don't actually update the balance
return nil
}
func validateUserBalance(owner string, name string, balance float64, currency string, lang string) error {
user, err := getUser(owner, name)
if err != nil {
return err
}
if user == nil {
return fmt.Errorf(i18n.Translate(lang, "general:The user: %s is not found"), fmt.Sprintf("%s/%s", owner, name))
}
// Convert the balance amount from transaction currency to user's balance currency
balanceCurrency := user.BalanceCurrency
var org *Organization
if balanceCurrency == "" {
// Get organization's balance currency as fallback
org, err = getOrganization("admin", owner)
if err == nil && org != nil && org.BalanceCurrency != "" {
balanceCurrency = org.BalanceCurrency
} else {
balanceCurrency = "USD"
}
}
convertedBalance := ConvertCurrency(balance, currency, balanceCurrency)
// Calculate new balance
newBalance := AddPrices(user.Balance, convertedBalance)
// Check balance credit limit
// User.BalanceCredit takes precedence over Organization.BalanceCredit
var balanceCredit float64
if user.BalanceCredit != 0 {
balanceCredit = user.BalanceCredit
} else {
// Get organization's balance credit as fallback
if org == nil {
org, err = getOrganization("admin", owner)
if err != nil {
return err
}
}
if org != nil {
balanceCredit = org.BalanceCredit
}
}
// Validate new balance against credit limit
if newBalance < balanceCredit {
return fmt.Errorf(i18n.Translate(lang, "general:Insufficient balance: new balance %v would be below credit limit %v"), newBalance, balanceCredit)
}
// In validation mode, we don't actually update the balance
return nil
}