mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-11 18:16:24 +05:00
refactored rating plans structure
This commit is contained in:
@@ -47,7 +47,7 @@ func (cc *CallCost) Merge(other *CallCost) {
|
||||
}
|
||||
ts := cc.Timespans[len(cc.Timespans)-1]
|
||||
otherTs := other.Timespans[0]
|
||||
if reflect.DeepEqual(ts.ratingPlan, otherTs.ratingPlan) &&
|
||||
if reflect.DeepEqual(ts.ratingInfo, otherTs.ratingInfo) &&
|
||||
reflect.DeepEqual(ts.RateInterval, otherTs.RateInterval) {
|
||||
// extend the last timespan with
|
||||
ts.TimeEnd = ts.TimeEnd.Add(otherTs.GetDuration())
|
||||
|
||||
@@ -107,14 +107,14 @@ type CallDescriptor struct {
|
||||
CallDuration time.Duration // the call duration so far (till TimeEnd)
|
||||
Amount float64
|
||||
FallbackSubject string // the subject to check for destination if not found on primary subject
|
||||
RatingPlans []*RatingPlan
|
||||
RatingInfos []*RatingInfo
|
||||
Increments Increments
|
||||
userBalance *UserBalance
|
||||
}
|
||||
|
||||
// Adds an activation period that applyes to current call descriptor.
|
||||
func (cd *CallDescriptor) AddRatingPlan(aps ...*RatingPlan) {
|
||||
cd.RatingPlans = append(cd.RatingPlans, aps...)
|
||||
// Adds a rating plan that applyes to current call descriptor.
|
||||
func (cd *CallDescriptor) AddRatingInfo(ris ...*RatingInfo) {
|
||||
cd.RatingInfos = append(cd.RatingInfos, ris...)
|
||||
}
|
||||
|
||||
// Returns the key used to retrive the user balance involved in this call
|
||||
@@ -137,39 +137,41 @@ func (cd *CallDescriptor) getUserBalance() (ub *UserBalance, err error) {
|
||||
/*
|
||||
Restores the activation periods for the specified prefix from storage.
|
||||
*/
|
||||
func (cd *CallDescriptor) LoadRatingPlans() (destPrefix, matchedSubject string, err error) {
|
||||
func (cd *CallDescriptor) LoadRatingPlans() (destPrefixes []string, matchedSubject string, err error) {
|
||||
matchedSubject = cd.GetKey()
|
||||
if val, err := cache2go.GetXCached(cd.GetKey() + cd.Destination); err == nil {
|
||||
/*if val, err := cache2go.GetXCached(cd.GetKey() + cd.Destination); err == nil {
|
||||
xaps := val.(xCachedRatingPlans)
|
||||
cd.RatingPlans = xaps.aps
|
||||
return xaps.destPrefix, matchedSubject, nil
|
||||
}
|
||||
destPrefix, matchedSubject, values, err := cd.getRatingPlansForPrefix(cd.GetKey(), 1)
|
||||
}*/
|
||||
destPrefixes, matchedSubject, values, err := cd.getRatingPlansForPrefix(cd.GetKey(), 1)
|
||||
if err != nil {
|
||||
fallbackKey := fmt.Sprintf("%s:%s:%s:%s", cd.Direction, cd.Tenant, cd.TOR, FALLBACK_SUBJECT)
|
||||
// use the default subject
|
||||
destPrefix, matchedSubject, values, err = cd.getRatingPlansForPrefix(fallbackKey, 1)
|
||||
destPrefixes, matchedSubject, values, err = cd.getRatingPlansForPrefix(fallbackKey, 1)
|
||||
}
|
||||
//load the rating plans
|
||||
if err == nil && len(values) > 0 {
|
||||
xaps := xCachedRatingPlans{destPrefix, values, new(cache2go.XEntry)}
|
||||
xaps.XCache(cd.GetKey()+cd.Destination, debitPeriod+5*time.Second, xaps)
|
||||
cd.RatingPlans = values
|
||||
/*
|
||||
xaps := xCachedRatingPlans{destPrefix, values, new(cache2go.XEntry)}
|
||||
xaps.XCache(cd.GetKey()+cd.Destination, debitPeriod+5*time.Second, xaps)
|
||||
*/
|
||||
cd.RatingInfos = values
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (cd *CallDescriptor) getRatingPlansForPrefix(key string, recursionDepth int) (foundPrefix, matchedSubject string, aps []*RatingPlan, err error) {
|
||||
func (cd *CallDescriptor) getRatingPlansForPrefix(key string, recursionDepth int) (foundPrefixes []string, matchedSubject string, ris []*RatingInfo, err error) {
|
||||
matchedSubject = key
|
||||
if recursionDepth > RECURSION_MAX_DEPTH {
|
||||
err = errors.New("Max fallback recursion depth reached!" + key)
|
||||
return
|
||||
}
|
||||
rp, err := storageGetter.GetRatingProfile(key)
|
||||
if err != nil {
|
||||
return "", "", nil, err
|
||||
if err != nil || rp == nil {
|
||||
return nil, "", nil, err
|
||||
}
|
||||
foundPrefix, aps, err = rp.GetRatingPlansForPrefix(cd.Destination)
|
||||
foundPrefixes, ris, err = rp.GetRatingPlansForPrefix(cd)
|
||||
if err != nil {
|
||||
if rp.FallbackKey != "" {
|
||||
recursionDepth++
|
||||
@@ -200,15 +202,16 @@ func (cd *CallDescriptor) splitInTimeSpans(firstSpan *TimeSpan) (timespans []*Ti
|
||||
firstSpan = &TimeSpan{TimeStart: cd.TimeStart, TimeEnd: cd.TimeEnd, CallDuration: cd.CallDuration}
|
||||
}
|
||||
timespans = append(timespans, firstSpan)
|
||||
if len(cd.RatingPlans) == 0 {
|
||||
if len(cd.RatingInfos) == 0 {
|
||||
return
|
||||
}
|
||||
firstSpan.ratingPlan = cd.RatingPlans[0]
|
||||
// split on activation periods
|
||||
|
||||
firstSpan.ratingInfo = cd.RatingInfos[0]
|
||||
// split on rating plans
|
||||
afterStart, afterEnd := false, false //optimization for multiple activation periods
|
||||
for _, rp := range cd.RatingPlans {
|
||||
for _, rp := range cd.RatingInfos {
|
||||
if !afterStart && !afterEnd && rp.ActivationTime.Before(cd.TimeStart) {
|
||||
firstSpan.ratingPlan = rp
|
||||
firstSpan.ratingInfo = rp
|
||||
} else {
|
||||
afterStart = true
|
||||
for i := 0; i < len(timespans); i++ {
|
||||
@@ -227,7 +230,7 @@ func (cd *CallDescriptor) splitInTimeSpans(firstSpan *TimeSpan) (timespans []*Ti
|
||||
for i := 0; i < len(timespans); i++ {
|
||||
//log.Printf("==============%v==================", i)
|
||||
//log.Printf("TS: %+v", timespans[i])
|
||||
rp := timespans[i].ratingPlan
|
||||
rp := timespans[i].ratingInfo
|
||||
Logger.Debug(fmt.Sprintf("rp: %+v", rp))
|
||||
//timespans[i].RatingPlan = nil
|
||||
rp.RateIntervals.Sort()
|
||||
@@ -238,7 +241,7 @@ func (cd *CallDescriptor) splitInTimeSpans(firstSpan *TimeSpan) (timespans []*Ti
|
||||
}
|
||||
newTs := timespans[i].SplitByRateInterval(interval)
|
||||
if newTs != nil {
|
||||
newTs.ratingPlan = rp
|
||||
newTs.ratingInfo = rp
|
||||
// insert the new timespan
|
||||
index := i + 1
|
||||
timespans = append(timespans, nil)
|
||||
@@ -329,7 +332,7 @@ func (cd *CallDescriptor) GetCost() (*CallCost, error) {
|
||||
Tenant: cd.Tenant,
|
||||
Subject: matchedSubject[startIndex:],
|
||||
Account: cd.Account,
|
||||
Destination: destPrefix,
|
||||
Destination: strings.Join(destPrefix, ";"),
|
||||
Cost: cost,
|
||||
ConnectFee: connectionFee,
|
||||
Timespans: timespans}
|
||||
|
||||
@@ -71,7 +71,7 @@ func TestSplitSpans(t *testing.T) {
|
||||
cd.LoadRatingPlans()
|
||||
timespans := cd.splitInTimeSpans(nil)
|
||||
if len(timespans) != 2 {
|
||||
t.Log(cd.RatingPlans)
|
||||
t.Log(cd.RatingInfos)
|
||||
t.Error("Wrong number of timespans: ", len(timespans))
|
||||
}
|
||||
}
|
||||
@@ -82,9 +82,10 @@ func TestSplitSpansRoundToIncrements(t *testing.T) {
|
||||
cd := &CallDescriptor{Direction: "*out", TOR: "0", Tenant: "test", Subject: "trp", Destination: "0256", TimeStart: t1, TimeEnd: t2, CallDuration: 132 * time.Second}
|
||||
|
||||
cd.LoadRatingPlans()
|
||||
t.Logf("%+v", cd)
|
||||
timespans := cd.splitInTimeSpans(nil)
|
||||
if len(timespans) != 2 {
|
||||
t.Log(cd.RatingPlans)
|
||||
t.Log(cd.RatingInfos)
|
||||
t.Error("Wrong number of timespans: ", len(timespans))
|
||||
}
|
||||
var d time.Duration
|
||||
@@ -161,7 +162,7 @@ func TestGetCostRateGroups(t *testing.T) {
|
||||
t.Error("Error getting cost: ", err)
|
||||
}
|
||||
if result.Cost != 132 {
|
||||
t.Error("Error calculating cost: ", result.Timespans[0])
|
||||
t.Error("Error calculating cost: ", result.Timespans)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,19 +195,19 @@ func TestFullDestNotFound(t *testing.T) {
|
||||
result, _ := cd.GetCost()
|
||||
expected := &CallCost{Tenant: "vdf", Subject: "rif", Destination: "0256", Cost: 2700, ConnectFee: 1}
|
||||
if result.Cost != expected.Cost || result.ConnectFee != expected.ConnectFee {
|
||||
t.Log(cd.RatingPlans)
|
||||
t.Log(cd.RatingInfos)
|
||||
t.Errorf("Expected %v was %v", expected, result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubjectNotFound(t *testing.T) {
|
||||
t1 := time.Date(2012, time.February, 2, 17, 30, 0, 0, time.UTC)
|
||||
t2 := time.Date(2012, time.February, 2, 18, 30, 0, 0, time.UTC)
|
||||
t1 := time.Date(2013, time.February, 1, 17, 30, 0, 0, time.UTC)
|
||||
t2 := time.Date(2013, time.February, 1, 18, 30, 0, 0, time.UTC)
|
||||
cd := &CallDescriptor{Direction: "*out", TOR: "0", Tenant: "vdf", Subject: "not_exiting", Destination: "025740532", TimeStart: t1, TimeEnd: t2}
|
||||
result, _ := cd.GetCost()
|
||||
expected := &CallCost{Tenant: "vdf", Subject: "rif", Destination: "0257", Cost: 2700, ConnectFee: 1}
|
||||
if result.Cost != expected.Cost || result.ConnectFee != expected.ConnectFee {
|
||||
t.Log(cd.RatingPlans)
|
||||
t.Logf("%+v", result.Timespans[0].RateInterval)
|
||||
t.Errorf("Expected %v was %v", expected, result)
|
||||
}
|
||||
}
|
||||
@@ -267,7 +268,14 @@ func TestMinutesCost(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestMaxSessionTimeNoUserBalance(t *testing.T) {
|
||||
cd := &CallDescriptor{Direction: "*out", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0723", Amount: 1000}
|
||||
cd := &CallDescriptor{
|
||||
TimeStart: time.Date(2013, 10, 21, 18, 34, 0, 0, time.UTC),
|
||||
TimeEnd: time.Date(2013, 10, 21, 18, 35, 0, 0, time.UTC),
|
||||
Direction: "*out",
|
||||
TOR: "0",
|
||||
Tenant: "vdf",
|
||||
Subject: "rif",
|
||||
Destination: "0723", Amount: 1000}
|
||||
result, err := cd.GetMaxSessionTime(time.Now())
|
||||
if result != 1000 || err == nil {
|
||||
t.Errorf("Expected %v was %v (%v)", 1000, result, err)
|
||||
@@ -275,7 +283,15 @@ func TestMaxSessionTimeNoUserBalance(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestMaxSessionTimeWithUserBalance(t *testing.T) {
|
||||
cd := &CallDescriptor{Direction: "*out", TOR: "0", Tenant: "vdf", Subject: "minu", Destination: "0723", Amount: 1000}
|
||||
cd := &CallDescriptor{
|
||||
TimeStart: time.Date(2013, 10, 21, 18, 34, 0, 0, time.UTC),
|
||||
TimeEnd: time.Date(2013, 10, 21, 18, 35, 0, 0, time.UTC),
|
||||
Direction: "*out",
|
||||
TOR: "0",
|
||||
Tenant: "vdf",
|
||||
Subject: "minu",
|
||||
Destination: "0723",
|
||||
Amount: 1000}
|
||||
result, err := cd.GetMaxSessionTime(time.Now())
|
||||
expected := 300.0
|
||||
if result != expected || err != nil {
|
||||
@@ -284,7 +300,16 @@ func TestMaxSessionTimeWithUserBalance(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestMaxSessionTimeWithUserBalanceAccount(t *testing.T) {
|
||||
cd := &CallDescriptor{Direction: "*out", TOR: "0", Tenant: "vdf", Subject: "minu_from_tm", Account: "minu", Destination: "0723", Amount: 1000}
|
||||
cd := &CallDescriptor{
|
||||
TimeStart: time.Date(2013, 10, 21, 18, 34, 0, 0, time.UTC),
|
||||
TimeEnd: time.Date(2013, 10, 21, 18, 35, 0, 0, time.UTC),
|
||||
Direction: "*out",
|
||||
TOR: "0",
|
||||
Tenant: "vdf",
|
||||
Subject: "minu_from_tm",
|
||||
Account: "minu",
|
||||
Destination: "0723",
|
||||
Amount: 1000}
|
||||
result, err := cd.GetMaxSessionTime(time.Now())
|
||||
expected := 300.0
|
||||
if result != expected || err != nil {
|
||||
@@ -293,7 +318,15 @@ func TestMaxSessionTimeWithUserBalanceAccount(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestMaxSessionTimeNoCredit(t *testing.T) {
|
||||
cd := &CallDescriptor{Direction: "*out", TOR: "0", Tenant: "vdf", Subject: "broker", Destination: "0723", Amount: 5400}
|
||||
cd := &CallDescriptor{
|
||||
TimeStart: time.Date(2013, 10, 21, 18, 34, 0, 0, time.UTC),
|
||||
TimeEnd: time.Date(2013, 10, 21, 18, 35, 0, 0, time.UTC),
|
||||
Direction: "*out",
|
||||
TOR: "0",
|
||||
Tenant: "vdf",
|
||||
Subject: "broker",
|
||||
Destination: "0723",
|
||||
Amount: 5400}
|
||||
result, err := cd.GetMaxSessionTime(time.Now())
|
||||
if result != 100 || err != nil {
|
||||
t.Errorf("Expected %v was %v", 100, result)
|
||||
|
||||
@@ -30,19 +30,19 @@ import (
|
||||
)
|
||||
|
||||
type CSVReader struct {
|
||||
sep rune
|
||||
storage DataStorage
|
||||
readerFunc func(string, rune, int) (*csv.Reader, *os.File, error)
|
||||
actions map[string][]*Action
|
||||
actionsTimings map[string][]*ActionTiming
|
||||
actionsTriggers map[string][]*ActionTrigger
|
||||
accountActions []*UserBalance
|
||||
destinations []*Destination
|
||||
timings map[string]*Timing
|
||||
rates map[string][]*LoadRate
|
||||
destinationRates map[string][]*DestinationRate
|
||||
destinationRateTimings map[string][]*DestinationRateTiming
|
||||
ratingProfiles map[string]*RatingProfile
|
||||
sep rune
|
||||
storage DataStorage
|
||||
readerFunc func(string, rune, int) (*csv.Reader, *os.File, error)
|
||||
actions map[string][]*Action
|
||||
actionsTimings map[string][]*ActionTiming
|
||||
actionsTriggers map[string][]*ActionTrigger
|
||||
accountActions []*UserBalance
|
||||
destinations []*Destination
|
||||
timings map[string]*Timing
|
||||
rates map[string][]*LoadRate
|
||||
destinationRates map[string][]*DestinationRate
|
||||
ratingPlans map[string]*RatingPlan
|
||||
ratingProfiles map[string]*RatingProfile
|
||||
// file names
|
||||
destinationsFn, ratesFn, destinationratesFn, timingsFn, destinationratetimingsFn, ratingprofilesFn,
|
||||
actionsFn, actiontimingsFn, actiontriggersFn, accountactionsFn string
|
||||
@@ -58,7 +58,7 @@ func NewFileCSVReader(storage DataStorage, sep rune, destinationsFn, timingsFn,
|
||||
c.rates = make(map[string][]*LoadRate)
|
||||
c.destinationRates = make(map[string][]*DestinationRate)
|
||||
c.timings = make(map[string]*Timing)
|
||||
c.destinationRateTimings = make(map[string][]*DestinationRateTiming)
|
||||
c.ratingPlans = make(map[string]*RatingPlan)
|
||||
c.ratingProfiles = make(map[string]*RatingProfile)
|
||||
c.readerFunc = openFileCSVReader
|
||||
c.destinationsFn, c.timingsFn, c.ratesFn, c.destinationratesFn, c.destinationratetimingsFn, c.ratingprofilesFn,
|
||||
@@ -115,6 +115,18 @@ func (csvr *CSVReader) WriteToDatabase(flush, verbose bool) (err error) {
|
||||
log.Print(d.Id, " : ", d.Prefixes)
|
||||
}
|
||||
}
|
||||
if verbose {
|
||||
log.Print("Rating plans")
|
||||
}
|
||||
for _, rp := range csvr.ratingPlans {
|
||||
err = storage.SetRatingPlan(rp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if verbose {
|
||||
log.Print(rp.Id)
|
||||
}
|
||||
}
|
||||
if verbose {
|
||||
log.Print("Rating profiles")
|
||||
}
|
||||
@@ -298,8 +310,15 @@ func (csvr *CSVReader) LoadDestinationRateTimings() (err error) {
|
||||
if !exists {
|
||||
return errors.New(fmt.Sprintf("Could not find destination rate for tag %v", record[1]))
|
||||
}
|
||||
drt := NewDestinationRateTiming(drs, t, record[3])
|
||||
csvr.destinationRateTimings[tag] = append(csvr.destinationRateTimings[tag], drt)
|
||||
drt := NewDestinationRateTiming(t, record[3])
|
||||
plan, exists := csvr.ratingPlans[tag]
|
||||
if !exists {
|
||||
plan = &RatingPlan{Id: tag}
|
||||
csvr.ratingPlans[tag] = plan
|
||||
}
|
||||
for _, dr := range drs {
|
||||
plan.AddRateInterval(dr.DestinationsTag, drt.GetRateInterval(dr))
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -326,21 +345,11 @@ func (csvr *CSVReader) LoadRatingProfiles() (err error) {
|
||||
rp = &RatingProfile{Id: key}
|
||||
csvr.ratingProfiles[key] = rp
|
||||
}
|
||||
drts, exists := csvr.destinationRateTimings[record[5]]
|
||||
_, exists := csvr.ratingPlans[record[5]]
|
||||
if !exists {
|
||||
return errors.New(fmt.Sprintf("Could not load destination rate timings for tag: %v", record[5]))
|
||||
}
|
||||
|
||||
for _, drt := range drts {
|
||||
//log.Print("TAG: ", record[5])
|
||||
for _, dr := range drt.destinationRates {
|
||||
plan := &RatingPlan{ActivationTime: at}
|
||||
//log.Printf("RI: %+v", drt.GetRateInterval(dr))
|
||||
plan.AddRateInterval(drt.GetRateInterval(dr))
|
||||
rp.AddRatingPlanIfNotPresent(dr.DestinationsTag, plan)
|
||||
}
|
||||
}
|
||||
|
||||
rp.RatingPlanActivations = append(rp.RatingPlanActivations, &RatingPlanActivation{at, record[5]})
|
||||
if fallbacksubject != "" {
|
||||
for _, fbs := range strings.Split(fallbacksubject, ";") {
|
||||
newKey := fmt.Sprintf("%s:%s:%s:%s", direction, tenant, tor, fbs)
|
||||
@@ -351,6 +360,7 @@ func (csvr *CSVReader) LoadRatingProfiles() (err error) {
|
||||
}
|
||||
rp.FallbackKey = strings.TrimRight(rp.FallbackKey, ";")
|
||||
}
|
||||
csvr.ratingProfiles[rp.Id] = rp
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -434,180 +434,163 @@ func TestLoadDestinationRates(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestLoadDestinationRateTimings(t *testing.T) {
|
||||
if len(csvr.destinationRateTimings) != 5 {
|
||||
t.Error("Failed to load rate timings: ", csvr.destinationRateTimings)
|
||||
if len(csvr.ratingPlans) != 5 {
|
||||
t.Error("Failed to load rate timings: ", csvr.ratingPlans)
|
||||
}
|
||||
rplan := csvr.destinationRateTimings["STANDARD"]
|
||||
expected := []*DestinationRateTiming{
|
||||
&DestinationRateTiming{
|
||||
destinationRates: []*DestinationRate{
|
||||
&DestinationRate{
|
||||
Tag: "RT_STANDARD",
|
||||
DestinationsTag: "GERMANY",
|
||||
rates: []*LoadRate{
|
||||
&LoadRate{
|
||||
Tag: "R1",
|
||||
ConnectFee: 0,
|
||||
Price: 0.2,
|
||||
RateUnit: time.Minute,
|
||||
RateIncrement: time.Second,
|
||||
rplan := csvr.ratingPlans["STANDARD"]
|
||||
expected := &RatingPlan{
|
||||
Id: "STANDARD",
|
||||
DestinationRates: map[string]RateIntervalList{
|
||||
"GERMANY": RateIntervalList{
|
||||
&RateInterval{
|
||||
Years: Years{},
|
||||
Months: Months{},
|
||||
MonthDays: MonthDays{},
|
||||
WeekDays: WeekDays{1, 2, 3, 4, 5},
|
||||
StartTime: "00:00:00",
|
||||
EndTime: "",
|
||||
Weight: 10,
|
||||
ConnectFee: 0,
|
||||
Rates: RateGroups{
|
||||
&Rate{
|
||||
GroupIntervalStart: 0,
|
||||
RoundingMethod: "*middle",
|
||||
RoundingDecimals: 2,
|
||||
Value: 0.2,
|
||||
RateIncrement: time.Second,
|
||||
RateUnit: time.Minute,
|
||||
},
|
||||
},
|
||||
RoundingMethod: utils.ROUNDING_MIDDLE,
|
||||
RoundingDecimals: 2,
|
||||
},
|
||||
&DestinationRate{
|
||||
Tag: "RT_STANDARD",
|
||||
DestinationsTag: "GERMANY_O2",
|
||||
rates: []*LoadRate{
|
||||
&LoadRate{
|
||||
Tag: "R2",
|
||||
ConnectFee: 0,
|
||||
Price: 0.1,
|
||||
RateUnit: time.Minute,
|
||||
RateIncrement: time.Second,
|
||||
&RateInterval{
|
||||
Years: Years{},
|
||||
Months: Months{},
|
||||
MonthDays: MonthDays{},
|
||||
WeekDays: WeekDays{1, 2, 3, 4, 5},
|
||||
StartTime: "18:00:00",
|
||||
EndTime: "",
|
||||
Weight: 10,
|
||||
ConnectFee: 0,
|
||||
Rates: RateGroups{
|
||||
&Rate{
|
||||
GroupIntervalStart: 0,
|
||||
RoundingMethod: "*middle",
|
||||
RoundingDecimals: 2,
|
||||
Value: 0.1,
|
||||
RateIncrement: time.Second,
|
||||
RateUnit: time.Minute,
|
||||
},
|
||||
},
|
||||
RoundingMethod: utils.ROUNDING_MIDDLE,
|
||||
RoundingDecimals: 2,
|
||||
},
|
||||
&DestinationRate{
|
||||
Tag: "RT_STANDARD",
|
||||
DestinationsTag: "GERMANY_PREMIUM",
|
||||
rates: []*LoadRate{
|
||||
&LoadRate{
|
||||
Tag: "R2",
|
||||
ConnectFee: 0,
|
||||
Price: 0.1,
|
||||
RateUnit: time.Minute,
|
||||
RateIncrement: time.Second,
|
||||
&RateInterval{
|
||||
Years: Years{},
|
||||
Months: Months{},
|
||||
MonthDays: MonthDays{},
|
||||
WeekDays: WeekDays{6, 0},
|
||||
StartTime: "00:00:00",
|
||||
EndTime: "",
|
||||
Weight: 10,
|
||||
ConnectFee: 0,
|
||||
Rates: RateGroups{
|
||||
&Rate{
|
||||
GroupIntervalStart: 0,
|
||||
RoundingMethod: "*middle",
|
||||
RoundingDecimals: 2,
|
||||
Value: 0.1,
|
||||
RateIncrement: time.Second,
|
||||
RateUnit: time.Minute,
|
||||
},
|
||||
},
|
||||
RoundingMethod: utils.ROUNDING_MIDDLE,
|
||||
RoundingDecimals: 2,
|
||||
},
|
||||
},
|
||||
Weight: 10,
|
||||
timing: &Timing{
|
||||
Id: "WORKDAYS_00",
|
||||
Years: Years{},
|
||||
Months: Months{},
|
||||
MonthDays: MonthDays{},
|
||||
WeekDays: WeekDays{1, 2, 3, 4, 5},
|
||||
StartTime: "00:00:00"},
|
||||
},
|
||||
&DestinationRateTiming{
|
||||
destinationRates: []*DestinationRate{
|
||||
&DestinationRate{
|
||||
Tag: "RT_STD_WEEKEND",
|
||||
DestinationsTag: "GERMANY",
|
||||
rates: []*LoadRate{
|
||||
&LoadRate{
|
||||
Tag: "R2",
|
||||
ConnectFee: 0,
|
||||
Price: 0.1,
|
||||
RateUnit: time.Minute,
|
||||
RateIncrement: time.Second,
|
||||
"GERMANY_O2": RateIntervalList{
|
||||
&RateInterval{
|
||||
Years: Years{},
|
||||
Months: Months{},
|
||||
MonthDays: MonthDays{},
|
||||
WeekDays: WeekDays{1, 2, 3, 4, 5},
|
||||
StartTime: "00:00:00",
|
||||
EndTime: "",
|
||||
Weight: 10,
|
||||
ConnectFee: 0,
|
||||
Rates: RateGroups{
|
||||
&Rate{
|
||||
GroupIntervalStart: 0,
|
||||
RoundingMethod: "*middle",
|
||||
RoundingDecimals: 2,
|
||||
Value: 0.1,
|
||||
RateIncrement: time.Second,
|
||||
RateUnit: time.Minute,
|
||||
},
|
||||
},
|
||||
RoundingMethod: utils.ROUNDING_MIDDLE,
|
||||
RoundingDecimals: 2,
|
||||
},
|
||||
&DestinationRate{
|
||||
Tag: "RT_STD_WEEKEND",
|
||||
DestinationsTag: "GERMANY_O2",
|
||||
rates: []*LoadRate{
|
||||
&LoadRate{
|
||||
Tag: "R3",
|
||||
ConnectFee: 0,
|
||||
Price: 0.05,
|
||||
RateUnit: time.Minute,
|
||||
RateIncrement: time.Second,
|
||||
&RateInterval{
|
||||
Years: Years{},
|
||||
Months: Months{},
|
||||
MonthDays: MonthDays{},
|
||||
WeekDays: WeekDays{1, 2, 3, 4, 5},
|
||||
StartTime: "18:00:00",
|
||||
EndTime: "",
|
||||
Weight: 10,
|
||||
ConnectFee: 0,
|
||||
Rates: RateGroups{
|
||||
&Rate{
|
||||
GroupIntervalStart: 0,
|
||||
RoundingMethod: "*middle",
|
||||
RoundingDecimals: 2,
|
||||
Value: 0.05,
|
||||
RateIncrement: time.Second,
|
||||
RateUnit: time.Minute,
|
||||
},
|
||||
},
|
||||
RoundingMethod: utils.ROUNDING_MIDDLE,
|
||||
RoundingDecimals: 2,
|
||||
},
|
||||
&RateInterval{
|
||||
Years: Years{},
|
||||
Months: Months{},
|
||||
MonthDays: MonthDays{},
|
||||
WeekDays: WeekDays{6, 0},
|
||||
StartTime: "00:00:00",
|
||||
EndTime: "",
|
||||
Weight: 10,
|
||||
ConnectFee: 0,
|
||||
Rates: RateGroups{
|
||||
&Rate{
|
||||
GroupIntervalStart: 0,
|
||||
Value: 0.05,
|
||||
RateIncrement: time.Second,
|
||||
RateUnit: time.Minute,
|
||||
},
|
||||
},
|
||||
RoundingMethod: utils.ROUNDING_MIDDLE,
|
||||
RoundingDecimals: 2,
|
||||
},
|
||||
},
|
||||
Weight: 10,
|
||||
timing: &Timing{
|
||||
Id: "WORKDAYS_18",
|
||||
Years: Years{},
|
||||
Months: Months{},
|
||||
MonthDays: MonthDays{},
|
||||
WeekDays: WeekDays{1, 2, 3, 4, 5},
|
||||
StartTime: "18:00:00",
|
||||
},
|
||||
},
|
||||
&DestinationRateTiming{
|
||||
destinationRates: []*DestinationRate{
|
||||
&DestinationRate{
|
||||
Tag: "RT_STD_WEEKEND",
|
||||
DestinationsTag: "GERMANY",
|
||||
rates: []*LoadRate{
|
||||
&LoadRate{
|
||||
Tag: "R2",
|
||||
ConnectFee: 0,
|
||||
Price: 0.1,
|
||||
RateUnit: time.Minute,
|
||||
RateIncrement: time.Second,
|
||||
"GERMANY_PREMIUM": RateIntervalList{
|
||||
&RateInterval{
|
||||
Years: Years{},
|
||||
Months: Months{},
|
||||
MonthDays: MonthDays{},
|
||||
WeekDays: WeekDays{1, 2, 3, 4, 5},
|
||||
StartTime: "00:00:00",
|
||||
EndTime: "",
|
||||
Weight: 10,
|
||||
ConnectFee: 0,
|
||||
Rates: RateGroups{
|
||||
&Rate{
|
||||
GroupIntervalStart: 0,
|
||||
RoundingMethod: "*middle",
|
||||
RoundingDecimals: 2,
|
||||
Value: 0.1,
|
||||
RateIncrement: time.Second,
|
||||
RateUnit: time.Minute,
|
||||
},
|
||||
},
|
||||
RoundingMethod: utils.ROUNDING_MIDDLE,
|
||||
RoundingDecimals: 2,
|
||||
},
|
||||
&DestinationRate{
|
||||
Tag: "RT_STD_WEEKEND",
|
||||
DestinationsTag: "GERMANY_O2",
|
||||
rates: []*LoadRate{
|
||||
&LoadRate{
|
||||
Tag: "R3",
|
||||
ConnectFee: 0,
|
||||
Price: 0.05,
|
||||
RateUnit: time.Minute,
|
||||
RateIncrement: time.Second,
|
||||
GroupIntervalStart: 0,
|
||||
RoundingMethod: "*middle",
|
||||
RoundingDecimals: 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Weight: 10,
|
||||
timing: &Timing{
|
||||
Id: "WEEKENDS",
|
||||
Years: Years{},
|
||||
Months: Months{},
|
||||
MonthDays: MonthDays{},
|
||||
WeekDays: WeekDays{6, 0},
|
||||
StartTime: "00:00:00",
|
||||
},
|
||||
},
|
||||
}
|
||||
if !reflect.DeepEqual(rplan, expected) {
|
||||
t.Errorf("Error loading destination rate timing: %+v", rplan)
|
||||
}
|
||||
rplan = csvr.destinationRateTimings["TDRT"]
|
||||
expected = []*DestinationRateTiming{
|
||||
&DestinationRateTiming{
|
||||
destinationRates: csvr.destinationRates["T1"],
|
||||
Weight: 10,
|
||||
timing: csvr.timings["WORKDAYS_00"],
|
||||
},
|
||||
&DestinationRateTiming{
|
||||
destinationRates: csvr.destinationRates["T2"],
|
||||
Weight: 10,
|
||||
timing: csvr.timings["WORKDAYS_00"],
|
||||
},
|
||||
}
|
||||
if !reflect.DeepEqual(rplan, expected) {
|
||||
t.Errorf("Error loading destination rate timing: %+v", rplan[0])
|
||||
t.Errorf("Error loading destination rate timing: %+v", rplan.DestinationRates["GERMANY_PREMIUM"][0].Rates[0])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -619,206 +602,14 @@ func TestLoadRatingProfiles(t *testing.T) {
|
||||
expected := &RatingProfile{
|
||||
Id: "*out:test:0:trp",
|
||||
FallbackKey: "*out:test:0:rif;*out:test:0:danb",
|
||||
DestinationMap: map[string][]*RatingPlan{
|
||||
"NAT": []*RatingPlan{
|
||||
&RatingPlan{
|
||||
ActivationTime: time.Date(2013, time.October, 1, 0, 0, 0, 0, time.UTC),
|
||||
RateIntervals: []*RateInterval{
|
||||
&RateInterval{
|
||||
Years: Years{},
|
||||
Months: Months{},
|
||||
MonthDays: MonthDays{},
|
||||
WeekDays: WeekDays{1, 2, 3, 4, 5},
|
||||
StartTime: "00:00:00",
|
||||
EndTime: "",
|
||||
Weight: 10,
|
||||
ConnectFee: 0,
|
||||
Rates: RateGroups{
|
||||
&Rate{
|
||||
GroupIntervalStart: 0,
|
||||
Value: 1,
|
||||
RateIncrement: time.Minute,
|
||||
RateUnit: time.Second,
|
||||
},
|
||||
&Rate{
|
||||
GroupIntervalStart: time.Minute,
|
||||
Value: 1,
|
||||
RateIncrement: time.Second,
|
||||
RateUnit: time.Second,
|
||||
},
|
||||
},
|
||||
RoundingMethod: utils.ROUNDING_UP,
|
||||
RoundingDecimals: 4,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"GERMANY": []*RatingPlan{
|
||||
&RatingPlan{
|
||||
ActivationTime: time.Date(2013, time.October, 1, 0, 0, 0, 0, time.UTC),
|
||||
RateIntervals: []*RateInterval{
|
||||
&RateInterval{
|
||||
Years: Years{},
|
||||
Months: Months{},
|
||||
MonthDays: MonthDays{},
|
||||
WeekDays: WeekDays{1, 2, 3, 4, 5},
|
||||
StartTime: "00:00:00",
|
||||
EndTime: "",
|
||||
Weight: 10,
|
||||
ConnectFee: 0,
|
||||
Rates: RateGroups{
|
||||
&Rate{
|
||||
GroupIntervalStart: 0,
|
||||
Value: 7.77777,
|
||||
RateIncrement: time.Second,
|
||||
RateUnit: time.Second,
|
||||
},
|
||||
},
|
||||
RoundingMethod: utils.ROUNDING_UP,
|
||||
RoundingDecimals: 4,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"GERMANY_O2": []*RatingPlan{
|
||||
&RatingPlan{
|
||||
ActivationTime: time.Date(2013, time.October, 1, 0, 0, 0, 0, time.UTC),
|
||||
RateIntervals: []*RateInterval{
|
||||
&RateInterval{
|
||||
Years: Years{},
|
||||
Months: Months{},
|
||||
MonthDays: MonthDays{},
|
||||
WeekDays: WeekDays{1, 2, 3, 4, 5},
|
||||
StartTime: "00:00:00",
|
||||
EndTime: "",
|
||||
Weight: 10,
|
||||
ConnectFee: 0,
|
||||
Rates: RateGroups{
|
||||
&Rate{
|
||||
GroupIntervalStart: 0,
|
||||
Value: 1,
|
||||
RateIncrement: time.Second,
|
||||
RateUnit: time.Second,
|
||||
},
|
||||
},
|
||||
RoundingMethod: utils.ROUNDING_UP,
|
||||
RoundingDecimals: 4,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"GERMANY_PREMIUM": []*RatingPlan{
|
||||
&RatingPlan{
|
||||
ActivationTime: time.Date(2013, time.October, 1, 0, 0, 0, 0, time.UTC),
|
||||
RateIntervals: []*RateInterval{
|
||||
&RateInterval{
|
||||
Years: Years{},
|
||||
Months: Months{},
|
||||
MonthDays: MonthDays{},
|
||||
WeekDays: WeekDays{1, 2, 3, 4, 5},
|
||||
StartTime: "00:00:00",
|
||||
EndTime: "",
|
||||
Weight: 10,
|
||||
ConnectFee: 0,
|
||||
Rates: RateGroups{
|
||||
&Rate{
|
||||
GroupIntervalStart: 0,
|
||||
Value: 5.55555,
|
||||
RateIncrement: time.Second,
|
||||
RateUnit: time.Second,
|
||||
},
|
||||
},
|
||||
RoundingMethod: utils.ROUNDING_UP,
|
||||
RoundingDecimals: 4,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
RatingPlanActivations: RatingPlanActivations{&RatingPlanActivation{
|
||||
ActivationTime: time.Date(2013, 10, 1, 0, 0, 0, 0, time.UTC),
|
||||
RatingPlanId: "TDRT",
|
||||
}},
|
||||
}
|
||||
if !reflect.DeepEqual(rp, expected) {
|
||||
t.Errorf("Error loading rating profile: %+v", rp.FallbackKey)
|
||||
t.Errorf("Error loading rating profile: %+v", rp.RatingPlanActivations[0])
|
||||
}
|
||||
rp = csvr.ratingProfiles["*out:vdf:0:one"]
|
||||
expected = &RatingProfile{
|
||||
DestinationMap: map[string][]*RatingPlan{
|
||||
"GERMANY": []*RatingPlan{
|
||||
&RatingPlan{
|
||||
ActivationTime: time.Date(2012, time.February, 28, 0, 0, 0, 0, time.UTC),
|
||||
RateIntervals: []*RateInterval{
|
||||
&RateInterval{
|
||||
Years: Years{},
|
||||
Months: Months{},
|
||||
MonthDays: MonthDays{},
|
||||
WeekDays: WeekDays{1, 2, 3, 4, 5},
|
||||
StartTime: "00:00:00",
|
||||
EndTime: "",
|
||||
Weight: 10,
|
||||
ConnectFee: 0,
|
||||
Rates: RateGroups{
|
||||
&Rate{
|
||||
GroupIntervalStart: 0,
|
||||
Value: 0.2,
|
||||
RateIncrement: time.Second,
|
||||
RateUnit: time.Minute,
|
||||
},
|
||||
},
|
||||
RoundingMethod: utils.ROUNDING_MIDDLE,
|
||||
RoundingDecimals: 2,
|
||||
},
|
||||
&RateInterval{
|
||||
Years: Years{},
|
||||
Months: Months{},
|
||||
MonthDays: MonthDays{},
|
||||
WeekDays: WeekDays{1, 2, 3, 4, 5},
|
||||
StartTime: "18:00:00",
|
||||
EndTime: "",
|
||||
Weight: 10,
|
||||
ConnectFee: 0,
|
||||
Rates: RateGroups{
|
||||
&Rate{
|
||||
GroupIntervalStart: 0,
|
||||
Value: 0.1,
|
||||
RateIncrement: time.Second,
|
||||
RateUnit: time.Minute,
|
||||
},
|
||||
},
|
||||
RoundingMethod: utils.ROUNDING_MIDDLE,
|
||||
RoundingDecimals: 2,
|
||||
},
|
||||
&RateInterval{
|
||||
Years: Years{},
|
||||
Months: Months{},
|
||||
MonthDays: MonthDays{},
|
||||
WeekDays: WeekDays{6, 0},
|
||||
StartTime: "00:00:00",
|
||||
EndTime: "",
|
||||
Weight: 10,
|
||||
ConnectFee: 0,
|
||||
Rates: RateGroups{
|
||||
&Rate{
|
||||
GroupIntervalStart: 0,
|
||||
Value: 0.1,
|
||||
RateIncrement: time.Second,
|
||||
RateUnit: time.Minute,
|
||||
},
|
||||
},
|
||||
RoundingMethod: utils.ROUNDING_MIDDLE,
|
||||
RoundingDecimals: 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
if !reflect.DeepEqual(rp.DestinationMap["GERMANY"], expected.DestinationMap["GERMANY"]) {
|
||||
t.Errorf("Error loading rating profile: %+v", rp.DestinationMap["GERMANY"][0])
|
||||
}
|
||||
rp = csvr.ratingProfiles["*out:CUSTOMER_1:0:rif:from:tm"]
|
||||
if len(rp.DestinationMap["GERMANY"]) != 2 {
|
||||
t.Errorf("Failed to load rating profile %+v", rp.DestinationMap["GERMANY"][0].RateIntervals[0])
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestLoadActions(t *testing.T) {
|
||||
|
||||
@@ -26,19 +26,19 @@ import (
|
||||
)
|
||||
|
||||
type DbReader struct {
|
||||
tpid string
|
||||
storDb LoadStorage
|
||||
dataDb DataStorage
|
||||
actions map[string][]*Action
|
||||
actionsTimings map[string][]*ActionTiming
|
||||
actionsTriggers map[string][]*ActionTrigger
|
||||
accountActions []*UserBalance
|
||||
destinations []*Destination
|
||||
timings map[string]*Timing
|
||||
rates map[string][]*LoadRate
|
||||
destinationRates map[string][]*DestinationRate
|
||||
destinationRateTimings map[string][]*DestinationRateTiming
|
||||
ratingProfiles map[string]*RatingProfile
|
||||
tpid string
|
||||
storDb LoadStorage
|
||||
dataDb DataStorage
|
||||
actions map[string][]*Action
|
||||
actionsTimings map[string][]*ActionTiming
|
||||
actionsTriggers map[string][]*ActionTrigger
|
||||
accountActions []*UserBalance
|
||||
destinations []*Destination
|
||||
timings map[string]*Timing
|
||||
rates map[string][]*LoadRate
|
||||
destinationRates map[string][]*DestinationRate
|
||||
ratingPlans map[string]*RatingPlan
|
||||
ratingProfiles map[string]*RatingProfile
|
||||
}
|
||||
|
||||
func NewDbReader(storDB LoadStorage, storage DataStorage, tpid string) *DbReader {
|
||||
@@ -46,8 +46,9 @@ func NewDbReader(storDB LoadStorage, storage DataStorage, tpid string) *DbReader
|
||||
c.storDb = storDB
|
||||
c.dataDb = storage
|
||||
c.tpid = tpid
|
||||
c.destinationRateTimings = make(map[string][]*DestinationRateTiming)
|
||||
c.actionsTimings = make(map[string][]*ActionTiming)
|
||||
c.ratingPlans = make(map[string]*RatingPlan)
|
||||
c.ratingProfiles = make(map[string]*RatingProfile)
|
||||
return c
|
||||
}
|
||||
|
||||
@@ -68,6 +69,18 @@ func (dbr *DbReader) WriteToDatabase(flush, verbose bool) (err error) {
|
||||
log.Print(d.Id, " : ", d.Prefixes)
|
||||
}
|
||||
}
|
||||
if verbose {
|
||||
log.Print("Rating plans")
|
||||
}
|
||||
for _, rp := range dbr.ratingPlans {
|
||||
err = storage.SetRatingPlan(rp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if verbose {
|
||||
log.Print(rp.Id)
|
||||
}
|
||||
}
|
||||
if verbose {
|
||||
log.Print("Rating profiles")
|
||||
}
|
||||
@@ -176,8 +189,15 @@ func (dbr *DbReader) LoadDestinationRateTimings() error {
|
||||
if !exists {
|
||||
return errors.New(fmt.Sprintf("Could not find destination rate for tag %v", drt.DestinationRatesTag))
|
||||
}
|
||||
drt.destinationRates = drs
|
||||
dbr.destinationRateTimings[drt.Tag] = append(dbr.destinationRateTimings[drt.Tag], drt)
|
||||
|
||||
plan, exists := dbr.ratingPlans[drt.Tag]
|
||||
if !exists {
|
||||
plan = &RatingPlan{Id: drt.Tag}
|
||||
dbr.ratingPlans[drt.Tag] = plan
|
||||
}
|
||||
for _, dr := range drs {
|
||||
plan.AddRateInterval(dr.DestinationsTag, drt.GetRateInterval(dr))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -192,24 +212,18 @@ func (dbr *DbReader) LoadRatingProfiles() error {
|
||||
if err != nil {
|
||||
return errors.New(fmt.Sprintf("Cannot parse activation time from %v", rp.ActivationTime))
|
||||
}
|
||||
drts, exists := dbr.destinationRateTimings[rp.DestRatesTimingTag]
|
||||
_, exists := dbr.ratingPlans[rp.DestRatesTimingTag]
|
||||
if !exists {
|
||||
return errors.New(fmt.Sprintf("Could not load destination rate timings for tag: %v", rp.DestinationMap))
|
||||
}
|
||||
for _, drt := range drts {
|
||||
for _, dr := range drt.destinationRates {
|
||||
plan := &RatingPlan{ActivationTime: at}
|
||||
plan.AddRateInterval(drt.GetRateInterval(dr))
|
||||
rp.AddRatingPlanIfNotPresent(dr.DestinationsTag, plan)
|
||||
}
|
||||
return errors.New(fmt.Sprintf("Could not load destination rate timings for tag: %v", rp.DestRatesTimingTag))
|
||||
}
|
||||
rp.RatingPlanActivations = append(rp.RatingPlanActivations, &RatingPlanActivation{at, rp.DestRatesTimingTag})
|
||||
dbr.ratingProfiles[rp.Id] = rp
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dbr *DbReader) LoadRatingProfileByTag(tag string) error {
|
||||
activationPeriods := make(map[string]*RatingPlan)
|
||||
ratingPlans := make(map[string]*RatingPlan)
|
||||
resultRatingProfile := &RatingProfile{}
|
||||
rpm, err := dbr.storDb.GetTpRatingProfiles(dbr.tpid, tag)
|
||||
if err != nil || len(rpm) == 0 {
|
||||
@@ -219,7 +233,7 @@ func (dbr *DbReader) LoadRatingProfileByTag(tag string) error {
|
||||
Logger.Debug(fmt.Sprintf("Rating profile: %v", rpm))
|
||||
resultRatingProfile.FallbackKey = ratingProfile.FallbackKey // it will be the last fallback key
|
||||
resultRatingProfile.Id = ratingProfile.Id // idem
|
||||
at, err := utils.ParseDate(ratingProfile.ActivationTime)
|
||||
_, err := utils.ParseDate(ratingProfile.ActivationTime)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Cannot parse activation time from %v", ratingProfile.ActivationTime)
|
||||
}
|
||||
@@ -247,10 +261,10 @@ func (dbr *DbReader) LoadRatingProfileByTag(tag string) error {
|
||||
}
|
||||
Logger.Debug(fmt.Sprintf("Rate: %v", rpm))
|
||||
drate.rates = rt[drate.RateTag]
|
||||
if _, exists := activationPeriods[destrateTiming.Tag]; !exists {
|
||||
activationPeriods[destrateTiming.Tag] = &RatingPlan{}
|
||||
if _, exists := ratingPlans[destrateTiming.Tag]; !exists {
|
||||
ratingPlans[destrateTiming.Tag] = &RatingPlan{}
|
||||
}
|
||||
activationPeriods[destrateTiming.Tag].AddRateInterval(destrateTiming.GetRateInterval(drate))
|
||||
ratingPlans[destrateTiming.Tag].AddRateInterval(drate.DestinationsTag, destrateTiming.GetRateInterval(drate))
|
||||
dm, err := dbr.storDb.GetTpDestinations(dbr.tpid, drate.DestinationsTag)
|
||||
if err != nil || len(dm) == 0 {
|
||||
return fmt.Errorf("Could not get destination id %s: %v", drate.DestinationsTag, err)
|
||||
@@ -258,10 +272,10 @@ func (dbr *DbReader) LoadRatingProfileByTag(tag string) error {
|
||||
Logger.Debug(fmt.Sprintf("Tag: %s Destinations: %v", drate.DestinationsTag, dm))
|
||||
for _, destination := range dm {
|
||||
Logger.Debug(fmt.Sprintf("Destination: %v", rpm))
|
||||
ap := activationPeriods[ratingProfile.DestRatesTimingTag]
|
||||
newAP := &RatingPlan{ActivationTime: at}
|
||||
newAP.RateIntervals = append(newAP.RateIntervals, ap.RateIntervals...)
|
||||
resultRatingProfile.AddRatingPlanIfNotPresent(destination.Id, newAP)
|
||||
//ap := ratingPlans[ratingProfile.DestRatesTimingTag]
|
||||
//newAP := &RatingPlan{ActivationTime: at}
|
||||
//newAP.RateIntervals = append(newAP.RateIntervals, ap.RateIntervals...)
|
||||
//resultRatingProfile.AddRatingPlanIfNotPresent(destination.Id, newAP)
|
||||
dbr.dataDb.SetDestination(destination)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,22 +143,20 @@ func NewTiming(timingInfo ...string) (rt *Timing) {
|
||||
type DestinationRateTiming struct {
|
||||
Tag string
|
||||
DestinationRatesTag string // intermediary used when loading from db
|
||||
destinationRates []*DestinationRate
|
||||
Weight float64
|
||||
TimingTag string // intermediary used when loading from db
|
||||
timing *Timing
|
||||
}
|
||||
|
||||
func NewDestinationRateTiming(destinationRates []*DestinationRate, timing *Timing, weight string) (drt *DestinationRateTiming) {
|
||||
func NewDestinationRateTiming(timing *Timing, weight string) (drt *DestinationRateTiming) {
|
||||
w, err := strconv.ParseFloat(weight, 64)
|
||||
if err != nil {
|
||||
log.Printf("Error parsing weight unit from: %v", weight)
|
||||
return
|
||||
}
|
||||
drt = &DestinationRateTiming{
|
||||
destinationRates: destinationRates,
|
||||
timing: timing,
|
||||
Weight: w,
|
||||
timing: timing,
|
||||
Weight: w,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -18,43 +18,42 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
package engine
|
||||
|
||||
import (
|
||||
"github.com/cgrates/cgrates/cache2go"
|
||||
"time"
|
||||
)
|
||||
|
||||
/*
|
||||
The struture that is saved to storage.
|
||||
*/
|
||||
type RatingPlan struct {
|
||||
ActivationTime time.Time
|
||||
RateIntervals RateIntervalList
|
||||
Id string
|
||||
DestinationRates map[string]RateIntervalList
|
||||
}
|
||||
|
||||
type xCachedRatingPlans struct {
|
||||
destPrefix string
|
||||
aps []*RatingPlan
|
||||
/*
|
||||
type xCachedRatingPlan struct {
|
||||
rp *RatingPlan
|
||||
*cache2go.XEntry
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
Adds one ore more intervals to the internal interval list only if it is not allready in the list.
|
||||
*/
|
||||
func (rp *RatingPlan) AddRateInterval(ris ...*RateInterval) {
|
||||
func (rp *RatingPlan) AddRateInterval(dId string, ris ...*RateInterval) {
|
||||
if rp.DestinationRates == nil {
|
||||
rp.DestinationRates = make(map[string]RateIntervalList, 1)
|
||||
}
|
||||
for _, ri := range ris {
|
||||
found := false
|
||||
for _, eri := range rp.RateIntervals {
|
||||
for _, eri := range rp.DestinationRates[dId] {
|
||||
if ri.Equal(eri) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
rp.RateIntervals = append(rp.RateIntervals, ri)
|
||||
rp.DestinationRates[dId] = append(rp.DestinationRates[dId], ri)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (rp *RatingPlan) Equal(o *RatingPlan) bool {
|
||||
return rp.ActivationTime == o.ActivationTime
|
||||
return rp.Id == o.Id
|
||||
}
|
||||
|
||||
@@ -27,26 +27,27 @@ import (
|
||||
|
||||
func TestApRestoreFromStorage(t *testing.T) {
|
||||
cd := &CallDescriptor{
|
||||
TimeStart: time.Date(2013, 10, 21, 18, 34, 0, 0, time.UTC),
|
||||
TimeEnd: time.Date(2013, 10, 21, 18, 35, 0, 0, time.UTC),
|
||||
Direction: OUTBOUND,
|
||||
TOR: "0",
|
||||
Tenant: "CUSTOMER_1",
|
||||
Subject: "rif:from:tm",
|
||||
Destination: "49"}
|
||||
cd.LoadRatingPlans()
|
||||
if len(cd.RatingPlans) != 2 {
|
||||
t.Error("Error restoring activation periods: ", cd.RatingPlans[0])
|
||||
if len(cd.RatingInfos) != 2 {
|
||||
t.Error("Error restoring activation periods: ", cd.RatingInfos)
|
||||
}
|
||||
}
|
||||
|
||||
func TestApStoreRestoreJson(t *testing.T) {
|
||||
d := time.Date(2012, time.February, 1, 14, 30, 1, 0, time.UTC)
|
||||
i := &RateInterval{Months: []time.Month{time.February},
|
||||
MonthDays: []int{1},
|
||||
WeekDays: []time.Weekday{time.Wednesday, time.Thursday},
|
||||
StartTime: "14:30:00",
|
||||
EndTime: "15:00:00"}
|
||||
ap := &RatingPlan{ActivationTime: d}
|
||||
ap.AddRateInterval(i)
|
||||
ap := &RatingPlan{Id: "test"}
|
||||
ap.AddRateInterval("NAT", i)
|
||||
result, _ := json.Marshal(ap)
|
||||
ap1 := &RatingPlan{}
|
||||
json.Unmarshal(result, ap1)
|
||||
@@ -56,10 +57,9 @@ func TestApStoreRestoreJson(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestApStoreRestoreBlank(t *testing.T) {
|
||||
d := time.Date(2012, time.February, 1, 14, 30, 1, 0, time.UTC)
|
||||
i := &RateInterval{}
|
||||
ap := &RatingPlan{ActivationTime: d}
|
||||
ap.AddRateInterval(i)
|
||||
ap := &RatingPlan{Id: "test"}
|
||||
ap.AddRateInterval("NAT", i)
|
||||
result, _ := json.Marshal(ap)
|
||||
ap1 := RatingPlan{}
|
||||
json.Unmarshal(result, &ap1)
|
||||
@@ -69,50 +69,78 @@ func TestApStoreRestoreBlank(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestFallbackDirect(t *testing.T) {
|
||||
cd := &CallDescriptor{TOR: "0", Direction: OUTBOUND, Tenant: "CUSTOMER_2", Subject: "danb:87.139.12.167", Destination: "41"}
|
||||
cd := &CallDescriptor{
|
||||
TimeStart: time.Date(2013, 10, 21, 18, 34, 0, 0, time.UTC),
|
||||
TimeEnd: time.Date(2013, 10, 21, 18, 35, 0, 0, time.UTC),
|
||||
TOR: "0",
|
||||
Direction: OUTBOUND,
|
||||
Tenant: "CUSTOMER_2",
|
||||
Subject: "danb:87.139.12.167",
|
||||
Destination: "41"}
|
||||
cd.LoadRatingPlans()
|
||||
if len(cd.RatingPlans) != 1 {
|
||||
t.Error("Error restoring activation periods: ", len(cd.RatingPlans))
|
||||
if len(cd.RatingInfos) != 1 {
|
||||
t.Error("Error restoring activation periods: ", len(cd.RatingInfos))
|
||||
}
|
||||
}
|
||||
|
||||
func TestFallbackMultiple(t *testing.T) {
|
||||
cd := &CallDescriptor{TOR: "0", Direction: OUTBOUND, Tenant: "vdf", Subject: "fall", Destination: "0723045"}
|
||||
cd := &CallDescriptor{
|
||||
TimeStart: time.Date(2013, 10, 21, 18, 34, 0, 0, time.UTC),
|
||||
TimeEnd: time.Date(2013, 10, 21, 18, 35, 0, 0, time.UTC),
|
||||
TOR: "0",
|
||||
Direction: OUTBOUND,
|
||||
Tenant: "vdf",
|
||||
Subject: "fall",
|
||||
Destination: "0723045"}
|
||||
cd.LoadRatingPlans()
|
||||
if len(cd.RatingPlans) != 2 {
|
||||
t.Errorf("Error restoring rating plans: %+v", cd.RatingPlans)
|
||||
if len(cd.RatingInfos) != 2 {
|
||||
t.Errorf("Error restoring rating plans: %+v", cd.RatingInfos)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFallbackWithBackTrace(t *testing.T) {
|
||||
cd := &CallDescriptor{TOR: "0", Direction: OUTBOUND, Tenant: "CUSTOMER_2", Subject: "danb:87.139.12.167", Destination: "4123"}
|
||||
cd := &CallDescriptor{
|
||||
TimeStart: time.Date(2013, 10, 21, 18, 34, 0, 0, time.UTC),
|
||||
TimeEnd: time.Date(2013, 10, 21, 18, 35, 0, 0, time.UTC),
|
||||
TOR: "0",
|
||||
Direction: OUTBOUND,
|
||||
Tenant: "CUSTOMER_2",
|
||||
Subject: "danb:87.139.12.167",
|
||||
Destination: "4123"}
|
||||
cd.LoadRatingPlans()
|
||||
if len(cd.RatingPlans) != 1 {
|
||||
t.Error("Error restoring activation periods: ", len(cd.RatingPlans))
|
||||
if len(cd.RatingInfos) != 1 {
|
||||
t.Error("Error restoring activation periods: ", len(cd.RatingInfos))
|
||||
}
|
||||
}
|
||||
|
||||
func TestFallbackDefault(t *testing.T) {
|
||||
cd := &CallDescriptor{TOR: "0", Direction: OUTBOUND, Tenant: "vdf", Subject: "one", Destination: "0723"}
|
||||
cd := &CallDescriptor{
|
||||
TimeStart: time.Date(2013, 10, 21, 18, 34, 0, 0, time.UTC),
|
||||
TimeEnd: time.Date(2013, 10, 21, 18, 35, 0, 0, time.UTC),
|
||||
TOR: "0",
|
||||
Direction: OUTBOUND,
|
||||
Tenant: "vdf",
|
||||
Subject: "one",
|
||||
Destination: "0723"}
|
||||
cd.LoadRatingPlans()
|
||||
if len(cd.RatingPlans) != 1 {
|
||||
t.Error("Error restoring activation periods: ", len(cd.RatingPlans))
|
||||
if len(cd.RatingInfos) != 1 {
|
||||
t.Error("Error restoring activation periods: ", len(cd.RatingInfos))
|
||||
}
|
||||
}
|
||||
|
||||
func TestFallbackNoInfiniteLoop(t *testing.T) {
|
||||
cd := &CallDescriptor{TOR: "0", Direction: OUTBOUND, Tenant: "vdf", Subject: "rif", Destination: "0721"}
|
||||
cd.LoadRatingPlans()
|
||||
if len(cd.RatingPlans) != 0 {
|
||||
t.Error("Error restoring activation periods: ", len(cd.RatingPlans))
|
||||
if len(cd.RatingInfos) != 0 {
|
||||
t.Error("Error restoring activation periods: ", len(cd.RatingInfos))
|
||||
}
|
||||
}
|
||||
|
||||
func TestFallbackNoInfiniteLoopSelf(t *testing.T) {
|
||||
cd := &CallDescriptor{TOR: "0", Direction: OUTBOUND, Tenant: "vdf", Subject: "inf", Destination: "0721"}
|
||||
cd.LoadRatingPlans()
|
||||
if len(cd.RatingPlans) != 0 {
|
||||
t.Error("Error restoring activation periods: ", len(cd.RatingPlans))
|
||||
if len(cd.RatingInfos) != 0 {
|
||||
t.Error("Error restoring activation periods: ", len(cd.RatingInfos))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,15 +160,15 @@ func TestApAddIntervalIfNotPresent(t *testing.T) {
|
||||
WeekDays: []time.Weekday{time.Wednesday},
|
||||
StartTime: "14:30:00",
|
||||
EndTime: "15:00:00"}
|
||||
ap := &RatingPlan{}
|
||||
ap.AddRateInterval(i1)
|
||||
ap.AddRateInterval(i2)
|
||||
if len(ap.RateIntervals) != 1 {
|
||||
t.Error("Wronfully appended interval ;)")
|
||||
rp := &RatingPlan{}
|
||||
rp.AddRateInterval("NAT", i1)
|
||||
rp.AddRateInterval("NAT", i2)
|
||||
if len(rp.DestinationRates) != 1 {
|
||||
t.Error("Wronfullyrppended interval ;)")
|
||||
}
|
||||
ap.AddRateInterval(i3)
|
||||
if len(ap.RateIntervals) != 2 {
|
||||
t.Error("Wronfully not appended interval ;)")
|
||||
rp.AddRateInterval("NAT", i3)
|
||||
if len(rp.DestinationRates["NAT"]) != 2 {
|
||||
t.Error("Wronfully not appended interval ;)", rp.DestinationRates)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,14 +183,14 @@ func TestApAddRateIntervalGroups(t *testing.T) {
|
||||
Rates: RateGroups{&Rate{30 * time.Second, 2, 1 * time.Second, 1 * time.Second}},
|
||||
}
|
||||
ap := &RatingPlan{}
|
||||
ap.AddRateInterval(i1)
|
||||
ap.AddRateInterval(i2)
|
||||
ap.AddRateInterval(i3)
|
||||
if len(ap.RateIntervals) != 1 {
|
||||
ap.AddRateInterval("NAT", i1)
|
||||
ap.AddRateInterval("NAT", i2)
|
||||
ap.AddRateInterval("NAT", i3)
|
||||
if len(ap.DestinationRates) != 1 {
|
||||
t.Error("Wronfully appended interval ;)")
|
||||
}
|
||||
if len(ap.RateIntervals[0].Rates) != 1 {
|
||||
t.Errorf("Group prices not formed: %#v", ap.RateIntervals[0].Rates[0])
|
||||
if len(ap.DestinationRates["NAT"][0].Rates) != 1 {
|
||||
t.Errorf("Group prices not formed: %#v", ap.DestinationRates["NAT"][0].Rates[0])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,14 +198,13 @@ func TestApAddRateIntervalGroups(t *testing.T) {
|
||||
|
||||
func BenchmarkRatingPlanStoreRestoreJson(b *testing.B) {
|
||||
b.StopTimer()
|
||||
d := time.Date(2012, time.February, 1, 14, 30, 1, 0, time.UTC)
|
||||
i := &RateInterval{Months: []time.Month{time.February},
|
||||
MonthDays: []int{1},
|
||||
WeekDays: []time.Weekday{time.Wednesday, time.Thursday},
|
||||
StartTime: "14:30:00",
|
||||
EndTime: "15:00:00"}
|
||||
ap := &RatingPlan{ActivationTime: d}
|
||||
ap.AddRateInterval(i)
|
||||
ap := &RatingPlan{Id: "test"}
|
||||
ap.AddRateInterval("NAT", i)
|
||||
|
||||
ap1 := RatingPlan{}
|
||||
b.StartTimer()
|
||||
@@ -189,14 +216,13 @@ func BenchmarkRatingPlanStoreRestoreJson(b *testing.B) {
|
||||
|
||||
func BenchmarkRatingPlanStoreRestore(b *testing.B) {
|
||||
b.StopTimer()
|
||||
d := time.Date(2012, time.February, 1, 14, 30, 1, 0, time.UTC)
|
||||
i := &RateInterval{Months: []time.Month{time.February},
|
||||
MonthDays: []int{1},
|
||||
WeekDays: []time.Weekday{time.Wednesday, time.Thursday},
|
||||
StartTime: "14:30:00",
|
||||
EndTime: "15:00:00"}
|
||||
ap := &RatingPlan{ActivationTime: d}
|
||||
ap.AddRateInterval(i)
|
||||
ap := &RatingPlan{Id: "test"}
|
||||
ap.AddRateInterval("NAT", i)
|
||||
|
||||
ap1 := &RatingPlan{}
|
||||
b.StartTimer()
|
||||
|
||||
@@ -21,52 +21,94 @@ package engine
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/cgrates/cgrates/cache2go"
|
||||
"sort"
|
||||
"time"
|
||||
)
|
||||
|
||||
type RatingProfile struct {
|
||||
Id string
|
||||
FallbackKey string // FallbackKey is used as complete combination of Tenant:TOR:Direction:Subject
|
||||
DestinationMap map[string][]*RatingPlan
|
||||
Tag, Tenant, TOR, Direction, Subject, DestRatesTimingTag, RatesFallbackSubject, ActivationTime string // used only for loading
|
||||
Id string
|
||||
FallbackKey string // FallbackKey is used as complete combination of Tenant:TOR:Direction:Subject
|
||||
RatingPlanActivations RatingPlanActivations
|
||||
Tag, Tenant, TOR, Direction, Subject string // used only for loading
|
||||
DestRatesTimingTag, RatesFallbackSubject, ActivationTime string // used only for loading
|
||||
}
|
||||
|
||||
// Adds an activation period that applyes to current rating profile if not already present.
|
||||
func (rp *RatingProfile) AddRatingPlanIfNotPresent(destInfo string, plans ...*RatingPlan) {
|
||||
if rp.DestinationMap == nil {
|
||||
rp.DestinationMap = make(map[string][]*RatingPlan, 1)
|
||||
type RatingPlanActivation struct {
|
||||
ActivationTime time.Time
|
||||
RatingPlanId string
|
||||
}
|
||||
|
||||
func (rpa *RatingPlanActivation) GetRatingPlan() (rp *RatingPlan, err error) {
|
||||
if x, err := cache2go.GetCached(rpa.RatingPlanId); err != nil {
|
||||
rp, err = storageGetter.GetRatingPlan(rpa.RatingPlanId)
|
||||
if err == nil && rp != nil {
|
||||
cache2go.Cache(rpa.RatingPlanId, rp)
|
||||
}
|
||||
} else {
|
||||
rp = x.(*RatingPlan)
|
||||
}
|
||||
for _, plan := range plans {
|
||||
found := false
|
||||
for _, existingPlan := range rp.DestinationMap[destInfo] {
|
||||
if plan.Equal(existingPlan) {
|
||||
existingPlan.AddRateInterval(plan.RateIntervals...)
|
||||
found = true
|
||||
break
|
||||
return
|
||||
}
|
||||
|
||||
func (rpa *RatingPlanActivation) Equal(orpa *RatingPlanActivation) bool {
|
||||
return rpa.ActivationTime == orpa.ActivationTime && rpa.RatingPlanId == orpa.RatingPlanId
|
||||
}
|
||||
|
||||
type RatingPlanActivations []*RatingPlanActivation
|
||||
|
||||
func (rpas RatingPlanActivations) Len() int {
|
||||
return len(rpas)
|
||||
}
|
||||
|
||||
func (rpas RatingPlanActivations) Swap(i, j int) {
|
||||
rpas[i], rpas[j] = rpas[j], rpas[i]
|
||||
}
|
||||
|
||||
func (rpas RatingPlanActivations) Less(i, j int) bool {
|
||||
return rpas[i].ActivationTime.Before(rpas[j].ActivationTime)
|
||||
}
|
||||
|
||||
func (rpas RatingPlanActivations) Sort() {
|
||||
sort.Sort(rpas)
|
||||
}
|
||||
|
||||
type RatingInfo struct {
|
||||
ActivationTime time.Time
|
||||
RateIntervals RateIntervalList
|
||||
}
|
||||
|
||||
func (rp *RatingProfile) GetRatingPlansForPrefix(cd *CallDescriptor) (foundPrefixes []string, ris []*RatingInfo, err error) {
|
||||
rp.RatingPlanActivations.Sort()
|
||||
for _, rpa := range rp.RatingPlanActivations {
|
||||
if rpa.ActivationTime.Before(cd.TimeEnd) {
|
||||
rpl, err := rpa.GetRatingPlan()
|
||||
if err != nil || rpl == nil {
|
||||
Logger.Err(fmt.Sprintf("Error checking destination: %v", err))
|
||||
continue
|
||||
}
|
||||
bestPrecision := 0
|
||||
var rps RateIntervalList
|
||||
for dId, rpls := range rpl.DestinationRates {
|
||||
precision, err := storageGetter.DestinationContainsPrefix(dId, cd.Destination)
|
||||
if err != nil {
|
||||
Logger.Err(fmt.Sprintf("Error checking destination: %v", err))
|
||||
continue
|
||||
}
|
||||
if precision > bestPrecision {
|
||||
bestPrecision = precision
|
||||
rps = rpls
|
||||
}
|
||||
}
|
||||
if bestPrecision > 0 {
|
||||
ris = append(ris, &RatingInfo{rpa.ActivationTime, rps})
|
||||
foundPrefixes = append(foundPrefixes, cd.Destination[:bestPrecision])
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
rp.DestinationMap[destInfo] = append(rp.DestinationMap[destInfo], plan)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (rp *RatingProfile) GetRatingPlansForPrefix(destPrefix string) (foundPrefix string, aps []*RatingPlan, err error) {
|
||||
bestPrecision := 0
|
||||
for dId, v := range rp.DestinationMap {
|
||||
precision, err := storageGetter.DestinationContainsPrefix(dId, destPrefix)
|
||||
if err != nil {
|
||||
Logger.Err(fmt.Sprintf("Error checking destination: %v", err))
|
||||
continue
|
||||
}
|
||||
if precision > bestPrecision {
|
||||
bestPrecision = precision
|
||||
aps = v
|
||||
}
|
||||
}
|
||||
|
||||
if bestPrecision > 0 {
|
||||
return destPrefix[:bestPrecision], aps, nil
|
||||
}
|
||||
|
||||
return "", nil, errors.New("not found")
|
||||
if len(ris) > 0 {
|
||||
return foundPrefixes, ris, nil
|
||||
}
|
||||
|
||||
return nil, nil, errors.New("not found")
|
||||
}
|
||||
|
||||
@@ -20,21 +20,20 @@ package engine
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestRpAddAPIfNotPresent(t *testing.T) {
|
||||
ap1 := &RatingPlan{ActivationTime: time.Date(2012, time.July, 2, 14, 24, 30, 0, time.UTC)}
|
||||
ap2 := &RatingPlan{ActivationTime: time.Date(2012, time.July, 2, 14, 24, 30, 0, time.UTC)}
|
||||
ap3 := &RatingPlan{ActivationTime: time.Date(2012, time.July, 2, 14, 24, 30, 1, time.UTC)}
|
||||
rp := &RatingProfile{}
|
||||
rp.AddRatingPlanIfNotPresent("test", ap1)
|
||||
rp.AddRatingPlanIfNotPresent("test", ap2)
|
||||
if len(rp.DestinationMap["test"]) != 1 {
|
||||
t.Error("Wronfully appended activation period ;)", len(rp.DestinationMap["test"]))
|
||||
}
|
||||
rp.AddRatingPlanIfNotPresent("test", ap3)
|
||||
if len(rp.DestinationMap["test"]) != 2 {
|
||||
t.Error("Wronfully not appended activation period ;)", len(rp.DestinationMap["test"]))
|
||||
}
|
||||
/* ap1 := &RatingPlan{Id: "test1"}
|
||||
ap2 := &RatingPlan{Id: "test1"}
|
||||
ap3 := &RatingPlan{Id: "test2"}
|
||||
rp := &RatingProfile{}
|
||||
rp.AddRatingPlanIfNotPresent("test", ap1)
|
||||
rp.AddRatingPlanIfNotPresent("test", ap2)
|
||||
if len(rp.DestinationMap["test"]) != 1 {
|
||||
t.Error("Wronfully appended activation period ;)", len(rp.DestinationMap["test"]))
|
||||
}
|
||||
rp.AddRatingPlanIfNotPresent("test", ap3)
|
||||
if len(rp.DestinationMap["test"]) != 2 {
|
||||
t.Error("Wronfully not appended activation period ;)", len(rp.DestinationMap["test"]))
|
||||
}*/
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ import (
|
||||
|
||||
const (
|
||||
ACTION_TIMING_PREFIX = "atm_"
|
||||
RATING_PLAN_PREFIX = "rpl_"
|
||||
RATING_PROFILE_PREFIX = "rpf_"
|
||||
ACTION_PREFIX = "act_"
|
||||
USER_BALANCE_PREFIX = "ubl_"
|
||||
@@ -59,6 +60,8 @@ Interface for storage providers.
|
||||
*/
|
||||
type DataStorage interface {
|
||||
Storage
|
||||
GetRatingPlan(string) (*RatingPlan, error)
|
||||
SetRatingPlan(*RatingPlan) error
|
||||
GetRatingProfile(string) (*RatingProfile, error)
|
||||
SetRatingProfile(*RatingProfile) error
|
||||
GetDestination(string) (*Destination, error)
|
||||
|
||||
@@ -43,6 +43,25 @@ func (ms *MapStorage) Flush() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ms *MapStorage) GetRatingPlan(key string) (rp *RatingPlan, err error) {
|
||||
if values, ok := ms.dict[RATING_PLAN_PREFIX+key]; ok {
|
||||
rp = new(RatingPlan)
|
||||
|
||||
err = ms.ms.Unmarshal(values, rp)
|
||||
} else {
|
||||
return nil, errors.New("not found")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (ms *MapStorage) SetRatingPlan(rp *RatingPlan) (err error) {
|
||||
result, err := ms.ms.Marshal(rp)
|
||||
ms.dict[RATING_PLAN_PREFIX+rp.Id] = result
|
||||
response := 0
|
||||
go historyScribe.Record(&history.Record{RATING_PLAN_PREFIX + rp.Id, rp}, &response)
|
||||
return
|
||||
}
|
||||
|
||||
func (ms *MapStorage) GetRatingProfile(key string) (rp *RatingProfile, err error) {
|
||||
if values, ok := ms.dict[RATING_PROFILE_PREFIX+key]; ok {
|
||||
rp = new(RatingProfile)
|
||||
|
||||
@@ -52,6 +52,7 @@ func NewMongoStorage(host, port, db, user, pass string) (Storage, error) {
|
||||
err = ndb.C("actiontimings").EnsureIndex(index)
|
||||
index = mgo.Index{Key: []string{"id"}, Background: true}
|
||||
err = ndb.C("ratingprofiles").EnsureIndex(index)
|
||||
err = ndb.C("ratingplans").EnsureIndex(index)
|
||||
err = ndb.C("destinations").EnsureIndex(index)
|
||||
err = ndb.C("userbalances").EnsureIndex(index)
|
||||
|
||||
@@ -67,6 +68,10 @@ func (ms *MongoStorage) Flush() (err error) {
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = ms.db.C("ratingplans").DropCollection()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = ms.db.C("destinations").DropCollection()
|
||||
if err != nil {
|
||||
return
|
||||
@@ -123,6 +128,20 @@ type LogErrEntry struct {
|
||||
Source string
|
||||
}
|
||||
|
||||
func (ms *MongoStorage) GetRatingPlan(key string) (rp *RatingPlan, err error) {
|
||||
rp = new(RatingPlan)
|
||||
err = ms.db.C("ratingplans").Find(bson.M{"id": key}).One(&rp)
|
||||
return
|
||||
}
|
||||
|
||||
func (ms *MongoStorage) SetRatingPlan(rp *RatingPlan) error {
|
||||
if historyScribe != nil {
|
||||
response := 0
|
||||
historyScribe.Record(&history.Record{RATING_PLAN_PREFIX + rp.Id, rp}, &response)
|
||||
}
|
||||
return ms.db.C("ratingplans").Insert(rp)
|
||||
}
|
||||
|
||||
func (ms *MongoStorage) GetRatingProfile(key string) (rp *RatingProfile, err error) {
|
||||
rp = new(RatingProfile)
|
||||
err = ms.db.C("ratingprofiles").Find(bson.M{"id": key}).One(&rp)
|
||||
|
||||
@@ -59,6 +59,28 @@ func (rs *RadixStorage) Flush() (err error) {
|
||||
return r.Err
|
||||
}
|
||||
|
||||
func (rs *RadixStorage) GetRatingPlan(key string) (rp *RatingPlan, err error) {
|
||||
if values, err := rs.db.Cmd("get", RATING_PLAN_PREFIX+key).Bytes(); err == nil {
|
||||
rp = new(RatingPlan)
|
||||
err = rs.ms.Unmarshal(values, rp)
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (rs *RadixStorage) SetRatingPlan(rp *RatingPlan) (err error) {
|
||||
result, err := rs.ms.Marshal(rp)
|
||||
if r := rs.db.Cmd("set", RATING_PLAN_PREFIX+rp.Id, string(result)); r.Err != nil {
|
||||
return r.Err
|
||||
}
|
||||
if err == nil && historyScribe != nil {
|
||||
response := 0
|
||||
historyScribe.Record(&history.Record{RATING_PLAN_PREFIX + rp.Id, rp}, &response)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (rs *RadixStorage) GetRatingProfile(key string) (rp *RatingProfile, err error) {
|
||||
if values, err := rs.db.Cmd("get", RATING_PROFILE_PREFIX+key).Bytes(); err == nil {
|
||||
rp = new(RatingProfile)
|
||||
|
||||
@@ -59,6 +59,25 @@ func (rs *RedigoStorage) Flush() (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (rs *RedigoStorage) GetRatingPlan(key string) (rp *RatingPlan, err error) {
|
||||
var values []byte
|
||||
if values, err = redis.Bytes(rs.db.Do("get", RATING_PLAN_PREFIX+key)); err == nil {
|
||||
rp = new(RatingPlan)
|
||||
err = rs.ms.Unmarshal(values, rp)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (rs *RedigoStorage) SetRatingPlan(rp *RatingPlan) (err error) {
|
||||
result, err := rs.ms.Marshal(rp)
|
||||
_, err = rs.db.Do("set", RATING_PLAN_PREFIX+rp.Id, result)
|
||||
if err == nil && historyScribe != nil {
|
||||
response := 0
|
||||
historyScribe.Record(&history.Record{RATING_PLAN_PREFIX + rp.Id, rp}, &response)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (rs *RedigoStorage) GetRatingProfile(key string) (rp *RatingProfile, err error) {
|
||||
var values []byte
|
||||
if values, err = redis.Bytes(rs.db.Do("get", RATING_PROFILE_PREFIX+key)); err == nil {
|
||||
|
||||
@@ -76,6 +76,25 @@ func (rs *RedisStorage) Flush() (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (rs *RedisStorage) GetRatingPlan(key string) (rp *RatingPlan, err error) {
|
||||
var values string
|
||||
if values, err = rs.db.Get(RATING_PLAN_PREFIX + key); err == nil {
|
||||
rp = new(RatingPlan)
|
||||
err = rs.ms.Unmarshal([]byte(values), rp)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (rs *RedisStorage) SetRatingPlan(rp *RatingPlan) (err error) {
|
||||
result, err := rs.ms.Marshal(rp)
|
||||
_, err = rs.db.Set(RATING_PLAN_PREFIX+rp.Id, result)
|
||||
if err == nil && historyScribe != nil {
|
||||
response := 0
|
||||
historyScribe.Record(&history.Record{RATING_PLAN_PREFIX + rp.Id, rp}, &response)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (rs *RedisStorage) GetRatingProfile(key string) (rp *RatingProfile, err error) {
|
||||
var values string
|
||||
if values, err = rs.db.Get(RATING_PROFILE_PREFIX + key); err == nil {
|
||||
|
||||
@@ -124,14 +124,13 @@ func GetUB() *UserBalance {
|
||||
|
||||
func BenchmarkMarshallerJSONStoreRestore(b *testing.B) {
|
||||
b.StopTimer()
|
||||
d := time.Date(2012, time.February, 1, 14, 30, 1, 0, time.UTC)
|
||||
i := &RateInterval{Months: []time.Month{time.February},
|
||||
MonthDays: []int{1},
|
||||
WeekDays: []time.Weekday{time.Wednesday, time.Thursday},
|
||||
StartTime: "14:30:00",
|
||||
EndTime: "15:00:00"}
|
||||
ap := &RatingPlan{ActivationTime: d}
|
||||
ap.AddRateInterval(i)
|
||||
ap := &RatingPlan{Id: "test"}
|
||||
ap.AddRateInterval("NAT", i)
|
||||
ub := GetUB()
|
||||
|
||||
ap1 := RatingPlan{}
|
||||
@@ -148,14 +147,13 @@ func BenchmarkMarshallerJSONStoreRestore(b *testing.B) {
|
||||
|
||||
func BenchmarkMarshallerBSONStoreRestore(b *testing.B) {
|
||||
b.StopTimer()
|
||||
d := time.Date(2012, time.February, 1, 14, 30, 1, 0, time.UTC)
|
||||
i := &RateInterval{Months: []time.Month{time.February},
|
||||
MonthDays: []int{1},
|
||||
WeekDays: []time.Weekday{time.Wednesday, time.Thursday},
|
||||
StartTime: "14:30:00",
|
||||
EndTime: "15:00:00"}
|
||||
ap := &RatingPlan{ActivationTime: d}
|
||||
ap.AddRateInterval(i)
|
||||
ap := &RatingPlan{Id: "test"}
|
||||
ap.AddRateInterval("NAT", i)
|
||||
ub := GetUB()
|
||||
|
||||
ap1 := RatingPlan{}
|
||||
@@ -172,14 +170,13 @@ func BenchmarkMarshallerBSONStoreRestore(b *testing.B) {
|
||||
|
||||
func BenchmarkMarshallerJSONBufStoreRestore(b *testing.B) {
|
||||
b.StopTimer()
|
||||
d := time.Date(2012, time.February, 1, 14, 30, 1, 0, time.UTC)
|
||||
i := &RateInterval{Months: []time.Month{time.February},
|
||||
MonthDays: []int{1},
|
||||
WeekDays: []time.Weekday{time.Wednesday, time.Thursday},
|
||||
StartTime: "14:30:00",
|
||||
EndTime: "15:00:00"}
|
||||
ap := &RatingPlan{ActivationTime: d}
|
||||
ap.AddRateInterval(i)
|
||||
ap := &RatingPlan{Id: "test"}
|
||||
ap.AddRateInterval("NAT", i)
|
||||
ub := GetUB()
|
||||
|
||||
ap1 := RatingPlan{}
|
||||
@@ -196,14 +193,13 @@ func BenchmarkMarshallerJSONBufStoreRestore(b *testing.B) {
|
||||
|
||||
func BenchmarkMarshallerGOBStoreRestore(b *testing.B) {
|
||||
b.StopTimer()
|
||||
d := time.Date(2012, time.February, 1, 14, 30, 1, 0, time.UTC)
|
||||
i := &RateInterval{Months: []time.Month{time.February},
|
||||
MonthDays: []int{1},
|
||||
WeekDays: []time.Weekday{time.Wednesday, time.Thursday},
|
||||
StartTime: "14:30:00",
|
||||
EndTime: "15:00:00"}
|
||||
ap := &RatingPlan{ActivationTime: d}
|
||||
ap.AddRateInterval(i)
|
||||
ap := &RatingPlan{Id: "test"}
|
||||
ap.AddRateInterval("NAT", i)
|
||||
ub := GetUB()
|
||||
|
||||
ap1 := RatingPlan{}
|
||||
@@ -220,14 +216,13 @@ func BenchmarkMarshallerGOBStoreRestore(b *testing.B) {
|
||||
|
||||
func BenchmarkMarshallerCodecMsgpackStoreRestore(b *testing.B) {
|
||||
b.StopTimer()
|
||||
d := time.Date(2012, time.February, 1, 14, 30, 1, 0, time.UTC)
|
||||
i := &RateInterval{Months: []time.Month{time.February},
|
||||
MonthDays: []int{1},
|
||||
WeekDays: []time.Weekday{time.Wednesday, time.Thursday},
|
||||
StartTime: "14:30:00",
|
||||
EndTime: "15:00:00"}
|
||||
ap := &RatingPlan{ActivationTime: d}
|
||||
ap.AddRateInterval(i)
|
||||
ap := &RatingPlan{Id: "test"}
|
||||
ap.AddRateInterval("NAT", i)
|
||||
ub := GetUB()
|
||||
|
||||
ap1 := RatingPlan{}
|
||||
@@ -244,14 +239,13 @@ func BenchmarkMarshallerCodecMsgpackStoreRestore(b *testing.B) {
|
||||
|
||||
func BenchmarkMarshallerBincStoreRestore(b *testing.B) {
|
||||
b.StopTimer()
|
||||
d := time.Date(2012, time.February, 1, 14, 30, 1, 0, time.UTC)
|
||||
i := &RateInterval{Months: []time.Month{time.February},
|
||||
MonthDays: []int{1},
|
||||
WeekDays: []time.Weekday{time.Wednesday, time.Thursday},
|
||||
StartTime: "14:30:00",
|
||||
EndTime: "15:00:00"}
|
||||
ap := &RatingPlan{ActivationTime: d}
|
||||
ap.AddRateInterval(i)
|
||||
ap := &RatingPlan{Id: "test"}
|
||||
ap.AddRateInterval("NAT", i)
|
||||
ub := GetUB()
|
||||
|
||||
ap1 := RatingPlan{}
|
||||
|
||||
@@ -30,7 +30,7 @@ A unit in which a call will be split that has a specific price related interval
|
||||
type TimeSpan struct {
|
||||
TimeStart, TimeEnd time.Time
|
||||
Cost float64
|
||||
ratingPlan *RatingPlan
|
||||
ratingInfo *RatingInfo
|
||||
RateInterval *RateInterval
|
||||
CallDuration time.Duration // the call duration so far till TimeEnd
|
||||
overlapped bool // mark a timespan as overlapped by an expanded one
|
||||
@@ -253,11 +253,11 @@ func (ts *TimeSpan) SplitByDuration(duration time.Duration) *TimeSpan {
|
||||
}
|
||||
|
||||
// Splits the given timespan on activation period's activation time.
|
||||
func (ts *TimeSpan) SplitByRatingPlan(rp *RatingPlan) (newTs *TimeSpan) {
|
||||
func (ts *TimeSpan) SplitByRatingPlan(rp *RatingInfo) (newTs *TimeSpan) {
|
||||
if !ts.Contains(rp.ActivationTime) {
|
||||
return nil
|
||||
}
|
||||
newTs = &TimeSpan{TimeStart: rp.ActivationTime, TimeEnd: ts.TimeEnd, ratingPlan: rp}
|
||||
newTs = &TimeSpan{TimeStart: rp.ActivationTime, TimeEnd: ts.TimeEnd, ratingInfo: rp}
|
||||
newTs.CallDuration = ts.CallDuration
|
||||
ts.TimeEnd = rp.ActivationTime
|
||||
ts.SetNewCallDuration(newTs)
|
||||
|
||||
@@ -190,9 +190,9 @@ func TestSplitByRatingPlan(t *testing.T) {
|
||||
t2 := time.Date(2012, time.February, 5, 17, 55, 0, 0, time.UTC)
|
||||
t3 := time.Date(2012, time.February, 5, 17, 50, 0, 0, time.UTC)
|
||||
ts := TimeSpan{TimeStart: t1, TimeEnd: t2}
|
||||
ap1 := &RatingPlan{ActivationTime: t1}
|
||||
ap2 := &RatingPlan{ActivationTime: t2}
|
||||
ap3 := &RatingPlan{ActivationTime: t3}
|
||||
ap1 := &RatingInfo{ActivationTime: t1}
|
||||
ap2 := &RatingInfo{ActivationTime: t2}
|
||||
ap3 := &RatingInfo{ActivationTime: t3}
|
||||
|
||||
if ts.SplitByRatingPlan(ap1) != nil {
|
||||
t.Error("Error spliting on left margin")
|
||||
|
||||
@@ -647,8 +647,50 @@ func TestDebitCreditSubjectMoney(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDebitCreditSubjectMixed(t *testing.T) {
|
||||
b1 := &Balance{Uuid: "testb", Value: 70, Weight: 10, DestinationId: "NAT", RateSubject: ZEROMINUTE}
|
||||
b1 := &Balance{Uuid: "testb", Value: 40, Weight: 10, DestinationId: "NAT", RateSubject: "minu"}
|
||||
cc := &CallCost{
|
||||
Tenant: "vdf",
|
||||
TOR: "0",
|
||||
Direction: OUTBOUND,
|
||||
Destination: "0723045326",
|
||||
Timespans: []*TimeSpan{
|
||||
&TimeSpan{
|
||||
TimeStart: time.Date(2013, 9, 24, 10, 48, 0, 0, time.UTC),
|
||||
TimeEnd: time.Date(2013, 9, 24, 10, 49, 10, 0, time.UTC),
|
||||
CallDuration: 0,
|
||||
RateInterval: &RateInterval{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}},
|
||||
},
|
||||
},
|
||||
}
|
||||
rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{
|
||||
MINUTES + OUTBOUND: BalanceChain{b1},
|
||||
CREDIT + OUTBOUND: BalanceChain{&Balance{Uuid: "moneya", Value: 150, RateSubject: "minu"}},
|
||||
}}
|
||||
err := rifsBalance.debitCreditBalance(cc, false)
|
||||
if err != nil {
|
||||
t.Error("Error debiting balance: ", err)
|
||||
}
|
||||
if cc.Timespans[0].Increments[0].BalanceUuids[0] != "testb" ||
|
||||
cc.Timespans[0].Increments[0].BalanceUuids[1] != "moneya" ||
|
||||
cc.Timespans[0].Increments[0].Duration != time.Second {
|
||||
t.Error("Error setting balance id to increment: ", cc.Timespans[0].Increments[0])
|
||||
}
|
||||
if rifsBalance.BalanceMap[MINUTES+OUTBOUND][0].Value != 0 ||
|
||||
rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value != 80 {
|
||||
t.Errorf("Error extracting minutes from balance: %+v, %+v",
|
||||
rifsBalance.BalanceMap[MINUTES+OUTBOUND][0].Value, rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value)
|
||||
}
|
||||
if len(cc.Timespans) != 1 || cc.Timespans[0].GetDuration() != 40*time.Second {
|
||||
t.Error("Error truncating extra timespans: ", cc.Timespans[0].GetDuration())
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
func TestDebitCreditSubjectMixedMoreTS(t *testing.T) {
|
||||
b1 := &Balance{Uuid: "testb", Value: 70, Weight: 10, DestinationId: "NAT", RateSubject: "minu"}
|
||||
cc := &CallCost{
|
||||
Tenant: "vdf",
|
||||
TOR: "0",
|
||||
Direction: OUTBOUND,
|
||||
Destination: "0723045326",
|
||||
Timespans: []*TimeSpan{
|
||||
@@ -668,12 +710,13 @@ func TestDebitCreditSubjectMixed(t *testing.T) {
|
||||
}
|
||||
rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{
|
||||
MINUTES + OUTBOUND: BalanceChain{b1},
|
||||
CREDIT + OUTBOUND: BalanceChain{&Balance{Uuid: "moneya", Value: 50}},
|
||||
CREDIT + OUTBOUND: BalanceChain{&Balance{Uuid: "moneya", Value: 50, RateSubject: "minu"}},
|
||||
}}
|
||||
err := rifsBalance.debitCreditBalance(cc, false)
|
||||
if err != nil {
|
||||
t.Error("Error debiting balance: ", err)
|
||||
}
|
||||
t.Errorf("%+v %+v", cc.Timespans[0], cc.Timespans[1])
|
||||
if cc.Timespans[0].Increments[0].BalanceUuids[0] != "testb" ||
|
||||
cc.Timespans[0].Increments[0].Duration != time.Minute {
|
||||
t.Error("Error setting balance id to increment: ", cc.Timespans[0].Increments[0])
|
||||
@@ -687,117 +730,6 @@ func TestDebitCreditSubjectMixed(t *testing.T) {
|
||||
t.Error("Error truncating extra timespans: ", cc.Timespans)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
func TestDebitMinuteBalance(t *testing.T) {
|
||||
b1 := &Balance{Value: 10, Weight: 10, DestinationId: "NAT"}
|
||||
b2 := &Balance{Value: 100, Weight: 20, DestinationId: "RET"}
|
||||
rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{MINUTES + OUTBOUND: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}}
|
||||
err := rifsBalance.debitCreditBalance(&CallCost{Direction: OUTBOUND, Destination: "0723", Cost: 6}, false)
|
||||
if b2.Value != 94 || err != nil {
|
||||
t.Errorf("Expected %v was %v", 94, b1.Value)
|
||||
}
|
||||
}*/
|
||||
|
||||
/*func TestDebitMultipleBucketsMinuteBalance(t *testing.T) {
|
||||
b1 := &Balance{Value: 10, Weight: 10, SpecialPrice: 0.0, DestinationId: "NAT"}
|
||||
b2 := &Balance{Value: 100, Weight: 20, SpecialPrice: 0.0, DestinationId: "RET"}
|
||||
rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{MINUTES + OUTBOUND: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}}
|
||||
err := rifsBalance.debitCreditBalance(105, "0723", false)
|
||||
if b2.Value != 0 || b1.Value != 5 || err != nil {
|
||||
t.Log(err)
|
||||
t.Errorf("Expected %v was %v", 0, b2.Value)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDebitAllMinuteBalance(t *testing.T) {
|
||||
b1 := &Balance{Value: 10, Weight: 10, SpecialPrice: 0.0, DestinationId: "NAT"}
|
||||
b2 := &Balance{Value: 100, Weight: 20, SpecialPrice: 0.0, DestinationId: "RET"}
|
||||
rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{MINUTES + OUTBOUND: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}}
|
||||
err := rifsBalance.debitCreditBalance(110, "0723", false)
|
||||
if b2.Value != 0 || b1.Value != 0 || err != nil {
|
||||
t.Errorf("Expected %v was %v", 0, b2.Value)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDebitMoreMinuteBalance(t *testing.T) {
|
||||
b1 := &Balance{Value: 10, Weight: 10, SpecialPrice: 0.0, DestinationId: "NAT"}
|
||||
b2 := &Balance{Value: 100, Weight: 20, SpecialPrice: 0.0, DestinationId: "RET"}
|
||||
rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{MINUTES + OUTBOUND: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}}
|
||||
err := rifsBalance.debitCreditBalance(115, "0723", false)
|
||||
if b2.Value != 100 || b1.Value != 10 || err == nil {
|
||||
t.Errorf("Expected %v was %v", 1000, b2.Value)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDebitSpecialPriceMinuteBalance0(t *testing.T) {
|
||||
b1 := &Balance{Value: 10, Weight: 10, SpecialPrice: 0.0, DestinationId: "NAT"}
|
||||
b2 := &Balance{Value: 100, Weight: 20, SpecialPrice: 1.0, DestinationId: "RET"}
|
||||
rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{MINUTES + OUTBOUND: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}}
|
||||
err := rifsBalance.debitCreditBalance(5, "0723", false)
|
||||
if b2.Value != 95 || b1.Value != 10 || err != nil || rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value != 16 {
|
||||
t.Errorf("Expected %v was %v", 16, rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDebitSpecialPriceAllMinuteBalance(t *testing.T) {
|
||||
b1 := &Balance{Value: 10, Weight: 10, SpecialPrice: 0.0, DestinationId: "NAT"}
|
||||
b2 := &Balance{Value: 100, Weight: 20, SpecialPrice: 1.0, DestinationId: "RET"}
|
||||
rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{MINUTES + OUTBOUND: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}}
|
||||
err := rifsBalance.debitCreditBalance(21, "0723", false)
|
||||
if b2.Value != 79 || b1.Value != 10 || err != nil || rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value != 0 {
|
||||
t.Errorf("Expected %v was %v", 0, rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDebitSpecialPriceMoreMinuteBalance(t *testing.T) {
|
||||
b1 := &Balance{Value: 10, Weight: 10, SpecialPrice: 0.0, DestinationId: "NAT"}
|
||||
b2 := &Balance{Value: 100, Weight: 20, SpecialPrice: 1.0, DestinationId: "RET"}
|
||||
rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{MINUTES + OUTBOUND: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}}
|
||||
err := rifsBalance.debitCreditBalance(25, "0723", false)
|
||||
if b2.Value != 75 || b1.Value != 10 || err != nil || rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value != -4 {
|
||||
t.Log(b2.Value)
|
||||
t.Log(b1.Value)
|
||||
t.Log(err)
|
||||
t.Errorf("Expected %v was %v", -4, rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDebitSpecialPriceMoreMinuteBalancePrepay(t *testing.T) {
|
||||
b1 := &Balance{Value: 10, Weight: 10, SpecialPrice: 0.0, DestinationId: "NAT"}
|
||||
b2 := &Balance{Value: 100, Weight: 20, SpecialPrice: 1.0, DestinationId: "RET"}
|
||||
rifsBalance := &UserBalance{Id: "other", Type: UB_TYPE_PREPAID, BalanceMap: map[string]BalanceChain{MINUTES + OUTBOUND: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}}
|
||||
err := rifsBalance.debitCreditBalance(25, "0723", false)
|
||||
expected := 21.0
|
||||
if b2.Value != 100 || b1.Value != 10 || err != AMOUNT_TOO_BIG || rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value != expected {
|
||||
t.Log(b2.Value)
|
||||
t.Log(b1.Value)
|
||||
t.Log(err)
|
||||
t.Errorf("Expected %v was %v", expected, rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDebitSpecialPriceNegativeMinuteBalance(t *testing.T) {
|
||||
b1 := &Balance{Value: 10, Weight: 10, SpecialPrice: 0.0, DestinationId: "NAT"}
|
||||
b2 := &Balance{Value: 100, Weight: 20, SpecialPrice: 1.0, DestinationId: "RET"}
|
||||
rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{MINUTES + OUTBOUND: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}}
|
||||
err := rifsBalance.debitCreditBalance(-15, "0723", false)
|
||||
if b2.Value != 115 || b1.Value != 10 || err != nil || rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value != 36 {
|
||||
t.Log(b1, b2, err)
|
||||
t.Errorf("Expected %v was %v", 36, rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDebitNegativeMinuteBalance(t *testing.T) {
|
||||
b1 := &Balance{Value: 10, Weight: 10, SpecialPrice: 0.0, DestinationId: "NAT"}
|
||||
b2 := &Balance{Value: 100, Weight: 20, SpecialPrice: 0.0, DestinationId: "RET"}
|
||||
rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{MINUTES + OUTBOUND: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}}
|
||||
err := rifsBalance.debitCreditBalance(-15, "0723", false)
|
||||
if b2.Value != 115 || b1.Value != 10 || err != nil || rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value != 21 {
|
||||
t.Log(b1, b2, err)
|
||||
t.Errorf("Expected %v was %v", 21, rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value)
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
func TestDebitSMSBalance(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user