File size: 5,471 Bytes
7856e60
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LogisticsConfig {
    pub weight_coefficient: f64,
    pub distance_coefficient: f64,
    pub complexity_bias: f64,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum LogisticsZone {
    Local,    // Within city (< 50km)
    Regional, // Within state (50 - 300km)
    National, // Outside state (> 300km)
}

impl Default for LogisticsConfig {
    fn default() -> Self {
        Self {
            weight_coefficient: 0.15, // Adjusted for Indian gram-based pricing
            distance_coefficient: 1.0,
            complexity_bias: 1.0,
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PricingFeatures {
    pub distance_km: f64,
    pub user_rate_per_km: f64,
    pub product_weight: f64,
    pub base_charge: f64,
    pub config: LogisticsConfig,
}

// ========================================================
// ADVANCED OOPS CONCEPTS: POLYMORPHIC ZONAL PRICING
// ========================================================

/// Strategy trait for calculating regional zone pricing coefficients.
pub trait ZonalPricingStrategy: Send + Sync {
    fn multiplier(&self) -> f64;
}

pub struct LocalZoneStrategy;
impl ZonalPricingStrategy for LocalZoneStrategy {
    fn multiplier(&self) -> f64 {
        1.0
    }
}

pub struct RegionalZoneStrategy;
impl ZonalPricingStrategy for RegionalZoneStrategy {
    fn multiplier(&self) -> f64 {
        1.5
    }
}

pub struct NationalZoneStrategy;
impl ZonalPricingStrategy for NationalZoneStrategy {
    fn multiplier(&self) -> f64 {
        2.5
    }
}

/// Factory pattern to retrieve the correct zonal pricing strategy object dynamically.
pub struct ZonalPricingStrategyFactory;

impl ZonalPricingStrategyFactory {
    pub fn get_strategy(distance_km: f64) -> Box<dyn ZonalPricingStrategy> {
        if distance_km < 50.0 {
            Box::new(LocalZoneStrategy)
        } else if distance_km < 300.0 {
            Box::new(RegionalZoneStrategy)
        } else {
            Box::new(NationalZoneStrategy)
        }
    }
}

// ========================================================
// PRICING ENGINE CLASS IMPLEMENTATION
// ========================================================

/// The `PricingEngine` handles the platform's economic logic, including dynamic delivery fees
/// and trust-adjusted platform service charges. Refactored using dynamic strategy patterns.
pub struct PricingEngine;

impl PricingEngine {
    /// Estimates the delivery fee using a multi-factor linear estimation model.
    ///
    /// The model accounts for:
    /// 1. **Zonal Logic**: Higher multipliers for cross-state shipments (polymorphically resolved).
    /// 2. **Volumetric Weight**: Uses a gram-to-kg conversion with configurable coefficients.
    /// 3. **Distance Variable**: Direct linear scaling with the merchant's custom rate-per-km.
    pub fn estimate_delivery_fee(features: PricingFeatures) -> f64 {
        let strategy = ZonalPricingStrategyFactory::get_strategy(features.distance_km);
        let zone_multiplier = strategy.multiplier();

        // Convert weight from grams to kg for institutional pricing parity
        let weight_kg = features.product_weight / 1000.0;
        let weight_component = weight_kg * features.config.weight_coefficient * 50.0; // ₹50 per kg baseline

        let base_with_zone = features.base_charge * zone_multiplier;
        let distance_component = features.distance_km
            * (features.user_rate_per_km * features.config.distance_coefficient);

        let raw_total = (base_with_zone + distance_component + weight_component)
            * features.config.complexity_bias;

        // Perform final rounding to match currency precision
        (raw_total * 100.0).round() / 100.0
    }

    /// Calculates the platform's service fee for a transaction.
    ///
    /// This is a **Secure Incentive** model:
    /// - Base fees are tiered by transaction value.
    /// - **Trust Discount**: High trust scores (0-100) provide a linear discount of up to 50%.
    pub fn calculate_platform_fee(_price: f64, _trust_score: f64) -> f64 {
        0.0
    }

    /// Aggregates all price components into a final total consumer charge.
    pub fn calculate_total_consumer_price(
        product_price: f64,
        delivery_fee: f64,
        tax_amount: f64,
        _trust_score: f64,
    ) -> f64 {
        ((product_price + delivery_fee + tax_amount) * 100.0).round() / 100.0
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_zero_distance_pricing() {
        let features = PricingFeatures {
            distance_km: 0.0,
            user_rate_per_km: 15.0,
            product_weight: 500.0,
            base_charge: 20.0,
            config: LogisticsConfig::default(),
        };
        let fee = PricingEngine::estimate_delivery_fee(features);
        // (0 * 15 + 0.5 * 0.15 * 50 + 20 * 1.0) * 1.0 = 23.75
        assert_eq!(fee, 23.75);
    }

    #[test]
    fn test_extreme_weight_pricing() {
        let features = PricingFeatures {
            distance_km: 10.0,
            user_rate_per_km: 15.0,
            product_weight: 100000.0, // 100kg
            base_charge: 20.0,
            config: LogisticsConfig::default(),
        };
        let fee = PricingEngine::estimate_delivery_fee(features);
        // (150 + 100 * 0.15 * 50 + 20) = 150 + 750 + 20 = 920.0
        assert_eq!(fee, 920.0);
    }
}