diff --git a/apps/carrier-connector/internal/currency/services/rateplan_core.go b/apps/carrier-connector/internal/currency/services/rateplan_core.go deleted file mode 100644 index 7bdf709..0000000 --- a/apps/carrier-connector/internal/currency/services/rateplan_core.go +++ /dev/null @@ -1,208 +0,0 @@ -package services - -import ( - "context" - "fmt" - "time" - - "github.com/sirupsen/logrus" - - "github.com/nutcas3/telecom-platform/apps/carrier-connector/internal/currency" - "github.com/nutcas3/telecom-platform/apps/carrier-connector/internal/rateplan" -) - -// RatePlanCurrencyIntegrator integrates currency system with rate plans -type RatePlanCurrencyIntegrator struct { - billingService currency.BillingService - 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, - ratePlanService rateplan.Service, - logger *logrus.Logger, - baseCurrency string, -) *RatePlanCurrencyIntegrator { - return &RatePlanCurrencyIntegrator{ - billingService: billingService, - exchangeService: exchangeService, - ratePlanService: ratePlanService, - logger: logger, - baseCurrency: baseCurrency, - } -} - -// 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 { - conversion, err := rpci.billingService.ConvertAmount(ctx, ¤cy.CurrencyConversionRequest{ - Amount: plan.BasePrice, - FromCurrency: plan.Currency, - ToCurrency: targetCurrency, - }) - if err != nil { - rpci.logger.WithError(err).Error("Failed to convert rate plan price") - return nil, fmt.Errorf("currency conversion failed: %w", err) - } - subscriptionPrice = conversion.ConvertedAmount - exchangeRate = conversion.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(), - } - - // Create subscription request - subscribeReq := &rateplan.SubscribeRequest{ - ProfileID: profileID, - RatePlanID: planID, - AutoRenew: true, - Metadata: subscription.Metadata, - } - - createdSubscription, err := rpci.ratePlanService.SubscribeToPlan(ctx, subscribeReq) - if err != nil { - return nil, fmt.Errorf("failed to create subscription: %w", err) - } - - // Process initial billing - billingReq := ¤cy.BillingRequest{ - ProfileID: profileID, - SubscriptionID: createdSubscription.ID, - Amount: subscriptionPrice, - Currency: targetCurrency, - Description: fmt.Sprintf("Initial subscription to %s", plan.Name), - BillingDate: time.Now(), - } - - _, err = rpci.billingService.ProcessBilling(ctx, billingReq) - if err != nil { - rpci.logger.WithError(err).Error("Failed to process initial billing") - // Don't fail the subscription if billing fails, but log it - } - - rpci.logger.WithFields(logrus.Fields{ - "profile_id": profileID, - "plan_id": planID, - "currency": targetCurrency, - "subscription_id": createdSubscription.ID, - }).Info("Rate plan subscription created with currency support") - - 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 { - rpci.logger.WithError(err).Warn("Failed to calculate overage cost") - } else { - baseCost += overageCost - } - } - - // Convert to requested currency - convertedCost := baseCost - exchangeRate := 1.0 - - if targetCurrency != plan.Currency { - conversion, err := rpci.exchangeService.ConvertAmount(ctx, baseCost, plan.Currency, targetCurrency) - if err != nil { - return nil, fmt.Errorf("currency conversion failed: %w", err) - } - convertedCost = conversion.ConvertedAmount - exchangeRate = conversion.ExchangeRate - } - - // Create billing summary - summary := ¤cy.BillingSummary{ - ProfileID: usageData.ProfileID, - TotalAmount: convertedCost, - Currency: targetCurrency, - BaseTotalAmount: baseCost, - BaseCurrency: plan.Currency, - TransactionCount: 1, - FromDate: time.Now().AddDate(0, -1, 0), - ToDate: time.Now(), - Breakdown: map[string]any{ - "plan_id": planID, - "plan_name": plan.Name, - "base_cost": plan.BasePrice, - "overage_cost": baseCost - plan.BasePrice, - "exchange_rate": exchangeRate, - "original_currency": plan.Currency, - }, - } - - return summary, nil -} - -// calculateOverageCost calculates overage costs for usage -func (rpci *RatePlanCurrencyIntegrator) calculateOverageCost(ctx context.Context, plan *rateplan.RatePlan, usage *rateplan.RatePlanUsage) (float64, error) { - 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 { - overageCost += float64(dataOverage) * plan.OverageRates.DataRate - } - } - - // Calculate voice overage - if plan.VoiceAllowance != nil && usage.VoiceUsed > plan.VoiceAllowance.Minutes { - voiceOverage := usage.VoiceUsed - plan.VoiceAllowance.Minutes - if plan.OverageRates != nil { - overageCost += float64(voiceOverage) * plan.OverageRates.VoiceRate - } - } - - // Calculate SMS overage - if plan.SMSAllowance != nil && usage.SMSUsed > plan.SMSAllowance.Messages { - smsOverage := usage.SMSUsed - plan.SMSAllowance.Messages - if plan.OverageRates != nil { - overageCost += float64(smsOverage) * plan.OverageRates.SMSRate - } - } - - return overageCost, nil -} diff --git a/apps/carrier-connector/internal/services/tenant_analytics_mock.go b/apps/carrier-connector/internal/services/tenant_analytics_mock.go deleted file mode 100644 index 44f7363..0000000 --- a/apps/carrier-connector/internal/services/tenant_analytics_mock.go +++ /dev/null @@ -1,148 +0,0 @@ -package services - -import ( - "time" - - "github.com/nutcas3/telecom-platform/apps/carrier-connector/internal/tenant" -) - -func (s *TenantServiceImpl) getMockResourceCount(resourceType string) int { - switch resourceType { - case "users": - return 25 - case "profiles": - return 150 - case "carriers": - return 5 - case "api_calls": - return 50000 - case "storage": - return 1024 - default: - return 0 - } -} - -// getMockQuotaLimit returns mock quota limit data -func (s *TenantServiceImpl) getMockQuotaLimit(resourceType string) int { - switch resourceType { - case "users": - return 100 - case "profiles": - return 500 - case "carriers": - return 20 - case "api_calls": - return 100000 - case "storage": - return 5120 - default: - return 0 - } -} - -// getMockQuotaUsed returns mock quota used data -func (s *TenantServiceImpl) getMockQuotaUsed(resourceType string) int { - switch resourceType { - case "users": - return 25 - case "profiles": - return 150 - case "carriers": - return 5 - case "api_calls": - return 50000 - case "storage": - return 1024 - default: - return 0 - } -} -func (s *TenantServiceImpl) parseTimeRange(timeRange string) (time.Time, time.Time) { - now := time.Now() - - switch timeRange { - case "1h": - return now.Add(-1 * time.Hour), now - case "24h": - return now.Add(-24 * time.Hour), now - case "7d": - return now.Add(-7 * 24 * time.Hour), now - case "30d": - return now.Add(-30 * 24 * time.Hour), now - case "90d": - return now.Add(-90 * 24 * time.Hour), now - default: - return now.Add(-24 * time.Hour), now - } -} - -func (s *TenantServiceImpl) parseAPIRequestEvent(event *tenant.TenantEvent) *tenant.APIRequestEvent { - // Implementation depends on event structure - return &tenant.APIRequestEvent{ - Timestamp: event.Timestamp, - Endpoint: "", - Method: "", - StatusCode: 200, - ResponseTime: 0, - UserID: event.UserID, - } -} - -func (s *TenantServiceImpl) parseErrorEvent(event *tenant.TenantEvent) *tenant.ErrorEvent { - // Implementation depends on event structure - return &tenant.ErrorEvent{ - Timestamp: event.Timestamp, - Error: "", - Context: event.EventData, - UserID: event.UserID, - } -} - -func (s *TenantServiceImpl) parseSlowQueryEvent(event *tenant.TenantEvent) *tenant.SlowQuery { - // Implementation depends on event structure - return &tenant.SlowQuery{ - Timestamp: event.Timestamp, - Query: "", - Duration: 0, - Context: event.EventData, - } -} - -func (s *TenantServiceImpl) calculateAPIPerformance(requests []*tenant.APIRequestEvent) *tenant.APIPerformance { - // Implementation would calculate performance metrics - return &tenant.APIPerformance{ - TotalRequests: len(requests), - AverageResponseTime: 0, - P95ResponseTime: 0, - ErrorRate: 0, - RequestsPerSecond: 0, - } -} - -// buildQuotaStatus builds quota status from usage stats -func (s *TenantServiceImpl) buildQuotaStatus(usageStats *tenant.TenantUsageStats) []*tenant.TenantUsage { - quotaStatus := make([]*tenant.TenantUsage, 0) - - // Create quota status for common resource types - resourceTypes := []string{"users", "profiles", "carriers", "api_calls", "storage"} - - for _, resourceType := range resourceTypes { - // Mock usage data - in real implementation, this would come from actual usage records - usage := &tenant.TenantUsage{ - ID: generateUsageID(resourceType, usageStats.TenantID), - TenantID: usageStats.TenantID, - ResourceType: resourceType, - ResourceCount: s.getMockResourceCount(resourceType), - QuotaLimit: s.getMockQuotaLimit(resourceType), - QuotaUsed: s.getMockQuotaUsed(resourceType), - QuotaRemaining: s.getMockQuotaLimit(resourceType) - s.getMockQuotaUsed(resourceType), - PeriodStart: getCurrentTime().AddDate(0, -1, 0), - PeriodEnd: getCurrentTime(), - } - - quotaStatus = append(quotaStatus, usage) - } - - return quotaStatus -}