mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-17 06:09:53 +05:00
AccountSv1.RefundCharges API
This commit is contained in:
@@ -30,7 +30,8 @@ import (
|
||||
)
|
||||
|
||||
// NewAccountS instantiates the AccountS
|
||||
func NewAccountS(cfg *config.CGRConfig, fltrS *engine.FilterS, connMgr *engine.ConnManager, dm *engine.DataManager) *AccountS {
|
||||
func NewAccountS(cfg *config.CGRConfig, fltrS *engine.FilterS,
|
||||
connMgr *engine.ConnManager, dm *engine.DataManager) *AccountS {
|
||||
return &AccountS{cfg, fltrS, connMgr, dm}
|
||||
}
|
||||
|
||||
@@ -90,7 +91,7 @@ func (aS *AccountS) matchingAccountsForEvent(ctx *context.Context, tnt string, c
|
||||
for _, acntID := range acntIDs {
|
||||
var refID string
|
||||
if lked {
|
||||
refID = guardian.Guardian.GuardIDs("",
|
||||
refID = guardian.Guardian.GuardIDs(utils.EmptyString,
|
||||
aS.cfg.GeneralCfg().LockingTimeout,
|
||||
utils.ConcatenatedKey(utils.CacheAccounts, tnt, acntID)) // RPC caching needs to be atomic
|
||||
}
|
||||
@@ -132,14 +133,16 @@ func (aS *AccountS) matchingAccountsForEvent(ctx *context.Context, tnt string, c
|
||||
}
|
||||
|
||||
// accountsDebit will debit an usage out of multiple accounts
|
||||
// concretes parameter limits the debits to concrete only balances
|
||||
// store is used for simulate only or complete debit
|
||||
func (aS *AccountS) accountsDebit(ctx *context.Context, acnts []*utils.AccountWithWeight,
|
||||
cgrEv *utils.CGREvent, concretes, store bool) (ec *utils.EventCharges, err error) {
|
||||
var usage *decimal.Big
|
||||
var usage *decimal.Big // total event usage
|
||||
if usage, err = engine.GetDecimalBigOpts(ctx, cgrEv.Tenant, cgrEv, aS.fltrS, aS.cfg.AccountSCfg().Opts.Usage,
|
||||
config.AccountsUsageDftOpt, utils.OptsAccountsUsage, utils.MetaUsage); err != nil {
|
||||
return
|
||||
}
|
||||
dbted := decimal.New(0, 0)
|
||||
dbted := decimal.New(0, 0) // amount debited so far
|
||||
acntBkps := make([]utils.AccountBalancesBackup, len(acnts))
|
||||
for i, acnt := range acnts {
|
||||
if usage.Cmp(decimal.New(0, 0)) == 0 {
|
||||
@@ -235,6 +238,59 @@ func (aS *AccountS) accountDebit(ctx *context.Context, acnt *utils.Account, usag
|
||||
return
|
||||
}
|
||||
|
||||
// refundCharges implements the mechanism of refunding the charges into accounts
|
||||
func (aS *AccountS) refundCharges(ctx *context.Context, tnt string, ecs *utils.EventCharges) (err error) {
|
||||
acnts := make(utils.AccountsWithWeight, 0, len(ecs.Accounts))
|
||||
acntsIdxed := make(map[string]*utils.Account) // so we can access Account easier
|
||||
alteredAcnts := make(utils.StringSet) // hold here the list of modified accounts
|
||||
for acntID := range ecs.Accounts {
|
||||
refID := guardian.Guardian.GuardIDs(utils.EmptyString,
|
||||
aS.cfg.GeneralCfg().LockingTimeout,
|
||||
utils.ConcatenatedKey(utils.CacheAccounts, tnt, acntID))
|
||||
var qAcnt *utils.Account
|
||||
if qAcnt, err = aS.dm.GetAccount(ctx, tnt, acntID); err != nil {
|
||||
guardian.Guardian.UnguardIDs(refID)
|
||||
if err == utils.ErrNotFound { // Account was removed in the mean time
|
||||
err = nil
|
||||
continue
|
||||
}
|
||||
unlockAccounts(acnts) // in case of errors will not have unlocks in upper layers
|
||||
return
|
||||
}
|
||||
acnts = append(acnts, &utils.AccountWithWeight{qAcnt, 0, refID})
|
||||
acntsIdxed[acntID] = qAcnt
|
||||
}
|
||||
acntBkps := make([]utils.AccountBalancesBackup, len(acnts)) // so we can restore in case of issues
|
||||
for i, acnt := range acnts {
|
||||
acntBkps[i] = acnt.AccountBalancesBackup()
|
||||
}
|
||||
for _, chrg := range ecs.Charges {
|
||||
acntChrg := ecs.Accounting[chrg.ChargingID]
|
||||
refundUnitsOnAccount(
|
||||
acntsIdxed[acntChrg.AccountID],
|
||||
uncompressUnits(acntChrg.Units, chrg.CompressFactor),
|
||||
ecs.Accounts[acntChrg.AccountID].Balances[acntChrg.BalanceID])
|
||||
alteredAcnts.Add(acntChrg.AccountID)
|
||||
for _, chrgID := range acntChrg.JoinedChargeIDs { // refund extra charges
|
||||
extraChrg := ecs.Accounting[chrgID]
|
||||
refundUnitsOnAccount(
|
||||
acntsIdxed[extraChrg.AccountID],
|
||||
uncompressUnits(extraChrg.Units, chrg.CompressFactor),
|
||||
ecs.Accounts[acntChrg.AccountID].Balances[extraChrg.BalanceID])
|
||||
alteredAcnts.Add(extraChrg.AccountID)
|
||||
}
|
||||
}
|
||||
for acntID := range alteredAcnts {
|
||||
if err = aS.dm.SetAccount(ctx, acntsIdxed[acntID], false); err != nil {
|
||||
restoreAccounts(ctx, aS.dm, acnts, acntBkps)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
unlockAccounts(acnts) // in case of errors will not have unlocks in upper layers
|
||||
return
|
||||
}
|
||||
|
||||
// V1AccountsForEvent returns the matching Accounts for Event
|
||||
func (aS *AccountS) V1AccountsForEvent(ctx *context.Context, args *utils.CGREvent, aps *[]*utils.Account) (err error) {
|
||||
var accIDs []string
|
||||
@@ -379,6 +435,15 @@ func (aS *AccountS) V1DebitConcretes(ctx *context.Context, args *utils.CGREvent,
|
||||
return
|
||||
}
|
||||
|
||||
// V1RefundCharges will refund charges recorded inside EventCharges
|
||||
func (aS *AccountS) V1RefundCharges(ctx *context.Context, args *utils.APIEventCharges, rply *string) (err error) {
|
||||
if err = aS.refundCharges(ctx, args.Tenant, args.EventCharges); err != nil {
|
||||
return
|
||||
}
|
||||
*rply = utils.OK
|
||||
return
|
||||
}
|
||||
|
||||
// V1ActionSetBalance performs an update for a specific balance in account
|
||||
func (aS *AccountS) V1ActionSetBalance(ctx *context.Context, args *utils.ArgsActSetBalance, rply *string) (err error) {
|
||||
if args.AccountID == utils.EmptyString {
|
||||
|
||||
@@ -352,3 +352,27 @@ func unlockAccounts(acnts utils.AccountsWithWeight) {
|
||||
guardian.Guardian.UnguardIDs(lkID)
|
||||
}
|
||||
}
|
||||
|
||||
// uncompressUnits returns the uncompressed value of the units if compressFactor is provided
|
||||
func uncompressUnits(units *utils.Decimal, cmprsFctr int) (tU *utils.Decimal) {
|
||||
tU = units
|
||||
if cmprsFctr > 1 {
|
||||
tU = &utils.Decimal{utils.MultiplyBig(tU.Big,
|
||||
decimal.New(int64(cmprsFctr), 0))}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// refundUnitsOnAccount is responsible for returning the units back to the balance
|
||||
// origBlnc is used for both it's ID as well as as a configuration backup in case when the balance is not longer present
|
||||
func refundUnitsOnAccount(acnt *utils.Account, units *utils.Decimal, origBlnc *utils.Balance) {
|
||||
if _, has := acnt.Balances[origBlnc.ID]; has {
|
||||
acnt.Balances[origBlnc.ID].Units = &utils.Decimal{
|
||||
utils.SumBig(
|
||||
acnt.Balances[origBlnc.ID].Units.Big,
|
||||
units.Big)}
|
||||
} else {
|
||||
acnt.Balances[origBlnc.ID] = origBlnc.Clone()
|
||||
acnt.Balances[origBlnc.ID].Units = &utils.Decimal{utils.CloneDecimalBig(units.Big)}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,6 +213,12 @@ func (aSv1 *AccountSv1) DebitConcretes(ctx *context.Context, args *utils.CGREven
|
||||
return aSv1.aS.V1DebitConcretes(ctx, args, eEc)
|
||||
}
|
||||
|
||||
// RefundCharges will refund charges recorded inside EventCharges
|
||||
func (aSv1 *AccountSv1) RefundCharges(ctx *context.Context,
|
||||
args *utils.APIEventCharges, rply *string) (err error) {
|
||||
return aSv1.aS.V1RefundCharges(ctx, args, rply)
|
||||
}
|
||||
|
||||
// ActionSetBalance performs a set balance action
|
||||
func (aSv1 *AccountSv1) ActionSetBalance(ctx *context.Context, args *utils.ArgsActSetBalance,
|
||||
eEc *string) (err error) {
|
||||
|
||||
@@ -462,6 +462,17 @@ func (apWws AccountsWithWeight) LockIDs() (lkIDs []string) {
|
||||
return
|
||||
}
|
||||
|
||||
// Account returns the Account object with ID
|
||||
func (apWws AccountsWithWeight) Account(acntID string) (acnt *Account) {
|
||||
for _, aWw := range apWws {
|
||||
if aWw.Account.ID == acntID {
|
||||
acnt = aWw.Account
|
||||
break
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// BalanceWithWeight attaches static Weight to Balance
|
||||
type BalanceWithWeight struct {
|
||||
*Balance
|
||||
|
||||
@@ -287,3 +287,10 @@ func (ac *AccountCharge) equals(nAc *AccountCharge) (eq bool) {
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// APIEventCharges is used in APIs, ie: refundCharges
|
||||
type APIEventCharges struct {
|
||||
Tenant string
|
||||
APIOpts map[string]interface{}
|
||||
*EventCharges
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user