From 4fe9d222840210dd78af1b045bb7945168c82823 Mon Sep 17 00:00:00 2001 From: nutcas3 Date: Sat, 2 May 2026 01:24:23 +0300 Subject: [PATCH] feat: Add repository types package with rate plan, subscription, usage, and analytics domain models - Add Repository interface with rate plan CRUD, subscription management, usage tracking, and analytics operations - Add RatePlan struct with pricing, allowances, overage rates, discounts, and metadata fields - Add RatePlanSubscription struct with billing cycle, auto-renewal, and applied discounts support - Add RatePlanUsage struct with cycle tracking and data/voice/SMS usage metrics - Add filter types for rate plans, subscriptions, usage analytics, and revenue analytics --- .../internal/repository/types.go | 315 ++++++++++++++++++ .../internal/services/analytics_service.go | 4 +- .../internal/services/billing_core.go | 27 +- .../internal/services/rateplan_core.go | 20 +- .../internal/services/service_analytics.go | 13 +- .../internal/services/service_methods.go | 31 +- 6 files changed, 365 insertions(+), 45 deletions(-) create mode 100644 apps/carrier-connector/internal/repository/types.go diff --git a/apps/carrier-connector/internal/repository/types.go b/apps/carrier-connector/internal/repository/types.go new file mode 100644 index 0000000..a58dc94 --- /dev/null +++ b/apps/carrier-connector/internal/repository/types.go @@ -0,0 +1,315 @@ +package repository + +import ( + "context" + "time" +) + +// Repository defines the interface for rate plan data operations +type Repository interface { + // Rate Plan operations + CreateRatePlan(ctx context.Context, plan *RatePlan) error + GetRatePlan(ctx context.Context, id string) (*RatePlan, error) + UpdateRatePlan(ctx context.Context, plan *RatePlan) error + DeleteRatePlan(ctx context.Context, id string) error + ListRatePlans(ctx context.Context, filter *RatePlanFilter) ([]*RatePlan, error) + + // Subscription operations + CreateSubscription(ctx context.Context, subscription *RatePlanSubscription) error + GetSubscription(ctx context.Context, id string) (*RatePlanSubscription, error) + UpdateSubscription(ctx context.Context, subscription *RatePlanSubscription) error + GetActiveSubscription(ctx context.Context, profileID string) (*RatePlanSubscription, error) + ListSubscriptions(ctx context.Context, profileID string, filter *SubscriptionFilter) ([]*RatePlanSubscription, error) + + // Usage operations + CreateUsage(ctx context.Context, usage *RatePlanUsage) error + GetUsage(ctx context.Context, id string) (*RatePlanUsage, error) + UpdateUsage(ctx context.Context, usage *RatePlanUsage) error + GetCurrentUsage(ctx context.Context, profileID string) (*RatePlanUsage, error) + ListUsageHistory(ctx context.Context, profileID string, limit int) ([]*RatePlanUsage, error) + + // Analytics operations + GetUsageAnalytics(ctx context.Context, filter *UsageAnalyticsFilter) (*UsageAnalytics, error) + GetRevenueAnalytics(ctx context.Context, filter *RevenueAnalyticsFilter) (*RevenueAnalytics, error) + GetPopularPlans(ctx context.Context, limit int) ([]*RatePlan, error) +} + +// RatePlanUsage represents usage data for a rate plan +type RatePlanUsage struct { + ID string `json:"id" gorm:"primaryKey"` + RatePlanID string `json:"rate_plan_id" gorm:"index"` + ProfileID string `json:"profile_id" gorm:"index"` + CycleStart time.Time `json:"cycle_start"` + CycleEnd time.Time `json:"cycle_end"` + DataUsed int64 `json:"data_used"` + VoiceUsed int64 `json:"voice_used"` + SMSUsed int64 `json:"sms_used"` + LastUpdated time.Time `json:"last_updated"` +} + +// TableName returns the table name for RatePlanUsage +func (RatePlanUsage) TableName() string { + return "rate_plan_usage" +} + +// RatePlanSubscription represents a subscription to a rate plan +type RatePlanSubscription struct { + ID string `json:"id" gorm:"primaryKey"` + ProfileID string `json:"profile_id" gorm:"index"` + RatePlanID string `json:"rate_plan_id" gorm:"index"` + Status SubscriptionStatus `json:"status" gorm:"index"` + StartedAt time.Time `json:"started_at"` + EndedAt *time.Time `json:"ended_at,omitempty"` + BillingCycle BillingCycle `json:"billing_cycle"` + NextBillingDate time.Time `json:"next_billing_date"` + AutoRenew bool `json:"auto_renew"` + CurrentCycle time.Time `json:"current_cycle"` + AppliedDiscounts []string `json:"applied_discounts,omitempty" gorm:"serializer:json"` + Metadata map[string]interface{} `json:"metadata,omitempty" gorm:"serializer:json"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} + +// TableName returns the table name for RatePlanSubscription +func (RatePlanSubscription) TableName() string { + return "rate_plan_subscriptions" +} + +// SubscriptionStatus represents the status of a subscription +type SubscriptionStatus string + +const ( + SubscriptionStatusActive SubscriptionStatus = "active" + SubscriptionStatusCancelled SubscriptionStatus = "cancelled" + SubscriptionStatusExpired SubscriptionStatus = "expired" + SubscriptionStatusSuspended SubscriptionStatus = "suspended" +) + +// BillingCycle represents the billing cycle type +type BillingCycle string + +const ( + BillingCycleDaily BillingCycle = "daily" + BillingCycleWeekly BillingCycle = "weekly" + BillingCycleMonthly BillingCycle = "monthly" + BillingCycleQuarterly BillingCycle = "quarterly" + BillingCycleYearly BillingCycle = "yearly" +) + +// SubscriptionFilter defines filtering options for subscription queries +type SubscriptionFilter struct { + Status SubscriptionStatus `json:"status,omitempty"` + RatePlanID string `json:"rate_plan_id,omitempty"` + StartedAfter *time.Time `json:"started_after,omitempty"` + StartedBefore *time.Time `json:"started_before,omitempty"` + Limit int `json:"limit,omitempty"` + Offset int `json:"offset,omitempty"` +} + +// RatePlan represents a rate plan +type RatePlan struct { + ID string `json:"id" gorm:"primaryKey"` + Name string `json:"name"` + Description string `json:"description"` + CarrierID string `json:"carrier_id" gorm:"index"` + Region string `json:"region" gorm:"index"` + PlanType PlanType `json:"plan_type"` + BasePrice float64 `json:"base_price"` + Currency string `json:"currency"` + BillingCycle BillingCycle `json:"billing_cycle"` + DataAllowance *DataAllowance `json:"data_allowance,omitempty" gorm:"serializer:json"` + VoiceAllowance *VoiceAllowance `json:"voice_allowance,omitempty" gorm:"serializer:json"` + SMSAllowance *SMSAllowance `json:"sms_allowance,omitempty" gorm:"serializer:json"` + OverageRates *OverageRates `json:"overage_rates,omitempty" gorm:"serializer:json"` + Discounts []*Discount `json:"discounts,omitempty" gorm:"serializer:json"` + ValidFrom time.Time `json:"valid_from"` + ValidTo *time.Time `json:"valid_to,omitempty"` + IsActive bool `json:"is_active"` + Status PlanStatus `json:"status"` + Metadata map[string]interface{} `json:"metadata,omitempty" gorm:"serializer:json"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} + +// TableName returns the table name for RatePlan +func (RatePlan) TableName() string { + return "rate_plans" +} + +// PlanType represents the type of rate plan +type PlanType string + +const ( + PlanTypeData PlanType = "data" + PlanTypeVoice PlanType = "voice" + PlanTypeSMS PlanType = "sms" + PlanTypeBundle PlanType = "bundle" +) + +// PlanStatus represents the status of a rate plan +type PlanStatus string + +const ( + PlanStatusActive PlanStatus = "active" + PlanStatusInactive PlanStatus = "inactive" + PlanStatusDraft PlanStatus = "draft" +) + +// DataAllowance represents data allowance configuration +type DataAllowance struct { + Amount int64 `json:"amount"` + Unit string `json:"unit"` + Unlimited bool `json:"unlimited"` +} + +// VoiceAllowance represents voice allowance configuration +type VoiceAllowance struct { + Minutes int64 `json:"minutes"` + Unlimited bool `json:"unlimited"` +} + +// SMSAllowance represents SMS allowance configuration +type SMSAllowance struct { + Messages int64 `json:"messages"` + Unlimited bool `json:"unlimited"` +} + +// OverageRates represents overage rate configuration +type OverageRates struct { + DataRate float64 `json:"data_rate"` + VoiceRate float64 `json:"voice_rate"` + SMSRate float64 `json:"sms_rate"` +} + +// Discount represents a discount configuration +type Discount struct { + ID string `json:"id"` + Type DiscountType `json:"type"` + Value float64 `json:"value"` + IsActive bool `json:"is_active"` +} + +// DiscountType represents the type of discount +type DiscountType string + +const ( + DiscountTypePercentage DiscountType = "percentage" + DiscountTypeFixed DiscountType = "fixed" +) + +// RatePlanFilter defines filtering options for rate plan queries +type RatePlanFilter struct { + CarrierID string `json:"carrier_id,omitempty"` + Region string `json:"region,omitempty"` + PlanType PlanType `json:"plan_type,omitempty"` + Status PlanStatus `json:"status,omitempty"` + IsActive *bool `json:"is_active,omitempty"` + MinPrice float64 `json:"min_price,omitempty"` + MaxPrice float64 `json:"max_price,omitempty"` + ValidFrom *time.Time `json:"valid_from,omitempty"` + ValidTo *time.Time `json:"valid_to,omitempty"` + Limit int `json:"limit,omitempty"` + Offset int `json:"offset,omitempty"` + SortBy string `json:"sort_by,omitempty"` + SortOrder string `json:"sort_order,omitempty"` +} + +// UsageAnalyticsFilter defines filtering options for usage analytics +type UsageAnalyticsFilter struct { + RatePlanID string `json:"rate_plan_id,omitempty"` + CarrierID string `json:"carrier_id,omitempty"` + Region string `json:"region,omitempty"` + StartDate time.Time `json:"start_date"` + EndDate time.Time `json:"end_date"` + GroupBy string `json:"group_by,omitempty"` +} + +// UsageAnalytics contains usage statistics +type UsageAnalytics struct { + TotalDataUsed int64 `json:"total_data_used"` + TotalVoiceUsed int64 `json:"total_voice_used"` + TotalSMSUsed int64 `json:"total_sms_used"` + ActiveUsers int `json:"active_users"` + AverageUsage map[string]float64 `json:"average_usage"` + UsageByPlan map[string]int64 `json:"usage_by_plan"` + UsageByRegion map[string]int64 `json:"usage_by_region"` + TimelineData []TimelineDataPoint `json:"timeline_data"` +} + +// RevenueAnalyticsFilter defines filtering options for revenue analytics +type RevenueAnalyticsFilter struct { + RatePlanID string `json:"rate_plan_id,omitempty"` + CarrierID string `json:"carrier_id,omitempty"` + Region string `json:"region,omitempty"` + StartDate time.Time `json:"start_date"` + EndDate time.Time `json:"end_date"` + GroupBy string `json:"group_by,omitempty"` +} + +// RevenueAnalytics contains revenue statistics +type RevenueAnalytics struct { + TotalRevenue float64 `json:"total_revenue"` + RevenueByPlan map[string]float64 `json:"revenue_by_plan"` + RevenueByCarrier map[string]float64 `json:"revenue_by_carrier"` + RevenueByRegion map[string]float64 `json:"revenue_by_region"` + AverageRevenue map[string]float64 `json:"average_revenue"` + TimelineData []TimelineDataPoint `json:"timeline_data"` +} + +// TimelineDataPoint represents a data point in time series +type TimelineDataPoint struct { + Timestamp time.Time `json:"timestamp"` + Value float64 `json:"value"` + Label string `json:"label,omitempty"` +} + +// SearchCriteria defines search criteria for rate plans +type SearchCriteria struct { + CarrierID string `json:"carrier_id,omitempty"` + Region string `json:"region,omitempty"` + PlanType PlanType `json:"plan_type,omitempty"` + MinPrice float64 `json:"min_price,omitempty"` + MaxPrice float64 `json:"max_price,omitempty"` + Limit int `json:"limit,omitempty"` + Offset int `json:"offset,omitempty"` + SortBy string `json:"sort_by,omitempty"` + SortOrder string `json:"sort_order,omitempty"` +} + +// SubscribeRequest represents a request to subscribe to a rate plan +type SubscribeRequest struct { + ProfileID string `json:"profile_id"` + RatePlanID string `json:"rate_plan_id"` + AutoRenew bool `json:"auto_renew"` + AppliedDiscounts []string `json:"applied_discounts,omitempty"` + Metadata map[string]interface{} `json:"metadata,omitempty"` +} + +// RecordUsageRequest represents a request to record usage +type RecordUsageRequest struct { + ProfileID string `json:"profile_id"` + DataUsed int64 `json:"data_used"` + VoiceUsed int64 `json:"voice_used"` + SMSUsed int64 `json:"sms_used"` +} + +// CalculateCostRequest represents a request to calculate cost +type CalculateCostRequest struct { + RatePlanID string `json:"rate_plan_id"` + DataUsed int64 `json:"data_used"` + VoiceUsed int64 `json:"voice_used"` + SMSUsed int64 `json:"sms_used"` + AppliedDiscounts []string `json:"applied_discounts,omitempty"` +} + +// RatePlanCostCalculation represents the result of a cost calculation +type RatePlanCostCalculation struct { + RatePlanID string `json:"rate_plan_id"` + BaseCost float64 `json:"base_cost"` + OverageCost float64 `json:"overage_cost"` + DiscountCost float64 `json:"discount_cost"` + TotalCost float64 `json:"total_cost"` + Currency string `json:"currency"` + Breakdown map[string]interface{} `json:"breakdown"` + CalculatedAt time.Time `json:"calculated_at"` +} diff --git a/apps/carrier-connector/internal/services/analytics_service.go b/apps/carrier-connector/internal/services/analytics_service.go index 354ad0e..6535854 100644 --- a/apps/carrier-connector/internal/services/analytics_service.go +++ b/apps/carrier-connector/internal/services/analytics_service.go @@ -194,9 +194,9 @@ func (s *AnalyticsServiceImpl) GetTopCurrenciesByRevenue(ctx context.Context, li // Convert to slice and sort var currencyRevenues []*currency.CurrencyRevenue - for currency, revenue := range revenueByCurrency { + for currCode, revenue := range revenueByCurrency { currencyRevenues = append(currencyRevenues, ¤cy.CurrencyRevenue{ - Currency: currency, + Currency: currCode, Revenue: revenue, }) } diff --git a/apps/carrier-connector/internal/services/billing_core.go b/apps/carrier-connector/internal/services/billing_core.go index fe5f148..311b747 100644 --- a/apps/carrier-connector/internal/services/billing_core.go +++ b/apps/carrier-connector/internal/services/billing_core.go @@ -6,19 +6,20 @@ import ( "time" "github.com/google/uuid" + "github.com/nutcas3/telecom-platform/apps/carrier-connector/internal/currency" "github.com/sirupsen/logrus" ) // BillingServiceImpl handles multi-currency billing operations type BillingServiceImpl struct { - repository Repository - exchangeService *ExchangeRateService + repository currency.Repository + exchangeService currency.ExchangeRateService logger *logrus.Logger baseCurrency string } // NewBillingService creates a new billing service -func NewBillingService(repository Repository, exchangeService *ExchangeRateService, logger *logrus.Logger, baseCurrency string) *BillingServiceImpl { +func NewBillingService(repository currency.Repository, exchangeService currency.ExchangeRateService, logger *logrus.Logger, baseCurrency string) *BillingServiceImpl { return &BillingServiceImpl{ repository: repository, exchangeService: exchangeService, @@ -28,7 +29,7 @@ func NewBillingService(repository Repository, exchangeService *ExchangeRateServi } // ProcessBilling processes a billing request in multi-currency context -func (s *BillingServiceImpl) ProcessBilling(ctx context.Context, req *BillingRequest) (*BillingResponse, error) { +func (s *BillingServiceImpl) ProcessBilling(ctx context.Context, req *currency.BillingRequest) (*currency.BillingResponse, error) { // Validate request if err := s.validateBillingRequest(req); err != nil { return nil, fmt.Errorf("invalid billing request: %w", err) @@ -49,18 +50,18 @@ func (s *BillingServiceImpl) ProcessBilling(ctx context.Context, req *BillingReq } // Create transaction - transaction := &Transaction{ + transaction := ¤cy.Transaction{ ID: uuid.New().String(), ProfileID: req.ProfileID, SubscriptionID: req.SubscriptionID, - Type: TransactionTypeSubscription, + Type: currency.TransactionTypeSubscription, Amount: req.Amount, Currency: req.Currency, BaseAmount: baseAmount, BaseCurrency: s.baseCurrency, ExchangeRate: exchangeRate, Description: req.Description, - Status: TransactionStatusPending, + Status: currency.TransactionStatusPending, CreatedAt: time.Now(), UpdatedAt: time.Now(), } @@ -72,7 +73,7 @@ func (s *BillingServiceImpl) ProcessBilling(ctx context.Context, req *BillingReq } // Process payment (in real implementation, this would integrate with payment gateway) - transaction.Status = TransactionStatusCompleted + transaction.Status = currency.TransactionStatusCompleted if err := s.repository.UpdateTransaction(ctx, transaction); err != nil { s.logger.WithError(err).Error("Failed to update transaction status") return nil, fmt.Errorf("failed to update transaction: %w", err) @@ -87,7 +88,7 @@ func (s *BillingServiceImpl) ProcessBilling(ctx context.Context, req *BillingReq "base_currency": s.baseCurrency, }).Info("Billing processed successfully") - return &BillingResponse{ + return ¤cy.BillingResponse{ TransactionID: transaction.ID, Amount: transaction.Amount, Currency: transaction.Currency, @@ -100,14 +101,14 @@ func (s *BillingServiceImpl) ProcessBilling(ctx context.Context, req *BillingReq } // ConvertAmount converts an amount between currencies -func (s *BillingServiceImpl) ConvertAmount(ctx context.Context, req *CurrencyConversionRequest) (*CurrencyConversionResponse, error) { +func (s *BillingServiceImpl) ConvertAmount(ctx context.Context, req *currency.CurrencyConversionRequest) (*currency.CurrencyConversionResponse, error) { return s.exchangeService.ConvertAmount(ctx, req.Amount, req.FromCurrency, req.ToCurrency) } // GetBillingHistory retrieves billing history for a profile -func (s *BillingServiceImpl) GetBillingHistory(ctx context.Context, profileID string, filter *TransactionFilter) ([]*Transaction, error) { +func (s *BillingServiceImpl) GetBillingHistory(ctx context.Context, profileID string, filter *currency.TransactionFilter) ([]*currency.Transaction, error) { if filter == nil { - filter = &TransactionFilter{} + filter = ¤cy.TransactionFilter{} } filter.ProfileID = profileID @@ -122,7 +123,7 @@ func (s *BillingServiceImpl) GetBillingHistory(ctx context.Context, profileID st } // validateBillingRequest validates a billing request -func (s *BillingServiceImpl) validateBillingRequest(req *BillingRequest) error { +func (s *BillingServiceImpl) validateBillingRequest(req *currency.BillingRequest) error { if req.ProfileID == "" { return fmt.Errorf("profile ID is required") } diff --git a/apps/carrier-connector/internal/services/rateplan_core.go b/apps/carrier-connector/internal/services/rateplan_core.go index 0f8916b..a98d2f4 100644 --- a/apps/carrier-connector/internal/services/rateplan_core.go +++ b/apps/carrier-connector/internal/services/rateplan_core.go @@ -38,7 +38,7 @@ func NewRatePlanCurrencyIntegrator( } // SubscribeToPlanWithCurrency subscribes to a rate plan with currency conversion -func (rpci *RatePlanCurrencyIntegrator) SubscribeToPlanWithCurrency(ctx context.Context, profileID string, planID string, currency string) (*rateplan.RatePlanSubscription, error) { +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 { @@ -49,8 +49,8 @@ func (rpci *RatePlanCurrencyIntegrator) SubscribeToPlanWithCurrency(ctx context. subscriptionPrice := plan.BasePrice exchangeRate := 1.0 - if currency != plan.Currency { - conversion, err := rpci.exchangeService.ConvertAmount(ctx, plan.BasePrice, plan.Currency, currency) + if targetCurrency != plan.Currency { + conversion, err := rpci.exchangeService.ConvertAmount(ctx, plan.BasePrice, plan.Currency, targetCurrency) if err != nil { rpci.logger.WithError(err).Error("Failed to convert rate plan price") return nil, fmt.Errorf("currency conversion failed: %w", err) @@ -67,7 +67,7 @@ func (rpci *RatePlanCurrencyIntegrator) SubscribeToPlanWithCurrency(ctx context. StartedAt: time.Now(), Metadata: map[string]interface{}{ "original_currency": plan.Currency, - "subscription_currency": currency, + "subscription_currency": targetCurrency, "original_price": plan.BasePrice, "subscription_price": subscriptionPrice, "exchange_rate": exchangeRate, @@ -94,7 +94,7 @@ func (rpci *RatePlanCurrencyIntegrator) SubscribeToPlanWithCurrency(ctx context. ProfileID: profileID, SubscriptionID: createdSubscription.ID, Amount: subscriptionPrice, - Currency: currency, + Currency: targetCurrency, Description: fmt.Sprintf("Initial subscription to %s", plan.Name), BillingDate: time.Now(), } @@ -108,7 +108,7 @@ func (rpci *RatePlanCurrencyIntegrator) SubscribeToPlanWithCurrency(ctx context. rpci.logger.WithFields(logrus.Fields{ "profile_id": profileID, "plan_id": planID, - "currency": currency, + "currency": targetCurrency, "subscription_id": createdSubscription.ID, }).Info("Rate plan subscription created with currency support") @@ -116,7 +116,7 @@ func (rpci *RatePlanCurrencyIntegrator) SubscribeToPlanWithCurrency(ctx context. } // CalculatePlanCostInCurrency calculates the cost of a rate plan in a specific currency -func (rpci *RatePlanCurrencyIntegrator) CalculatePlanCostInCurrency(ctx context.Context, planID string, currency string, usageData *rateplan.RatePlanUsage) (*currency.BillingSummary, error) { +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 { @@ -140,8 +140,8 @@ func (rpci *RatePlanCurrencyIntegrator) CalculatePlanCostInCurrency(ctx context. convertedCost := baseCost exchangeRate := 1.0 - if currency != plan.Currency { - conversion, err := rpci.exchangeService.ConvertAmount(ctx, baseCost, plan.Currency, currency) + 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) } @@ -153,7 +153,7 @@ func (rpci *RatePlanCurrencyIntegrator) CalculatePlanCostInCurrency(ctx context. summary := ¤cy.BillingSummary{ ProfileID: usageData.ProfileID, TotalAmount: convertedCost, - Currency: currency, + Currency: targetCurrency, BaseTotalAmount: baseCost, BaseCurrency: plan.Currency, TransactionCount: 1, diff --git a/apps/carrier-connector/internal/services/service_analytics.go b/apps/carrier-connector/internal/services/service_analytics.go index 67b42bf..a951e6b 100644 --- a/apps/carrier-connector/internal/services/service_analytics.go +++ b/apps/carrier-connector/internal/services/service_analytics.go @@ -2,10 +2,12 @@ package services import ( "context" + + "github.com/nutcas3/telecom-platform/apps/carrier-connector/internal/repository" ) // GetUsage retrieves usage for a profile -func (s *Service) GetUsage(ctx context.Context, profileID string) (*RatePlanUsage, error) { +func (s *Service) GetUsage(ctx context.Context, profileID string) (*repository.RatePlanUsage, error) { usage, err := s.repo.GetCurrentUsage(ctx, profileID) if err != nil { s.logger.WithError(err).WithField("profile_id", profileID).Error("Failed to get usage") @@ -14,8 +16,9 @@ func (s *Service) GetUsage(ctx context.Context, profileID string) (*RatePlanUsag return usage, nil } + // GetUsageHistory retrieves usage history for a profile -func (s *Service) GetUsageHistory(ctx context.Context, profileID string, limit int) ([]*RatePlanUsage, error) { +func (s *Service) GetUsageHistory(ctx context.Context, profileID string, limit int) ([]*repository.RatePlanUsage, error) { usageHistory, err := s.repo.ListUsageHistory(ctx, profileID, limit) if err != nil { s.logger.WithError(err).Error("Failed to get usage history") @@ -26,7 +29,7 @@ func (s *Service) GetUsageHistory(ctx context.Context, profileID string, limit i } // GetUsageAnalytics retrieves usage analytics -func (s *Service) GetUsageAnalytics(ctx context.Context, filter *UsageAnalyticsFilter) (*UsageAnalytics, error) { +func (s *Service) GetUsageAnalytics(ctx context.Context, filter *repository.UsageAnalyticsFilter) (*repository.UsageAnalytics, error) { analytics, err := s.repo.GetUsageAnalytics(ctx, filter) if err != nil { s.logger.WithError(err).Error("Failed to get usage analytics") @@ -37,7 +40,7 @@ func (s *Service) GetUsageAnalytics(ctx context.Context, filter *UsageAnalyticsF } // GetRevenueAnalytics retrieves revenue analytics -func (s *Service) GetRevenueAnalytics(ctx context.Context, filter *RevenueAnalyticsFilter) (*RevenueAnalytics, error) { +func (s *Service) GetRevenueAnalytics(ctx context.Context, filter *repository.RevenueAnalyticsFilter) (*repository.RevenueAnalytics, error) { analytics, err := s.repo.GetRevenueAnalytics(ctx, filter) if err != nil { s.logger.WithError(err).Error("Failed to get revenue analytics") @@ -48,7 +51,7 @@ func (s *Service) GetRevenueAnalytics(ctx context.Context, filter *RevenueAnalyt } // GetPopularPlans retrieves the most popular rate plans -func (s *Service) GetPopularPlans(ctx context.Context, limit int) ([]*RatePlan, error) { +func (s *Service) GetPopularPlans(ctx context.Context, limit int) ([]*repository.RatePlan, error) { plans, err := s.repo.GetPopularPlans(ctx, limit) if err != nil { s.logger.WithError(err).Error("Failed to get popular plans") diff --git a/apps/carrier-connector/internal/services/service_methods.go b/apps/carrier-connector/internal/services/service_methods.go index f758caf..9fb58f5 100644 --- a/apps/carrier-connector/internal/services/service_methods.go +++ b/apps/carrier-connector/internal/services/service_methods.go @@ -3,9 +3,11 @@ package services import ( "fmt" "time" + + "github.com/nutcas3/telecom-platform/apps/carrier-connector/internal/repository" ) -func (s *Service) validateRatePlan(plan *RatePlan) error { +func (s *Service) validateRatePlan(plan *repository.RatePlan) error { if plan.Name == "" { return fmt.Errorf("rate plan name is required") } @@ -30,7 +32,7 @@ func (s *Service) validateRatePlan(plan *RatePlan) error { return nil } -func (s *Service) validateSubscribeRequest(req *SubscribeRequest) error { +func (s *Service) validateSubscribeRequest(req *repository.SubscribeRequest) error { if req.ProfileID == "" { return fmt.Errorf("profile ID is required") } @@ -40,37 +42,36 @@ func (s *Service) validateSubscribeRequest(req *SubscribeRequest) error { return nil } -func (s *Service) calculateNextBillingDate(cycle BillingCycle, from time.Time) time.Time { +func (s *Service) calculateNextBillingDate(cycle repository.BillingCycle, from time.Time) time.Time { switch cycle { - case BillingCycleDaily: + case repository.BillingCycleDaily: return from.AddDate(0, 0, 1) - case BillingCycleWeekly: + case repository.BillingCycleWeekly: return from.AddDate(0, 0, 7) - case BillingCycleMonthly: + case repository.BillingCycleMonthly: return from.AddDate(0, 1, 0) - case BillingCycleQuarterly: + case repository.BillingCycleQuarterly: return from.AddDate(0, 3, 0) - case BillingCycleYearly: + case repository.BillingCycleYearly: return from.AddDate(1, 0, 0) default: return from.AddDate(0, 1, 0) // Default to monthly } } -func (s *Service) calculateCycleEnd(cycle BillingCycle, cycleStart time.Time) time.Time { +func (s *Service) calculateCycleEnd(cycle repository.BillingCycle, cycleStart time.Time) time.Time { switch cycle { - case BillingCycleDaily: + case repository.BillingCycleDaily: return cycleStart.AddDate(0, 0, 1).Add(-time.Nanosecond) - case BillingCycleWeekly: + case repository.BillingCycleWeekly: return cycleStart.AddDate(0, 0, 7).Add(-time.Nanosecond) - case BillingCycleMonthly: + case repository.BillingCycleMonthly: return cycleStart.AddDate(0, 1, 0).Add(-time.Nanosecond) - case BillingCycleQuarterly: + case repository.BillingCycleQuarterly: return cycleStart.AddDate(0, 3, 0).Add(-time.Nanosecond) - case BillingCycleYearly: + case repository.BillingCycleYearly: return cycleStart.AddDate(1, 0, 0).Add(-time.Nanosecond) default: return cycleStart.AddDate(0, 1, 0).Add(-time.Nanosecond) // Default to monthly } } -