fix MMS43 pricing

This commit is contained in:
Arnaud Delcasse 2025-10-13 19:57:12 +02:00
parent e36cd6e557
commit 75d0288d2d
2 changed files with 181 additions and 27 deletions

View File

@ -7,6 +7,17 @@ func NewMMS43PricingService() (*MMS43PricingService, error) {
} }
func (s *MMS43PricingService) Prices(params PricingParams) (map[string]Price, error) { func (s *MMS43PricingService) Prices(params PricingParams) (map[string]Price, error) {
// Determine which distance to use based on mobility type
// For solidarity transport: use driver distance
// For organized carpool: use passenger distance
var distanceForPricing int64
if params.MobilityType == "solidarity_transport" {
distanceForPricing = params.SharedMobility.DriverDistance
} else {
// organized_carpool or default
distanceForPricing = params.SharedMobility.PassengerDistance
}
// Calculate passenger price // Calculate passenger price
var passengerAmount float64 var passengerAmount float64
@ -16,12 +27,12 @@ func (s *MMS43PricingService) Prices(params PricingParams) (map[string]Price, er
// Trip is free // Trip is free
passengerAmount = 0.0 passengerAmount = 0.0
} else { } else {
// Price is 0.15€/km for passenger distance // Price is 0.15€/km based on mobility type distance
passengerAmount = 0.15 * float64(params.SharedMobility.PassengerDistance) passengerAmount = 0.15 * float64(distanceForPricing)
} }
// Driver indemnification is always 0.30€/km for driver distance // Driver indemnification is always 0.30€/km based on mobility type distance
driverAmount := 0.30 * float64(params.SharedMobility.DriverDistance) driverAmount := 0.30 * float64(distanceForPricing)
return map[string]Price{ return map[string]Price{
"passenger": { "passenger": {

View File

@ -14,8 +14,9 @@ func TestMMS43Prices(t *testing.T) {
expectedError bool expectedError bool
}{ }{
{ {
name: "First trip - should be free for passenger", name: "Solidarity transport - First trip - should be free for passenger",
params: PricingParams{ params: PricingParams{
MobilityType: "solidarity_transport",
Beneficiary: BeneficiaryParams{ Beneficiary: BeneficiaryParams{
History: 0, // First trip History: 0, // First trip
Priority: false, Priority: false,
@ -27,13 +28,33 @@ func TestMMS43Prices(t *testing.T) {
}, },
expectedPrices: map[string]Price{ expectedPrices: map[string]Price{
"passenger": {Amount: 0.0, Currency: "EUR"}, // Free for first trip "passenger": {Amount: 0.0, Currency: "EUR"}, // Free for first trip
"driver": {Amount: 4.5, Currency: "EUR"}, // 15km * 0.30€/km "driver": {Amount: 4.5, Currency: "EUR"}, // 15km * 0.30€/km (driver distance)
}, },
expectedError: false, expectedError: false,
}, },
{ {
name: "Second trip - should be free for passenger", name: "Organized carpool - First trip - should be free for passenger",
params: PricingParams{ params: PricingParams{
MobilityType: "organized_carpool",
Beneficiary: BeneficiaryParams{
History: 0, // First trip
Priority: false,
},
SharedMobility: SharedMobilityParams{
PassengerDistance: 10, // 10km in meters
DriverDistance: 15, // 15km in meters
},
},
expectedPrices: map[string]Price{
"passenger": {Amount: 0.0, Currency: "EUR"}, // Free for first trip
"driver": {Amount: 3.0, Currency: "EUR"}, // 10km * 0.30€/km (passenger distance)
},
expectedError: false,
},
{
name: "Solidarity transport - Second trip - should be free for passenger",
params: PricingParams{
MobilityType: "solidarity_transport",
Beneficiary: BeneficiaryParams{ Beneficiary: BeneficiaryParams{
History: 1, // Second trip History: 1, // Second trip
Priority: false, Priority: false,
@ -45,31 +66,71 @@ func TestMMS43Prices(t *testing.T) {
}, },
expectedPrices: map[string]Price{ expectedPrices: map[string]Price{
"passenger": {Amount: 0.0, Currency: "EUR"}, // Free for second trip "passenger": {Amount: 0.0, Currency: "EUR"}, // Free for second trip
"driver": {Amount: 7.5, Currency: "EUR"}, // 25km * 0.30€/km "driver": {Amount: 7.5, Currency: "EUR"}, // 25km * 0.30€/km (driver distance)
}, },
expectedError: false, expectedError: false,
}, },
{ {
name: "Third trip - passenger should pay", name: "Organized carpool - Second trip - should be free for passenger",
params: PricingParams{ params: PricingParams{
MobilityType: "organized_carpool",
Beneficiary: BeneficiaryParams{
History: 1, // Second trip
Priority: false,
},
SharedMobility: SharedMobilityParams{
PassengerDistance: 20, // 20km in meters
DriverDistance: 25, // 25km in meters
},
},
expectedPrices: map[string]Price{
"passenger": {Amount: 0.0, Currency: "EUR"}, // Free for second trip
"driver": {Amount: 6.0, Currency: "EUR"}, // 20km * 0.30€/km (passenger distance)
},
expectedError: false,
},
{
name: "Solidarity transport - Third trip - passenger should pay",
params: PricingParams{
MobilityType: "solidarity_transport",
Beneficiary: BeneficiaryParams{ Beneficiary: BeneficiaryParams{
History: 2, // Third trip History: 2, // Third trip
Priority: false, Priority: false,
}, },
SharedMobility: SharedMobilityParams{ SharedMobility: SharedMobilityParams{
PassengerDistance: 10, // 10km in meters PassengerDistance: 10, // 10km in meters
DriverDistance: 10, // 10km in meters DriverDistance: 12, // 12km in meters
}, },
}, },
expectedPrices: map[string]Price{ expectedPrices: map[string]Price{
"passenger": {Amount: 1.5, Currency: "EUR"}, // 10km * 0.15€/km "passenger": {Amount: 1.8, Currency: "EUR"}, // 12km * 0.15€/km (driver distance)
"driver": {Amount: 3.0, Currency: "EUR"}, // 10km * 0.30€/km "driver": {Amount: 3.6, Currency: "EUR"}, // 12km * 0.30€/km (driver distance)
}, },
expectedError: false, expectedError: false,
}, },
{ {
name: "Multiple trips - passenger pays normal rate", name: "Organized carpool - Third trip - passenger should pay",
params: PricingParams{ params: PricingParams{
MobilityType: "organized_carpool",
Beneficiary: BeneficiaryParams{
History: 2, // Third trip
Priority: false,
},
SharedMobility: SharedMobilityParams{
PassengerDistance: 10, // 10km in meters
DriverDistance: 12, // 12km in meters
},
},
expectedPrices: map[string]Price{
"passenger": {Amount: 1.5, Currency: "EUR"}, // 10km * 0.15€/km (passenger distance)
"driver": {Amount: 3.0, Currency: "EUR"}, // 10km * 0.30€/km (passenger distance)
},
expectedError: false,
},
{
name: "Solidarity transport - Multiple trips - passenger pays normal rate",
params: PricingParams{
MobilityType: "solidarity_transport",
Beneficiary: BeneficiaryParams{ Beneficiary: BeneficiaryParams{
History: 5, // Multiple trips History: 5, // Multiple trips
Priority: false, Priority: false,
@ -80,14 +141,34 @@ func TestMMS43Prices(t *testing.T) {
}, },
}, },
expectedPrices: map[string]Price{ expectedPrices: map[string]Price{
"passenger": {Amount: 4.5, Currency: "EUR"}, // 30km * 0.15€/km "passenger": {Amount: 5.25, Currency: "EUR"}, // 35km * 0.15€/km (driver distance)
"driver": {Amount: 10.5, Currency: "EUR"}, // 35km * 0.30€/km "driver": {Amount: 10.5, Currency: "EUR"}, // 35km * 0.30€/km (driver distance)
}, },
expectedError: false, expectedError: false,
}, },
{ {
name: "Zero distance - should return zero amounts", name: "Organized carpool - Multiple trips - passenger pays normal rate",
params: PricingParams{ params: PricingParams{
MobilityType: "organized_carpool",
Beneficiary: BeneficiaryParams{
History: 5, // Multiple trips
Priority: false,
},
SharedMobility: SharedMobilityParams{
PassengerDistance: 30, // 30km in meters
DriverDistance: 35, // 35km in meters
},
},
expectedPrices: map[string]Price{
"passenger": {Amount: 4.5, Currency: "EUR"}, // 30km * 0.15€/km (passenger distance)
"driver": {Amount: 9.0, Currency: "EUR"}, // 30km * 0.30€/km (passenger distance)
},
expectedError: false,
},
{
name: "Solidarity transport - Zero distance - should return zero amounts",
params: PricingParams{
MobilityType: "solidarity_transport",
Beneficiary: BeneficiaryParams{ Beneficiary: BeneficiaryParams{
History: 3, History: 3,
Priority: false, Priority: false,
@ -104,8 +185,9 @@ func TestMMS43Prices(t *testing.T) {
expectedError: false, expectedError: false,
}, },
{ {
name: "Large distances - check calculation accuracy", name: "Solidarity transport - Large distances - check calculation accuracy",
params: PricingParams{ params: PricingParams{
MobilityType: "solidarity_transport",
Beneficiary: BeneficiaryParams{ Beneficiary: BeneficiaryParams{
History: 10, History: 10,
Priority: false, Priority: false,
@ -116,14 +198,34 @@ func TestMMS43Prices(t *testing.T) {
}, },
}, },
expectedPrices: map[string]Price{ expectedPrices: map[string]Price{
"passenger": {Amount: 15.0, Currency: "EUR"}, // 100km * 0.15€/km "passenger": {Amount: 18.0, Currency: "EUR"}, // 120km * 0.15€/km (driver distance)
"driver": {Amount: 36.0, Currency: "EUR"}, // 120km * 0.30€/km "driver": {Amount: 36.0, Currency: "EUR"}, // 120km * 0.30€/km (driver distance)
}, },
expectedError: false, expectedError: false,
}, },
{ {
name: "Edge case - exactly 2 trips history (3rd trip)", name: "Organized carpool - Large distances - check calculation accuracy",
params: PricingParams{ params: PricingParams{
MobilityType: "organized_carpool",
Beneficiary: BeneficiaryParams{
History: 10,
Priority: false,
},
SharedMobility: SharedMobilityParams{
PassengerDistance: 100, // 100km in meters
DriverDistance: 120, // 120km in meters
},
},
expectedPrices: map[string]Price{
"passenger": {Amount: 15.0, Currency: "EUR"}, // 100km * 0.15€/km (passenger distance)
"driver": {Amount: 30.0, Currency: "EUR"}, // 100km * 0.30€/km (passenger distance)
},
expectedError: false,
},
{
name: "Solidarity transport - Edge case - exactly 2 trips history (3rd trip)",
params: PricingParams{
MobilityType: "solidarity_transport",
Beneficiary: BeneficiaryParams{ Beneficiary: BeneficiaryParams{
History: 2, // Exactly at the limit History: 2, // Exactly at the limit
Priority: false, Priority: false,
@ -134,14 +236,15 @@ func TestMMS43Prices(t *testing.T) {
}, },
}, },
expectedPrices: map[string]Price{ expectedPrices: map[string]Price{
"passenger": {Amount: 0.6, Currency: "EUR"}, // 4km * 0.15€/km (first paid trip) "passenger": {Amount: 0.75, Currency: "EUR"}, // 5km * 0.15€/km (first paid trip, driver distance)
"driver": {Amount: 1.5, Currency: "EUR"}, // 5km * 0.30€/km "driver": {Amount: 1.5, Currency: "EUR"}, // 5km * 0.30€/km (driver distance)
}, },
expectedError: false, expectedError: false,
}, },
{ {
name: "Small distances with fractions", name: "Solidarity transport - Small distances with fractions",
params: PricingParams{ params: PricingParams{
MobilityType: "solidarity_transport",
Beneficiary: BeneficiaryParams{ Beneficiary: BeneficiaryParams{
History: 3, History: 3,
Priority: false, Priority: false,
@ -152,8 +255,8 @@ func TestMMS43Prices(t *testing.T) {
}, },
}, },
expectedPrices: map[string]Price{ expectedPrices: map[string]Price{
"passenger": {Amount: 0.3, Currency: "EUR"}, // 2km * 0.15€/km "passenger": {Amount: 0.45, Currency: "EUR"}, // 3km * 0.15€/km (driver distance)
"driver": {Amount: 0.9, Currency: "EUR"}, // 3km * 0.30€/km "driver": {Amount: 0.9, Currency: "EUR"}, // 3km * 0.30€/km (driver distance)
}, },
expectedError: false, expectedError: false,
}, },
@ -183,8 +286,9 @@ func TestMMS43PricingEdgeCases(t *testing.T) {
pricingService, err := NewMMS43PricingService() pricingService, err := NewMMS43PricingService()
assert.NoError(t, err) assert.NoError(t, err)
t.Run("Negative history should still work", func(t *testing.T) { t.Run("Solidarity transport - Negative history should still work", func(t *testing.T) {
params := PricingParams{ params := PricingParams{
MobilityType: "solidarity_transport",
Beneficiary: BeneficiaryParams{ Beneficiary: BeneficiaryParams{
History: -1, // Edge case: negative history History: -1, // Edge case: negative history
}, },
@ -201,8 +305,28 @@ func TestMMS43PricingEdgeCases(t *testing.T) {
assert.InDelta(t, 3.0, prices["driver"].Amount, 0.001) assert.InDelta(t, 3.0, prices["driver"].Amount, 0.001)
}) })
t.Run("Very high history", func(t *testing.T) { t.Run("Organized carpool - Negative history should still work", func(t *testing.T) {
params := PricingParams{ params := PricingParams{
MobilityType: "organized_carpool",
Beneficiary: BeneficiaryParams{
History: -1, // Edge case: negative history
},
SharedMobility: SharedMobilityParams{
PassengerDistance: 10,
DriverDistance: 12,
},
}
prices, err := pricingService.Prices(params)
assert.NoError(t, err)
// Should still be free (2 - (-1) = 3 > 0)
assert.InDelta(t, 0.0, prices["passenger"].Amount, 0.001)
assert.InDelta(t, 3.0, prices["driver"].Amount, 0.001) // Uses passenger distance
})
t.Run("Solidarity transport - Very high history", func(t *testing.T) {
params := PricingParams{
MobilityType: "solidarity_transport",
Beneficiary: BeneficiaryParams{ Beneficiary: BeneficiaryParams{
History: 100, History: 100,
}, },
@ -218,5 +342,24 @@ func TestMMS43PricingEdgeCases(t *testing.T) {
assert.InDelta(t, 1.5, prices["passenger"].Amount, 0.001) assert.InDelta(t, 1.5, prices["passenger"].Amount, 0.001)
assert.InDelta(t, 3.0, prices["driver"].Amount, 0.001) assert.InDelta(t, 3.0, prices["driver"].Amount, 0.001)
}) })
t.Run("Organized carpool - Very high history", func(t *testing.T) {
params := PricingParams{
MobilityType: "organized_carpool",
Beneficiary: BeneficiaryParams{
History: 100,
},
SharedMobility: SharedMobilityParams{
PassengerDistance: 10,
DriverDistance: 12,
},
}
prices, err := pricingService.Prices(params)
assert.NoError(t, err)
// Should charge normal rate based on passenger distance
assert.InDelta(t, 1.5, prices["passenger"].Amount, 0.001) // 10km * 0.15
assert.InDelta(t, 3.0, prices["driver"].Amount, 0.001) // 10km * 0.30
})
} }