Files
telecom-platform/apps/carrier-connector/internal/services/pricing_integration.go
T
nutcas3 4205d4c2aa refactor: Replace unused context parameters with blank identifiers and fix variable references
- Replace ctx parameter with _ in provisionTenantContext, provisionDatabaseSchema, provisionStorageResources, configureCarrier, configureRatePlans, setupPaymentProcessing, and validateCompliance methods
- Fix tenant_id scope to use wrappedTx.tenantID instead of r.tenantID in TenantScopedTransaction
- Fix pricing effectiveness analytics to use ratePlanAnalytics.AverageDiscount and ratePlanAnalytics.TotalSavings instead of analytics.
2026-05-03 10:31:49 +03:00

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: ratePlanAnalytics.AverageDiscount,
TotalSavings: ratePlanAnalytics.TotalSavings,
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"`
}