init
This commit is contained in:
186
internal/service/exchange_rate_service.go
Normal file
186
internal/service/exchange_rate_service.go
Normal file
@@ -0,0 +1,186 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"accounting-app/internal/models"
|
||||
"accounting-app/internal/repository"
|
||||
)
|
||||
|
||||
// Common exchange rate service errors
|
||||
var (
|
||||
ErrInvalidRate = errors.New("exchange rate must be positive")
|
||||
ErrInvalidEffectiveDate = errors.New("effective date cannot be in the future")
|
||||
)
|
||||
|
||||
// ExchangeRateService handles business logic for exchange rates
|
||||
type ExchangeRateService struct {
|
||||
repo *repository.ExchangeRateRepository
|
||||
}
|
||||
|
||||
// NewExchangeRateService creates a new ExchangeRateService instance
|
||||
func NewExchangeRateService(repo *repository.ExchangeRateRepository) *ExchangeRateService {
|
||||
return &ExchangeRateService{repo: repo}
|
||||
}
|
||||
|
||||
// CreateExchangeRate creates a new exchange rate
|
||||
func (s *ExchangeRateService) CreateExchangeRate(rate *models.ExchangeRate) error {
|
||||
// Validate rate value
|
||||
if rate.Rate <= 0 {
|
||||
return ErrInvalidRate
|
||||
}
|
||||
|
||||
// Validate effective date (should not be in the future)
|
||||
if rate.EffectiveDate.After(time.Now()) {
|
||||
return ErrInvalidEffectiveDate
|
||||
}
|
||||
|
||||
// Validate currencies are different
|
||||
if rate.FromCurrency == rate.ToCurrency {
|
||||
return repository.ErrSameCurrency
|
||||
}
|
||||
|
||||
return s.repo.Create(rate)
|
||||
}
|
||||
|
||||
// GetExchangeRateByID retrieves an exchange rate by its ID
|
||||
func (s *ExchangeRateService) GetExchangeRateByID(id uint) (*models.ExchangeRate, error) {
|
||||
return s.repo.GetByID(id)
|
||||
}
|
||||
|
||||
// GetAllExchangeRates retrieves all exchange rates
|
||||
func (s *ExchangeRateService) GetAllExchangeRates() ([]models.ExchangeRate, error) {
|
||||
return s.repo.GetAll()
|
||||
}
|
||||
|
||||
// UpdateExchangeRate updates an existing exchange rate
|
||||
func (s *ExchangeRateService) UpdateExchangeRate(rate *models.ExchangeRate) error {
|
||||
// Validate rate value
|
||||
if rate.Rate <= 0 {
|
||||
return ErrInvalidRate
|
||||
}
|
||||
|
||||
// Validate effective date (should not be in the future)
|
||||
if rate.EffectiveDate.After(time.Now()) {
|
||||
return ErrInvalidEffectiveDate
|
||||
}
|
||||
|
||||
// Validate currencies are different
|
||||
if rate.FromCurrency == rate.ToCurrency {
|
||||
return repository.ErrSameCurrency
|
||||
}
|
||||
|
||||
return s.repo.Update(rate)
|
||||
}
|
||||
|
||||
// DeleteExchangeRate deletes an exchange rate by its ID
|
||||
func (s *ExchangeRateService) DeleteExchangeRate(id uint) error {
|
||||
return s.repo.Delete(id)
|
||||
}
|
||||
|
||||
// GetExchangeRateByCurrencyPair retrieves the most recent exchange rate for a currency pair
|
||||
func (s *ExchangeRateService) GetExchangeRateByCurrencyPair(fromCurrency, toCurrency models.Currency) (*models.ExchangeRate, error) {
|
||||
return s.repo.GetByCurrencyPair(fromCurrency, toCurrency)
|
||||
}
|
||||
|
||||
// GetExchangeRateByCurrencyPairAndDate retrieves the exchange rate for a currency pair on a specific date
|
||||
func (s *ExchangeRateService) GetExchangeRateByCurrencyPairAndDate(fromCurrency, toCurrency models.Currency, date time.Time) (*models.ExchangeRate, error) {
|
||||
return s.repo.GetByCurrencyPairAndDate(fromCurrency, toCurrency, date)
|
||||
}
|
||||
|
||||
// GetLatestExchangeRates retrieves the most recent exchange rate for each currency pair
|
||||
func (s *ExchangeRateService) GetLatestExchangeRates() ([]models.ExchangeRate, error) {
|
||||
return s.repo.GetLatestRates()
|
||||
}
|
||||
|
||||
// ConvertCurrency converts an amount from one currency to another using the most recent exchange rate
|
||||
func (s *ExchangeRateService) ConvertCurrency(amount float64, fromCurrency, toCurrency models.Currency) (float64, error) {
|
||||
// If currencies are the same, return the original amount
|
||||
if fromCurrency == toCurrency {
|
||||
return amount, nil
|
||||
}
|
||||
|
||||
// Get the exchange rate
|
||||
rate, err := s.repo.GetByCurrencyPair(fromCurrency, toCurrency)
|
||||
if err != nil {
|
||||
// If direct rate not found, try inverse rate
|
||||
if errors.Is(err, repository.ErrExchangeRateNotFound) {
|
||||
inverseRate, inverseErr := s.repo.GetByCurrencyPair(toCurrency, fromCurrency)
|
||||
if inverseErr != nil {
|
||||
return 0, fmt.Errorf("no exchange rate found for %s to %s: %w", fromCurrency, toCurrency, err)
|
||||
}
|
||||
// Use inverse rate: 1 / rate
|
||||
if inverseRate.Rate == 0 {
|
||||
return 0, errors.New("invalid inverse exchange rate (zero)")
|
||||
}
|
||||
return amount / inverseRate.Rate, nil
|
||||
}
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return amount * rate.Rate, nil
|
||||
}
|
||||
|
||||
// ConvertCurrencyOnDate converts an amount from one currency to another using the exchange rate on a specific date
|
||||
func (s *ExchangeRateService) ConvertCurrencyOnDate(amount float64, fromCurrency, toCurrency models.Currency, date time.Time) (float64, error) {
|
||||
// If currencies are the same, return the original amount
|
||||
if fromCurrency == toCurrency {
|
||||
return amount, nil
|
||||
}
|
||||
|
||||
// Get the exchange rate for the specific date
|
||||
rate, err := s.repo.GetByCurrencyPairAndDate(fromCurrency, toCurrency, date)
|
||||
if err != nil {
|
||||
// If direct rate not found, try inverse rate
|
||||
if errors.Is(err, repository.ErrExchangeRateNotFound) {
|
||||
inverseRate, inverseErr := s.repo.GetByCurrencyPairAndDate(toCurrency, fromCurrency, date)
|
||||
if inverseErr != nil {
|
||||
return 0, fmt.Errorf("no exchange rate found for %s to %s on %s: %w", fromCurrency, toCurrency, date.Format("2006-01-02"), err)
|
||||
}
|
||||
// Use inverse rate: 1 / rate
|
||||
if inverseRate.Rate == 0 {
|
||||
return 0, errors.New("invalid inverse exchange rate (zero)")
|
||||
}
|
||||
return amount / inverseRate.Rate, nil
|
||||
}
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return amount * rate.Rate, nil
|
||||
}
|
||||
|
||||
// GetExchangeRateByCurrency retrieves all exchange rates involving a specific currency
|
||||
func (s *ExchangeRateService) GetExchangeRateByCurrency(currency models.Currency) ([]models.ExchangeRate, error) {
|
||||
return s.repo.GetByCurrency(currency)
|
||||
}
|
||||
|
||||
// SetExchangeRate creates or updates an exchange rate for a currency pair
|
||||
// This is a convenience method for users to set rates without worrying about create vs update
|
||||
func (s *ExchangeRateService) SetExchangeRate(fromCurrency, toCurrency models.Currency, rate float64, effectiveDate time.Time) error {
|
||||
// Validate rate value
|
||||
if rate <= 0 {
|
||||
return ErrInvalidRate
|
||||
}
|
||||
|
||||
// Validate effective date
|
||||
if effectiveDate.After(time.Now()) {
|
||||
return ErrInvalidEffectiveDate
|
||||
}
|
||||
|
||||
// Validate currencies are different
|
||||
if fromCurrency == toCurrency {
|
||||
return repository.ErrSameCurrency
|
||||
}
|
||||
|
||||
// Create new exchange rate entry
|
||||
exchangeRate := &models.ExchangeRate{
|
||||
FromCurrency: fromCurrency,
|
||||
ToCurrency: toCurrency,
|
||||
Rate: rate,
|
||||
EffectiveDate: effectiveDate,
|
||||
}
|
||||
|
||||
return s.repo.Create(exchangeRate)
|
||||
}
|
||||
Reference in New Issue
Block a user