mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-11 18:16:24 +05:00
improved expiration time
This commit is contained in:
@@ -147,14 +147,10 @@ func (self *Apier) SetRatingProfile(attrs AttrSetRatingProfile, reply *string) e
|
||||
}
|
||||
dbReader := rater.NewDbReader(self.StorDb, self.DataDb, attrs.TPid)
|
||||
|
||||
newRP, err := dbReader.LoadRatingProfileByTag(attrs.RateProfileId)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error())
|
||||
}
|
||||
err = self.DataDb.SetRatingProfile(newRP)
|
||||
if err != nil {
|
||||
if err := dbReader.LoadRatingProfileByTag(attrs.RateProfileId); err != nil {
|
||||
return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error())
|
||||
}
|
||||
|
||||
*reply = "OK"
|
||||
return nil
|
||||
}
|
||||
@@ -240,3 +236,22 @@ func (self *Apier) AddAccount(attr *AttrAccount, reply *float64) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type AttrSetAccountAction struct {
|
||||
TPid string
|
||||
RateProfileId string
|
||||
}
|
||||
|
||||
// Process dependencies and load a specific rating profile from storDb into dataDb.
|
||||
func (self *Apier) SetAccountAction(attrs AttrSetAccountAction, reply *string) error {
|
||||
if missing := utils.MissingStructFields(&attrs, []string{"TPid", "RateProfileId"}); len(missing) != 0 {
|
||||
return fmt.Errorf("%s:%v", utils.ERR_MANDATORY_IE_MISSING, missing)
|
||||
}
|
||||
dbReader := rater.NewDbReader(self.StorDb, self.DataDb, attrs.TPid)
|
||||
|
||||
if err := dbReader.LoadAccountActionByTag(attrs.RateProfileId); err != nil {
|
||||
return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error())
|
||||
}
|
||||
*reply = "OK"
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -21,19 +21,21 @@ package rater
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"time"
|
||||
)
|
||||
|
||||
/*
|
||||
Structure to be filled for each tariff plan with the bonus value for received calls minutes.
|
||||
*/
|
||||
type Action struct {
|
||||
Id string
|
||||
ActionType string
|
||||
BalanceId string
|
||||
Direction string
|
||||
Units float64
|
||||
Weight float64
|
||||
MinuteBucket *MinuteBucket
|
||||
Id string
|
||||
ActionType string
|
||||
BalanceId string
|
||||
Direction string
|
||||
ExpirationDate time.Time
|
||||
Units float64
|
||||
Weight float64
|
||||
MinuteBucket *MinuteBucket
|
||||
}
|
||||
|
||||
const (
|
||||
@@ -164,17 +166,10 @@ func genericDebit(ub *UserBalance, a *Action) (err error) {
|
||||
if ub.BalanceMap == nil {
|
||||
ub.BalanceMap = make(map[string]BalanceChain)
|
||||
}
|
||||
switch a.BalanceId {
|
||||
case CREDIT:
|
||||
ub.debitBalance(CREDIT, a.Units, false)
|
||||
case SMS:
|
||||
ub.debitBalance(SMS, a.Units, false)
|
||||
case MINUTES:
|
||||
if a.BalanceId == MINUTES {
|
||||
ub.debitMinuteBucket(a.MinuteBucket)
|
||||
case TRAFFIC:
|
||||
ub.debitBalance(TRAFFIC, a.Units, false)
|
||||
case TRAFFIC_TIME:
|
||||
ub.debitBalance(TRAFFIC_TIME, a.Units, false)
|
||||
} else {
|
||||
ub.debitBalanceAction(a)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -508,7 +508,7 @@ func TestActionTopupResetCredit(t *testing.T) {
|
||||
len(ub.UnitCounters) != 1 ||
|
||||
len(ub.MinuteBuckets) != 2 ||
|
||||
ub.ActionTriggers[0].Executed != true || ub.ActionTriggers[1].Executed != true {
|
||||
t.Errorf("Topup reset action failed: %#v", ub)
|
||||
t.Errorf("Topup reset action failed: %#v", ub.BalanceMap[CREDIT+OUTBOUND].GetTotalValue())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -542,14 +542,14 @@ func TestActionTopupCredit(t *testing.T) {
|
||||
MinuteBuckets: []*MinuteBucket{&MinuteBucket{Seconds: 10, Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, Percent: 0, DestinationId: "RET"}},
|
||||
ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, Direction: OUTBOUND, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceId: CREDIT, Direction: OUTBOUND, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}},
|
||||
}
|
||||
a := &Action{BalanceId: CREDIT, Units: 10}
|
||||
a := &Action{BalanceId: CREDIT, Direction: OUTBOUND, Units: 10}
|
||||
topupAction(ub, a)
|
||||
if ub.Type != UB_TYPE_PREPAID ||
|
||||
ub.BalanceMap[CREDIT+OUTBOUND].GetTotalValue() != 110 ||
|
||||
len(ub.UnitCounters) != 1 ||
|
||||
len(ub.MinuteBuckets) != 2 ||
|
||||
ub.ActionTriggers[0].Executed != true || ub.ActionTriggers[1].Executed != true {
|
||||
t.Error("Topup action failed!", ub)
|
||||
t.Error("Topup action failed!", ub.BalanceMap[CREDIT+OUTBOUND].GetTotalValue())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -583,14 +583,14 @@ func TestActionDebitCredit(t *testing.T) {
|
||||
MinuteBuckets: []*MinuteBucket{&MinuteBucket{Seconds: 10, Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, Percent: 0, DestinationId: "RET"}},
|
||||
ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, Direction: OUTBOUND, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceId: CREDIT, Direction: OUTBOUND, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}},
|
||||
}
|
||||
a := &Action{BalanceId: CREDIT, Units: 10}
|
||||
a := &Action{BalanceId: CREDIT, Direction: OUTBOUND, Units: 10}
|
||||
debitAction(ub, a)
|
||||
if ub.Type != UB_TYPE_PREPAID ||
|
||||
ub.BalanceMap[CREDIT+OUTBOUND].GetTotalValue() != 90 ||
|
||||
len(ub.UnitCounters) != 1 ||
|
||||
len(ub.MinuteBuckets) != 2 ||
|
||||
ub.ActionTriggers[0].Executed != true || ub.ActionTriggers[1].Executed != true {
|
||||
t.Error("Debit action failed!", ub)
|
||||
t.Error("Debit action failed!", ub.BalanceMap[CREDIT+OUTBOUND].GetTotalValue())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -379,27 +379,33 @@ func (csvr *CSVReader) LoadActions() (err error) {
|
||||
if err != nil {
|
||||
return errors.New(fmt.Sprintf("Could not parse action units: %v", err))
|
||||
}
|
||||
unix, err := strconv.ParseInt(record[5], 10, 64)
|
||||
if err != nil {
|
||||
return errors.New(fmt.Sprintf("Could not parse expiration date: %v", err))
|
||||
}
|
||||
expDate := time.Unix(unix, 0)
|
||||
var a *Action
|
||||
if record[2] != MINUTES {
|
||||
a = &Action{
|
||||
ActionType: record[1],
|
||||
BalanceId: record[2],
|
||||
Direction: record[3],
|
||||
Units: units,
|
||||
ActionType: record[1],
|
||||
BalanceId: record[2],
|
||||
Direction: record[3],
|
||||
Units: units,
|
||||
ExpirationDate: expDate,
|
||||
}
|
||||
} else {
|
||||
price, percent := 0.0, 0.0
|
||||
value, err := strconv.ParseFloat(record[7], 64)
|
||||
value, err := strconv.ParseFloat(record[8], 64)
|
||||
if err != nil {
|
||||
return errors.New(fmt.Sprintf("Could not parse action price: %v", err))
|
||||
}
|
||||
if record[6] == PERCENT {
|
||||
if record[7] == PERCENT {
|
||||
percent = value
|
||||
}
|
||||
if record[6] == ABSOLUTE {
|
||||
if record[7] == ABSOLUTE {
|
||||
price = value
|
||||
}
|
||||
minutesWeight, err := strconv.ParseFloat(record[8], 64)
|
||||
minutesWeight, err := strconv.ParseFloat(record[9], 64)
|
||||
if err != nil {
|
||||
return errors.New(fmt.Sprintf("Could not parse action minutes weight: %v", err))
|
||||
}
|
||||
@@ -408,17 +414,19 @@ func (csvr *CSVReader) LoadActions() (err error) {
|
||||
return errors.New(fmt.Sprintf("Could not parse action weight: %v", err))
|
||||
}
|
||||
a = &Action{
|
||||
Id: utils.GenUUID(),
|
||||
ActionType: record[1],
|
||||
BalanceId: record[2],
|
||||
Direction: record[3],
|
||||
Weight: weight,
|
||||
Id: utils.GenUUID(),
|
||||
ActionType: record[1],
|
||||
BalanceId: record[2],
|
||||
Direction: record[3],
|
||||
Weight: weight,
|
||||
ExpirationDate: expDate,
|
||||
MinuteBucket: &MinuteBucket{
|
||||
Seconds: units,
|
||||
Weight: minutesWeight,
|
||||
Price: price,
|
||||
Percent: percent,
|
||||
DestinationId: record[5],
|
||||
Seconds: units,
|
||||
Weight: minutesWeight,
|
||||
Price: price,
|
||||
Percent: percent,
|
||||
DestinationId: record[6],
|
||||
ExpirationDate: expDate,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ vdf,0,OUT,inf,inf,STANDARD,2012-02-28T00:00:00Z
|
||||
vdf,0,OUT,fall,one|rif,PREMIUM,2012-02-28T00:00:00Z
|
||||
`
|
||||
actions = `
|
||||
MINI,TOPUP,MINUTES,OUT,100,NAT,ABSOLUTE,0,10,10
|
||||
MINI,TOPUP,MINUTES,OUT,100,1374239002,NAT,ABSOLUTE,0,10,10
|
||||
`
|
||||
actionTimings = `
|
||||
MORE_MINUTES,MINI,ONE_TIME_RUN,10
|
||||
|
||||
@@ -28,8 +28,8 @@ import (
|
||||
|
||||
type DbReader struct {
|
||||
tpid string
|
||||
storDB DataStorage
|
||||
storage DataStorage
|
||||
storDb DataStorage
|
||||
dataDb DataStorage
|
||||
actions map[string][]*Action
|
||||
actionsTimings map[string][]*ActionTiming
|
||||
actionsTriggers map[string][]*ActionTrigger
|
||||
@@ -44,8 +44,8 @@ type DbReader struct {
|
||||
|
||||
func NewDbReader(storDB DataStorage, storage DataStorage, tpid string) *DbReader {
|
||||
c := new(DbReader)
|
||||
c.storDB = storDB
|
||||
c.storage = storage
|
||||
c.storDb = storDB
|
||||
c.dataDb = storage
|
||||
c.tpid = tpid
|
||||
c.activationPeriods = make(map[string]*ActivationPeriod)
|
||||
c.actionsTimings = make(map[string][]*ActionTiming)
|
||||
@@ -53,7 +53,7 @@ func NewDbReader(storDB DataStorage, storage DataStorage, tpid string) *DbReader
|
||||
}
|
||||
|
||||
func (dbr *DbReader) WriteToDatabase(flush, verbose bool) (err error) {
|
||||
storage := dbr.storage
|
||||
storage := dbr.dataDb
|
||||
if flush {
|
||||
storage.Flush()
|
||||
}
|
||||
@@ -121,22 +121,22 @@ func (dbr *DbReader) WriteToDatabase(flush, verbose bool) (err error) {
|
||||
}
|
||||
|
||||
func (dbr *DbReader) LoadDestinations() (err error) {
|
||||
dbr.destinations, err = dbr.storDB.GetTpDestinations(dbr.tpid, "")
|
||||
dbr.destinations, err = dbr.storDb.GetTpDestinations(dbr.tpid, "")
|
||||
return
|
||||
}
|
||||
|
||||
func (dbr *DbReader) LoadTimings() (err error) {
|
||||
dbr.timings, err = dbr.storDB.GetTpTimings(dbr.tpid, "")
|
||||
dbr.timings, err = dbr.storDb.GetTpTimings(dbr.tpid, "")
|
||||
return err
|
||||
}
|
||||
|
||||
func (dbr *DbReader) LoadRates() (err error) {
|
||||
dbr.rates, err = dbr.storDB.GetTpRates(dbr.tpid, "")
|
||||
dbr.rates, err = dbr.storDb.GetTpRates(dbr.tpid, "")
|
||||
return err
|
||||
}
|
||||
|
||||
func (dbr *DbReader) LoadDestinationRates() (err error) {
|
||||
dbr.destinationRates, err = dbr.storDB.GetTpDestinationRates(dbr.tpid, "")
|
||||
dbr.destinationRates, err = dbr.storDb.GetTpDestinationRates(dbr.tpid, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -153,7 +153,7 @@ func (dbr *DbReader) LoadDestinationRates() (err error) {
|
||||
}
|
||||
|
||||
func (dbr *DbReader) LoadDestinationRateTimings() error {
|
||||
rts, err := dbr.storDB.GetTpDestinationRateTimings(dbr.tpid, "")
|
||||
rts, err := dbr.storDb.GetTpDestinationRateTimings(dbr.tpid, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -179,7 +179,7 @@ func (dbr *DbReader) LoadDestinationRateTimings() error {
|
||||
}
|
||||
|
||||
func (dbr *DbReader) LoadRatingProfiles() error {
|
||||
rpfs, err := dbr.storDB.GetTpRatingProfiles(dbr.tpid, "")
|
||||
rpfs, err := dbr.storDb.GetTpRatingProfiles(dbr.tpid, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -200,75 +200,74 @@ func (dbr *DbReader) LoadRatingProfiles() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dbr *DbReader) LoadRatingProfileByTag(tag string) (*RatingProfile, error) {
|
||||
func (dbr *DbReader) LoadRatingProfileByTag(tag string) error {
|
||||
activationPeriods := make(map[string]*ActivationPeriod)
|
||||
resultRatingProfile := &RatingProfile{Id: tag}
|
||||
rpm, err := dbr.storDB.GetTpRatingProfiles(dbr.tpid, tag)
|
||||
rpm, err := dbr.storDb.GetTpRatingProfiles(dbr.tpid, tag)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
} else if len(rpm) == 0 {
|
||||
return nil, fmt.Errorf("No RateProfile with id: %s", tag)
|
||||
return fmt.Errorf("No RateProfile with id: %s", tag)
|
||||
}
|
||||
for _, ratingProfile := range rpm {
|
||||
resultRatingProfile.FallbackKey = ratingProfile.FallbackKey // it will be the last fallback key
|
||||
at := time.Unix(ratingProfile.activationTime, 0)
|
||||
drtm, err := dbr.storDB.GetTpDestinationRateTimings(dbr.tpid, ratingProfile.destRatesTimingTag)
|
||||
drtm, err := dbr.storDb.GetTpDestinationRateTimings(dbr.tpid, ratingProfile.destRatesTimingTag)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
} else if len(drtm) == 0 {
|
||||
return nil, fmt.Errorf("No DestRateTimings profile with id: %s", ratingProfile.destRatesTimingTag)
|
||||
return fmt.Errorf("No DestRateTimings profile with id: %s", ratingProfile.destRatesTimingTag)
|
||||
}
|
||||
for _, destrateTiming := range drtm {
|
||||
tm, err := dbr.storDB.GetTpTimings(dbr.tpid, destrateTiming.TimingsTag)
|
||||
tm, err := dbr.storDb.GetTpTimings(dbr.tpid, destrateTiming.TimingsTag)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
} else if len(tm) == 0 {
|
||||
return nil, fmt.Errorf("No Timings profile with id: %s", destrateTiming.TimingsTag)
|
||||
return fmt.Errorf("No Timings profile with id: %s", destrateTiming.TimingsTag)
|
||||
}
|
||||
destrateTiming.timing = tm[destrateTiming.TimingsTag]
|
||||
drm, err := dbr.storDB.GetTpDestinationRates(dbr.tpid, destrateTiming.DestinationRatesTag)
|
||||
drm, err := dbr.storDb.GetTpDestinationRates(dbr.tpid, destrateTiming.DestinationRatesTag)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
} else if len(drm) == 0 {
|
||||
return nil, fmt.Errorf("No Timings profile with id: %s", destrateTiming.DestinationRatesTag)
|
||||
return fmt.Errorf("No Timings profile with id: %s", destrateTiming.DestinationRatesTag)
|
||||
}
|
||||
for _, drate := range drm[destrateTiming.DestinationRatesTag] {
|
||||
rt, err := dbr.storDB.GetTpRates(dbr.tpid, drate.RateTag)
|
||||
rt, err := dbr.storDb.GetTpRates(dbr.tpid, drate.RateTag)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
} else if len(rt) == 0 {
|
||||
return nil, fmt.Errorf("No Rates profile with id: %s", drate.RateTag)
|
||||
return fmt.Errorf("No Rates profile with id: %s", drate.RateTag)
|
||||
}
|
||||
drate.Rate = rt[drate.RateTag]
|
||||
if _, exists := activationPeriods[destrateTiming.Tag]; !exists {
|
||||
activationPeriods[destrateTiming.Tag] = &ActivationPeriod{}
|
||||
}
|
||||
activationPeriods[destrateTiming.Tag].AddIntervalIfNotPresent(destrateTiming.GetInterval(drate))
|
||||
dm, err := dbr.storDB.GetTpDestinations(dbr.tpid, drate.DestinationsTag)
|
||||
dm, err := dbr.storDb.GetTpDestinations(dbr.tpid, drate.DestinationsTag)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
for _, destination := range dm {
|
||||
ap := activationPeriods[ratingProfile.destRatesTimingTag]
|
||||
newAP := &ActivationPeriod{ActivationTime: at}
|
||||
newAP.Intervals = append(newAP.Intervals, ap.Intervals...)
|
||||
resultRatingProfile.AddActivationPeriodIfNotPresent(destination.Id, newAP)
|
||||
dbr.storage.SetDestination(destination)
|
||||
dbr.dataDb.SetDestination(destination)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return resultRatingProfile, nil
|
||||
return dbr.dataDb.SetRatingProfile(resultRatingProfile)
|
||||
}
|
||||
|
||||
func (dbr *DbReader) LoadActions() (err error) {
|
||||
dbr.actions, err = dbr.storDB.GetTpActions(dbr.tpid, "")
|
||||
dbr.actions, err = dbr.storDb.GetTpActions(dbr.tpid, "")
|
||||
return err
|
||||
}
|
||||
|
||||
func (dbr *DbReader) LoadActionTimings() (err error) {
|
||||
atsMap, err := dbr.storDB.GetTpActionTimings(dbr.tpid, "")
|
||||
atsMap, err := dbr.storDb.GetTpActionTimings(dbr.tpid, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -301,12 +300,12 @@ func (dbr *DbReader) LoadActionTimings() (err error) {
|
||||
}
|
||||
|
||||
func (dbr *DbReader) LoadActionTriggers() (err error) {
|
||||
dbr.actionsTriggers, err = dbr.storDB.GetTpActionTriggers(dbr.tpid, "")
|
||||
dbr.actionsTriggers, err = dbr.storDb.GetTpActionTriggers(dbr.tpid, "")
|
||||
return err
|
||||
}
|
||||
|
||||
func (dbr *DbReader) LoadAccountActions() (err error) {
|
||||
acs, err := dbr.storDB.GetTpAccountActions(dbr.tpid, "")
|
||||
acs, err := dbr.storDb.GetTpAccountActions(dbr.tpid, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -335,3 +334,117 @@ func (dbr *DbReader) LoadAccountActions() (err error) {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dbr *DbReader) LoadAccountActionsByTag(tag string) error {
|
||||
accountActions, err := dbr.storDb.GetTpAccountActions(dbr.tpid, tag)
|
||||
if err != nil || len(accountActions) != 1 {
|
||||
return err
|
||||
}
|
||||
accountAction := accountActions[0]
|
||||
actionTimingsMap, err := dbr.storDb.GetTpActionTimings(dbr.tpid, accountAction.ActionTimingsTag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
actionTriggersMap, err := dbr.storDb.GetTpActionTriggers(dbr.tpid, accountAction.ActionTriggersTag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// collecting action ids
|
||||
var actionsIds []string
|
||||
for _, atms := range actionTimingsMap {
|
||||
for _, atm := range atms {
|
||||
actionsIds = append(actionsIds, atm.ActionsId)
|
||||
}
|
||||
}
|
||||
for _, atrs := range actionTriggersMap {
|
||||
for _, atr := range atrs {
|
||||
actionsIds = append(actionsIds, atr.ActionsId)
|
||||
}
|
||||
}
|
||||
var acts map[string][]*Action
|
||||
for _, actId := range actionsIds {
|
||||
actions, err := dbr.storDb.GetTpActions(dbr.tpid, actId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for id, act := range actions {
|
||||
acts[id] = act
|
||||
}
|
||||
}
|
||||
|
||||
// write action timings
|
||||
for k, atms := range actionTimingsMap {
|
||||
err = dbr.storDb.SetActionTimings(k, atms)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// write actions
|
||||
for k, as := range acts {
|
||||
err = dbr.storDb.SetActions(k, as)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
activationPeriods := make(map[string]*ActivationPeriod)
|
||||
resultRatingProfile := &RatingProfile{Id: tag}
|
||||
rpm, err := dbr.storDb.GetTpRatingProfiles(dbr.tpid, tag)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if len(rpm) == 0 {
|
||||
return fmt.Errorf("No RateProfile with id: %s", tag)
|
||||
}
|
||||
for _, ratingProfile := range rpm {
|
||||
resultRatingProfile.FallbackKey = ratingProfile.FallbackKey // it will be the last fallback key
|
||||
at := time.Unix(ratingProfile.activationTime, 0)
|
||||
drtm, err := dbr.storDb.GetTpDestinationRateTimings(dbr.tpid, ratingProfile.destRatesTimingTag)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if len(drtm) == 0 {
|
||||
return fmt.Errorf("No DestRateTimings profile with id: %s", ratingProfile.destRatesTimingTag)
|
||||
}
|
||||
for _, destrateTiming := range drtm {
|
||||
tm, err := dbr.storDb.GetTpTimings(dbr.tpid, destrateTiming.TimingsTag)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if len(tm) == 0 {
|
||||
return fmt.Errorf("No Timings profile with id: %s", destrateTiming.TimingsTag)
|
||||
}
|
||||
destrateTiming.timing = tm[destrateTiming.TimingsTag]
|
||||
drm, err := dbr.storDb.GetTpDestinationRates(dbr.tpid, destrateTiming.DestinationRatesTag)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if len(drm) == 0 {
|
||||
return fmt.Errorf("No Timings profile with id: %s", destrateTiming.DestinationRatesTag)
|
||||
}
|
||||
for _, drate := range drm[destrateTiming.DestinationRatesTag] {
|
||||
rt, err := dbr.storDb.GetTpRates(dbr.tpid, drate.RateTag)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if len(rt) == 0 {
|
||||
return fmt.Errorf("No Rates profile with id: %s", drate.RateTag)
|
||||
}
|
||||
drate.Rate = rt[drate.RateTag]
|
||||
if _, exists := activationPeriods[destrateTiming.Tag]; !exists {
|
||||
activationPeriods[destrateTiming.Tag] = &ActivationPeriod{}
|
||||
}
|
||||
activationPeriods[destrateTiming.Tag].AddIntervalIfNotPresent(destrateTiming.GetInterval(drate))
|
||||
dm, err := dbr.storDb.GetTpDestinations(dbr.tpid, drate.DestinationsTag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, destination := range dm {
|
||||
ap := activationPeriods[ratingProfile.destRatesTimingTag]
|
||||
newAP := &ActivationPeriod{ActivationTime: at}
|
||||
newAP.Intervals = append(newAP.Intervals, ap.Intervals...)
|
||||
resultRatingProfile.AddActivationPeriodIfNotPresent(destination.Id, newAP)
|
||||
dbr.dataDb.SetDestination(destination)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dbr.dataDb.SetRatingProfile(resultRatingProfile)
|
||||
}
|
||||
|
||||
@@ -62,6 +62,10 @@ func (mb *MinuteBucket) Equal(o *MinuteBucket) bool {
|
||||
mb.Percent == o.Percent
|
||||
}
|
||||
|
||||
func (mb *MinuteBucket) IsExpired() bool {
|
||||
return !mb.ExpirationDate.IsZero() && mb.ExpirationDate.Before(time.Now())
|
||||
}
|
||||
|
||||
/*
|
||||
Structure to store minute buckets according to weight, precision or price.
|
||||
*/
|
||||
|
||||
@@ -112,6 +112,7 @@ func (a *Action) Store() (result string, err error) {
|
||||
result += a.ActionType + "|"
|
||||
result += a.BalanceId + "|"
|
||||
result += a.Direction + "|"
|
||||
result += a.ExpirationDate.Format(time.RFC3339) + "|"
|
||||
result += strconv.FormatFloat(a.Units, 'f', -1, 64) + "|"
|
||||
result += strconv.FormatFloat(a.Weight, 'f', -1, 64)
|
||||
if a.MinuteBucket != nil {
|
||||
@@ -125,20 +126,24 @@ func (a *Action) Store() (result string, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (a *Action) Restore(input string) error {
|
||||
func (a *Action) Restore(input string) (err error) {
|
||||
elements := strings.Split(input, "|")
|
||||
if len(elements) < 6 {
|
||||
if len(elements) < 7 {
|
||||
return notEnoughElements
|
||||
}
|
||||
a.Id = elements[0]
|
||||
a.ActionType = elements[1]
|
||||
a.BalanceId = elements[2]
|
||||
a.Direction = elements[3]
|
||||
a.Units, _ = strconv.ParseFloat(elements[4], 64)
|
||||
a.Weight, _ = strconv.ParseFloat(elements[5], 64)
|
||||
if len(elements) == 7 {
|
||||
a.ExpirationDate, err = time.Parse(time.RFC3339, elements[4])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.Units, _ = strconv.ParseFloat(elements[5], 64)
|
||||
a.Weight, _ = strconv.ParseFloat(elements[6], 64)
|
||||
if len(elements) == 8 {
|
||||
a.MinuteBucket = &MinuteBucket{}
|
||||
if err := a.MinuteBucket.Restore(elements[6]); err != nil {
|
||||
if err := a.MinuteBucket.Restore(elements[7]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,8 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/cgrates/cgrates/utils"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
type SQLStorage struct {
|
||||
@@ -550,10 +552,10 @@ func (self *SQLStorage) GetTPActions(tpid, actsId string) (*utils.TPActions, err
|
||||
i := 0
|
||||
for rows.Next() {
|
||||
i++ //Keep here a reference so we know we got at least one result
|
||||
var action, balanceId, dir, destId, rateType string
|
||||
var action, balanceId, dir, destId, rateType string
|
||||
var expTime int64
|
||||
var units, rate, minutesWeight, weight float64
|
||||
if err = rows.Scan(&action, &balanceId, &dir, &units, &expTime, &destId, &rateType, &rate, &minutesWeight, &weight); err!= nil {
|
||||
if err = rows.Scan(&action, &balanceId, &dir, &units, &expTime, &destId, &rateType, &rate, &minutesWeight, &weight); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
acts.Actions = append(acts.Actions, utils.Action{action, balanceId, dir, units, expTime, destId, rateType, rate, minutesWeight, weight})
|
||||
@@ -632,7 +634,7 @@ func (self *SQLStorage) GetTPActionTimings(tpid, atId string) (map[string][]*uti
|
||||
i++ //Keep here a reference so we know we got at least one result
|
||||
var tag, actionsId, timingId string
|
||||
var weight float64
|
||||
if err = rows.Scan(&tag, &actionsId, &timingId, &weight); err!= nil {
|
||||
if err = rows.Scan(&tag, &actionsId, &timingId, &weight); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ats[tag] = append(ats[tag], &utils.TPActionTimingsRow{actionsId, timingId, weight})
|
||||
@@ -969,17 +971,23 @@ func (self *SQLStorage) GetTpActions(tpid, tag string) (map[string][]*Action, er
|
||||
for rows.Next() {
|
||||
var id int
|
||||
var units, rate, minutes_weight, weight float64
|
||||
var tpid, tag, action, balance_tag, direction, destinations_tag, rate_type string
|
||||
if err := rows.Scan(&id, &tpid, &tag, &action, &balance_tag, &direction, &units, &destinations_tag, &rate_type, &rate, &minutes_weight, &weight); err != nil {
|
||||
var tpid, tag, action, balance_tag, direction, destinations_tag, rate_type, expirationDate string
|
||||
if err := rows.Scan(&id, &tpid, &tag, &action, &balance_tag, &direction, &units, &expirationDate, &destinations_tag, &rate_type, &rate, &minutes_weight, &weight); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
unix, err := strconv.ParseInt(expirationDate, 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
expDate := time.Unix(unix, 0)
|
||||
var a *Action
|
||||
if balance_tag != MINUTES {
|
||||
a = &Action{
|
||||
ActionType: action,
|
||||
BalanceId: balance_tag,
|
||||
Direction: direction,
|
||||
Units: units,
|
||||
ActionType: action,
|
||||
BalanceId: balance_tag,
|
||||
Direction: direction,
|
||||
Units: units,
|
||||
ExpirationDate: expDate,
|
||||
}
|
||||
} else {
|
||||
var percent, price float64
|
||||
@@ -990,17 +998,19 @@ func (self *SQLStorage) GetTpActions(tpid, tag string) (map[string][]*Action, er
|
||||
price = rate
|
||||
}
|
||||
a = &Action{
|
||||
Id: utils.GenUUID(),
|
||||
ActionType: action,
|
||||
BalanceId: balance_tag,
|
||||
Direction: direction,
|
||||
Weight: weight,
|
||||
Id: utils.GenUUID(),
|
||||
ActionType: action,
|
||||
BalanceId: balance_tag,
|
||||
Direction: direction,
|
||||
Weight: weight,
|
||||
ExpirationDate: expDate,
|
||||
MinuteBucket: &MinuteBucket{
|
||||
Seconds: units,
|
||||
Weight: minutes_weight,
|
||||
Price: price,
|
||||
Percent: percent,
|
||||
DestinationId: destinations_tag,
|
||||
Seconds: units,
|
||||
Weight: minutes_weight,
|
||||
Price: price,
|
||||
Percent: percent,
|
||||
DestinationId: destinations_tag,
|
||||
ExpirationDate: expDate,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ package rater
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/cgrates/cgrates/utils"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -62,8 +63,7 @@ type Balance struct {
|
||||
}
|
||||
|
||||
func (b *Balance) Equal(o *Balance) bool {
|
||||
return b.Value == o.Value ||
|
||||
b.ExpirationDate.Equal(o.ExpirationDate) ||
|
||||
return b.ExpirationDate.Equal(o.ExpirationDate) ||
|
||||
b.Weight == o.Weight
|
||||
}
|
||||
|
||||
@@ -150,6 +150,9 @@ func (ub *UserBalance) getSecondsForPrefix(prefix string) (seconds, credit float
|
||||
return
|
||||
}
|
||||
for _, mb := range ub.MinuteBuckets {
|
||||
if mb.IsExpired() {
|
||||
continue
|
||||
}
|
||||
d, err := GetDestination(mb.DestinationId)
|
||||
if err != nil {
|
||||
continue
|
||||
@@ -177,6 +180,9 @@ func (ub *UserBalance) debitMinuteBucket(newMb *MinuteBucket) error {
|
||||
}
|
||||
found := false
|
||||
for _, mb := range ub.MinuteBuckets {
|
||||
if mb.IsExpired() {
|
||||
continue
|
||||
}
|
||||
if mb.Equal(newMb) {
|
||||
mb.Seconds -= newMb.Seconds
|
||||
found = true
|
||||
@@ -242,6 +248,29 @@ func (ub *UserBalance) debitMinutesBalance(amount float64, prefix string, count
|
||||
return nil
|
||||
}
|
||||
|
||||
// Debits some amount of user's specified balance adding the balance if it does not exists.
|
||||
// Returns the remaining credit in user's balance.
|
||||
func (ub *UserBalance) debitBalanceAction(a *Action) float64 {
|
||||
newBalance := &Balance{
|
||||
Id: utils.GenUUID(),
|
||||
ExpirationDate: a.ExpirationDate,
|
||||
Weight: a.Weight,
|
||||
}
|
||||
found := false
|
||||
id := a.BalanceId + a.Direction
|
||||
for _, b := range ub.BalanceMap[id] {
|
||||
if b.Equal(newBalance) {
|
||||
b.Value -= a.Units
|
||||
found = true
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
newBalance.Value -= a.Units
|
||||
ub.BalanceMap[id] = append(ub.BalanceMap[id], newBalance)
|
||||
}
|
||||
return ub.BalanceMap[a.BalanceId+OUTBOUND].GetTotalValue()
|
||||
}
|
||||
|
||||
/*
|
||||
Debits some amount of user's specified balance. Returns the remaining credit in user's balance.
|
||||
*/
|
||||
@@ -397,3 +426,21 @@ func (ub *UserBalance) initMinuteCounters() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (ub *UserBalance) CleanExpiredBalancesAndBuckets() {
|
||||
for key, _ := range ub.BalanceMap {
|
||||
bm := ub.BalanceMap[key]
|
||||
for i := 0; i < len(bm); i++ {
|
||||
if bm[i].IsExpired() {
|
||||
// delete it
|
||||
bm = append(bm[:i], bm[i+1:]...)
|
||||
}
|
||||
}
|
||||
ub.BalanceMap[key] = bm
|
||||
}
|
||||
for i := 0; i < len(ub.MinuteBuckets); i++ {
|
||||
if ub.MinuteBuckets[i].IsExpired() {
|
||||
ub.MinuteBuckets = append(ub.MinuteBuckets[:i], ub.MinuteBuckets[i+1:]...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -469,6 +469,27 @@ func TestUserBalanceExecuteTriggeredActionsOrder(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCleanExpired(t *testing.T) {
|
||||
ub := &UserBalance{
|
||||
Id: "TEST_UB_OREDER",
|
||||
BalanceMap: map[string]BalanceChain{CREDIT + OUTBOUND: BalanceChain{
|
||||
&Balance{ExpirationDate: time.Now().Add(10 * time.Second)},
|
||||
&Balance{ExpirationDate: time.Date(2013, 7, 18, 14, 33, 0, 0, time.UTC)},
|
||||
&Balance{ExpirationDate: time.Now().Add(10 * time.Second)}}},
|
||||
MinuteBuckets: []*MinuteBucket{
|
||||
&MinuteBucket{ExpirationDate: time.Date(2013, 7, 18, 14, 33, 0, 0, time.UTC)},
|
||||
&MinuteBucket{ExpirationDate: time.Now().Add(10 * time.Second)},
|
||||
},
|
||||
}
|
||||
ub.CleanExpiredBalancesAndBuckets()
|
||||
if len(ub.BalanceMap[CREDIT+OUTBOUND]) != 2 {
|
||||
t.Error("Error cleaning expired balances!")
|
||||
}
|
||||
if len(ub.MinuteBuckets) != 1 {
|
||||
t.Error("Error cleaning expired minute buckets!")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserBalanceUnitCounting(t *testing.T) {
|
||||
ub := &UserBalance{}
|
||||
ub.countUnits(&Action{BalanceId: CREDIT, Direction: OUTBOUND, Units: 10})
|
||||
|
||||
Reference in New Issue
Block a user