mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-11 18:16:24 +05:00
rounding methods and decimals for interval
This commit is contained in:
@@ -49,7 +49,7 @@ func TestApStoreRestoreJson(t *testing.T) {
|
||||
ap := &ActivationPeriod{ActivationTime: d}
|
||||
ap.AddInterval(i)
|
||||
result, _ := json.Marshal(ap)
|
||||
expected := "{\"ActivationTime\":\"2012-02-01T14:30:01Z\",\"Intervals\":[{\"Years\":null,\"Months\":[2],\"MonthDays\":[1],\"WeekDays\":[3,4],\"StartTime\":\"14:30:00\",\"EndTime\":\"15:00:00\",\"Weight\":0,\"ConnectFee\":0,\"Price\":0,\"PricedUnits\":0,\"RateIncrements\":0}]}"
|
||||
expected := "{\"ActivationTime\":\"2012-02-01T14:30:01Z\",\"Intervals\":[{\"Years\":null,\"Months\":[2],\"MonthDays\":[1],\"WeekDays\":[3,4],\"StartTime\":\"14:30:00\",\"EndTime\":\"15:00:00\",\"Weight\":0,\"ConnectFee\":0,\"Price\":0,\"PricedUnits\":0,\"RateIncrements\":0,\"RoundingMethod\":\"\",\"RoundingDecimals\":0}]}"
|
||||
if string(result) != expected {
|
||||
t.Errorf("Expected %q was %q", expected, result)
|
||||
}
|
||||
@@ -66,7 +66,7 @@ func TestApStoreRestoreBlank(t *testing.T) {
|
||||
ap := &ActivationPeriod{ActivationTime: d}
|
||||
ap.AddInterval(i)
|
||||
result, _ := json.Marshal(ap)
|
||||
expected := "{\"ActivationTime\":\"2012-02-01T14:30:01Z\",\"Intervals\":[{\"Years\":null,\"Months\":null,\"MonthDays\":null,\"WeekDays\":null,\"StartTime\":\"\",\"EndTime\":\"\",\"Weight\":0,\"ConnectFee\":0,\"Price\":0,\"PricedUnits\":0,\"RateIncrements\":0}]}"
|
||||
expected := "{\"ActivationTime\":\"2012-02-01T14:30:01Z\",\"Intervals\":[{\"Years\":null,\"Months\":null,\"MonthDays\":null,\"WeekDays\":null,\"StartTime\":\"\",\"EndTime\":\"\",\"Weight\":0,\"ConnectFee\":0,\"Price\":0,\"PricedUnits\":0,\"RateIncrements\":0,\"RoundingMethod\":\"\",\"RoundingDecimals\":0}]}"
|
||||
if string(result) != expected {
|
||||
t.Errorf("Expected %q was %q", expected, result)
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@ package rater
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/cgrates/cgrates/utils"
|
||||
"math"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -37,6 +39,8 @@ type Interval struct {
|
||||
WeekDays WeekDays
|
||||
StartTime, EndTime string // ##:##:## format
|
||||
Weight, ConnectFee, Price, PricedUnits, RateIncrements float64
|
||||
RoundingMethod string
|
||||
RoundingDecimals int
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -132,3 +136,13 @@ func (i *Interval) Equal(o *Interval) bool {
|
||||
i.StartTime == o.StartTime &&
|
||||
i.EndTime == o.EndTime
|
||||
}
|
||||
|
||||
func (i *Interval) GetCost(duration float64) (cost float64) {
|
||||
|
||||
if i.PricedUnits != 0 {
|
||||
cost = math.Ceil(duration/i.RateIncrements) * i.RateIncrements * (i.Price / i.PricedUnits)
|
||||
} else {
|
||||
cost = math.Ceil(duration/i.RateIncrements) * i.RateIncrements * i.Price
|
||||
}
|
||||
return utils.Round(cost, i.RoundingDecimals, i.RoundingMethod)
|
||||
}
|
||||
|
||||
@@ -424,13 +424,15 @@ func (i *Interval) Store() (result string, err error) {
|
||||
result += strconv.FormatFloat(i.ConnectFee, 'f', -1, 64) + ";"
|
||||
result += strconv.FormatFloat(i.Price, 'f', -1, 64) + ";"
|
||||
result += strconv.FormatFloat(i.PricedUnits, 'f', -1, 64) + ";"
|
||||
result += strconv.FormatFloat(i.RateIncrements, 'f', -1, 64)
|
||||
result += strconv.FormatFloat(i.RateIncrements, 'f', -1, 64) + ";"
|
||||
result += i.RoundingMethod + ";"
|
||||
result += strconv.Itoa(i.RoundingDecimals)
|
||||
return
|
||||
}
|
||||
|
||||
func (i *Interval) Restore(input string) error {
|
||||
is := strings.Split(input, ";")
|
||||
if len(is) != 11 {
|
||||
if len(is) != 13 {
|
||||
return notEnoughElements
|
||||
}
|
||||
if err := i.Years.Restore(is[0]); err != nil {
|
||||
@@ -452,6 +454,8 @@ func (i *Interval) Restore(input string) error {
|
||||
i.Price, _ = strconv.ParseFloat(is[8], 64)
|
||||
i.PricedUnits, _ = strconv.ParseFloat(is[9], 64)
|
||||
i.RateIncrements, _ = strconv.ParseFloat(is[10], 64)
|
||||
i.RoundingMethod = is[11]
|
||||
i.RoundingDecimals, _ = strconv.Atoi(is[12])
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ func TestSimpleMarshallerApStoreRestore(t *testing.T) {
|
||||
ap := &ActivationPeriod{ActivationTime: d}
|
||||
ap.AddInterval(i)
|
||||
result, err := ap.Store()
|
||||
expected := "1328106601000000000|;2;1;3,4;14:30:00;15:00:00;0;0;0;0;0"
|
||||
expected := "1328106601000000000|;2;1;3,4;14:30:00;15:00:00;0;0;0;0;0;;0"
|
||||
if err != nil || !reflect.DeepEqual(result, expected) {
|
||||
t.Errorf("Expected %q was %q", expected, result)
|
||||
}
|
||||
@@ -47,7 +47,7 @@ func TestSimpleMarshallerApStoreRestore(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSimpleMarshallerApRestoreFromString(t *testing.T) {
|
||||
s := "1325376000000000000|;1,2,3,4,5,6,7,8,9,10,11,12;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;1,2,3,4,5,6,0;00:00:00;;10;0;0.2;60;1\n"
|
||||
s := "1325376000000000000|;1,2,3,4,5,6,7,8,9,10,11,12;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;1,2,3,4,5,6,0;00:00:00;;10;0;0.2;60;1;;0\n"
|
||||
ap := &ActivationPeriod{}
|
||||
err := ap.Restore(s)
|
||||
if err != nil || len(ap.Intervals) != 1 {
|
||||
@@ -68,7 +68,7 @@ func TestRpStoreRestore(t *testing.T) {
|
||||
rp := &RatingProfile{FallbackKey: "test"}
|
||||
rp.AddActivationPeriodIfNotPresent("0723", ap)
|
||||
result, err := rp.Store()
|
||||
expected := "test>0723=1328106601000000000|;2;1;3,4;14:30:00;15:00:00;0;0;0;0;0"
|
||||
expected := "test>0723=1328106601000000000|;2;1;3,4;14:30:00;15:00:00;0;0;0;0;0;;0"
|
||||
if err != nil || !reflect.DeepEqual(result, expected) {
|
||||
t.Errorf("Expected %q was %q", expected, result)
|
||||
}
|
||||
@@ -101,7 +101,7 @@ func TestActionTimingStoreRestore(t *testing.T) {
|
||||
ActionsId: "Commando",
|
||||
}
|
||||
r, err := at.Store()
|
||||
if err != nil || r != "some uuid|test|one,two,three|;1,2,3,4,5,6,7,8,9,10,11,12;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;1,2,3,4,5;18:00:00;00:00:00;10;0;1;60;1|10|Commando" {
|
||||
if err != nil || r != "some uuid|test|one,two,three|;1,2,3,4,5,6,7,8,9,10,11,12;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;1,2,3,4,5;18:00:00;00:00:00;10;0;1;60;1;;0|10|Commando" {
|
||||
t.Errorf("Error serializing action timing: %v", string(r))
|
||||
}
|
||||
o := &ActionTiming{}
|
||||
@@ -146,7 +146,7 @@ func TestIntervalStoreRestore(t *testing.T) {
|
||||
RateIncrements: 1,
|
||||
}
|
||||
r, err := i.Store()
|
||||
if err != nil || r != ";1,2,3,4,5,6,7,8,9,10,11,12;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;1,2,3,4,5;18:00:00;00:00:00;10;0;1;60;1" {
|
||||
if err != nil || r != ";1,2,3,4,5,6,7,8,9,10,11,12;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;1,2,3,4,5;18:00:00;00:00:00;10;0;1;60;1;;0" {
|
||||
t.Errorf("Error serializing interval: %v", string(r))
|
||||
}
|
||||
o := &Interval{}
|
||||
@@ -157,7 +157,7 @@ func TestIntervalStoreRestore(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIntervalRestoreFromString(t *testing.T) {
|
||||
s := ";1,2,3,4,5,6,7,8,9,10,11,12;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;1,2,3,4,5,6,0;00:00:00;;10;0;0.2;60;1"
|
||||
s := ";1,2,3,4,5,6,7,8,9,10,11,12;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;1,2,3,4,5,6,0;00:00:00;;10;0;0.2;60;0;;1"
|
||||
i := Interval{}
|
||||
err := i.Restore(s)
|
||||
if err != nil || i.Price != 0.2 {
|
||||
|
||||
@@ -20,7 +20,6 @@ package rater
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -64,11 +63,7 @@ func (ts *TimeSpan) getCost(cd *CallDescriptor) (cost float64) {
|
||||
if i.RateIncrements == 0 {
|
||||
i.RateIncrements = 1
|
||||
}
|
||||
if i.PricedUnits != 0 {
|
||||
cost = math.Ceil(duration/i.RateIncrements) * i.RateIncrements * (i.Price / i.PricedUnits)
|
||||
} else {
|
||||
cost = math.Ceil(duration/i.RateIncrements) * i.RateIncrements * i.Price
|
||||
}
|
||||
cost = i.GetCost(duration)
|
||||
// if userBalance, err := cd.getUserBalance(); err == nil && userBalance != nil {
|
||||
// userBalance.mux.RLock()
|
||||
// if percentageDiscount, err := userBalance.getVolumeDiscount(cd.Destination, INBOUND); err == nil && percentageDiscount > 0 {
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"crypto/sha1"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
@@ -63,3 +64,33 @@ func GenUUID() string {
|
||||
|
||||
return hex.EncodeToString(uuid)
|
||||
}
|
||||
|
||||
// Round return rounded version of x with prec precision.
|
||||
//
|
||||
// Special cases are:
|
||||
// Round(±0) = ±0
|
||||
// Round(±Inf) = ±Inf
|
||||
// Round(NaN) = NaN
|
||||
func Round(x float64, prec int, method string) float64 {
|
||||
var rounder float64
|
||||
pow := math.Pow(10, float64(prec))
|
||||
intermed := x * pow
|
||||
_, frac := math.Modf(intermed)
|
||||
|
||||
switch method {
|
||||
case "*up":
|
||||
rounder = math.Ceil(intermed)
|
||||
case "*down":
|
||||
rounder = math.Floor(intermed)
|
||||
case "*middle":
|
||||
if frac >= 0.5 {
|
||||
rounder = math.Ceil(intermed)
|
||||
} else {
|
||||
rounder = math.Floor(intermed)
|
||||
}
|
||||
default:
|
||||
rounder = intermed
|
||||
}
|
||||
|
||||
return rounder / pow
|
||||
}
|
||||
|
||||
@@ -39,3 +39,83 @@ func TestUUID(t *testing.T) {
|
||||
t.Fatalf("GenUUID error %s", uuid)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRoundUp(t *testing.T) {
|
||||
result := Round(12.52, 0, "*middle")
|
||||
expected := 13.0
|
||||
if result != expected {
|
||||
t.Errorf("Error rounding up: sould be %v was %v", expected, result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRoundUpMiddle(t *testing.T) {
|
||||
result := Round(12.5, 0, "*middle")
|
||||
expected := 13.0
|
||||
if result != expected {
|
||||
t.Errorf("Error rounding up: sould be %v was %v", expected, result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRoundDown(t *testing.T) {
|
||||
result := Round(12.49, 0, "*middle")
|
||||
expected := 12.0
|
||||
if result != expected {
|
||||
t.Errorf("Error rounding up: sould be %v was %v", expected, result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRoundPrec(t *testing.T) {
|
||||
result := Round(12.49, 1, "*middle")
|
||||
expected := 12.5
|
||||
if result != expected {
|
||||
t.Errorf("Error rounding up: sould be %v was %v", expected, result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRoundPrecNothing(t *testing.T) {
|
||||
result := Round(12.49, 2, "*middle")
|
||||
expected := 12.49
|
||||
if result != expected {
|
||||
t.Errorf("Error rounding up: sould be %v was %v", expected, result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRoundPrecNoTouch(t *testing.T) {
|
||||
result := Round(12.49, 2, "")
|
||||
expected := 12.49
|
||||
if result != expected {
|
||||
t.Errorf("Error rounding up: sould be %v was %v", expected, result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRoundByMethodUp1(t *testing.T) {
|
||||
result := Round(12.49, 1, "*up")
|
||||
expected := 12.5
|
||||
if result != expected {
|
||||
t.Errorf("Error rounding up: sould be %v was %v", expected, result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRoundByMethodUp2(t *testing.T) {
|
||||
result := Round(12.21, 1, "*up")
|
||||
expected := 12.3
|
||||
if result != expected {
|
||||
t.Errorf("Error rounding up: sould be %v was %v", expected, result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRoundByMethodDown1(t *testing.T) {
|
||||
result := Round(12.49, 1, "*down")
|
||||
expected := 12.4
|
||||
if result != expected {
|
||||
t.Errorf("Error rounding up: sould be %v was %v", expected, result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRoundByMethodDown2(t *testing.T) {
|
||||
result := Round(12.21, 1, "*down")
|
||||
expected := 12.2
|
||||
if result != expected {
|
||||
t.Errorf("Error rounding up: sould be %v was %v", expected, result)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user