Revise CDR rerating

If the reRate parameter is set to true, also set the refund to true.

The rerate parameter is now no longer hardcoded to true for the RateCDRs API.If
required, the "*rerate" flag must be provided by the caller.

In case CostDetails is not populated, retrieve it from StorDB if possible
and add it to the CGREvent before converting to CDRs.

Now that the refund happens before the debit, revise the expected values for
the testV1CDRsProcessEventWithRefund subtest within the
apier/v1/cdrs_it_test.go file.
This commit is contained in:
ionutboangiu
2023-04-19 02:54:59 -04:00
committed by Dan Christian Bogos
parent f4c1fa0d3d
commit 373df2329c
4 changed files with 34 additions and 18 deletions

View File

@@ -288,7 +288,7 @@ func testV2CDRsRateCDRs(t *testing.T) {
if err := cdrsRpc.Call(utils.CDRsV1RateCDRs, &engine.ArgRateCDRs{
RPCCDRsFilter: utils.RPCCDRsFilter{NotRunIDs: []string{"raw"}},
Flags: []string{"*chargers:false"},
Flags: []string{"*chargers:false", utils.MetaRerate},
}, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if reply != utils.OK {
@@ -664,7 +664,7 @@ func testV2CDRsRateCDRsWithRatingPlan(t *testing.T) {
if err := cdrsRpc.Call(utils.CDRsV1RateCDRs, &engine.ArgRateCDRs{
RPCCDRsFilter: utils.RPCCDRsFilter{NotRunIDs: []string{"raw"}, Accounts: []string{"testV2CDRsProcessCDR4"}},
Flags: []string{"*chargers:true"},
Flags: []string{"*chargers:true", utils.MetaRerate},
}, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if reply != utils.OK {

View File

@@ -500,6 +500,30 @@ func (cdrS *CDRServer) processEvents(evs []*utils.CGREvent,
cdrs := make([]*CDR, len(cgrEvs))
if refund || ralS || store || reRate || export {
for i, cgrEv := range cgrEvs {
if refund {
if _, has := cgrEv.Event[utils.CostDetails]; !has {
// if CostDetails is not populated or is nil, look for it inside the previously stored cdr
var cgrID string // prepare CGRID to filter for previous CDR
if val, has := cgrEv.Event[utils.CGRID]; !has {
cgrID = utils.Sha1(utils.IfaceAsString(cgrEv.Event[utils.OriginID]),
utils.IfaceAsString(cgrEv.Event[utils.OriginHost]))
} else {
cgrID = utils.IfaceAsString(val)
}
var prevCDRs []*CDR // only one should be returned
if prevCDRs, _, err = cdrS.cdrDb.GetCDRs(
&utils.CDRsFilter{CGRIDs: []string{cgrID},
RunIDs: []string{utils.IfaceAsString(cgrEv.Event[utils.RunID])}}, false); err != nil {
utils.Logger.Err(
fmt.Sprintf("<%s> could not retrieve previously stored CDR, error: <%s>",
utils.CDRs, err.Error()))
err = utils.ErrPartiallyExecuted
return
} else {
cgrEv.Event[utils.CostDetails] = prevCDRs[0].CostDetails
}
}
}
if cdrs[i], err = NewMapEvent(cgrEv.Event).AsCDR(cdrS.cgrCfg,
cgrEv.Tenant, cdrS.cgrCfg.GeneralCfg().DefaultTimezone); err != nil {
utils.Logger.Warning(
@@ -516,25 +540,13 @@ func (cdrS *CDRServer) processEvents(evs []*utils.CGREvent,
}
if refund {
for i, cdr := range cdrs {
if cdr.CostDetails == nil { // if CostDetails is not populated, look for it inside the previously stored cdr
var prevCDRs []*CDR // only one should be returned
if prevCDRs, _, err = cdrS.cdrDb.GetCDRs(
&utils.CDRsFilter{CGRIDs: []string{cdr.CGRID},
RunIDs: []string{cdr.RunID}}, false); err != nil {
utils.Logger.Info(
fmt.Sprintf("<%s> could not retrieve previously stored CDR, error: <%s>",
utils.CDRs, err.Error()))
continue
}
cdr.CostDetails = prevCDRs[0].CostDetails
}
if rfnd, errRfd := cdrS.refundEventCost(cdr.CostDetails,
cdr.RequestType, cdr.ToR); errRfd != nil {
utils.Logger.Warning(
fmt.Sprintf("<%s> error: <%s> refunding CDR %+v",
utils.CDRs, errRfd.Error(), utils.ToJSON(cdr)))
} else if rfnd {
cdr.CostDetails = nil
cdr.CostDetails = nil // this makes sure that the rater will recalculate (and debit) the cost
procFlgs[i].Add(utils.MetaRefund)
}
}
@@ -1093,6 +1105,10 @@ func (cdrS *CDRServer) V1RateCDRs(arg *ArgRateCDRs, reply *string) (err error) {
if flgs.Has(utils.MetaAttributes) {
attrS = flgs.GetBool(utils.MetaAttributes)
}
var reRate bool
if flgs.Has(utils.MetaRerate) {
reRate = flgs.GetBool(utils.MetaRerate)
}
if chrgS && len(cdrS.cgrCfg.CdrsCfg().ChargerSConns) == 0 {
return utils.NewErrNotConnected(utils.ChargerS)
@@ -1104,7 +1120,7 @@ func (cdrS *CDRServer) V1RateCDRs(arg *ArgRateCDRs, reply *string) (err error) {
cgrEvs[i].APIOpts = arg.APIOpts
}
if _, err = cdrS.processEvents(cgrEvs, chrgS, attrS, false,
true, store, true, export, thdS, statS); err != nil {
true, store, reRate, export, thdS, statS); err != nil {
return utils.NewErrServerError(err)
}

View File

@@ -339,7 +339,7 @@ func testRerateCDRsGetAccountAfterProcessEvent2(t *testing.T) {
func testRerateCDRsRerateCDRs(t *testing.T) {
var reply string
if err := rrCdrsRPC.Call(utils.CDRsV1RateCDRs, &engine.ArgRateCDRs{
Flags: []string{utils.MetaRALs},
Flags: []string{utils.MetaRerate},
RPCCDRsFilter: utils.RPCCDRsFilter{
OrderBy: utils.AnswerTime,
CGRIDs: []string{rrCdrsUUID},

View File

@@ -439,7 +439,7 @@ func testRerateExpGetAccountAfterProcessEvent3(t *testing.T) {
func testRerateExpRerateCDRs(t *testing.T) {
var reply string
if err := ng2RPC.Call(utils.CDRsV1RateCDRs, &engine.ArgRateCDRs{
Flags: []string{utils.MetaRALs},
Flags: []string{utils.MetaRerate},
RPCCDRsFilter: utils.RPCCDRsFilter{
OrderBy: utils.AnswerTime,
CGRIDs: []string{ng1UUID, ng2UUID},