mirror of
https://github.com/andwati/telecom-platform.git
synced 2026-10-18 02:52:53 +00:00
feat: Fix type conversions, rename ExchangeRateProvider to RateProviderType, and improve currency integration
- Fix GetRevenueAnalytics return type by dereferencing metrics struct - Fix getCarrierMetrics to use int64 for Count and convert to int for ActiveCarriers - Rename ExchangeRateProvider to RateProviderType for consistency - Change RatePlanCurrencyIntegrator.exchangeService from pointer to interface type - Add function documentation comments for NewRatePlanCurrencyIntegrator, SubscribeToPlanWith
This commit is contained in:
@@ -64,7 +64,8 @@ func (s *Service) GetDashboard(ctx context.Context, filter *AnalyticsFilter) (*D
|
||||
|
||||
// GetRevenueAnalytics retrieves detailed revenue analytics
|
||||
func (s *Service) GetRevenueAnalytics(ctx context.Context, filter *AnalyticsFilter) (*RevenueMetrics, error) {
|
||||
return s.getRevenueMetrics(ctx, filter)
|
||||
metrics, err := s.getRevenueMetrics(ctx, filter)
|
||||
return &metrics, err
|
||||
}
|
||||
|
||||
func (s *Service) getRevenueMetrics(ctx context.Context, filter *AnalyticsFilter) (RevenueMetrics, error) {
|
||||
@@ -141,9 +142,11 @@ func (s *Service) getCarrierMetrics(ctx context.Context, filter *AnalyticsFilter
|
||||
}
|
||||
|
||||
// Query carrier stats
|
||||
var activeCount int64
|
||||
s.db.WithContext(ctx).Table("carriers").
|
||||
Where("is_active = ?", true).
|
||||
Count((*int64)(&metrics.ActiveCarriers))
|
||||
Count(&activeCount)
|
||||
metrics.ActiveCarriers = int(activeCount)
|
||||
|
||||
return metrics, nil
|
||||
}
|
||||
|
||||
@@ -11,19 +11,19 @@ import (
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// ExchangeRateProvider defines external rate providers
|
||||
type ExchangeRateProvider string
|
||||
// RateProviderType defines external rate providers
|
||||
type RateProviderType string
|
||||
|
||||
const (
|
||||
ProviderOpenExchange ExchangeRateProvider = "openexchangerates"
|
||||
ProviderFixer ExchangeRateProvider = "fixer"
|
||||
ProviderXE ExchangeRateProvider = "xe"
|
||||
ProviderInternal ExchangeRateProvider = "internal"
|
||||
ProviderOpenExchange RateProviderType = "openexchangerates"
|
||||
ProviderFixer RateProviderType = "fixer"
|
||||
ProviderXE RateProviderType = "xe"
|
||||
ProviderInternal RateProviderType = "internal"
|
||||
)
|
||||
|
||||
// RealTimeExchangeService provides real-time exchange rates
|
||||
type RealTimeExchangeService struct {
|
||||
provider ExchangeRateProvider
|
||||
provider RateProviderType
|
||||
apiKey string
|
||||
baseCurrency string
|
||||
rates map[string]float64
|
||||
@@ -36,7 +36,7 @@ type RealTimeExchangeService struct {
|
||||
|
||||
// ExchangeRateConfig configures the exchange rate service
|
||||
type ExchangeRateConfig struct {
|
||||
Provider ExchangeRateProvider
|
||||
Provider RateProviderType
|
||||
APIKey string
|
||||
BaseCurrency string
|
||||
CacheTTL time.Duration
|
||||
|
||||
@@ -13,15 +13,16 @@ import (
|
||||
|
||||
type RatePlanCurrencyIntegrator struct {
|
||||
billingService currency.BillingService
|
||||
exchangeService *currency.ExchangeRateService
|
||||
exchangeService currency.ExchangeRateService
|
||||
ratePlanService rateplan.Service
|
||||
logger *logrus.Logger
|
||||
baseCurrency string
|
||||
}
|
||||
|
||||
// NewRatePlanCurrencyIntegrator creates a new rate plan currency integrator
|
||||
func NewRatePlanCurrencyIntegrator(
|
||||
billingService currency.BillingService,
|
||||
exchangeService *currency.ExchangeRateService,
|
||||
exchangeService currency.ExchangeRateService,
|
||||
ratePlanService rateplan.Service,
|
||||
logger *logrus.Logger,
|
||||
baseCurrency string,
|
||||
@@ -35,22 +36,24 @@ func NewRatePlanCurrencyIntegrator(
|
||||
}
|
||||
}
|
||||
|
||||
// SubscribeToPlanWithCurrency subscribes to a rate plan with currency conversion
|
||||
func (rpci *RatePlanCurrencyIntegrator) SubscribeToPlanWithCurrency(ctx context.Context, profileID string, planID string, targetCurrency string) (*rateplan.RatePlanSubscription, error) {
|
||||
// Get the rate plan
|
||||
plan, err := rpci.ratePlanService.GetRatePlan(ctx, planID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get rate plan: %w", err)
|
||||
}
|
||||
|
||||
// Convert price to requested currency if needed
|
||||
subscriptionPrice := plan.BasePrice
|
||||
exchangeRate := 1.0
|
||||
|
||||
if targetCurrency != plan.Currency {
|
||||
conversionReq := ¤cy.CurrencyConversionRequest{
|
||||
conversion, err := rpci.billingService.ConvertAmount(ctx, ¤cy.CurrencyConversionRequest{
|
||||
Amount: plan.BasePrice,
|
||||
FromCurrency: plan.Currency,
|
||||
ToCurrency: targetCurrency,
|
||||
}
|
||||
conversion, err := rpci.billingService.ConvertAmount(ctx, conversionReq)
|
||||
})
|
||||
if err != nil {
|
||||
rpci.logger.WithError(err).Error("Failed to convert rate plan price")
|
||||
return nil, fmt.Errorf("currency conversion failed: %w", err)
|
||||
@@ -59,19 +62,28 @@ func (rpci *RatePlanCurrencyIntegrator) SubscribeToPlanWithCurrency(ctx context.
|
||||
exchangeRate = conversion.ExchangeRate
|
||||
}
|
||||
|
||||
metadata := map[string]any{
|
||||
"original_currency": plan.Currency,
|
||||
"subscription_currency": targetCurrency,
|
||||
"original_price": plan.BasePrice,
|
||||
"subscription_price": subscriptionPrice,
|
||||
"exchange_rate": exchangeRate,
|
||||
// Create subscription with currency information
|
||||
subscription := &rateplan.RatePlanSubscription{
|
||||
ProfileID: profileID,
|
||||
RatePlanID: planID,
|
||||
Status: rateplan.SubscriptionStatusActive,
|
||||
StartedAt: time.Now(),
|
||||
Metadata: map[string]any{
|
||||
"original_currency": plan.Currency,
|
||||
"subscription_currency": targetCurrency,
|
||||
"original_price": plan.BasePrice,
|
||||
"subscription_price": subscriptionPrice,
|
||||
"exchange_rate": exchangeRate,
|
||||
},
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
}
|
||||
|
||||
subscribeReq := &rateplan.SubscribeRequest{
|
||||
ProfileID: profileID,
|
||||
RatePlanID: planID,
|
||||
AutoRenew: true,
|
||||
Metadata: metadata,
|
||||
Metadata: subscription.Metadata,
|
||||
}
|
||||
|
||||
createdSubscription, err := rpci.ratePlanService.SubscribeToPlan(ctx, subscribeReq)
|
||||
@@ -79,6 +91,7 @@ func (rpci *RatePlanCurrencyIntegrator) SubscribeToPlanWithCurrency(ctx context.
|
||||
return nil, fmt.Errorf("failed to create subscription: %w", err)
|
||||
}
|
||||
|
||||
// Process initial billing
|
||||
billingReq := ¤cy.BillingRequest{
|
||||
ProfileID: profileID,
|
||||
SubscriptionID: createdSubscription.ID,
|
||||
@@ -104,14 +117,18 @@ func (rpci *RatePlanCurrencyIntegrator) SubscribeToPlanWithCurrency(ctx context.
|
||||
return createdSubscription, nil
|
||||
}
|
||||
|
||||
// CalculatePlanCostInCurrency calculates the cost of a rate plan in a specific currency
|
||||
func (rpci *RatePlanCurrencyIntegrator) CalculatePlanCostInCurrency(ctx context.Context, planID string, targetCurrency string, usageData *rateplan.RatePlanUsage) (*currency.BillingSummary, error) {
|
||||
// Get the rate plan
|
||||
plan, err := rpci.ratePlanService.GetRatePlan(ctx, planID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get rate plan: %w", err)
|
||||
}
|
||||
|
||||
// Calculate base cost
|
||||
baseCost := plan.BasePrice
|
||||
|
||||
// Add overage costs if usage data is provided
|
||||
if usageData != nil {
|
||||
overageCost, err := rpci.calculateOverageCost(ctx, plan, usageData)
|
||||
if err != nil {
|
||||
@@ -121,16 +138,12 @@ func (rpci *RatePlanCurrencyIntegrator) CalculatePlanCostInCurrency(ctx context.
|
||||
}
|
||||
}
|
||||
|
||||
// Convert to requested currency
|
||||
convertedCost := baseCost
|
||||
exchangeRate := 1.0
|
||||
|
||||
if targetCurrency != plan.Currency {
|
||||
conversionReq := ¤cy.CurrencyConversionRequest{
|
||||
Amount: baseCost,
|
||||
FromCurrency: plan.Currency,
|
||||
ToCurrency: targetCurrency,
|
||||
}
|
||||
conversion, err := rpci.billingService.ConvertAmount(ctx, conversionReq)
|
||||
conversion, err := rpci.exchangeService.ConvertAmount(ctx, baseCost, plan.Currency, targetCurrency)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("currency conversion failed: %w", err)
|
||||
}
|
||||
@@ -138,6 +151,7 @@ func (rpci *RatePlanCurrencyIntegrator) CalculatePlanCostInCurrency(ctx context.
|
||||
exchangeRate = conversion.ExchangeRate
|
||||
}
|
||||
|
||||
// Create billing summary
|
||||
summary := ¤cy.BillingSummary{
|
||||
ProfileID: usageData.ProfileID,
|
||||
TotalAmount: convertedCost,
|
||||
@@ -160,13 +174,11 @@ func (rpci *RatePlanCurrencyIntegrator) CalculatePlanCostInCurrency(ctx context.
|
||||
return summary, nil
|
||||
}
|
||||
|
||||
// calculateOverageCost calculates overage costs for usage
|
||||
func (rpci *RatePlanCurrencyIntegrator) calculateOverageCost(ctx context.Context, plan *rateplan.RatePlan, usage *rateplan.RatePlanUsage) (float64, error) {
|
||||
// Check for context cancellation before calculation
|
||||
if err := ctx.Err(); err != nil {
|
||||
return 0.0, fmt.Errorf("overage calculation cancelled: %w", err)
|
||||
}
|
||||
overageCost := 0.0
|
||||
|
||||
// Calculate data overage
|
||||
if plan.DataAllowance != nil && usage.DataUsed > plan.DataAllowance.Amount {
|
||||
dataOverage := usage.DataUsed - plan.DataAllowance.Amount
|
||||
if plan.OverageRates != nil {
|
||||
@@ -174,6 +186,7 @@ func (rpci *RatePlanCurrencyIntegrator) calculateOverageCost(ctx context.Context
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate voice overage
|
||||
if plan.VoiceAllowance != nil && usage.VoiceUsed > plan.VoiceAllowance.Minutes {
|
||||
voiceOverage := usage.VoiceUsed - plan.VoiceAllowance.Minutes
|
||||
if plan.OverageRates != nil {
|
||||
@@ -181,6 +194,7 @@ func (rpci *RatePlanCurrencyIntegrator) calculateOverageCost(ctx context.Context
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate SMS overage
|
||||
if plan.SMSAllowance != nil && usage.SMSUsed > plan.SMSAllowance.Messages {
|
||||
smsOverage := usage.SMSUsed - plan.SMSAllowance.Messages
|
||||
if plan.OverageRates != nil {
|
||||
|
||||
Reference in New Issue
Block a user