func (b *Balance) debitUnits(cd *CallDescriptor, ub *Account, moneyBalances Balances, count bool, dryRun, debitConnectFee bool) (cc *CallCost, err error) { if !b.IsActiveAt(cd.TimeStart) || b.GetValue() <= 0 { return } if duration, err := utils.ParseZeroRatingSubject(b.RatingSubject); err == nil { // we have *zero based units cc = cd.CreateCallCost() cc.Timespans = append(cc.Timespans, &TimeSpan{ TimeStart: cd.TimeStart, TimeEnd: cd.TimeEnd, }) ts := cc.Timespans[0] ts.RoundToDuration(duration) ts.RateInterval = &RateInterval{ Rating: &RIRate{ Rates: RateGroups{ &Rate{ GroupIntervalStart: 0, Value: 0, RateIncrement: duration, RateUnit: duration, }, }, }, } prefix, destid := b.getMatchingPrefixAndDestID(cd.Destination) if prefix == "" { prefix = cd.Destination } if destid == "" { destid = utils.ANY } ts.setRatingInfo(&RatingInfo{ MatchedSubject: b.Uuid, MatchedPrefix: prefix, MatchedDestId: destid, RatingPlanId: utils.META_NONE, }) ts.createIncrementsSlice() //log.Printf("CC: %+v", ts) for incIndex, inc := range ts.Increments { //log.Printf("INCREMENET: %+v", inc) amount := inc.Duration.Seconds() if b.Factor != nil { amount = utils.Round(amount/b.Factor.GetValue(cd.TOR), globalRoundingDecimals, utils.ROUNDING_UP) } if b.GetValue() >= amount { b.SubstractValue(amount) inc.BalanceInfo.Unit = &UnitInfo{ UUID: b.Uuid, ID: b.ID, Value: b.Value, DestinationID: cc.Destination, Consumed: amount, TOR: cc.TOR, RateInterval: nil, } inc.BalanceInfo.AccountID = ub.ID inc.Cost = 0 inc.paid = true if count { ub.countUnits(amount, cc.TOR, cc, b) } } else { inc.paid = false // delete the rest of the unpiad increments/timespans if incIndex == 0 { // cat the entire current timespan cc.Timespans = nil } else { ts.SplitByIncrement(incIndex) } if len(cc.Timespans) == 0 { cc = nil } return cc, nil } } } else { // get the cost from balance //log.Printf("::::::: %+v", cd) cc, err = b.GetCost(cd, true) if err != nil { return nil, err } if debitConnectFee { // this is the first add, debit the connect fee if ub.DebitConnectionFee(cc, moneyBalances, count, true) == false { // found blocker balance return nil, nil } } cc.Timespans.Decompress() //log.Printf("CC: %+v", cc) for tsIndex, ts := range cc.Timespans { if ts.Increments == nil { ts.createIncrementsSlice() } if ts.RateInterval == nil { utils.Logger.Err(fmt.Sprintf("Nil RateInterval ERROR on TS: %+v, CC: %+v, from CD: %+v", ts, cc, cd)) return nil, errors.New("timespan with no rate interval assigned") } maxCost, strategy := ts.RateInterval.GetMaxCost() for incIndex, inc := range ts.Increments { // debit minutes and money amount := inc.Duration.Seconds() if b.Factor != nil { amount = utils.Round(amount/b.Factor.GetValue(cd.TOR), globalRoundingDecimals, utils.ROUNDING_UP) } cost := inc.Cost inc.paid = false if strategy == utils.MAX_COST_DISCONNECT && cd.MaxCostSoFar >= maxCost { // cat the entire current timespan cc.maxCostDisconect = true if dryRun { if incIndex == 0 { // cat the entire current timespan cc.Timespans = cc.Timespans[:tsIndex] } else { ts.SplitByIncrement(incIndex) cc.Timespans = cc.Timespans[:tsIndex+1] } return cc, nil } } if strategy == utils.MAX_COST_FREE && cd.MaxCostSoFar >= maxCost { cost, inc.Cost = 0.0, 0.0 inc.BalanceInfo.Monetary = &MonetaryInfo{ UUID: b.Uuid, ID: b.ID, Value: b.Value, RateInterval: ts.RateInterval, } inc.BalanceInfo.AccountID = ub.ID inc.paid = true if count { ub.countUnits(cost, utils.MONETARY, cc, b) } // go to nextincrement continue } var moneyBal *Balance for _, mb := range moneyBalances { if mb.GetValue() >= cost { moneyBal = mb break } } if (cost == 0 || moneyBal != nil) && b.GetValue() >= amount { b.SubstractValue(amount) inc.BalanceInfo.Unit = &UnitInfo{ UUID: b.Uuid, ID: b.ID, Value: b.Value, DestinationID: cc.Destination, Consumed: amount, TOR: cc.TOR, RateInterval: ts.RateInterval, } inc.BalanceInfo.AccountID = ub.ID if cost != 0 { moneyBal.SubstractValue(cost) inc.BalanceInfo.Monetary = &MonetaryInfo{ UUID: moneyBal.Uuid, ID: moneyBal.ID, Value: moneyBal.Value, } cd.MaxCostSoFar += cost } inc.paid = true if count { ub.countUnits(amount, cc.TOR, cc, b) if cost != 0 { ub.countUnits(cost, utils.MONETARY, cc, moneyBal) } } } else { inc.paid = false // delete the rest of the unpiad increments/timespans if incIndex == 0 { // cat the entire current timespan cc.Timespans = cc.Timespans[:tsIndex] } else { ts.SplitByIncrement(incIndex) cc.Timespans = cc.Timespans[:tsIndex+1] } if len(cc.Timespans) == 0 { cc = nil } return cc, nil } } } } return }
func (b *Balance) DebitUnits(cd *CallDescriptor, ub *Account, moneyBalances BalanceChain, count bool, dryRun bool) (cc *CallCost, err error) { if !b.IsActiveAt(cd.TimeStart) || b.GetValue() <= 0 { return } if duration, err := utils.ParseZeroRatingSubject(b.RatingSubject); err == nil { // we have *zero based units cc = cd.CreateCallCost() cc.Timespans = append(cc.Timespans, &TimeSpan{ TimeStart: cd.TimeStart, TimeEnd: cd.TimeEnd, }) seconds := duration.Seconds() amount := seconds ts := cc.Timespans[0] ts.RoundToDuration(duration) ts.RateInterval = &RateInterval{ Rating: &RIRate{ Rates: RateGroups{ &Rate{ GroupIntervalStart: 0, Value: 0, RateIncrement: duration, RateUnit: duration, }, }, }, } prefix, destid := b.getMatchingPrefixAndDestId(cd.Destination) if prefix == "" { prefix = cd.Destination } if destid == "" { destid = utils.ANY } ts.setRatingInfo(&RatingInfo{ MatchedSubject: b.Uuid, MatchedPrefix: prefix, MatchedDestId: destid, RatingPlanId: utils.META_NONE, }) ts.createIncrementsSlice() //log.Printf("CC: %+v", ts) for incIndex, inc := range ts.Increments { //log.Printf("INCREMENET: %+v", inc) if seconds == 1 { amount = inc.Duration.Seconds() } if b.GetValue() >= amount { b.SubstractValue(amount) inc.BalanceInfo.UnitBalanceUuid = b.Uuid inc.BalanceInfo.AccountId = ub.Id inc.UnitInfo = &UnitInfo{cc.Destination, amount, cc.TOR} inc.Cost = 0 inc.paid = true if count { ub.countUnits(&Action{BalanceType: cc.TOR, Direction: cc.Direction, Balance: &Balance{Value: amount, DestinationIds: cc.Destination}}) } } else { inc.paid = false // delete the rest of the unpiad increments/timespans if incIndex == 0 { // cat the entire current timespan cc.Timespans = nil } else { ts.SplitByIncrement(incIndex) } if len(cc.Timespans) == 0 { cc = nil } return cc, nil } } } else { // get the cost from balance //log.Printf("::::::: %+v", cd) cc, err = b.GetCost(cd, true) cc.Timespans.Decompress() //log.Printf("CC: %+v", cc) if err != nil { return nil, fmt.Errorf("Error getting new cost for balance subject: %v", err) } for tsIndex, ts := range cc.Timespans { if ts.Increments == nil { ts.createIncrementsSlice() } if ts.RateInterval == nil { Logger.Err(fmt.Sprintf("Nil RateInterval ERROR on TS: %+v, CC: %+v, from CD: %+v", ts, cc, cd)) return nil, errors.New("timespan with no rate interval assigned") } maxCost, strategy := ts.RateInterval.GetMaxCost() for incIndex, inc := range ts.Increments { // debit minutes and money seconds := inc.Duration.Seconds() cost := inc.Cost //log.Printf("INC: %+v", inc) inc.paid = false if strategy == utils.MAX_COST_DISCONNECT && cd.MaxCostSoFar >= maxCost { // cat the entire current timespan cc.maxCostDisconect = true if dryRun { if incIndex == 0 { // cat the entire current timespan cc.Timespans = cc.Timespans[:tsIndex] } else { ts.SplitByIncrement(incIndex) cc.Timespans = cc.Timespans[:tsIndex+1] } return cc, nil } } if strategy == utils.MAX_COST_FREE && cd.MaxCostSoFar >= maxCost { cost, inc.Cost = 0.0, 0.0 inc.BalanceInfo.MoneyBalanceUuid = b.Uuid inc.BalanceInfo.AccountId = ub.Id inc.paid = true if count { ub.countUnits(&Action{BalanceType: utils.MONETARY, Direction: cc.Direction, Balance: &Balance{Value: cost, DestinationIds: cc.Destination}}) } // go to nextincrement continue } var moneyBal *Balance for _, mb := range moneyBalances { if mb.GetValue() >= cost { moneyBal = mb break } } if (cost == 0 || moneyBal != nil) && b.GetValue() >= seconds { b.SubstractValue(seconds) inc.BalanceInfo.UnitBalanceUuid = b.Uuid inc.BalanceInfo.AccountId = ub.Id inc.UnitInfo = &UnitInfo{cc.Destination, seconds, cc.TOR} if cost != 0 { inc.BalanceInfo.MoneyBalanceUuid = moneyBal.Uuid moneyBal.SubstractValue(cost) cd.MaxCostSoFar += cost } inc.paid = true if count { ub.countUnits(&Action{BalanceType: cc.TOR, Direction: cc.Direction, Balance: &Balance{Value: seconds, DestinationIds: cc.Destination}}) if cost != 0 { ub.countUnits(&Action{BalanceType: utils.MONETARY, Direction: cc.Direction, Balance: &Balance{Value: cost, DestinationIds: cc.Destination}}) } } } else { inc.paid = false // delete the rest of the unpiad increments/timespans if incIndex == 0 { // cat the entire current timespan cc.Timespans = cc.Timespans[:tsIndex] } else { ts.SplitByIncrement(incIndex) cc.Timespans = cc.Timespans[:tsIndex+1] } if len(cc.Timespans) == 0 { cc = nil } return cc, nil } } } } return }
func (b *Balance) DebitUnits(cc *CallCost, count bool, ub *Account, moneyBalances BalanceChain) error { for tsIndex := 0; tsIndex < len(cc.Timespans); tsIndex++ { if b.Value <= 0 { return nil } ts := cc.Timespans[tsIndex] if ts.Increments == nil { ts.createIncrementsSlice() } if paid, _ := ts.IsPaid(); paid { continue } tsWasSplit := false for incrementIndex, increment := range ts.Increments { if tsWasSplit { break } if increment.paid { continue } if duration, err := utils.ParseZeroRatingSubject(b.RatingSubject); err == nil { seconds := duration.Seconds() amount := seconds if seconds == 1 { amount = increment.Duration.Seconds() } if b.Value >= amount { newTs := ts 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(duration) newTs.RateInterval = &RateInterval{ Rating: &RIRate{ Rates: RateGroups{ &Rate{ GroupIntervalStart: 0, Value: 0, RateIncrement: duration, RateUnit: duration, }, }, }, } 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] } b.SubstractAmount(amount) inc.BalanceInfo.UnitBalanceUuid = b.Uuid inc.BalanceInfo.AccountId = ub.Id inc.UnitInfo = &UnitInfo{cc.Destination, amount, cc.TOR} inc.Cost = 0 inc.paid = true if count { ub.countUnits(&Action{BalanceType: cc.TOR, Direction: cc.Direction, Balance: &Balance{Value: amount, DestinationId: cc.Destination}}) } } continue } // get the new rate cd := cc.CreateCallDescriptor() cd.Subject = b.RatingSubject cd.TimeStart = ts.GetTimeStartForIncrement(incrementIndex) cd.TimeEnd = cc.Timespans[len(cc.Timespans)-1].TimeEnd cd.DurationIndex = cc.Timespans[len(cc.Timespans)-1].DurationIndex newCC, err := b.GetCost(cd) if err != nil { Logger.Err(fmt.Sprintf("Error getting new cost for balance subject: %v", err)) continue } //debit new callcost var paidTs []*TimeSpan for _, nts := range newCC.Timespans { nts.createIncrementsSlice() paidTs = append(paidTs, nts) for _, nInc := range nts.Increments { // debit minutes and money seconds := nInc.Duration.Seconds() cost := nInc.Cost var moneyBal *Balance for _, mb := range moneyBalances { if mb.Value >= cost { moneyBal = mb break } } if (cost == 0 || moneyBal != nil) && b.Value >= seconds { b.SubstractAmount(seconds) nInc.BalanceInfo.UnitBalanceUuid = b.Uuid nInc.BalanceInfo.AccountId = ub.Id nInc.UnitInfo = &UnitInfo{newCC.Destination, seconds, cc.TOR} if cost != 0 { nInc.BalanceInfo.MoneyBalanceUuid = moneyBal.Uuid moneyBal.SubstractAmount(cost) } nInc.paid = true if count { ub.countUnits(&Action{BalanceType: newCC.TOR, Direction: newCC.Direction, Balance: &Balance{Value: seconds, DestinationId: newCC.Destination}}) if cost != 0 { ub.countUnits(&Action{BalanceType: CREDIT, Direction: newCC.Direction, Balance: &Balance{Value: cost, DestinationId: newCC.Destination}}) } } } else { increment.paid = false break } } } // make sure the last paid ts is split by the unpaid increment to retain // original rating interval if len(paidTs) > 0 { lastPaidTs := paidTs[len(paidTs)-1] if isPaid, lastPaidIncrementIndex := lastPaidTs.IsPaid(); !isPaid { if lastPaidIncrementIndex > 0 { // shorten the last paid ts lastPaidTs.SplitByIncrement(lastPaidIncrementIndex) } else { // delete if not paid paidTs[len(paidTs)-1] = nil paidTs = paidTs[:len(paidTs)-1] } } } newTs := ts.SplitByIncrement(incrementIndex) increment.paid = (&cc.Timespans).OverlapWithTimeSpans(paidTs, newTs, tsIndex) tsWasSplit = increment.paid if !increment.paid { break } } } return nil }