refound money

This commit is contained in:
Radu Ioan Fericean
2013-10-02 20:09:42 +03:00
parent 09238f7355
commit 6cd7edd8a2
9 changed files with 118 additions and 51 deletions

View File

@@ -26,7 +26,7 @@ import (
// Can hold different units as seconds or monetary
type Balance struct {
Id string
Uuid string
Value float64
ExpirationDate time.Time
Weight float64
@@ -51,7 +51,7 @@ func (b *Balance) IsExpired() bool {
func (b *Balance) Clone() *Balance {
return &Balance{
Id: b.Id,
Uuid: b.Uuid,
Value: b.Value,
DestinationId: b.DestinationId,
ExpirationDate: b.ExpirationDate,
@@ -138,3 +138,12 @@ func (bc BalanceChain) Clone() BalanceChain {
}
return newChain
}
func (bc BalanceChain) GetBalance(uuid string) *Balance {
for _, balance := range bc {
if balance.Uuid == uuid {
return balance
}
}
return nil
}

View File

@@ -108,6 +108,7 @@ type CallDescriptor struct {
Amount float64
FallbackSubject string // the subject to check for destination if not found on primary subject
RatingPlans []*RatingPlan
Increments Increments
userBalance *UserBalance
}
@@ -408,6 +409,14 @@ func (cd *CallDescriptor) MaxDebit(startTime time.Time) (cc *CallCost, err error
return cd.Debit()
}
func (cd *CallDescriptor) RefoundIncrements() (left float64, err error) {
if userBalance, err := cd.getUserBalance(); err == nil && userBalance != nil {
defer storageGetter.SetUserBalance(userBalance)
userBalance.refoundIncrements(cd.Increments, true)
}
return 0.0, err
}
/*
Interface method used to add/substract an amount of cents from user's money balance.
The amount filed has to be filled in call descriptor.

View File

@@ -381,7 +381,7 @@ func (csvr *CSVReader) LoadActions() (err error) {
Weight: weight,
ExpirationString: record[5],
Balance: &Balance{
Id: utils.GenUUID(),
Uuid: utils.GenUUID(),
Value: units,
Weight: minutesWeight,
SpecialPrice: value,

View File

@@ -552,7 +552,7 @@ func TestLoadActions(t *testing.T) {
ExpirationString: UNLIMITED,
Weight: 10,
Balance: &Balance{
Id: as[0].Balance.Id,
Uuid: as[0].Balance.Uuid,
Value: 10,
Weight: 10,
},
@@ -565,7 +565,7 @@ func TestLoadActions(t *testing.T) {
ExpirationString: UNLIMITED,
Weight: 10,
Balance: &Balance{
Id: as[1].Balance.Id,
Uuid: as[1].Balance.Uuid,
Value: 100,
Weight: 10,
SpecialPriceType: PRICE_ABSOLUTE,

View File

@@ -76,6 +76,18 @@ func (rs *Responder) MaxDebit(arg CallDescriptor, reply *CallCost) (err error) {
return
}
func (rs *Responder) RefoundIncrements(arg CallDescriptor, reply *float64) (err error) {
if rs.Bal != nil {
*reply, err = rs.callMethod(&arg, "Responder.RefoundIncrements")
} else {
r, e := AccLock.Guard(arg.GetUserBalanceKey(), func() (float64, error) {
return arg.RefoundIncrements()
})
*reply, err = r, e
}
return
}
func (rs *Responder) DebitCents(arg CallDescriptor, reply *float64) (err error) {
if rs.Bal != nil {
*reply, err = rs.callMethod(&arg, "Responder.DebitCents")
@@ -340,6 +352,7 @@ type Connector interface {
GetCost(CallDescriptor, *CallCost) error
Debit(CallDescriptor, *CallCost) error
MaxDebit(CallDescriptor, *CallCost) error
RefoundIncrements(Increments, *float64) error
DebitCents(CallDescriptor, *float64) error
DebitSeconds(CallDescriptor, *float64) error
GetMaxSessionTime(CallDescriptor, *float64) error
@@ -360,6 +373,9 @@ func (rcc *RPCClientConnector) Debit(cd CallDescriptor, cc *CallCost) error {
func (rcc *RPCClientConnector) MaxDebit(cd CallDescriptor, cc *CallCost) error {
return rcc.Client.Call("Responder.MaxDebit", cd, cc)
}
func (rcc *RPCClientConnector) RefoundIncrements(cd CallDescriptor, resp *float64) error {
return rcc.Client.Call("Responder.RefoundIncrements", cd, resp)
}
func (rcc *RPCClientConnector) DebitCents(cd CallDescriptor, resp *float64) error {
return rcc.Client.Call("Responder.DebitCents", cd, resp)
}

View File

@@ -33,13 +33,13 @@ type TimeSpan struct {
RateInterval *RateInterval
CallDuration time.Duration // the call duration so far till TimeEnd
overlapped bool // mark a timespan as overlapped by an expanded one
Increments []*Increment
Increments Increments
}
type Increment struct {
Duration time.Duration
Cost float64
BalanceId string
BalanceUuid string
BalanceType string
BalanceRateInterval *RateInterval
MinuteInfo *MinuteInfo
@@ -52,6 +52,16 @@ type MinuteInfo struct {
Price float64
}
type Increments []*Increment
func (incs Increments) GetTotalCost() float64 {
cost := 0.0
for _, increment := range incs {
cost += increment.Cost
}
return cost
}
// Returns the duration of the timespan
func (ts *TimeSpan) GetDuration() time.Duration {
return ts.TimeEnd.Sub(ts.TimeStart)

View File

@@ -108,8 +108,8 @@ func (ub *UserBalance) debitBalanceAction(a *Action) error {
if a == nil {
return errors.New("nil minute action!")
}
if a.Balance.Id == "" {
a.Balance.Id = utils.GenUUID()
if a.Balance.Uuid == "" {
a.Balance.Uuid = utils.GenUUID()
}
if ub.BalanceMap == nil {
ub.BalanceMap = make(map[string]BalanceChain, 0)
@@ -204,7 +204,7 @@ func (ub *UserBalance) debitCreditBalance(cc *CallCost, count bool) error {
amount := increment.Duration.Seconds()
if b.Value >= amount {
b.Value -= amount
increment.BalanceId = b.Id
increment.BalanceUuid = b.Uuid
increment.MinuteInfo = &MinuteInfo{b.DestinationId, amount, 0}
paid = true
if count {
@@ -259,7 +259,7 @@ func (ub *UserBalance) debitCreditBalance(cc *CallCost, count bool) error {
}
cc.Timespans = newTimespans
b.Value -= amount
newTs.Increments[0].BalanceId = b.Id
newTs.Increments[0].BalanceUuid = b.Uuid
newTs.Increments[0].MinuteInfo = &MinuteInfo{b.DestinationId, amount, 0}
paid = true
if count {
@@ -309,7 +309,7 @@ func (ub *UserBalance) debitCreditBalance(cc *CallCost, count bool) error {
amount := increment.Cost
if b.Value >= amount {
b.Value -= amount
increment.BalanceId = b.Id
increment.BalanceUuid = b.Uuid
paid = true
if count {
ub.countUnits(&Action{BalanceId: CREDIT, Direction: cc.Direction, Balance: &Balance{Value: amount, DestinationId: cc.Destination}})
@@ -337,6 +337,25 @@ func (ub *UserBalance) debitCreditBalance(cc *CallCost, count bool) error {
return nil
}
func (ub *UserBalance) refoundIncrements(increments Increments, count bool) {
for _, increment := range increments {
var balance *Balance
for _, balanceChain := range ub.BalanceMap {
if balance = balanceChain.GetBalance(increment.BalanceUuid); balance != nil {
break
}
}
if balance != nil {
balance.Value += increment.Cost
if count {
ub.countUnits(&Action{BalanceId: increment.BalanceType, Direction: OUTBOUND, Balance: &Balance{Value: increment.Cost}})
}
} else {
// TODO: where should put the money?
}
}
}
/*
Debits some amount of user's specified balance. Returns the remaining credit in user's balance.
*/

View File

@@ -46,7 +46,7 @@ func populateTestActionsForTriggers() {
}
func TestBalanceStoreRestore(t *testing.T) {
b := &Balance{Value: 14, Weight: 1, Id: "test", ExpirationDate: time.Date(2013, time.July, 15, 17, 48, 0, 0, time.UTC)}
b := &Balance{Value: 14, Weight: 1, Uuid: "test", ExpirationDate: time.Date(2013, time.July, 15, 17, 48, 0, 0, time.UTC)}
marsh := NewCodecMsgpackMarshaler()
output, err := marsh.Marshal(b)
if err != nil {
@@ -188,7 +188,7 @@ func TestDebitNegativeMoneyBalance(t *testing.T) {
*/
func TestDebitCreditZeroSecond(t *testing.T) {
b1 := &Balance{Id: "testb", Value: 10, Weight: 10, DestinationId: "NAT", RateSubject: ZEROSECOND}
b1 := &Balance{Uuid: "testb", Value: 10, Weight: 10, DestinationId: "NAT", RateSubject: ZEROSECOND}
cc := &CallCost{
Direction: OUTBOUND,
Destination: "0723045326",
@@ -206,7 +206,7 @@ func TestDebitCreditZeroSecond(t *testing.T) {
if err != nil {
t.Error("Error debiting balance: ", err)
}
if cc.Timespans[0].Increments[0].BalanceId != "testb" {
if cc.Timespans[0].Increments[0].BalanceUuid != "testb" {
t.Error("Error setting balance id to increment: ", cc.Timespans[0].Increments[0])
}
if rifsBalance.BalanceMap[MINUTES+OUTBOUND][0].Value != 0 ||
@@ -216,7 +216,7 @@ func TestDebitCreditZeroSecond(t *testing.T) {
}
func TestDebitCreditZeroMinute(t *testing.T) {
b1 := &Balance{Id: "testb", Value: 70, Weight: 10, DestinationId: "NAT", RateSubject: ZEROMINUTE}
b1 := &Balance{Uuid: "testb", Value: 70, Weight: 10, DestinationId: "NAT", RateSubject: ZEROMINUTE}
cc := &CallCost{
Direction: OUTBOUND,
Destination: "0723045326",
@@ -237,7 +237,7 @@ func TestDebitCreditZeroMinute(t *testing.T) {
if err != nil {
t.Error("Error debiting balance: ", err)
}
if cc.Timespans[0].Increments[0].BalanceId != "testb" ||
if cc.Timespans[0].Increments[0].BalanceUuid != "testb" ||
cc.Timespans[0].Increments[0].Duration != time.Minute {
t.Error("Error setting balance id to increment: ", cc.Timespans[0].Increments[0])
}
@@ -248,8 +248,8 @@ func TestDebitCreditZeroMinute(t *testing.T) {
}
}
func TestDebitCreditZeroMixedMinute(t *testing.T) {
b1 := &Balance{Id: "testm", Value: 70, Weight: 5, DestinationId: "NAT", RateSubject: ZEROMINUTE}
b2 := &Balance{Id: "tests", Value: 10, Weight: 10, DestinationId: "NAT", RateSubject: ZEROSECOND}
b1 := &Balance{Uuid: "testm", Value: 70, Weight: 5, DestinationId: "NAT", RateSubject: ZEROMINUTE}
b2 := &Balance{Uuid: "tests", Value: 10, Weight: 10, DestinationId: "NAT", RateSubject: ZEROSECOND}
cc := &CallCost{
Direction: OUTBOUND,
Destination: "0723045326",
@@ -270,8 +270,8 @@ func TestDebitCreditZeroMixedMinute(t *testing.T) {
if err != nil {
t.Error("Error debiting balance: ", err)
}
if cc.Timespans[0].Increments[0].BalanceId != "tests" ||
cc.Timespans[1].Increments[0].BalanceId != "testm" {
if cc.Timespans[0].Increments[0].BalanceUuid != "tests" ||
cc.Timespans[1].Increments[0].BalanceUuid != "testm" {
t.Error("Error setting balance id to increment: ", cc.Timespans[0].Increments[0], cc.Timespans[1].Increments[0])
}
if rifsBalance.BalanceMap[MINUTES+OUTBOUND][1].Value != 0 ||
@@ -282,7 +282,7 @@ func TestDebitCreditZeroMixedMinute(t *testing.T) {
}
func TestDebitCreditNoCredit(t *testing.T) {
b1 := &Balance{Id: "testb", Value: 70, Weight: 10, DestinationId: "NAT", RateSubject: ZEROMINUTE}
b1 := &Balance{Uuid: "testb", Value: 70, Weight: 10, DestinationId: "NAT", RateSubject: ZEROMINUTE}
cc := &CallCost{
Direction: OUTBOUND,
Destination: "0723045326",
@@ -308,7 +308,7 @@ func TestDebitCreditNoCredit(t *testing.T) {
if err != nil {
t.Error("Error debiting balance: ", err)
}
if cc.Timespans[0].Increments[0].BalanceId != "testb" ||
if cc.Timespans[0].Increments[0].BalanceUuid != "testb" ||
cc.Timespans[0].Increments[0].Duration != time.Minute {
t.Error("Error setting balance id to increment: ", cc.Timespans[0].Increments[0])
}
@@ -322,7 +322,7 @@ func TestDebitCreditNoCredit(t *testing.T) {
}
func TestDebitCreditHasCredit(t *testing.T) {
b1 := &Balance{Id: "testb", Value: 70, Weight: 10, DestinationId: "NAT", RateSubject: ZEROMINUTE}
b1 := &Balance{Uuid: "testb", Value: 70, Weight: 10, DestinationId: "NAT", RateSubject: ZEROMINUTE}
cc := &CallCost{
Direction: OUTBOUND,
Destination: "0723045326",
@@ -343,13 +343,13 @@ func TestDebitCreditHasCredit(t *testing.T) {
}
rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{
MINUTES + OUTBOUND: BalanceChain{b1},
CREDIT + OUTBOUND: BalanceChain{&Balance{Id: "moneya", Value: 50}},
CREDIT + OUTBOUND: BalanceChain{&Balance{Uuid: "moneya", Value: 50}},
}}
err := rifsBalance.debitCreditBalance(cc, false)
if err != nil {
t.Error("Error debiting balance: ", err)
}
if cc.Timespans[0].Increments[0].BalanceId != "testb" ||
if cc.Timespans[0].Increments[0].BalanceUuid != "testb" ||
cc.Timespans[0].Increments[0].Duration != time.Minute {
t.Error("Error setting balance id to increment: ", cc.Timespans[0].Increments[0])
}
@@ -364,7 +364,7 @@ func TestDebitCreditHasCredit(t *testing.T) {
}
func TestDebitCreditSplitMinutesMoney(t *testing.T) {
b1 := &Balance{Id: "testb", Value: 10, Weight: 10, DestinationId: "NAT", RateSubject: ZEROSECOND}
b1 := &Balance{Uuid: "testb", Value: 10, Weight: 10, DestinationId: "NAT", RateSubject: ZEROSECOND}
cc := &CallCost{
Direction: OUTBOUND,
Destination: "0723045326",
@@ -379,13 +379,13 @@ func TestDebitCreditSplitMinutesMoney(t *testing.T) {
}
rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{
MINUTES + OUTBOUND: BalanceChain{b1},
CREDIT + OUTBOUND: BalanceChain{&Balance{Id: "moneya", Value: 50}},
CREDIT + OUTBOUND: BalanceChain{&Balance{Uuid: "moneya", Value: 50}},
}}
err := rifsBalance.debitCreditBalance(cc, false)
if err != nil {
t.Error("Error debiting balance: ", err)
}
if cc.Timespans[0].Increments[0].BalanceId != "testb" ||
if cc.Timespans[0].Increments[0].BalanceUuid != "testb" ||
cc.Timespans[0].Increments[0].Duration != 10*time.Second {
t.Error("Error setting balance id to increment: ", cc.Timespans[0].Increments[0])
}
@@ -400,7 +400,7 @@ func TestDebitCreditSplitMinutesMoney(t *testing.T) {
}
func TestDebitCreditMoreTimespans(t *testing.T) {
b1 := &Balance{Id: "testb", Value: 150, Weight: 10, DestinationId: "NAT", RateSubject: ZEROMINUTE}
b1 := &Balance{Uuid: "testb", Value: 150, Weight: 10, DestinationId: "NAT", RateSubject: ZEROMINUTE}
cc := &CallCost{
Direction: OUTBOUND,
Destination: "0723045326",
@@ -426,7 +426,7 @@ func TestDebitCreditMoreTimespans(t *testing.T) {
if err != nil {
t.Error("Error debiting balance: ", err)
}
if cc.Timespans[0].Increments[0].BalanceId != "testb" ||
if cc.Timespans[0].Increments[0].BalanceUuid != "testb" ||
cc.Timespans[0].Increments[0].Duration != time.Minute {
t.Error("Error setting balance id to increment: ", cc.Timespans[0].Increments[0])
}
@@ -437,8 +437,8 @@ func TestDebitCreditMoreTimespans(t *testing.T) {
}
func TestDebitCreditMoreTimespansMixed(t *testing.T) {
b1 := &Balance{Id: "testb", Value: 70, Weight: 10, DestinationId: "NAT", RateSubject: ZEROMINUTE}
b2 := &Balance{Id: "testa", Value: 150, Weight: 5, DestinationId: "NAT", RateSubject: ZEROSECOND}
b1 := &Balance{Uuid: "testb", Value: 70, Weight: 10, DestinationId: "NAT", RateSubject: ZEROMINUTE}
b2 := &Balance{Uuid: "testa", Value: 150, Weight: 5, DestinationId: "NAT", RateSubject: ZEROSECOND}
cc := &CallCost{
Direction: OUTBOUND,
Destination: "0723045326",
@@ -464,7 +464,7 @@ func TestDebitCreditMoreTimespansMixed(t *testing.T) {
if err != nil {
t.Error("Error debiting balance: ", err)
}
if cc.Timespans[0].Increments[0].BalanceId != "testb" ||
if cc.Timespans[0].Increments[0].BalanceUuid != "testb" ||
cc.Timespans[0].Increments[0].Duration != time.Minute {
t.Error("Error setting balance id to increment: ", cc.Timespans[0].Increments[0])
}
@@ -476,7 +476,7 @@ func TestDebitCreditMoreTimespansMixed(t *testing.T) {
}
func TestDebitCreditNoConectFeeCredit(t *testing.T) {
b1 := &Balance{Id: "testb", Value: 70, Weight: 10, DestinationId: "NAT", RateSubject: ZEROMINUTE}
b1 := &Balance{Uuid: "testb", Value: 70, Weight: 10, DestinationId: "NAT", RateSubject: ZEROMINUTE}
cc := &CallCost{
Direction: OUTBOUND,
Destination: "0723045326",
@@ -529,14 +529,14 @@ func TestDebitCreditMoneyOnly(t *testing.T) {
},
}
rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{
CREDIT + OUTBOUND: BalanceChain{&Balance{Id: "money", Value: 50}},
CREDIT + OUTBOUND: BalanceChain{&Balance{Uuid: "money", Value: 50}},
}}
err := rifsBalance.debitCreditBalance(cc, false)
if err != nil {
t.Error("Error debiting balance: ", err)
}
if cc.Timespans[0].Increments[0].BalanceId != "money" ||
if cc.Timespans[0].Increments[0].BalanceUuid != "money" ||
cc.Timespans[0].Increments[0].Duration != 10*time.Second {
t.Error("Error setting balance id to increment: ", cc.Timespans[0].Increments[0])
}

View File

@@ -245,30 +245,34 @@ func (sm *FSSessionManager) OnChannelHangupComplete(ev Event) {
hangupTime = time.Now()
}
end := lastCC.Timespans[len(lastCC.Timespans)-1].TimeEnd
refoundDuration := end.Sub(hangupTime).Seconds()
cost := 0.0
refoundDuration := end.Sub(hangupTime)
var refoundIncrements []*engine.Increment
engine.Logger.Info(fmt.Sprintf("Refund duration: %v", refoundDuration))
for i := len(lastCC.Timespans) - 1; i >= 0; i-- {
ts := lastCC.Timespans[i]
tsDuration := ts.GetDuration().Seconds()
tsDuration := ts.GetDuration()
if refoundDuration <= tsDuration {
// find procentage
procentage := (refoundDuration * 100) / tsDuration
tmpCost := (procentage * ts.Cost) / 100
ts.Cost -= tmpCost
cost += tmpCost
// set the end time to now
ts.TimeEnd = hangupTime
lastRefoundedIncrementIndex := 0
var lastRefoundedIncrement *engine.Increment
for incrementIndex, increment := range ts.Increments {
if increment.Duration <= refoundDuration {
refoundIncrements = append(refoundIncrements, increment)
refoundDuration -= increment.Duration
lastRefoundedIncrementIndex = incrementIndex
lastRefoundedIncrement = increment
}
}
ts.SplitByIncrement(lastRefoundedIncrementIndex, lastRefoundedIncrement)
break // do not go to other timespans
} else {
cost += ts.Cost
// remove the timestamp entirely
refoundIncrements = append(refoundIncrements, ts.Increments...)
// remove the timespan entirely
lastCC.Timespans = lastCC.Timespans[:i]
// continue to the next timespan with what is left to refound
refoundDuration -= tsDuration
}
}
if cost > 0 {
if len(refoundIncrements) > 0 {
cd := &engine.CallDescriptor{
Direction: lastCC.Direction,
Tenant: lastCC.Tenant,
@@ -276,7 +280,7 @@ func (sm *FSSessionManager) OnChannelHangupComplete(ev Event) {
Subject: lastCC.Subject,
Account: lastCC.Account,
Destination: lastCC.Destination,
Amount: -cost,
Increments: refoundIncrements,
// FallbackSubject: lastCC.FallbackSubject, // TODO: check how to best add it
}
var response float64