Example #1
0
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
}
Example #2
0
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
}
Example #3
0
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
}