mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-11 18:16:24 +05:00
EventCharges population for zero cost abstract debits
This commit is contained in:
@@ -25,14 +25,16 @@ import (
|
||||
)
|
||||
|
||||
// newAbstractBalance constructs an abstractBalanceOperator
|
||||
func newAbstractBalanceOperator(blnCfg *utils.Balance, cncrtBlncs []*concreteBalance,
|
||||
func newAbstractBalanceOperator(acntID string, blnCfg *utils.Balance,
|
||||
cncrtBlncs []*concreteBalance,
|
||||
fltrS *engine.FilterS, connMgr *engine.ConnManager,
|
||||
attrSConns, rateSConns []string) balanceOperator {
|
||||
return &abstractBalance{blnCfg, cncrtBlncs, fltrS, connMgr, attrSConns, rateSConns}
|
||||
return &abstractBalance{acntID, blnCfg, cncrtBlncs, fltrS, connMgr, attrSConns, rateSConns}
|
||||
}
|
||||
|
||||
// abstractBalance is the operator for *abstract balance type
|
||||
type abstractBalance struct {
|
||||
acntID string
|
||||
blnCfg *utils.Balance
|
||||
cncrtBlncs []*concreteBalance // paying balances
|
||||
fltrS *engine.FilterS
|
||||
@@ -96,7 +98,56 @@ func (aB *abstractBalance) debitAbstracts(usage *decimal.Big,
|
||||
(costIcrm.FixedFee == nil ||
|
||||
costIcrm.FixedFee.Cmp(decimal.New(0, 0)) == 0) {
|
||||
// cost 0, no need of concrete
|
||||
ec = &utils.EventCharges{Abstracts: &utils.Decimal{usage}}
|
||||
ec = utils.NewEventCharges()
|
||||
ec.Abstracts = &utils.Decimal{usage}
|
||||
// UnitFactors
|
||||
var ufID string
|
||||
if hasUF {
|
||||
ufID = utils.UUIDSha1Prefix()
|
||||
ec.UnitFactors[ufID] = uF
|
||||
}
|
||||
// Rating
|
||||
ratingID := utils.UUIDSha1Prefix()
|
||||
ec.Rating[ratingID] = &utils.RateSInterval{
|
||||
IntervalStart: utils.NewDecimal(0, 0),
|
||||
Increments: []*utils.RateSIncrement{
|
||||
{
|
||||
IncrementStart: utils.NewDecimal(0, 0),
|
||||
Rate: &utils.Rate{
|
||||
ID: utils.MetaCostIncrement,
|
||||
IntervalRates: []*utils.IntervalRate{
|
||||
{
|
||||
FixedFee: utils.NewDecimal(0, 0),
|
||||
},
|
||||
},
|
||||
},
|
||||
CompressFactor: 1,
|
||||
Usage: &utils.Decimal{usage},
|
||||
},
|
||||
},
|
||||
CompressFactor: 1,
|
||||
}
|
||||
acntID := utils.UUIDSha1Prefix()
|
||||
ec.Accounting[acntID] = &utils.AccountCharge{
|
||||
AccountID: aB.acntID,
|
||||
BalanceID: aB.blnCfg.ID,
|
||||
Units: &utils.Decimal{usage},
|
||||
BalanceLimit: blncLmt,
|
||||
UnitFactorID: ufID,
|
||||
RatingID: ratingID,
|
||||
}
|
||||
ec.ChargingIntervals = []*utils.ChargingInterval{
|
||||
{
|
||||
Increments: []*utils.ChargingIncrement{
|
||||
{
|
||||
Units: &utils.Decimal{usage},
|
||||
AccountChargeID: acntID,
|
||||
CompressFactor: 1,
|
||||
},
|
||||
},
|
||||
CompressFactor: 1,
|
||||
},
|
||||
}
|
||||
} else {
|
||||
// attempt to debit usage with cost
|
||||
if ec, err = maxDebitAbstractsFromConcretes(aB.cncrtBlncs, usage,
|
||||
|
||||
@@ -59,7 +59,10 @@ func TestABDebitUsageFromConcretes(t *testing.T) {
|
||||
},
|
||||
}}
|
||||
expectedEvCharg := &utils.EventCharges{
|
||||
Concretes: utils.NewDecimal(5, 0),
|
||||
Concretes: utils.NewDecimal(5, 0),
|
||||
Accounting: make(map[string]*utils.AccountCharge),
|
||||
UnitFactors: make(map[string]*utils.UnitFactor),
|
||||
Rating: make(map[string]*utils.RateSInterval),
|
||||
}
|
||||
// consume only from first balance
|
||||
if evCh, err := debitAbstractsFromConcretes(aB.cncrtBlncs,
|
||||
@@ -81,7 +84,10 @@ func TestABDebitUsageFromConcretes(t *testing.T) {
|
||||
aB.cncrtBlncs[0].blnCfg.Units = utils.NewDecimal(500, 0)
|
||||
aB.cncrtBlncs[1].blnCfg.Units = utils.NewDecimal(125, 2)
|
||||
expectedEvCharg = &utils.EventCharges{
|
||||
Concretes: utils.NewDecimal(9, 0),
|
||||
Concretes: utils.NewDecimal(9, 0),
|
||||
Accounting: make(map[string]*utils.AccountCharge),
|
||||
UnitFactors: make(map[string]*utils.UnitFactor),
|
||||
Rating: make(map[string]*utils.RateSInterval),
|
||||
}
|
||||
|
||||
if evCh, err := debitAbstractsFromConcretes(aB.cncrtBlncs,
|
||||
|
||||
@@ -138,59 +138,6 @@ func (aS *AccountS) matchingAccountsForEvent(tnt string, cgrEv *utils.CGREvent,
|
||||
return
|
||||
}
|
||||
|
||||
// accountDebit will debit the usage out of an Account
|
||||
func (aS *AccountS) accountDebit(acnt *utils.AccountProfile, usage *decimal.Big,
|
||||
cgrEv *utils.CGREvent, concretes bool) (ec *utils.EventCharges, err error) {
|
||||
|
||||
// Find balances matching event
|
||||
blcsWithWeight := make(utils.BalancesWithWeight, 0, len(acnt.Balances))
|
||||
for _, blnCfg := range acnt.Balances {
|
||||
var weight float64
|
||||
if weight, err = engine.WeightFromDynamics(blnCfg.Weights,
|
||||
aS.fltrS, cgrEv.Tenant, cgrEv.AsDataProvider()); err != nil {
|
||||
return
|
||||
}
|
||||
blcsWithWeight = append(blcsWithWeight, &utils.BalanceWithWeight{blnCfg, weight})
|
||||
}
|
||||
blcsWithWeight.Sort()
|
||||
var blncOpers []balanceOperator
|
||||
if blncOpers, err = newBalanceOperators(blcsWithWeight.Balances(), aS.fltrS, aS.connMgr,
|
||||
aS.cfg.AccountSCfg().AttributeSConns, aS.cfg.AccountSCfg().RateSConns); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for i, blncOper := range blncOpers {
|
||||
debFunc := blncOper.debitAbstracts
|
||||
if concretes {
|
||||
debFunc = blncOper.debitConcretes
|
||||
}
|
||||
if i == 0 {
|
||||
ec = utils.NewEventCharges()
|
||||
}
|
||||
if usage.Cmp(decimal.New(0, 0)) == 0 {
|
||||
return // no more debit
|
||||
}
|
||||
var ecDbt *utils.EventCharges
|
||||
if ecDbt, err = debFunc(new(decimal.Big).Copy(usage), cgrEv); err != nil {
|
||||
if err == utils.ErrFilterNotPassingNoCaps ||
|
||||
err == utils.ErrNotImplemented {
|
||||
err = nil
|
||||
continue
|
||||
}
|
||||
return
|
||||
}
|
||||
var used *decimal.Big
|
||||
if concretes {
|
||||
used = ecDbt.Concretes.Big
|
||||
} else {
|
||||
used = ecDbt.Abstracts.Big
|
||||
}
|
||||
usage = utils.SubstractBig(usage, used)
|
||||
ec.Merge(ecDbt)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// accountsDebit will debit an usage out of multiple accounts
|
||||
func (aS *AccountS) accountsDebit(acnts []*utils.AccountProfileWithWeight,
|
||||
cgrEv *utils.CGREvent, concretes, store bool) (ec *utils.EventCharges, err error) {
|
||||
@@ -212,11 +159,9 @@ func (aS *AccountS) accountsDebit(acnts []*utils.AccountProfileWithWeight,
|
||||
} else {
|
||||
usage = decimal.New(int64(usgEv), 0)
|
||||
}
|
||||
ec = utils.NewEventCharges()
|
||||
acntBkps := make([]utils.AccountBalancesBackup, len(acnts))
|
||||
for i, acnt := range acnts {
|
||||
if i == 0 {
|
||||
ec = utils.NewEventCharges()
|
||||
}
|
||||
if usage.Cmp(decimal.New(0, 0)) == 0 {
|
||||
return // no more debits
|
||||
}
|
||||
@@ -247,6 +192,56 @@ func (aS *AccountS) accountsDebit(acnts []*utils.AccountProfileWithWeight,
|
||||
return
|
||||
}
|
||||
|
||||
// accountDebit will debit the usage out of an Account
|
||||
func (aS *AccountS) accountDebit(acnt *utils.AccountProfile, usage *decimal.Big,
|
||||
cgrEv *utils.CGREvent, concretes bool) (ec *utils.EventCharges, err error) {
|
||||
|
||||
// Find balances matching event
|
||||
blcsWithWeight := make(utils.BalancesWithWeight, 0, len(acnt.Balances))
|
||||
for _, blnCfg := range acnt.Balances {
|
||||
var weight float64
|
||||
if weight, err = engine.WeightFromDynamics(blnCfg.Weights,
|
||||
aS.fltrS, cgrEv.Tenant, cgrEv.AsDataProvider()); err != nil {
|
||||
return
|
||||
}
|
||||
blcsWithWeight = append(blcsWithWeight, &utils.BalanceWithWeight{blnCfg, weight})
|
||||
}
|
||||
blcsWithWeight.Sort()
|
||||
var blncOpers []balanceOperator
|
||||
if blncOpers, err = newBalanceOperators(acnt.ID, blcsWithWeight.Balances(), aS.fltrS, aS.connMgr,
|
||||
aS.cfg.AccountSCfg().AttributeSConns, aS.cfg.AccountSCfg().RateSConns); err != nil {
|
||||
return
|
||||
}
|
||||
ec = utils.NewEventCharges()
|
||||
for _, blncOper := range blncOpers {
|
||||
debFunc := blncOper.debitAbstracts
|
||||
if concretes {
|
||||
debFunc = blncOper.debitConcretes
|
||||
}
|
||||
if usage.Cmp(decimal.New(0, 0)) == 0 {
|
||||
return // no more debit
|
||||
}
|
||||
var ecDbt *utils.EventCharges
|
||||
if ecDbt, err = debFunc(new(decimal.Big).Copy(usage), cgrEv); err != nil {
|
||||
if err == utils.ErrFilterNotPassingNoCaps ||
|
||||
err == utils.ErrNotImplemented {
|
||||
err = nil
|
||||
continue
|
||||
}
|
||||
return
|
||||
}
|
||||
var used *decimal.Big
|
||||
if concretes {
|
||||
used = ecDbt.Concretes.Big
|
||||
} else {
|
||||
used = ecDbt.Abstracts.Big
|
||||
}
|
||||
usage = utils.SubstractBig(usage, used)
|
||||
ec.Merge(ecDbt)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// V1AccountProfilesForEvent returns the matching AccountProfiles for Event
|
||||
func (aS *AccountS) V1AccountProfilesForEvent(args *utils.ArgsAccountsForEvent, aps *[]*utils.AccountProfile) (err error) {
|
||||
var acnts utils.AccountProfilesWithWeight
|
||||
|
||||
@@ -44,14 +44,15 @@ func restoreUnitsFromClones(cBs []*concreteBalance, clnedUnts []*utils.Decimal)
|
||||
}
|
||||
|
||||
// newConcreteBalance constructs a concreteBalanceOperator
|
||||
func newConcreteBalanceOperator(blnCfg *utils.Balance,
|
||||
func newConcreteBalanceOperator(acntID string, blnCfg *utils.Balance,
|
||||
fltrS *engine.FilterS, connMgr *engine.ConnManager,
|
||||
attrSConns, rateSConns []string) balanceOperator {
|
||||
return &concreteBalance{blnCfg, fltrS, connMgr, attrSConns, rateSConns}
|
||||
return &concreteBalance{acntID, blnCfg, fltrS, connMgr, attrSConns, rateSConns}
|
||||
}
|
||||
|
||||
// concreteBalance is the operator for *concrete balance type
|
||||
type concreteBalance struct {
|
||||
acntID string
|
||||
blnCfg *utils.Balance
|
||||
fltrS *engine.FilterS
|
||||
connMgr *engine.ConnManager
|
||||
|
||||
@@ -31,7 +31,7 @@ import (
|
||||
)
|
||||
|
||||
// newAccountBalances constructs accountBalances
|
||||
func newBalanceOperators(blnCfgs []*utils.Balance,
|
||||
func newBalanceOperators(acntID string, blnCfgs []*utils.Balance,
|
||||
fltrS *engine.FilterS, connMgr *engine.ConnManager,
|
||||
attrSConns, rateSConns []string) (blncOpers []balanceOperator, err error) {
|
||||
|
||||
@@ -41,7 +41,7 @@ func newBalanceOperators(blnCfgs []*utils.Balance,
|
||||
if blnCfg.Type != utils.MetaConcrete {
|
||||
continue
|
||||
}
|
||||
blncOpers[i] = newConcreteBalanceOperator(blnCfg,
|
||||
blncOpers[i] = newConcreteBalanceOperator(acntID, blnCfg,
|
||||
fltrS, connMgr, attrSConns, rateSConns)
|
||||
cncrtBlncs = append(cncrtBlncs, blncOpers[i].(*concreteBalance))
|
||||
}
|
||||
@@ -50,8 +50,8 @@ func newBalanceOperators(blnCfgs []*utils.Balance,
|
||||
if blnCfg.Type == utils.MetaConcrete {
|
||||
continue
|
||||
}
|
||||
if blncOpers[i], err = newBalanceOperator(blnCfg, cncrtBlncs, fltrS, connMgr,
|
||||
attrSConns, rateSConns); err != nil {
|
||||
if blncOpers[i], err = newBalanceOperator(acntID, blnCfg, cncrtBlncs,
|
||||
fltrS, connMgr, attrSConns, rateSConns); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -61,16 +61,16 @@ func newBalanceOperators(blnCfgs []*utils.Balance,
|
||||
|
||||
// newBalanceOperator instantiates balanceOperator interface
|
||||
// cncrtBlncs are needed for abstract balance debits
|
||||
func newBalanceOperator(blncCfg *utils.Balance, cncrtBlncs []*concreteBalance,
|
||||
func newBalanceOperator(acntID string, blncCfg *utils.Balance, cncrtBlncs []*concreteBalance,
|
||||
fltrS *engine.FilterS, connMgr *engine.ConnManager,
|
||||
attrSConns, rateSConns []string) (bP balanceOperator, err error) {
|
||||
switch blncCfg.Type {
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported balance type: <%s>", blncCfg.Type)
|
||||
case utils.MetaConcrete:
|
||||
return newConcreteBalanceOperator(blncCfg, fltrS, connMgr, attrSConns, rateSConns), nil
|
||||
return newConcreteBalanceOperator(acntID, blncCfg, fltrS, connMgr, attrSConns, rateSConns), nil
|
||||
case utils.MetaAbstract:
|
||||
return newAbstractBalanceOperator(blncCfg, cncrtBlncs, fltrS, connMgr, attrSConns, rateSConns), nil
|
||||
return newAbstractBalanceOperator(acntID, blncCfg, cncrtBlncs, fltrS, connMgr, attrSConns, rateSConns), nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ func TestNewAccountBalanceOperators(t *testing.T) {
|
||||
}
|
||||
filters := engine.NewFilterS(config.NewDefaultCGRConfig(), nil, nil)
|
||||
|
||||
concrete, err := newBalanceOperator(acntPrf.Balances["BL1"], nil, filters, nil, nil, nil)
|
||||
concrete, err := newBalanceOperator(acntPrf.ID, acntPrf.Balances["BL1"], nil, filters, nil, nil, nil)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
@@ -64,12 +64,13 @@ func TestNewAccountBalanceOperators(t *testing.T) {
|
||||
cncrtBlncs = append(cncrtBlncs, concrete.(*concreteBalance))
|
||||
|
||||
expected := &abstractBalance{
|
||||
acntID: acntPrf.ID,
|
||||
blnCfg: acntPrf.Balances["BL0"],
|
||||
fltrS: filters,
|
||||
cncrtBlncs: cncrtBlncs,
|
||||
}
|
||||
blnCfgs := []*utils.Balance{acntPrf.Balances["BL0"], acntPrf.Balances["BL1"]}
|
||||
if blcOp, err := newBalanceOperators(blnCfgs, filters, nil,
|
||||
if blcOp, err := newBalanceOperators(acntPrf.ID, blnCfgs, filters, nil,
|
||||
nil, nil); err != nil {
|
||||
t.Error(err)
|
||||
} else {
|
||||
@@ -84,7 +85,7 @@ func TestNewAccountBalanceOperators(t *testing.T) {
|
||||
|
||||
acntPrf.Balances["BL1"].Type = "INVALID_TYPE"
|
||||
expectedErr := "unsupported balance type: <INVALID_TYPE>"
|
||||
if _, err := newBalanceOperators(blnCfgs, filters, nil,
|
||||
if _, err := newBalanceOperators(acntPrf.ID, blnCfgs, filters, nil,
|
||||
nil, nil); err == nil || err.Error() != expectedErr {
|
||||
t.Errorf("Expected %+v, received %+v", expectedErr, err)
|
||||
}
|
||||
@@ -224,7 +225,10 @@ func TestDebitUsageFromConcretes(t *testing.T) {
|
||||
fltrS: filterS,
|
||||
}
|
||||
expectedEvCh := &utils.EventCharges{
|
||||
Concretes: utils.NewDecimal(710, 0),
|
||||
Concretes: utils.NewDecimal(710, 0),
|
||||
Accounting: make(map[string]*utils.AccountCharge),
|
||||
UnitFactors: make(map[string]*utils.UnitFactor),
|
||||
Rating: make(map[string]*utils.RateSInterval),
|
||||
}
|
||||
|
||||
if evCh, err := debitAbstractsFromConcretes([]*concreteBalance{cb1, cb2}, decimal.New(700, 0), &utils.CostIncrement{
|
||||
@@ -292,7 +296,10 @@ func TestDebitUsageFromConcretesFromRateS(t *testing.T) {
|
||||
}
|
||||
|
||||
expectedEvCh := &utils.EventCharges{
|
||||
Concretes: utils.NewDecimal(100, 0),
|
||||
Concretes: utils.NewDecimal(100, 0),
|
||||
Accounting: make(map[string]*utils.AccountCharge),
|
||||
UnitFactors: make(map[string]*utils.UnitFactor),
|
||||
Rating: make(map[string]*utils.RateSInterval),
|
||||
}
|
||||
|
||||
if evCh, err := debitAbstractsFromConcretes([]*concreteBalance{cb1, cb2}, decimal.New(700, 0), &utils.CostIncrement{
|
||||
|
||||
@@ -58,9 +58,6 @@ func (ap *AccountProfile) RestoreFromBackup(abb AccountBalancesBackup) {
|
||||
}
|
||||
}
|
||||
|
||||
// AccountBalanceBackups is used to create balance snapshots as backups
|
||||
type AccountBalancesBackup map[string]*decimal.Big
|
||||
|
||||
// AccountBalancesBackup returns a backup of all balance values
|
||||
func (ap *AccountProfile) AccountBalancesBackup() (abb AccountBalancesBackup) {
|
||||
if ap.Balances != nil {
|
||||
@@ -72,6 +69,9 @@ func (ap *AccountProfile) AccountBalancesBackup() (abb AccountBalancesBackup) {
|
||||
return
|
||||
}
|
||||
|
||||
// AccountBalanceBackups is used to create balance snapshots as backups
|
||||
type AccountBalancesBackup map[string]*decimal.Big
|
||||
|
||||
// NewDefaultBalance returns a balance with default costIncrements
|
||||
func NewDefaultBalance(id string) *Balance {
|
||||
const torFltr = "*string:~*req.ToR:"
|
||||
|
||||
@@ -967,6 +967,7 @@ const (
|
||||
RecurrentFee = "RecurrentFee"
|
||||
Diktats = "Diktats"
|
||||
BalanceIDs = "BalanceIDs"
|
||||
MetaCostIncrement = "*costIncrement"
|
||||
)
|
||||
|
||||
// Migrator Action
|
||||
|
||||
@@ -24,7 +24,11 @@ import (
|
||||
|
||||
// NewEventChargers instantiates the EventChargers in a central place
|
||||
func NewEventCharges() (ec *EventCharges) {
|
||||
ec = new(EventCharges)
|
||||
ec = &EventCharges{
|
||||
Accounting: make(map[string]*AccountCharge),
|
||||
UnitFactors: make(map[string]*UnitFactor),
|
||||
Rating: make(map[string]*RateSInterval),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -36,8 +40,9 @@ type EventCharges struct {
|
||||
ChargingIntervals []*ChargingInterval
|
||||
Accounts []*AccountProfile
|
||||
|
||||
Accounting *ChargingAccountS
|
||||
Rating *ChargingRateS
|
||||
Accounting map[string]*AccountCharge
|
||||
UnitFactors map[string]*UnitFactor
|
||||
Rating map[string]*RateSInterval
|
||||
}
|
||||
|
||||
// Merge will merge the event charges into existing
|
||||
@@ -83,3 +88,27 @@ type ExtEventCharges struct {
|
||||
Abstracts *float64
|
||||
Concretes *float64
|
||||
}
|
||||
|
||||
type ChargingInterval struct {
|
||||
Increments []*ChargingIncrement // specific increments applied to this interval
|
||||
CompressFactor int
|
||||
}
|
||||
|
||||
// ChargingIncrement represents one unit charged inside an interval
|
||||
type ChargingIncrement struct {
|
||||
Units *Decimal
|
||||
AccountChargeID string // Account charging information
|
||||
CompressFactor int
|
||||
}
|
||||
|
||||
// AccountCharge represents one Account charge
|
||||
type AccountCharge struct {
|
||||
AccountID string
|
||||
BalanceID string
|
||||
Units *Decimal
|
||||
BalanceLimit *Decimal // the minimum balance value accepted
|
||||
UnitFactorID string // identificator in ChargingUnitFactors
|
||||
AttributeIDs []string // list of attribute profiles matched
|
||||
RatingID string // identificator in cost increments
|
||||
JoinedChargeIDs []string // identificator of extra account charges
|
||||
}
|
||||
|
||||
@@ -26,7 +26,11 @@ import (
|
||||
)
|
||||
|
||||
func TestECNewEventCharges(t *testing.T) {
|
||||
expected := &EventCharges{}
|
||||
expected := &EventCharges{
|
||||
Accounting: make(map[string]*AccountCharge),
|
||||
UnitFactors: make(map[string]*UnitFactor),
|
||||
Rating: make(map[string]*RateSInterval),
|
||||
}
|
||||
received := NewEventCharges()
|
||||
|
||||
if !reflect.DeepEqual(expected, received) {
|
||||
|
||||
@@ -17,29 +17,3 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
package utils
|
||||
|
||||
type ChargingRateS map[string]*RateSInterval
|
||||
|
||||
type ChargingInterval struct {
|
||||
Increments []*ChargingIncrement // specific increments applied to this interval
|
||||
CompressFactor int
|
||||
}
|
||||
|
||||
// ChargingIncrement represents one unit charged inside an interval
|
||||
type ChargingIncrement struct {
|
||||
Units *Decimal
|
||||
AccountChargeID string // Account charged information
|
||||
CompressFactor int
|
||||
}
|
||||
|
||||
type ChargingAccountS map[string]*AccountCharge
|
||||
|
||||
// AccountCharge represents one Account charge
|
||||
type AccountCharge struct {
|
||||
BalanceID string
|
||||
Units *Decimal
|
||||
UnitFactorID string // identificator in unit factors
|
||||
AttributeIDs []string // list of attribute profiles matched
|
||||
CostID string // identificator in cost increments
|
||||
JoinedChargeIDs []string // identificator of extra account charges
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user