mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-21 23:28:44 +05:00
rating subject can have multiple predefined values
Valid values can start with *zero + any duration 1m, 15m, 1h10m
This commit is contained in:
@@ -136,56 +136,50 @@ func (b *Balance) DebitMinutes(cc *CallCost, count bool, ub *UserBalance, moneyB
|
||||
if increment.paid {
|
||||
continue
|
||||
}
|
||||
if b.RateSubject == ZEROSECOND || b.RateSubject == "" {
|
||||
amount := increment.Duration.Seconds()
|
||||
if b.Value >= amount {
|
||||
b.Value -= amount
|
||||
increment.SetMinuteBalance(b.Uuid)
|
||||
increment.MinuteInfo = &MinuteInfo{cc.Destination, amount}
|
||||
increment.Cost = 0
|
||||
increment.paid = true
|
||||
if count {
|
||||
ub.countUnits(&Action{BalanceId: MINUTES, Direction: cc.Direction, Balance: &Balance{Value: amount, DestinationId: cc.Destination}})
|
||||
}
|
||||
if duration, err := utils.ParseZeroRatingSubject(b.RateSubject); err == nil {
|
||||
seconds := duration.Seconds()
|
||||
amount := seconds
|
||||
if seconds == 1 {
|
||||
amount = increment.Duration.Seconds()
|
||||
}
|
||||
continue
|
||||
}
|
||||
if b.RateSubject == ZEROMINUTE {
|
||||
amount := time.Minute.Seconds()
|
||||
if b.Value >= amount { // balance has at least 60 seconds
|
||||
newTs := ts
|
||||
if incrementIndex != 0 {
|
||||
// if increment it's not at the begining we must split the timespan
|
||||
newTs = ts.SplitByIncrement(incrementIndex)
|
||||
}
|
||||
newTs.RoundToDuration(time.Minute)
|
||||
newTs.RateInterval = &RateInterval{
|
||||
Rating: &RIRate{
|
||||
Rates: RateGroups{
|
||||
&Rate{
|
||||
GroupIntervalStart: 0,
|
||||
Value: 0,
|
||||
RateIncrement: time.Minute,
|
||||
RateUnit: time.Minute,
|
||||
inc := increment
|
||||
if seconds > 1 { // we need to recreate increments
|
||||
if incrementIndex != 0 {
|
||||
// if increment it's not at the begining we must split the timespan
|
||||
newTs = ts.SplitByIncrement(incrementIndex)
|
||||
}
|
||||
newTs.RoundToDuration(time.Minute)
|
||||
newTs.RateInterval = &RateInterval{
|
||||
Rating: &RIRate{
|
||||
Rates: RateGroups{
|
||||
&Rate{
|
||||
GroupIntervalStart: 0,
|
||||
Value: 0,
|
||||
RateIncrement: time.Minute,
|
||||
RateUnit: time.Minute,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
newTs.createIncrementsSlice()
|
||||
// insert the new timespan
|
||||
if newTs != ts {
|
||||
tsIndex++
|
||||
cc.Timespans = append(cc.Timespans, nil)
|
||||
copy(cc.Timespans[tsIndex+1:], cc.Timespans[tsIndex:])
|
||||
cc.Timespans[tsIndex] = newTs
|
||||
tsWasSplit = true
|
||||
}
|
||||
cc.Timespans.RemoveOverlapedFromIndex(tsIndex)
|
||||
inc = newTs.Increments[0]
|
||||
}
|
||||
newTs.createIncrementsSlice()
|
||||
// insert the new timespan
|
||||
if newTs != ts {
|
||||
tsIndex++
|
||||
cc.Timespans = append(cc.Timespans, nil)
|
||||
copy(cc.Timespans[tsIndex+1:], cc.Timespans[tsIndex:])
|
||||
cc.Timespans[tsIndex] = newTs
|
||||
tsWasSplit = true
|
||||
}
|
||||
cc.Timespans.RemoveOverlapedFromIndex(tsIndex)
|
||||
b.Value -= amount
|
||||
newTs.Increments[0].SetMinuteBalance(b.Uuid)
|
||||
newTs.Increments[0].MinuteInfo = &MinuteInfo{cc.Destination, amount}
|
||||
newTs.Increments[0].Cost = 0
|
||||
newTs.Increments[0].paid = true
|
||||
inc.SetMinuteBalance(b.Uuid)
|
||||
inc.MinuteInfo = &MinuteInfo{cc.Destination, amount}
|
||||
inc.Cost = 0
|
||||
inc.paid = true
|
||||
if count {
|
||||
ub.countUnits(&Action{BalanceId: MINUTES, Direction: cc.Direction, Balance: &Balance{Value: amount, DestinationId: cc.Destination}})
|
||||
}
|
||||
|
||||
@@ -531,7 +531,6 @@ func (cd *CallDescriptor) MaxDebit() (cc *CallCost, err error) {
|
||||
if err != nil || remainingDuration == 0 {
|
||||
return new(CallCost), errors.New("no more credit")
|
||||
}
|
||||
log.Print("REM_DUR: ", remainingDuration)
|
||||
if remainingDuration > 0 { // for postpaying client returns -1
|
||||
cd.TimeEnd = cd.TimeStart.Add(remainingDuration)
|
||||
}
|
||||
|
||||
@@ -48,9 +48,6 @@ const (
|
||||
TRIGGER_MAX_COUNTER = "*max_counter"
|
||||
TRIGGER_MIN_BALANCE = "*min_balance"
|
||||
TRIGGER_MAX_BALANCE = "*max_balance"
|
||||
// minute subjects
|
||||
ZEROSECOND = "*zerosecond"
|
||||
ZEROMINUTE = "*zerominute"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@@ -214,7 +214,7 @@ func TestDebitNegativeMoneyBalance(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDebitCreditZeroSecond(t *testing.T) {
|
||||
b1 := &Balance{Uuid: "testb", Value: 10, Weight: 10, DestinationId: "NAT", RateSubject: ZEROSECOND}
|
||||
b1 := &Balance{Uuid: "testb", Value: 10, Weight: 10, DestinationId: "NAT", RateSubject: "*zero1s"}
|
||||
cc := &CallCost{
|
||||
Direction: OUTBOUND,
|
||||
Destination: "0723045326",
|
||||
@@ -243,7 +243,7 @@ func TestDebitCreditZeroSecond(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDebitCreditZeroMinute(t *testing.T) {
|
||||
b1 := &Balance{Uuid: "testb", Value: 70, Weight: 10, DestinationId: "NAT", RateSubject: ZEROMINUTE}
|
||||
b1 := &Balance{Uuid: "testb", Value: 70, Weight: 10, DestinationId: "NAT", RateSubject: "*zero1m"}
|
||||
cc := &CallCost{
|
||||
Direction: OUTBOUND,
|
||||
Destination: "0723045326",
|
||||
@@ -276,8 +276,8 @@ func TestDebitCreditZeroMinute(t *testing.T) {
|
||||
}
|
||||
}
|
||||
func TestDebitCreditZeroMixedMinute(t *testing.T) {
|
||||
b1 := &Balance{Uuid: "testm", Value: 70, Weight: 5, DestinationId: "NAT", RateSubject: ZEROMINUTE}
|
||||
b2 := &Balance{Uuid: "tests", Value: 10, Weight: 10, DestinationId: "NAT", RateSubject: ZEROSECOND}
|
||||
b1 := &Balance{Uuid: "testm", Value: 70, Weight: 5, DestinationId: "NAT", RateSubject: "*zero1m"}
|
||||
b2 := &Balance{Uuid: "tests", Value: 10, Weight: 10, DestinationId: "NAT", RateSubject: "*zero1s"}
|
||||
cc := &CallCost{
|
||||
Direction: OUTBOUND,
|
||||
Destination: "0723045326",
|
||||
@@ -311,7 +311,7 @@ func TestDebitCreditZeroMixedMinute(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDebitCreditNoCredit(t *testing.T) {
|
||||
b1 := &Balance{Uuid: "testb", Value: 70, Weight: 10, DestinationId: "NAT", RateSubject: ZEROMINUTE}
|
||||
b1 := &Balance{Uuid: "testb", Value: 70, Weight: 10, DestinationId: "NAT", RateSubject: "*zero1m"}
|
||||
cc := &CallCost{
|
||||
Direction: OUTBOUND,
|
||||
Destination: "0723045326",
|
||||
@@ -351,7 +351,7 @@ func TestDebitCreditNoCredit(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDebitCreditHasCredit(t *testing.T) {
|
||||
b1 := &Balance{Uuid: "testb", Value: 70, Weight: 10, DestinationId: "NAT", RateSubject: ZEROMINUTE}
|
||||
b1 := &Balance{Uuid: "testb", Value: 70, Weight: 10, DestinationId: "NAT", RateSubject: "*zero1m"}
|
||||
cc := &CallCost{
|
||||
Direction: OUTBOUND,
|
||||
Destination: "0723045326",
|
||||
@@ -393,7 +393,7 @@ func TestDebitCreditHasCredit(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDebitCreditSplitMinutesMoney(t *testing.T) {
|
||||
b1 := &Balance{Uuid: "testb", Value: 10, Weight: 10, DestinationId: "NAT", RateSubject: ZEROSECOND}
|
||||
b1 := &Balance{Uuid: "testb", Value: 10, Weight: 10, DestinationId: "NAT", RateSubject: "*zero1s"}
|
||||
cc := &CallCost{
|
||||
Direction: OUTBOUND,
|
||||
Destination: "0723045326",
|
||||
@@ -430,7 +430,7 @@ func TestDebitCreditSplitMinutesMoney(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDebitCreditMoreTimespans(t *testing.T) {
|
||||
b1 := &Balance{Uuid: "testb", Value: 150, Weight: 10, DestinationId: "NAT", RateSubject: ZEROMINUTE}
|
||||
b1 := &Balance{Uuid: "testb", Value: 150, Weight: 10, DestinationId: "NAT", RateSubject: "*zero1m"}
|
||||
cc := &CallCost{
|
||||
Direction: OUTBOUND,
|
||||
Destination: "0723045326",
|
||||
@@ -467,8 +467,8 @@ func TestDebitCreditMoreTimespans(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDebitCreditMoreTimespansMixed(t *testing.T) {
|
||||
b1 := &Balance{Uuid: "testb", Value: 70, Weight: 10, DestinationId: "NAT", RateSubject: ZEROMINUTE}
|
||||
b2 := &Balance{Uuid: "testa", Value: 150, Weight: 5, DestinationId: "NAT", RateSubject: ZEROSECOND}
|
||||
b1 := &Balance{Uuid: "testb", Value: 70, Weight: 10, DestinationId: "NAT", RateSubject: "*zero1m"}
|
||||
b2 := &Balance{Uuid: "testa", Value: 150, Weight: 5, DestinationId: "NAT", RateSubject: "*zero1s"}
|
||||
cc := &CallCost{
|
||||
Direction: OUTBOUND,
|
||||
Destination: "0723045326",
|
||||
@@ -506,7 +506,7 @@ func TestDebitCreditMoreTimespansMixed(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDebitCreditNoConectFeeCredit(t *testing.T) {
|
||||
b1 := &Balance{Uuid: "testb", Value: 70, Weight: 10, DestinationId: "NAT", RateSubject: ZEROMINUTE}
|
||||
b1 := &Balance{Uuid: "testb", Value: 70, Weight: 10, DestinationId: "NAT", RateSubject: "*zero1m"}
|
||||
cc := &CallCost{
|
||||
Direction: OUTBOUND,
|
||||
Destination: "0723045326",
|
||||
|
||||
163
utils/consts.go
163
utils/consts.go
@@ -1,87 +1,88 @@
|
||||
package utils
|
||||
|
||||
const (
|
||||
VERSION = "0.9.1c4"
|
||||
POSTGRES = "postgres"
|
||||
MYSQL = "mysql"
|
||||
MONGO = "mongo"
|
||||
REDIS = "redis"
|
||||
LOCALHOST = "127.0.0.1"
|
||||
FSCDR_FILE_CSV = "freeswitch_file_csv"
|
||||
FSCDR_HTTP_JSON = "freeswitch_http_json"
|
||||
NOT_IMPLEMENTED = "not implemented"
|
||||
PREPAID = "prepaid"
|
||||
POSTPAID = "postpaid"
|
||||
PSEUDOPREPAID = "pseudoprepaid"
|
||||
RATED = "rated"
|
||||
ERR_NOT_IMPLEMENTED = "NOT_IMPLEMENTED"
|
||||
ERR_SERVER_ERROR = "SERVER_ERROR"
|
||||
ERR_NOT_FOUND = "NOT_FOUND"
|
||||
ERR_MANDATORY_IE_MISSING = "MANDATORY_IE_MISSING"
|
||||
ERR_EXISTS = "EXISTS"
|
||||
ERR_BROKEN_REFERENCE = "BROKEN_REFERENCE"
|
||||
TBL_TP_TIMINGS = "tp_timings"
|
||||
TBL_TP_DESTINATIONS = "tp_destinations"
|
||||
TBL_TP_RATES = "tp_rates"
|
||||
TBL_TP_DESTINATION_RATES = "tp_destination_rates"
|
||||
TBL_TP_RATING_PLANS = "tp_rating_plans"
|
||||
TBL_TP_RATE_PROFILES = "tp_rating_profiles"
|
||||
TBL_TP_ACTIONS = "tp_actions"
|
||||
TBL_TP_ACTION_PLANS = "tp_action_plans"
|
||||
TBL_TP_ACTION_TRIGGERS = "tp_action_triggers"
|
||||
TBL_TP_ACCOUNT_ACTIONS = "tp_account_actions"
|
||||
TBL_CDRS_PRIMARY = "cdrs_primary"
|
||||
TBL_CDRS_EXTRA = "cdrs_extra"
|
||||
TBL_COST_DETAILS = "cost_details"
|
||||
TBL_RATED_CDRS = "rated_cdrs"
|
||||
TIMINGS_CSV = "Timings.csv"
|
||||
DESTINATIONS_CSV = "Destinations.csv"
|
||||
RATES_CSV = "Rates.csv"
|
||||
DESTINATION_RATES_CSV = "DestinationRates.csv"
|
||||
RATING_PLANS_CSV = "RatingPlans.csv"
|
||||
RATING_PROFILES_CSV = "RatingProfiles.csv"
|
||||
ACTIONS_CSV = "Actions.csv"
|
||||
ACTION_PLANS_CSV = "ActionPlans.csv"
|
||||
ACTION_TRIGGERS_CSV = "ActionTriggers.csv"
|
||||
ACCOUNT_ACTIONS_CSV = "AccountActions.csv"
|
||||
TIMINGS_NRCOLS = 6
|
||||
DESTINATIONS_NRCOLS = 2
|
||||
RATES_NRCOLS = 8
|
||||
DESTINATION_RATES_NRCOLS = 3
|
||||
DESTRATE_TIMINGS_NRCOLS = 4
|
||||
RATE_PROFILES_NRCOLS = 7
|
||||
ACTIONS_NRCOLS = 11
|
||||
ACTION_PLANS_NRCOLS = 4
|
||||
ACTION_TRIGGERS_NRCOLS = 8
|
||||
ACCOUNT_ACTIONS_NRCOLS = 5
|
||||
ROUNDING_UP = "*up"
|
||||
ROUNDING_MIDDLE = "*middle"
|
||||
ROUNDING_DOWN = "*down"
|
||||
ANY = "*any"
|
||||
COMMENT_CHAR = '#'
|
||||
CSV_SEP = ','
|
||||
FALLBACK_SEP = ';'
|
||||
JSON = "json"
|
||||
MSGPACK = "msgpack"
|
||||
CSV_LOAD = "CSVLOAD"
|
||||
CGRID = "cgrid"
|
||||
ACCID = "accid"
|
||||
CDRHOST = "cdrhost"
|
||||
CDRSOURCE = "cdrsource"
|
||||
REQTYPE = "reqtype"
|
||||
DIRECTION = "direction"
|
||||
TENANT = "tenant"
|
||||
TOR = "tor"
|
||||
ACCOUNT = "account"
|
||||
SUBJECT = "subject"
|
||||
DESTINATION = "destination"
|
||||
ANSWER_TIME = "answer_time"
|
||||
DURATION = "duration"
|
||||
DEFAULT_RUNID = "default"
|
||||
STATIC_VALUE_PREFIX = "^"
|
||||
CDRE_CSV = "csv"
|
||||
CDRE_DRYRUN = "dry_run"
|
||||
INTERNAL = "internal"
|
||||
VERSION = "0.9.1c4"
|
||||
POSTGRES = "postgres"
|
||||
MYSQL = "mysql"
|
||||
MONGO = "mongo"
|
||||
REDIS = "redis"
|
||||
LOCALHOST = "127.0.0.1"
|
||||
FSCDR_FILE_CSV = "freeswitch_file_csv"
|
||||
FSCDR_HTTP_JSON = "freeswitch_http_json"
|
||||
NOT_IMPLEMENTED = "not implemented"
|
||||
PREPAID = "prepaid"
|
||||
POSTPAID = "postpaid"
|
||||
PSEUDOPREPAID = "pseudoprepaid"
|
||||
RATED = "rated"
|
||||
ERR_NOT_IMPLEMENTED = "NOT_IMPLEMENTED"
|
||||
ERR_SERVER_ERROR = "SERVER_ERROR"
|
||||
ERR_NOT_FOUND = "NOT_FOUND"
|
||||
ERR_MANDATORY_IE_MISSING = "MANDATORY_IE_MISSING"
|
||||
ERR_EXISTS = "EXISTS"
|
||||
ERR_BROKEN_REFERENCE = "BROKEN_REFERENCE"
|
||||
TBL_TP_TIMINGS = "tp_timings"
|
||||
TBL_TP_DESTINATIONS = "tp_destinations"
|
||||
TBL_TP_RATES = "tp_rates"
|
||||
TBL_TP_DESTINATION_RATES = "tp_destination_rates"
|
||||
TBL_TP_RATING_PLANS = "tp_rating_plans"
|
||||
TBL_TP_RATE_PROFILES = "tp_rating_profiles"
|
||||
TBL_TP_ACTIONS = "tp_actions"
|
||||
TBL_TP_ACTION_PLANS = "tp_action_plans"
|
||||
TBL_TP_ACTION_TRIGGERS = "tp_action_triggers"
|
||||
TBL_TP_ACCOUNT_ACTIONS = "tp_account_actions"
|
||||
TBL_CDRS_PRIMARY = "cdrs_primary"
|
||||
TBL_CDRS_EXTRA = "cdrs_extra"
|
||||
TBL_COST_DETAILS = "cost_details"
|
||||
TBL_RATED_CDRS = "rated_cdrs"
|
||||
TIMINGS_CSV = "Timings.csv"
|
||||
DESTINATIONS_CSV = "Destinations.csv"
|
||||
RATES_CSV = "Rates.csv"
|
||||
DESTINATION_RATES_CSV = "DestinationRates.csv"
|
||||
RATING_PLANS_CSV = "RatingPlans.csv"
|
||||
RATING_PROFILES_CSV = "RatingProfiles.csv"
|
||||
ACTIONS_CSV = "Actions.csv"
|
||||
ACTION_PLANS_CSV = "ActionPlans.csv"
|
||||
ACTION_TRIGGERS_CSV = "ActionTriggers.csv"
|
||||
ACCOUNT_ACTIONS_CSV = "AccountActions.csv"
|
||||
TIMINGS_NRCOLS = 6
|
||||
DESTINATIONS_NRCOLS = 2
|
||||
RATES_NRCOLS = 8
|
||||
DESTINATION_RATES_NRCOLS = 3
|
||||
DESTRATE_TIMINGS_NRCOLS = 4
|
||||
RATE_PROFILES_NRCOLS = 7
|
||||
ACTIONS_NRCOLS = 11
|
||||
ACTION_PLANS_NRCOLS = 4
|
||||
ACTION_TRIGGERS_NRCOLS = 8
|
||||
ACCOUNT_ACTIONS_NRCOLS = 5
|
||||
ROUNDING_UP = "*up"
|
||||
ROUNDING_MIDDLE = "*middle"
|
||||
ROUNDING_DOWN = "*down"
|
||||
ANY = "*any"
|
||||
COMMENT_CHAR = '#'
|
||||
CSV_SEP = ','
|
||||
FALLBACK_SEP = ';'
|
||||
JSON = "json"
|
||||
MSGPACK = "msgpack"
|
||||
CSV_LOAD = "CSVLOAD"
|
||||
CGRID = "cgrid"
|
||||
ACCID = "accid"
|
||||
CDRHOST = "cdrhost"
|
||||
CDRSOURCE = "cdrsource"
|
||||
REQTYPE = "reqtype"
|
||||
DIRECTION = "direction"
|
||||
TENANT = "tenant"
|
||||
TOR = "tor"
|
||||
ACCOUNT = "account"
|
||||
SUBJECT = "subject"
|
||||
DESTINATION = "destination"
|
||||
ANSWER_TIME = "answer_time"
|
||||
DURATION = "duration"
|
||||
DEFAULT_RUNID = "default"
|
||||
STATIC_VALUE_PREFIX = "^"
|
||||
CDRE_CSV = "csv"
|
||||
CDRE_DRYRUN = "dry_run"
|
||||
INTERNAL = "internal"
|
||||
ZERO_RATING_SUBJECT_PREFIX = "*zero"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@@ -195,3 +195,14 @@ func MinDuration(d1, d2 time.Duration) time.Duration {
|
||||
}
|
||||
return d2
|
||||
}
|
||||
|
||||
func ParseZeroRatingSubject(rateSubj string) (time.Duration, error) {
|
||||
if rateSubj == "" {
|
||||
rateSubj = ZERO_RATING_SUBJECT_PREFIX + "1s"
|
||||
}
|
||||
if !strings.HasPrefix(rateSubj, ZERO_RATING_SUBJECT_PREFIX) {
|
||||
return 0, errors.New("malformed rating subject: " + rateSubj)
|
||||
}
|
||||
durStr := rateSubj[len(ZERO_RATING_SUBJECT_PREFIX):]
|
||||
return time.ParseDuration(durStr)
|
||||
}
|
||||
|
||||
@@ -346,3 +346,13 @@ func TestMinDuration(t *testing.T) {
|
||||
t.Error("Error getting min duration: ", minD1, minD2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseZeroRatingSubject(t *testing.T) {
|
||||
subj := []string{"", "*zero1s", "*zero5m", "*zero10h"}
|
||||
dur := []time.Duration{time.Second, time.Second, 5 * time.Minute, 10 * time.Hour}
|
||||
for i, s := range subj {
|
||||
if d, err := ParseZeroRatingSubject(s); err != nil || d != dur[i] {
|
||||
t.Error("Error parsing rating subject: ", s, d, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user