Example #1
0
// Write one CDR and test it's results only for content buffer
func TestWriteCdr(t *testing.T) {
	wrBuf := &bytes.Buffer{}
	logDb, _ := engine.NewMapStorage()
	cfg, _ := config.NewDefaultCGRConfig()
	fixedWidth := utils.CDRE_FIXED_WIDTH
	exportTpl := &config.CgrXmlCdreCfg{
		CdrFormat: &fixedWidth,
		Header:    &config.CgrXmlCfgCdrHeader{Fields: hdrCfgFlds},
		Content:   &config.CgrXmlCfgCdrContent{Fields: contentCfgFlds},
		Trailer:   &config.CgrXmlCfgCdrTrailer{Fields: trailerCfgFlds},
	}
	cdr := &utils.StoredCdr{CgrId: utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC).String()),
		TOR: utils.VOICE, OrderId: 1, AccId: "dsafdsaf", CdrHost: "192.168.1.1",
		ReqType: "rated", Direction: "*out", Tenant: "cgrates.org",
		Category: "call", Account: "1001", Subject: "1001", Destination: "1002",
		SetupTime:  time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC),
		AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC),
		Usage:      time.Duration(10) * time.Second, MediationRunId: utils.DEFAULT_RUNID, Cost: 2.34567,
		ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"},
	}
	cdre, err := NewCdrExporter([]*utils.StoredCdr{cdr}, logDb, exportTpl.AsCdreConfig(), utils.CDRE_FIXED_WIDTH, ',', "fwv_1", 0.0, 0.0, 0, 4, cfg.RoundingDecimals, "", -1, cfg.HttpSkipTlsVerify)
	if err != nil {
		t.Error(err)
	}
	eHeader := "10   VOI0000007111308420024031415390001                                                                                                         \n"
	eContentOut := "201001        1001                1002                    0211  07111308420010          1       3dsafdsaf                             0002.34570\n"
	eTrailer := "90   VOI0000000000100000010071113084260071113084200                                                                                             \n"
	if err := cdre.writeOut(wrBuf); err != nil {
		t.Error(err)
	}
	allOut := wrBuf.String()
	eAllOut := eHeader + eContentOut + eTrailer
	if math.Mod(float64(len(allOut)), 145) != 0 {
		t.Error("Unexpected export content length", len(allOut))
	} else if len(allOut) != len(eAllOut) {
		t.Errorf("Output does not match expected length. Have output %q, expecting: %q", allOut, eAllOut)
	}
	// Test stats
	if !cdre.firstCdrATime.Equal(cdr.AnswerTime) {
		t.Error("Unexpected firstCdrATime in stats: ", cdre.firstCdrATime)
	} else if !cdre.lastCdrATime.Equal(cdr.AnswerTime) {
		t.Error("Unexpected lastCdrATime in stats: ", cdre.lastCdrATime)
	} else if cdre.numberOfRecords != 1 {
		t.Error("Unexpected number of records in the stats: ", cdre.numberOfRecords)
	} else if cdre.totalDuration != cdr.Usage {
		t.Error("Unexpected total duration in the stats: ", cdre.totalDuration)
	} else if cdre.totalCost != utils.Round(cdr.Cost, cdre.roundDecimals, utils.ROUNDING_MIDDLE) {
		t.Error("Unexpected total cost in the stats: ", cdre.totalCost)
	}
	if cdre.FirstOrderId() != 1 {
		t.Error("Unexpected FirstOrderId", cdre.FirstOrderId())
	}
	if cdre.LastOrderId() != 1 {
		t.Error("Unexpected LastOrderId", cdre.LastOrderId())
	}
	if cdre.TotalCost() != utils.Round(cdr.Cost, cdre.roundDecimals, utils.ROUNDING_MIDDLE) {
		t.Error("Unexpected TotalCost: ", cdre.TotalCost())
	}
}
Example #2
0
func SureTaxProcessCdr(cdr *CDR) error {
	stCfg := config.CgrConfig().SureTaxCfg()
	if stCfg == nil {
		return errors.New("Invalid SureTax configuration")
	}
	if sureTaxClient == nil { // First time used, init the client here
		tr := &http.Transport{
			TLSClientConfig: &tls.Config{InsecureSkipVerify: config.CgrConfig().HttpSkipTlsVerify},
		}
		sureTaxClient = &http.Client{Transport: tr}
	}
	req, err := NewSureTaxRequest(cdr, stCfg)
	if err != nil {
		return err
	}
	jsnContent, err := json.Marshal(req)
	if err != nil {
		return err
	}
	resp, err := sureTaxClient.Post(stCfg.Url, "application/json", bytes.NewBuffer(jsnContent))
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	respBody, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	if resp.StatusCode > 299 {
		return fmt.Errorf("Unexpected status code received: %d", resp.StatusCode)
	}
	var respFull SureTaxResponse
	if err := json.Unmarshal(respBody, &respFull); err != nil {
		return err
	}
	var stResp STResponse
	if err := json.Unmarshal([]byte(respFull.D), &stResp); err != nil {
		return err
	}
	if stResp.ResponseCode != "9999" {
		cdr.ExtraInfo = stResp.HeaderMessage
		return nil // No error because the request was processed by SureTax, error will be in the ExtraInfo
	}
	// Write cost to CDR
	totalTax, err := strconv.ParseFloat(stResp.TotalTax, 64)
	if err != nil {
		cdr.ExtraInfo = err.Error()
	}
	if !stCfg.IncludeLocalCost {
		cdr.Cost = utils.Round(totalTax, config.CgrConfig().RoundingDecimals, utils.ROUNDING_MIDDLE)
	} else {
		cdr.Cost = utils.Round(cdr.Cost+totalTax, config.CgrConfig().RoundingDecimals, utils.ROUNDING_MIDDLE)
	}
	// Add response into extra fields to be available for later review
	cdr.ExtraFields[utils.META_SURETAX] = respFull.D
	return nil
}
Example #3
0
func SureTaxProcessCdr(cdr *StoredCdr) error {
	stCfg := config.CgrConfig().SureTaxCfg()
	if stCfg == nil {
		return errors.New("Invalid SureTax configuration")
	}
	if sureTaxClient == nil { // First time used, init the client here
		tr := &http.Transport{
			TLSClientConfig: &tls.Config{InsecureSkipVerify: config.CgrConfig().HttpSkipTlsVerify},
		}
		sureTaxClient = &http.Client{Transport: tr}
	}
	req, err := NewSureTaxRequest(cdr, stCfg)
	if err != nil {
		return err
	}

	body, err := json.Marshal(req)
	if err != nil {
		return err
	}
	utils.Logger.Debug(fmt.Sprintf("###SureTax NewSureTaxRequest: %+v, ItemList: %+v\n", req, req.ItemList[0]))
	resp, err := sureTaxClient.Post(stCfg.Url, "application/json", bytes.NewBuffer(body))
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	respBody, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	if resp.StatusCode > 299 {
		return fmt.Errorf("Unexpected status code received: %d", resp.StatusCode)
	}
	var stResp SureTaxResponse
	if err := json.Unmarshal(respBody, &stResp); err != nil {
		return err
	}
	utils.Logger.Debug(fmt.Sprintf("###SureTax received response: %+v\n", stResp))
	if stResp.ResponseCode != 9999 {
		cdr.ExtraInfo = stResp.HeaderMessage
		return nil // No error because the request was processed by SureTax, error will be in the ExtraInfo
	}
	// Write cost to CDR
	if !stCfg.IncludeLocalCost {
		cdr.Cost = utils.Round(stResp.TotalTax, config.CgrConfig().RoundingDecimals, utils.ROUNDING_MIDDLE)
	} else {
		cdr.Cost = utils.Round(cdr.Cost+stResp.TotalTax, config.CgrConfig().RoundingDecimals, utils.ROUNDING_MIDDLE)
	}
	// Add response into extra fields to be available for later review
	cdr.ExtraFields[utils.META_SURETAX] = string(respBody)
	return nil
}
Example #4
0
// Useful for CDR generation
func (kev KamEvent) ParseEventValue(rsrFld *utils.RSRField, timezone string) string {
	sTime, _ := kev.GetSetupTime(utils.META_DEFAULT, config.CgrConfig().DefaultTimezone)
	aTime, _ := kev.GetAnswerTime(utils.META_DEFAULT, config.CgrConfig().DefaultTimezone)
	duration, _ := kev.GetDuration(utils.META_DEFAULT)
	switch rsrFld.Id {
	case utils.CGRID:
		return rsrFld.ParseValue(kev.GetCgrId(timezone))
	case utils.TOR:
		return rsrFld.ParseValue(utils.VOICE)
	case utils.ACCID:
		return rsrFld.ParseValue(kev.GetUUID())
	case utils.CDRHOST:
		return rsrFld.ParseValue(kev.GetOriginatorIP(utils.META_DEFAULT))
	case utils.CDRSOURCE:
		return rsrFld.ParseValue(kev.GetCdrSource())
	case utils.REQTYPE:
		return rsrFld.ParseValue(kev.GetReqType(utils.META_DEFAULT))
	case utils.DIRECTION:
		return rsrFld.ParseValue(kev.GetDirection(utils.META_DEFAULT))
	case utils.TENANT:
		return rsrFld.ParseValue(kev.GetTenant(utils.META_DEFAULT))
	case utils.CATEGORY:
		return rsrFld.ParseValue(kev.GetCategory(utils.META_DEFAULT))
	case utils.ACCOUNT:
		return rsrFld.ParseValue(kev.GetAccount(utils.META_DEFAULT))
	case utils.SUBJECT:
		return rsrFld.ParseValue(kev.GetSubject(utils.META_DEFAULT))
	case utils.DESTINATION:
		return rsrFld.ParseValue(kev.GetDestination(utils.META_DEFAULT))
	case utils.SETUP_TIME:
		return rsrFld.ParseValue(sTime.String())
	case utils.ANSWER_TIME:
		return rsrFld.ParseValue(aTime.String())
	case utils.USAGE:
		return rsrFld.ParseValue(strconv.FormatFloat(utils.Round(duration.Seconds(), 0, utils.ROUNDING_MIDDLE), 'f', -1, 64))
	case utils.PDD:
		return rsrFld.ParseValue(strconv.FormatFloat(utils.Round(duration.Seconds(), 0, utils.ROUNDING_MIDDLE), 'f', -1, 64))
	case utils.SUPPLIER:
		return rsrFld.ParseValue(kev.GetSupplier(utils.META_DEFAULT))
	case utils.DISCONNECT_CAUSE:
		return rsrFld.ParseValue(kev.GetDisconnectCause(utils.META_DEFAULT))
	case utils.MEDI_RUNID:
		return rsrFld.ParseValue(utils.META_DEFAULT)
	case utils.COST:
		return rsrFld.ParseValue("-1.0")
	default:
		return rsrFld.ParseValue(kev.GetExtraFields()[rsrFld.Id])
	}
}
Example #5
0
func (incs Increments) GetTotalCost() float64 {
	cost := 0.0
	for _, increment := range incs {
		cost += increment.GetCost()
	}
	return utils.Round(cost, globalRoundingDecimals, utils.ROUNDING_MIDDLE)
}
Example #6
0
func (cc *CallCost) Round() {
	if len(cc.Timespans) == 0 || cc.Timespans[0] == nil {
		return
	}
	var totalCorrectionCost float64
	for _, ts := range cc.Timespans {
		if len(ts.Increments) == 0 {
			continue // safe check
		}
		inc := ts.Increments[0]
		if inc.BalanceInfo.Monetary == nil || inc.Cost == 0 {
			// this is a unit payied timespan, nothing to round
			continue
		}
		cost := ts.CalculateCost()
		roundedCost := utils.Round(cost, ts.RateInterval.Rating.RoundingDecimals,
			ts.RateInterval.Rating.RoundingMethod)
		correctionCost := roundedCost - cost
		//log.Print(cost, roundedCost, correctionCost)
		if correctionCost != 0 {
			ts.RoundIncrement = &Increment{
				Cost:        correctionCost,
				BalanceInfo: inc.BalanceInfo,
			}
			totalCorrectionCost += correctionCost
			ts.Cost += correctionCost
		}
	}
	cc.Cost += totalCorrectionCost
}
Example #7
0
// Interface method used to add/substract an amount of cents or bonus seconds (as returned by GetCost method)
// from user's money balance.
func (cd *CallDescriptor) debit(account *Account) (cc *CallCost, err error) {
	cc, err = cd.GetCost()
	cc.Timespans.Decompress()
	if err != nil {
		Logger.Err(fmt.Sprintf("<Rater> Error getting cost for account key %v: %v", cd.GetAccountKey(), err))
		return
	}
	//Logger.Debug(fmt.Sprintf("<Rater> Attempting to debit from %v, value: %v", cd.GetAccountKey(), cc.Cost+cc.ConnectFee))
	defer accountingStorage.SetAccount(account)
	//ub, _ := json.Marshal(account)
	//Logger.Debug(fmt.Sprintf("Account: %s", ub))
	//cCost, _ := json.Marshal(cc)
	//Logger.Debug(fmt.Sprintf("CallCost: %s", cCost))
	if cc.Cost != 0 || (cc.deductConnectFee && cc.GetConnectFee() != 0) {
		account.debitCreditBalance(cc, true)
	}
	cost := 0.0
	// re-calculate call cost after balances
	if cc.deductConnectFee { // add back the connectFee
		cost += cc.GetConnectFee()
	}
	for _, ts := range cc.Timespans {
		cost += ts.getCost()
		cost = utils.Round(cost, globalRoundingDecimals, utils.ROUNDING_MIDDLE) // just get rid of the extra decimals
	}
	cc.Cost = cost
	cc.Timespans.Compress()
	return
}
Example #8
0
func (kev KamEvent) AsKamAuthReply(maxSessionTime float64, suppliers string, resErr error) (*KamAuthReply, error) {
	var err error
	kar := &KamAuthReply{Event: CGR_AUTH_REPLY, Suppliers: suppliers}
	if resErr != nil {
		kar.Error = resErr.Error()
	}
	if _, hasIt := kev[KAM_TR_INDEX]; !hasIt {
		return nil, utils.NewErrMandatoryIeMissing(KAM_TR_INDEX, "")
	}
	if kar.TransactionIndex, err = strconv.Atoi(kev[KAM_TR_INDEX]); err != nil {
		return nil, err
	}
	if _, hasIt := kev[KAM_TR_LABEL]; !hasIt {
		return nil, utils.NewErrMandatoryIeMissing(KAM_TR_LABEL, "")
	}
	if kar.TransactionLabel, err = strconv.Atoi(kev[KAM_TR_LABEL]); err != nil {
		return nil, err
	}
	if maxSessionTime != -1 { // Convert maxSessionTime from nanoseconds into seconds
		maxSessionDur := time.Duration(maxSessionTime)
		maxSessionTime = maxSessionDur.Seconds()
	}
	kar.MaxSessionTime = int(utils.Round(maxSessionTime, 0, utils.ROUNDING_MIDDLE))
	return kar, nil
}
Example #9
0
func (PDD *PDDMetric) GetValue() float64 {
	if PDD.count == 0 {
		return STATS_NA
	}
	val := PDD.sum.Seconds() / PDD.count
	return utils.Round(val, globalRoundingDecimals, utils.ROUNDING_MIDDLE)
}
Example #10
0
// cgr-console 'cost Category="call" Tenant="cgrates.org" Subject="1001" Destination="1004" TimeStart="2015-11-07T08:42:26Z" TimeEnd="2015-11-07T08:57:26Z"'
func TestDmtAgentSendCCRUpdate2(t *testing.T) {
	cdr := &engine.CDR{CGRID: utils.Sha1("testccr1", time.Date(2015, 11, 7, 8, 42, 20, 0, time.UTC).String()), OrderID: 123, ToR: utils.VOICE,
		OriginID: "testccr1", OriginHost: "192.168.1.1", Source: utils.UNIT_TEST, RequestType: utils.META_RATED, Direction: "*out",
		Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1004", Supplier: "SUPPL1",
		SetupTime: time.Date(2015, 11, 7, 8, 42, 20, 0, time.UTC), AnswerTime: time.Date(2015, 11, 7, 8, 42, 26, 0, time.UTC), RunID: utils.DEFAULT_RUNID,
		Usage: time.Duration(600) * time.Second, PDD: time.Duration(7) * time.Second, ExtraFields: map[string]string{"Service-Context-Id": "*****@*****.**"},
	}
	ccr := storedCdrToCCR(cdr, "UNIT_TEST", daCfg.DiameterAgentCfg().OriginRealm, daCfg.DiameterAgentCfg().VendorId,
		daCfg.DiameterAgentCfg().ProductName, utils.DIAMETER_FIRMWARE_REVISION, daCfg.DiameterAgentCfg().DebitInterval, false)
	m, err := ccr.AsDiameterMessage()
	if err != nil {
		t.Error(err)
	}
	if err := dmtClient.SendMessage(m); err != nil {
		t.Error(err)
	}
	time.Sleep(time.Duration(*waitRater) * time.Millisecond)
	msg := dmtClient.ReceivedMessage(rplyTimeout)
	if avps, err := msg.FindAVPsWithPath([]interface{}{"Granted-Service-Unit", "CC-Time"}, dict.UndefinedVendorID); err != nil {
		t.Error(err)
	} else if len(avps) == 0 {
		t.Error("Granted-Service-Unit not found")
	} else if strCCTime := avpValAsString(avps[0]); strCCTime != "300" {
		t.Errorf("Expecting 300, received: %s", strCCTime)
	}
	var acnt *engine.Account
	attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"}
	eAcntVal := 9.002800
	if err := apierRpc.Call("ApierV2.GetAccount", attrs, &acnt); err != nil {
		t.Error(err)
	} else if utils.Round(acnt.BalanceMap[utils.MONETARY].GetTotalValue(), 5, utils.ROUNDING_MIDDLE) != eAcntVal {
		t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.MONETARY].GetTotalValue())
	}
}
Example #11
0
// Interface method used to add/substract an amount of cents or bonus seconds (as returned by GetCost method)
// from user's money balance.
func (cd *CallDescriptor) debit(account *Account, dryRun bool, goNegative bool) (cc *CallCost, err error) {
	if cd.TimeEnd.Sub(cd.TimeStart) == 0 {
		return cd.CreateCallCost(), nil
	}
	if !dryRun {
		defer accountingStorage.SetAccount(account)
	}
	if cd.TOR == "" {
		cd.TOR = utils.VOICE
	}
	//log.Printf("Debit CD: %+v", cd)
	cc, err = account.debitCreditBalance(cd, !dryRun, dryRun, goNegative)
	//log.Printf("HERE: %+v %v", cc, err)
	if err != nil {
		utils.Logger.Err(fmt.Sprintf("<Rater> Error getting cost for account key <%s>: %s", cd.GetAccountKey(), err.Error()))
		return nil, err
	}
	cost := 0.0
	// calculate call cost after balances
	if cc.deductConnectFee { // add back the connectFee
		cost += cc.GetConnectFee()
	}
	for _, ts := range cc.Timespans {
		cost += ts.getCost()
		cost = utils.Round(cost, globalRoundingDecimals, utils.ROUNDING_MIDDLE) // just get rid of the extra decimals
	}
	cc.Cost = cost
	cc.Timespans.Compress()
	//log.Printf("OUT CC: ", cc)
	return
}
Example #12
0
func (acd *ACDMetric) GetValue() float64 {
	if acd.count == 0 {
		return STATS_NA
	}
	val := acd.sum.Seconds() / acd.count
	return utils.Round(val, globalRoundingDecimals, utils.ROUNDING_MIDDLE)
}
Example #13
0
func (b *Balance) SetValue(amount float64) {
	b.Value = amount
	b.Value = utils.Round(b.GetValue(), globalRoundingDecimals, utils.ROUNDING_MIDDLE)
	b.dirty = true

	// publish event
	accountId := ""
	allowNegative := ""
	disabled := ""
	if b.account != nil {
		accountId = b.account.Id
		allowNegative = strconv.FormatBool(b.account.AllowNegative)
		disabled = strconv.FormatBool(b.account.Disabled)
	}
	Publish(CgrEvent{
		"EventName":            utils.EVT_ACCOUNT_BALANCE_MODIFIED,
		"Uuid":                 b.Uuid,
		"Id":                   b.Id,
		"Value":                strconv.FormatFloat(b.Value, 'f', -1, 64),
		"ExpirationDate":       b.ExpirationDate.String(),
		"Weight":               strconv.FormatFloat(b.Weight, 'f', -1, 64),
		"DestinationIds":       b.DestinationIds,
		"RatingSubject":        b.RatingSubject,
		"Category":             b.Category,
		"SharedGroup":          b.SharedGroup,
		"TimingIDs":            b.TimingIDs,
		"Account":              accountId,
		"AccountAllowNegative": allowNegative,
		"AccountDisabled":      disabled,
	})
}
Example #14
0
func (acc *ACCMetric) GetValue() float64 {
	if acc.count == 0 {
		return STATS_NA
	}
	val := acc.sum / acc.count
	return utils.Round(val, globalRoundingDecimals, utils.ROUNDING_MIDDLE)
}
Example #15
0
// Handle various meta functions used in header/trailer
func (cdre *CdrExporter) metaHandler(tag, arg string) (string, error) {
	switch tag {
	case META_EXPORTID:
		return cdre.exportId, nil
	case META_TIMENOW:
		return time.Now().Format(arg), nil
	case META_FIRSTCDRATIME:
		return cdre.firstCdrATime.Format(arg), nil
	case META_LASTCDRATIME:
		return cdre.lastCdrATime.Format(arg), nil
	case META_NRCDRS:
		return strconv.Itoa(cdre.numberOfRecords), nil
	case META_DURCDRS:
		emulatedCdr := &engine.CDR{ToR: utils.VOICE, Usage: cdre.totalDuration}
		return emulatedCdr.FormatUsage(arg), nil
	case META_SMSUSAGE:
		emulatedCdr := &engine.CDR{ToR: utils.SMS, Usage: cdre.totalSmsUsage}
		return emulatedCdr.FormatUsage(arg), nil
	case META_MMSUSAGE:
		emulatedCdr := &engine.CDR{ToR: utils.MMS, Usage: cdre.totalMmsUsage}
		return emulatedCdr.FormatUsage(arg), nil
	case META_GENERICUSAGE:
		emulatedCdr := &engine.CDR{ToR: utils.GENERIC, Usage: cdre.totalGenericUsage}
		return emulatedCdr.FormatUsage(arg), nil
	case META_DATAUSAGE:
		emulatedCdr := &engine.CDR{ToR: utils.DATA, Usage: cdre.totalDataUsage}
		return emulatedCdr.FormatUsage(arg), nil
	case META_COSTCDRS:
		return strconv.FormatFloat(utils.Round(cdre.totalCost, cdre.cgrPrecision, utils.ROUNDING_MIDDLE), 'f', -1, 64), nil
	default:
		return "", fmt.Errorf("Unsupported METATAG: %s", tag)
	}
}
Example #16
0
func (asr *ASRMetric) GetValue() float64 {
	if asr.count == 0 {
		return STATS_NA
	}
	val := asr.answered / asr.count * 100
	return utils.Round(val, globalRoundingDecimals, utils.ROUNDING_MIDDLE)
}
Example #17
0
File: cdr.go Project: rinor/cgrates
// Formats usage on export
func (cdr *CDR) FormatUsage(layout string) string {
	if utils.IsSliceMember([]string{utils.DATA, utils.SMS, utils.MMS, utils.GENERIC}, cdr.ToR) {
		return strconv.FormatFloat(utils.Round(cdr.Usage.Seconds(), 0, utils.ROUNDING_MIDDLE), 'f', -1, 64)
	}
	switch layout {
	default:
		return strconv.FormatFloat(float64(cdr.Usage.Nanoseconds())/1000000000, 'f', -1, 64)
	}
}
Example #18
0
func (bc Balances) GetTotalValue() (total float64) {
	for _, b := range bc {
		if !b.IsExpired() && b.IsActive() {
			total += b.GetValue()
		}
	}
	total = utils.Round(total, globalRoundingDecimals, utils.ROUNDING_MIDDLE)
	return
}
Example #19
0
func (cd *CallDescriptor) getCost() (*CallCost, error) {

	// check for 0 duration
	if cd.GetDuration() == 0 {
		cc := cd.CreateCallCost()
		// add RatingInfo
		err := cd.LoadRatingPlans()
		if err == nil && len(cd.RatingInfos) > 0 {
			ts := &TimeSpan{
				TimeStart: cd.TimeStart,
				TimeEnd:   cd.TimeEnd,
			}
			ts.setRatingInfo(cd.RatingInfos[0])
			cc.Timespans = append(cc.Timespans, ts)
		}
		return cc, nil
	}
	if cd.DurationIndex < cd.TimeEnd.Sub(cd.TimeStart) {
		cd.DurationIndex = cd.TimeEnd.Sub(cd.TimeStart)
	}
	if cd.TOR == "" {
		cd.TOR = utils.VOICE
	}
	err := cd.LoadRatingPlans()
	//log.Print("ERR: ", err)
	//log.Print("RI: ", utils.ToJSON(cd.RatingInfos))
	if err != nil {
		//utils.Logger.Err(fmt.Sprintf("error getting cost for key <%s>: %s", cd.GetKey(cd.Subject), err.Error()))
		return &CallCost{Cost: -1}, err
	}
	timespans := cd.splitInTimeSpans()
	cost := 0.0

	for i, ts := range timespans {
		ts.createIncrementsSlice()
		// only add connect fee if this is the first/only call cost request
		//log.Printf("Interval: %+v", ts.RateInterval.Timing)
		if cd.LoopIndex == 0 && i == 0 && ts.RateInterval != nil {
			cost += ts.RateInterval.Rating.ConnectFee
		}
		cost += ts.CalculateCost()
	}

	//startIndex := len(fmt.Sprintf("%s:%s:%s:", cd.Direction, cd.Tenant, cd.Category))
	cc := cd.CreateCallCost()
	cc.Cost = cost
	cc.Timespans = timespans

	// global rounding
	roundingDecimals, roundingMethod := cc.GetLongestRounding()
	cc.Cost = utils.Round(cc.Cost, roundingDecimals, roundingMethod)
	//utils.Logger.Info(fmt.Sprintf("<Rater> Get Cost: %s => %v", cd.GetKey(), cc))
	cc.Timespans.Compress()
	cc.UpdateRatedUsage()
	return cc, err
}
Example #20
0
// Init a new request to be sent out to SureTax
func NewSureTaxRequest(cdr *StoredCdr, stCfg *config.SureTaxCfg) (*SureTaxRequest, error) {
	if stCfg == nil {
		return nil, errors.New("Invalid SureTax config.")
	}
	aTimeLoc := cdr.AnswerTime.In(stCfg.Timezone)
	revenue := utils.Round(cdr.Cost, 4, utils.ROUNDING_MIDDLE)
	unts, err := strconv.ParseInt(cdr.FieldsAsString(stCfg.Units), 10, 64)
	if err != nil {
		return nil, err
	}
	taxExempt := []string{}
	definedTaxExtempt := cdr.FieldsAsString(stCfg.TaxExemptionCodeList)
	if len(definedTaxExtempt) != 0 {
		taxExempt = strings.Split(cdr.FieldsAsString(stCfg.TaxExemptionCodeList), ",")
	}
	stReq := new(STRequest)
	stReq.ClientNumber = stCfg.ClientNumber
	stReq.BusinessUnit = stCfg.BusinessUnit
	stReq.ValidationKey = stCfg.ValidationKey
	stReq.DataYear = strconv.Itoa(aTimeLoc.Year())
	stReq.DataMonth = strconv.Itoa(int(aTimeLoc.Month()))
	stReq.TotalRevenue = revenue
	stReq.ReturnFileCode = stCfg.ReturnFileCode
	stReq.ClientTracking = cdr.FieldsAsString(stCfg.ClientTracking)
	stReq.ResponseGroup = stCfg.ResponseGroup
	stReq.ResponseType = stCfg.ResponseType
	stReq.ItemList = []*STRequestItem{
		&STRequestItem{
			CustomerNumber:       cdr.FieldsAsString(stCfg.CustomerNumber),
			OrigNumber:           cdr.FieldsAsString(stCfg.OrigNumber),
			TermNumber:           cdr.FieldsAsString(stCfg.TermNumber),
			BillToNumber:         cdr.FieldsAsString(stCfg.BillToNumber),
			Zipcode:              cdr.FieldsAsString(stCfg.Zipcode),
			Plus4:                cdr.FieldsAsString(stCfg.Plus4),
			P2PZipcode:           cdr.FieldsAsString(stCfg.P2PZipcode),
			P2PPlus4:             cdr.FieldsAsString(stCfg.P2PPlus4),
			TransDate:            aTimeLoc.Format("2006-01-02T15:04:05"),
			Revenue:              revenue,
			Units:                unts,
			UnitType:             cdr.FieldsAsString(stCfg.UnitType),
			Seconds:              int64(cdr.Usage.Seconds()),
			TaxIncludedCode:      cdr.FieldsAsString(stCfg.TaxIncluded),
			TaxSitusRule:         cdr.FieldsAsString(stCfg.TaxSitusRule),
			TransTypeCode:        cdr.FieldsAsString(stCfg.TransTypeCode),
			SalesTypeCode:        cdr.FieldsAsString(stCfg.SalesTypeCode),
			RegulatoryCode:       stCfg.RegulatoryCode,
			TaxExemptionCodeList: taxExempt,
		},
	}
	jsnContent, err := json.Marshal(stReq)
	if err != nil {
		return nil, err
	}
	return &SureTaxRequest{Request: string(jsnContent)}, nil
}
Example #21
0
// Returns the cost of the timespan according to the relevant cost interval.
// It also sets the Cost field of this timespan (used for refund on session
// manager debit loop where the cost cannot be recalculated)
func (ts *TimeSpan) calculateCost() float64 {
	if ts.Increments.Length() == 0 {
		if ts.RateInterval == nil {
			return 0
		}
		return ts.RateInterval.GetCost(ts.GetDuration(), ts.GetGroupStart())
	} else {
		cost := ts.Increments.GetTotalCost()
		return utils.Round(cost, globalRoundingDecimals, utils.ROUNDING_MIDDLE)
	}
}
Example #22
0
func (cc *CallCost) updateCost() {
	cost := 0.0
	if cc.deductConnectFee { // add back the connectFee
		cost += cc.GetConnectFee()
	}
	for _, ts := range cc.Timespans {
		ts.Cost = ts.CalculateCost()
		cost += ts.Cost
		cost = utils.Round(cost, globalRoundingDecimals, utils.ROUNDING_MIDDLE) // just get rid of the extra decimals
	}
	cc.Cost = cost
}
Example #23
0
// Returns the cost of the timespan according to the relevant cost interval.
// It also sets the Cost field of this timespan (used for refund on session
// manager debit loop where the cost cannot be recalculated)
func (ts *TimeSpan) getCost() float64 {
	if ts.Increments.Length() == 0 {
		if ts.RateInterval == nil {
			return 0
		}
		cost := ts.RateInterval.GetCost(ts.GetDuration(), ts.GetGroupStart())
		ts.Cost = utils.Round(cost, ts.RateInterval.Rating.RoundingDecimals, ts.RateInterval.Rating.RoundingMethod)
		return ts.Cost
	} else {
		return ts.Increments[0].Cost * float64(ts.Increments.Length())
	}
}
Example #24
0
// Returns the cost of the timespan according to the relevant cost interval.
// It also sets the Cost field of this timespan (used for refund on session
// manager debit loop where the cost cannot be recalculated)
func (ts *TimeSpan) getCost() float64 {
	if ts.Increments.Length() == 0 {
		if ts.RateInterval == nil {
			return 0
		}
		cost := ts.RateInterval.GetCost(ts.GetDuration(), ts.GetGroupStart())
		ts.Cost = utils.Round(cost, ts.RateInterval.Rating.RoundingDecimals, ts.RateInterval.Rating.RoundingMethod)
		return ts.Cost
	} else {
		cost := 0.0
		// some increments may have 0 cost because of the max  cost strategy
		for _, inc := range ts.Increments {
			cost += inc.Cost
		}
		if ts.RateInterval != nil && ts.RateInterval.Rating != nil {
			return utils.Round(cost, ts.RateInterval.Rating.RoundingDecimals, ts.RateInterval.Rating.RoundingMethod)
		} else {
			return utils.Round(cost, globalRoundingDecimals, utils.ROUNDING_MIDDLE)
		}
	}
}
Example #25
0
func metaSum(m *diam.Message, argsTpl utils.RSRFields, passAtIndex, roundingDecimals int) (string, error) {
	valStr := composedFieldvalue(m, argsTpl, passAtIndex, nil)
	handlerArgs := strings.Split(valStr, utils.HandlerArgSep)
	var summed float64
	for _, arg := range handlerArgs {
		val, err := strconv.ParseFloat(arg, 64)
		if err != nil {
			return "", err
		}
		summed += val
	}
	return strconv.FormatFloat(utils.Round(summed, roundingDecimals, utils.ROUNDING_MIDDLE), 'f', -1, 64), nil
}
Example #26
0
// Init a new request to be sent out to SureTax
func NewSureTaxRequest(clientNumber, validationKey string, timezone *time.Location, originationNrTpl, terminationNrTpl utils.RSRFields, cdr *StoredCdr) (*SureTaxRequest, error) {
	if clientNumber == "" {
		return nil, utils.NewErrMandatoryIeMissing("ClientNumber")
	}
	if validationKey == "" {
		return nil, utils.NewErrMandatoryIeMissing("ValidationKey")
	}
	aTime := cdr.AnswerTime.In(timezone)
	stReq := &SureTaxRequest{ClientNumber: clientNumber,
		ValidationKey:  validationKey,
		DataYear:       strconv.Itoa(aTime.Year()),
		DataMonth:      strconv.Itoa(int(aTime.Month())),
		TotalRevenue:   utils.Round(cdr.Cost, 4, utils.ROUNDING_MIDDLE),
		ReturnFileCode: "0",
		ClientTracking: cdr.CgrId,
		ResponseGroup:  "03",
		ResponseType:   "",
		ItemList: []*STRequestItem{
			&STRequestItem{
				OrigNumber:           cdr.FieldsAsString(originationNrTpl),
				TermNumber:           cdr.FieldsAsString(terminationNrTpl),
				BillToNumber:         cdr.FieldsAsString(originationNrTpl),
				TransDate:            aTime.Format("2006-01-02T15:04:05"),
				Revenue:              utils.Round(cdr.Cost, 4, utils.ROUNDING_MIDDLE),
				Units:                1,
				UnitType:             "00",
				Seconds:              int64(utils.Round(cdr.Usage.Seconds(), 0, utils.ROUNDING_MIDDLE)),
				TaxIncludedCode:      "0",
				TaxSitusRule:         "1",
				TransTypeCode:        "010101",
				SalesTypeCode:        "R",
				RegulatoryCode:       "01",
				TaxExemptionCodeList: []string{"00"},
			},
		},
	}
	return stReq, nil
}
Example #27
0
// metaValueExponent will multiply the float value with the exponent provided.
// Expects 2 arguments in template separated by |
func metaValueExponent(m *diam.Message, argsTpl utils.RSRFields, roundingDecimals int) (string, error) {
	valStr := composedFieldvalue(m, argsTpl, 0, nil)
	handlerArgs := strings.Split(valStr, utils.HandlerArgSep)
	if len(handlerArgs) != 2 {
		return "", errors.New("Unexpected number of arguments")
	}
	val, err := strconv.ParseFloat(handlerArgs[0], 64)
	if err != nil {
		return "", err
	}
	exp, err := strconv.Atoi(handlerArgs[1])
	if err != nil {
		return "", err
	}
	res := val * math.Pow10(exp)
	return strconv.FormatFloat(utils.Round(res, roundingDecimals, utils.ROUNDING_MIDDLE), 'f', -1, 64), nil
}
Example #28
0
/*
Creates a CallCost structure with the cost information calculated for the received CallDescriptor.
*/
func (cd *CallDescriptor) GetCost() (*CallCost, error) {
	if cd.DurationIndex < cd.TimeEnd.Sub(cd.TimeStart) {
		cd.DurationIndex = cd.TimeEnd.Sub(cd.TimeStart)
	}
	if cd.TOR == "" {
		cd.TOR = MINUTES
	}
	err := cd.LoadRatingPlans()
	if err != nil {
		Logger.Err(fmt.Sprintf("error getting cost for key %s: %v", cd.GetKey(cd.Subject), err))
		return &CallCost{Cost: -1}, err
	}

	timespans := cd.splitInTimeSpans()
	cost := 0.0

	for i, ts := range timespans {
		// only add connect fee if this is the first/only call cost request
		if cd.LoopIndex == 0 && i == 0 && ts.RateInterval != nil {
			cost += ts.RateInterval.Rating.ConnectFee
		}
		cost += ts.getCost()
	}
	//startIndex := len(fmt.Sprintf("%s:%s:%s:", cd.Direction, cd.Tenant, cd.Category))
	cc := &CallCost{
		Direction:        cd.Direction,
		Category:         cd.Category,
		Tenant:           cd.Tenant,
		Account:          cd.Account,
		Destination:      cd.Destination,
		Subject:          cd.Subject,
		Cost:             cost,
		Timespans:        timespans,
		deductConnectFee: cd.LoopIndex == 0,
		TOR:              cd.TOR,
	}
	// global rounding
	roundingDecimals, roundingMethod := cc.GetLongestRounding()
	cc.Cost = utils.Round(cc.Cost, roundingDecimals, roundingMethod)
	//Logger.Info(fmt.Sprintf("<Rater> Get Cost: %s => %v", cd.GetKey(), cc))
	cc.Timespans.Compress()
	return cc, err
}
Example #29
0
func (cd *CallDescriptor) getCost() (*CallCost, error) {
	// check for 0 duration
	if cd.TimeEnd.Sub(cd.TimeStart) == 0 {
		return cd.CreateCallCost(), nil
	}
	if cd.DurationIndex < cd.TimeEnd.Sub(cd.TimeStart) {
		cd.DurationIndex = cd.TimeEnd.Sub(cd.TimeStart)
	}
	if cd.TOR == "" {
		cd.TOR = utils.VOICE
	}
	err := cd.LoadRatingPlans()
	if err != nil {
		utils.Logger.Err(fmt.Sprintf("error getting cost for key <%s>: %s", cd.GetKey(cd.Subject), err.Error()))
		return &CallCost{Cost: -1}, err
	}
	timespans := cd.splitInTimeSpans()
	cost := 0.0

	for i, ts := range timespans {
		// only add connect fee if this is the first/only call cost request
		//log.Printf("Interval: %+v", ts.RateInterval.Timing)
		if cd.LoopIndex == 0 && i == 0 && ts.RateInterval != nil {
			cost += ts.RateInterval.Rating.ConnectFee
		}
		cost += ts.getCost()
	}

	//startIndex := len(fmt.Sprintf("%s:%s:%s:", cd.Direction, cd.Tenant, cd.Category))
	cc := cd.CreateCallCost()
	cc.Cost = cost
	cc.Timespans = timespans

	// global rounding
	roundingDecimals, roundingMethod := cc.GetLongestRounding()
	cc.Cost = utils.Round(cc.Cost, roundingDecimals, roundingMethod)
	//utils.Logger.Info(fmt.Sprintf("<Rater> Get Cost: %s => %v", cd.GetKey(), cc))
	cc.Timespans.Compress()
	return cc, err
}
Example #30
0
/*
Creates a CallCost structure with the cost information calculated for the received CallDescriptor.
*/
func (cd *CallDescriptor) GetCost() (*CallCost, error) {
	cd.account = nil // make sure it's not cached
	cc, err := cd.getCost()
	if err != nil || cd.GetDuration() == 0 {
		return cc, err
	}

	cost := 0.0
	for i, ts := range cc.Timespans {
		// only add connect fee if this is the first/only call cost request
		//log.Printf("Interval: %+v", ts.RateInterval.Timing)
		if cd.LoopIndex == 0 && i == 0 && ts.RateInterval != nil {
			cost += ts.RateInterval.Rating.ConnectFee
		}
		//log.Printf("TS: %+v", ts)
		// handle max cost
		maxCost, strategy := ts.RateInterval.GetMaxCost()

		ts.Cost = ts.calculateCost()
		cost += ts.Cost
		cd.MaxCostSoFar += cost
		//log.Print("Before: ", cost)
		if strategy != "" && maxCost > 0 {
			//log.Print("HERE: ", strategy, maxCost)
			if strategy == utils.MAX_COST_FREE && cd.MaxCostSoFar >= maxCost {
				cost = maxCost
				cd.MaxCostSoFar = maxCost
			}

		}
		//log.Print("Cost: ", cost)
	}
	cc.Cost = cost
	// global rounding
	roundingDecimals, roundingMethod := cc.GetLongestRounding()
	cc.Cost = utils.Round(cc.Cost, roundingDecimals, roundingMethod)

	return cc, nil
}