mirror of
https://github.com/andwati/telecom-platform.git
synced 2026-10-28 15:42:16 +00:00
- Replace map[string]interface{} with map[string]any in UpdateCarrierMetrics
- Replace interface{} with any for GeneratedAt field in PricingEffectiveness struct
- Replace loop-based empty string checks with slices.Contains in ValidateConditions
- Replace range loop index initialization with range-only syntax in calculateAPIPerformance
- Add slices import for standard library slice utilities
240 lines
8.3 KiB
Go
240 lines
8.3 KiB
Go
package services
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"github.com/nutcas3/telecom-platform/apps/carrier-connector/internal/id"
|
|
"github.com/nutcas3/telecom-platform/apps/carrier-connector/internal/pricing"
|
|
"github.com/nutcas3/telecom-platform/apps/carrier-connector/internal/rateplan"
|
|
"github.com/sirupsen/logrus"
|
|
)
|
|
|
|
// PricingIntegration integrates dynamic pricing with rate plans
|
|
type PricingIntegration struct {
|
|
pricingService pricing.Service
|
|
rateplanService rateplan.Service
|
|
logger *logrus.Logger
|
|
}
|
|
|
|
// NewPricingIntegration creates a new pricing integration service
|
|
func NewPricingIntegration(
|
|
pricingService pricing.Service,
|
|
rateplanService rateplan.Service,
|
|
logger *logrus.Logger,
|
|
) *PricingIntegration {
|
|
return &PricingIntegration{
|
|
pricingService: pricingService,
|
|
rateplanService: rateplanService,
|
|
logger: logger,
|
|
}
|
|
}
|
|
|
|
// CalculateRatePlanPrice calculates the price for a rate plan with dynamic pricing
|
|
func (pi *PricingIntegration) CalculateRatePlanPrice(ctx context.Context, tenantID, ratePlanID string, quantity int, customerID string) (*pricing.PricingResult, error) {
|
|
// Get rate plan
|
|
ratePlan, err := pi.rateplanService.GetRatePlan(ctx, ratePlanID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get rate plan: %w", err)
|
|
}
|
|
|
|
// Create pricing context
|
|
pricingCtx := &pricing.PricingContext{
|
|
TenantID: tenantID,
|
|
CustomerID: customerID,
|
|
ProductID: ratePlanID,
|
|
BasePrice: ratePlan.BasePrice,
|
|
Currency: ratePlan.Currency,
|
|
Quantity: quantity,
|
|
Location: ratePlan.Region,
|
|
Time: id.GetCurrentTime(),
|
|
Metadata: map[string]any{
|
|
"rate_plan_name": ratePlan.Name,
|
|
"carrier_id": ratePlan.CarrierID,
|
|
"plan_type": ratePlan.PlanType,
|
|
},
|
|
}
|
|
|
|
// Calculate price with dynamic pricing
|
|
result, err := pi.pricingService.CalculatePrice(ctx, pricingCtx)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to calculate price: %w", err)
|
|
}
|
|
|
|
pi.logger.WithFields(logrus.Fields{
|
|
"tenant_id": tenantID,
|
|
"rate_plan_id": ratePlanID,
|
|
"customer_id": customerID,
|
|
"original_price": result.OriginalPrice,
|
|
"final_price": result.FinalPrice,
|
|
"discount": result.Discount,
|
|
}).Info("Rate plan price calculated with dynamic pricing")
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// ApplyPricingToSubscription applies pricing rules to a subscription
|
|
func (pi *PricingIntegration) ApplyPricingToSubscription(ctx context.Context, subscription *rateplan.RatePlanSubscription) (*rateplan.RatePlanSubscription, error) {
|
|
// Get rate plan
|
|
_, err := pi.rateplanService.GetRatePlan(ctx, subscription.RatePlanID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get rate plan: %w", err)
|
|
}
|
|
|
|
// Calculate pricing
|
|
result, err := pi.CalculateRatePlanPrice(ctx, subscription.ProfileID, subscription.RatePlanID, 1, subscription.ProfileID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Update subscription metadata with pricing information
|
|
if subscription.Metadata == nil {
|
|
subscription.Metadata = make(map[string]any)
|
|
}
|
|
|
|
subscription.Metadata["dynamic_pricing"] = map[string]any{
|
|
"original_price": result.OriginalPrice,
|
|
"final_price": result.FinalPrice,
|
|
"discount": result.Discount,
|
|
"applied_rules": result.AppliedRules,
|
|
"calculated_at": id.GetCurrentTime(),
|
|
}
|
|
|
|
return subscription, nil
|
|
}
|
|
|
|
// CreatePricingRuleFromRatePlan creates a pricing rule based on a rate plan
|
|
func (pi *PricingIntegration) CreatePricingRuleFromRatePlan(ctx context.Context, tenantID string, ratePlanID string, ruleType pricing.RuleType, conditions pricing.RuleConditions, actions pricing.RuleActions) (*pricing.PricingRule, error) {
|
|
// Get rate plan
|
|
ratePlan, err := pi.rateplanService.GetRatePlan(ctx, ratePlanID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get rate plan: %w", err)
|
|
}
|
|
|
|
// Create pricing rule
|
|
rule := &pricing.PricingRule{
|
|
ID: id.GenerateRuleID(),
|
|
Name: fmt.Sprintf("Dynamic pricing for %s", ratePlan.Name),
|
|
Description: fmt.Sprintf("Automatically generated pricing rule for rate plan %s", ratePlan.Name),
|
|
TenantID: tenantID,
|
|
Type: ruleType,
|
|
Priority: 100, // High priority for auto-generated rules
|
|
IsActive: true,
|
|
Conditions: conditions,
|
|
Actions: actions,
|
|
Metadata: map[string]any{
|
|
"rate_plan_id": ratePlanID,
|
|
"rate_plan_name": ratePlan.Name,
|
|
"auto_generated": true,
|
|
"created_at": id.GetCurrentTime(),
|
|
},
|
|
}
|
|
|
|
// Create rule
|
|
createdRule, err := pi.pricingService.CreateRule(ctx, rule)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create pricing rule: %w", err)
|
|
}
|
|
|
|
pi.logger.WithFields(logrus.Fields{
|
|
"tenant_id": tenantID,
|
|
"rate_plan_id": ratePlanID,
|
|
"rule_id": createdRule.ID,
|
|
"rule_type": ruleType,
|
|
}).Info("Pricing rule created from rate plan")
|
|
|
|
return createdRule, nil
|
|
}
|
|
|
|
// GetPricingEffectiveness analyzes the effectiveness of pricing rules
|
|
func (pi *PricingIntegration) GetPricingEffectiveness(ctx context.Context, tenantID string) (*PricingEffectiveness, error) {
|
|
// Get pricing analytics
|
|
analytics, err := pi.pricingService.GetAnalytics(ctx, tenantID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get pricing analytics: %w", err)
|
|
}
|
|
|
|
// Calculate actual conversion rate
|
|
conversionRate := calculateConversionRate(analytics)
|
|
|
|
// Get rate plan analytics using actual data
|
|
ratePlanAnalytics := &RatePlanPricingAnalytics{
|
|
TotalRatePlans: analytics.TotalRules, // Use total rules as proxy for rate plans
|
|
PlansWithPricing: analytics.ActiveRules, // Use active rules as proxy for plans with pricing
|
|
AverageDiscount: analytics.DiscountStats.AverageDiscount,
|
|
TotalSavings: analytics.DiscountStats.TotalDiscountValue,
|
|
ConversionRate: conversionRate,
|
|
}
|
|
|
|
// Calculate effectiveness
|
|
effectiveness := &PricingEffectiveness{
|
|
TotalRules: analytics.TotalRules,
|
|
ActiveRules: analytics.ActiveRules,
|
|
TotalRatePlans: ratePlanAnalytics.TotalRatePlans,
|
|
PlansWithPricing: ratePlanAnalytics.PlansWithPricing,
|
|
AverageDiscountRate: analytics.DiscountStats.AverageDiscount,
|
|
TotalSavings: analytics.DiscountStats.TotalDiscountValue,
|
|
RulesByType: analytics.RulesByType,
|
|
ConversionImprovement: ratePlanAnalytics.ConversionRate,
|
|
GeneratedAt: id.GetCurrentTime(),
|
|
}
|
|
|
|
return effectiveness, nil
|
|
}
|
|
|
|
// calculateConversionRate calculates the actual conversion rate based on pricing analytics
|
|
func calculateConversionRate(analytics *pricing.PricingAnalytics) float64 {
|
|
// Conversion rate = (Total Discounts Applied / Total Pricing Opportunities) * 100
|
|
|
|
if analytics.TotalRules == 0 {
|
|
return 0.0
|
|
}
|
|
|
|
// Calculate conversion rate based on discount statistics
|
|
// If we have discount data, use it to calculate conversion
|
|
if analytics.DiscountStats.TotalDiscounts > 0 {
|
|
// Conversion rate based on successful discount applications
|
|
conversionRate := (float64(analytics.DiscountStats.TotalDiscounts) / float64(analytics.TotalRules)) * 100
|
|
|
|
// Clamp to reasonable bounds (0-100%)
|
|
if conversionRate > 100.0 {
|
|
conversionRate = 100.0
|
|
} else if conversionRate < 0.0 {
|
|
conversionRate = 0.0
|
|
}
|
|
|
|
return conversionRate
|
|
}
|
|
|
|
// Fallback: use active rules as proxy for conversion
|
|
// Active rules / Total rules gives us a basic conversion metric
|
|
activeRuleRatio := (float64(analytics.ActiveRules) / float64(analytics.TotalRules)) * 100
|
|
|
|
// Apply a realistic conversion factor (not all active rules result in conversions)
|
|
// Assume 30-70% of active rules actually convert to pricing changes
|
|
conversionFactor := 0.5 // 50% conversion assumption
|
|
conversionRate := activeRuleRatio * conversionFactor
|
|
|
|
return conversionRate
|
|
}
|
|
|
|
type PricingEffectiveness struct {
|
|
TotalRules int `json:"total_rules"`
|
|
ActiveRules int `json:"active_rules"`
|
|
TotalRatePlans int `json:"total_rate_plans"`
|
|
PlansWithPricing int `json:"plans_with_pricing"`
|
|
AverageDiscountRate float64 `json:"average_discount_rate"`
|
|
TotalSavings float64 `json:"total_savings"`
|
|
RulesByType map[string]int `json:"rules_by_type"`
|
|
ConversionImprovement float64 `json:"conversion_improvement"`
|
|
GeneratedAt any `json:"generated_at"`
|
|
}
|
|
|
|
type RatePlanPricingAnalytics struct {
|
|
TotalRatePlans int `json:"total_rate_plans"`
|
|
PlansWithPricing int `json:"plans_with_pricing"`
|
|
AverageDiscount float64 `json:"average_discount"`
|
|
TotalSavings float64 `json:"total_savings"`
|
|
ConversionRate float64 `json:"conversion_rate"`
|
|
}
|