From c6fccc893de55e0dd7904ff7e0172f74a22d003e Mon Sep 17 00:00:00 2001 From: nutcas3 Date: Sun, 3 May 2026 10:52:29 +0300 Subject: [PATCH] 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 --- .../internal/analytics/service.go | 7 ++- .../internal/currency/exchange_rates.go | 16 ++--- .../internal/services/rateplan_core.go | 58 ++++++++++++------- 3 files changed, 49 insertions(+), 32 deletions(-) diff --git a/apps/carrier-connector/internal/analytics/service.go b/apps/carrier-connector/internal/analytics/service.go index 8521f53..5ba5648 100644 --- a/apps/carrier-connector/internal/analytics/service.go +++ b/apps/carrier-connector/internal/analytics/service.go @@ -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 } diff --git a/apps/carrier-connector/internal/currency/exchange_rates.go b/apps/carrier-connector/internal/currency/exchange_rates.go index cc02856..62840eb 100644 --- a/apps/carrier-connector/internal/currency/exchange_rates.go +++ b/apps/carrier-connector/internal/currency/exchange_rates.go @@ -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 diff --git a/apps/carrier-connector/internal/services/rateplan_core.go b/apps/carrier-connector/internal/services/rateplan_core.go index 3e86e72..f3269a9 100644 --- a/apps/carrier-connector/internal/services/rateplan_core.go +++ b/apps/carrier-connector/internal/services/rateplan_core.go @@ -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 {