diff --git a/data/storage/mysql/create_cdrs_tables.sql b/data/storage/mysql/create_cdrs_tables.sql index 43ffe8412..4f93413dc 100644 --- a/data/storage/mysql/create_cdrs_tables.sql +++ b/data/storage/mysql/create_cdrs_tables.sql @@ -49,7 +49,6 @@ CREATE TABLE cdrs_extra ( DROP TABLE IF EXISTS cost_details; CREATE TABLE cost_details ( id int(11) NOT NULL AUTO_INCREMENT, - cost_time datetime NOT NULL, cgrid char(40) NOT NULL, runid varchar(64) NOT NULL, tor varchar(16) NOT NULL, @@ -75,7 +74,6 @@ CREATE TABLE cost_details ( DROP TABLE IF EXISTS rated_cdrs; CREATE TABLE `rated_cdrs` ( id int(11) NOT NULL AUTO_INCREMENT, - mediation_time datetime NOT NULL, cgrid char(40) NOT NULL, runid varchar(64) NOT NULL, reqtype varchar(24) NOT NULL, diff --git a/data/storage/postgres/create_cdrs_tables.sql b/data/storage/postgres/create_cdrs_tables.sql index 63b3cbef0..0f2f22ee3 100644 --- a/data/storage/postgres/create_cdrs_tables.sql +++ b/data/storage/postgres/create_cdrs_tables.sql @@ -47,8 +47,6 @@ CREATE TABLE cdrs_extra ( DROP TABLE IF EXISTS cost_details; CREATE TABLE cost_details ( id SERIAL PRIMARY KEY, - cost_time TIMESTAMP NOT NULL, - cost_source VARCHAR(64) NOT NULL, cgrid CHAR(40) NOT NULL, runid VARCHAR(64) NOT NULL, tor VARCHAR(16) NOT NULL, @@ -60,6 +58,7 @@ CREATE TABLE cost_details ( destination VARCHAR(128) NOT NULL, cost NUMERIC(20,4) NOT NULL, timespans text, + cost_source VARCHAR(64) NOT NULL, created_at TIMESTAMP, updated_at TIMESTAMP, deleted_at TIMESTAMP, @@ -72,7 +71,6 @@ CREATE TABLE cost_details ( DROP TABLE IF EXISTS rated_cdrs; CREATE TABLE rated_cdrs ( id SERIAL PRIMARY KEY, - mediation_time TIMESTAMP NOT NULL, cgrid CHAR(40) NOT NULL, runid VARCHAR(64) NOT NULL, reqtype VARCHAR(24) NOT NULL, diff --git a/engine/models.go b/engine/models.go index c6743a38c..6b18e41e9 100644 --- a/engine/models.go +++ b/engine/models.go @@ -296,7 +296,7 @@ type TblCdrsPrimary struct { } func (t TblCdrsPrimary) TableName() string { - return "cdrs_primary" + return utils.TBL_CDRS_PRIMARY } type TblCdrsExtra struct { @@ -308,12 +308,11 @@ type TblCdrsExtra struct { } func (t TblCdrsExtra) TableName() string { - return "cdrs_extra" + return utils.TBL_CDRS_EXTRA } type TblCostDetail struct { Id int64 - CostSource string Cgrid string Runid string Tor string @@ -325,29 +324,37 @@ type TblCostDetail struct { Destination string Cost float64 Timespans string + CostSource string CreatedAt time.Time UpdatedAt time.Time DeletedAt time.Time } -type TblRatedCdr struct { - Id int64 - MediationTime time.Time - Cgrid string - Runid string - Reqtype string - Direction string - Tenant string - Category string - Account string - Subject string - Destination string - SetupTime time.Time - AnswerTime time.Time - Usage float64 - Cost float64 - ExtraInfo string - CreatedAt time.Time - UpdatedAt time.Time - DeletedAt time.Time +func (t TblCostDetail) TableName() string { + return utils.TBL_COST_DETAILS +} + +type TblRatedCdr struct { + Id int64 + Cgrid string + Runid string + Reqtype string + Direction string + Tenant string + Category string + Account string + Subject string + Destination string + SetupTime time.Time + AnswerTime time.Time + Usage float64 + Cost float64 + ExtraInfo string + CreatedAt time.Time + UpdatedAt time.Time + DeletedAt time.Time +} + +func (t TblRatedCdr) TableName() string { + return utils.TBL_RATED_CDRS } diff --git a/engine/storage_mysql.go b/engine/storage_mysql.go index 6520f8ed7..852e17f32 100644 --- a/engine/storage_mysql.go +++ b/engine/storage_mysql.go @@ -19,6 +19,7 @@ along with this program. If not, see package engine import ( + "encoding/json" "fmt" "github.com/cgrates/cgrates/config" "github.com/cgrates/cgrates/utils" @@ -75,3 +76,32 @@ func (self *MySQLStorage) SetTPTiming(tm *utils.ApierTPTiming) error { } return nil } + +func (self *MySQLStorage) LogCallCost(cgrid, source, runid string, cc *CallCost) (err error) { + tss, err := json.Marshal(cc.Timespans) + if err != nil { + Logger.Err(fmt.Sprintf("Error marshalling timespans to json: %v", err)) + return err + } + _, err = self.Db.Exec(fmt.Sprintf("INSERT INTO %s (cgrid,runid,tor,direction,tenant,category,account,subject,destination,cost,timespans,cost_source,created_at) VALUES ('%s','%s','%s','%s','%s','%s','%s','%s','%s',%f,'%s','%s',%d) ON DUPLICATE KEY UPDATE tor=values(tor),direction=values(direction),tenant=values(tenant),category=values(category),account=values(account),subject=values(subject),destination=values(destination),cost=values(cost),timespans=values(timespans),cost_source=values(cost_source),updated_at=%d", + utils.TBL_COST_DETAILS, + cgrid, + runid, + cc.TOR, + cc.Direction, + cc.Tenant, + cc.Category, + cc.Account, + cc.Subject, + cc.Destination, + cc.Cost, + tss, + source, + time.Now().Unix(), + time.Now().Unix())) + if err != nil { + Logger.Err(fmt.Sprintf("failed to execute insert statement: %v", err)) + return err + } + return nil +} diff --git a/engine/storage_mysql_local_test.go b/engine/storage_mysql_local_test.go index d2dea9c59..2d82dd9d5 100644 --- a/engine/storage_mysql_local_test.go +++ b/engine/storage_mysql_local_test.go @@ -69,6 +69,16 @@ func TestMySQLSetGetTPTiming(t *testing.T) { } else if !reflect.DeepEqual(tm, tmgs[tm.TimingId]) { t.Errorf("Expecting: %+v, received: %+v", tm, tmgs[tm.TimingId]) } + // Update + tm.Time = "00:00:01" + if err := mysqlDb.SetTPTiming(tm); err != nil { + t.Error(err.Error()) + } + if tmgs, err := mysqlDb.GetTpTimings(TEST_SQL, tm.TimingId); err != nil { + t.Error(err.Error()) + } else if !reflect.DeepEqual(tm, tmgs[tm.TimingId]) { + t.Errorf("Expecting: %+v, received: %+v", tmgs[tm.TimingId]) + } } func TestMySQLSetGetTPDestination(t *testing.T) { @@ -532,6 +542,16 @@ func TestMySQLCallCost(t *testing.T) { } else if !reflect.DeepEqual(cc, ccRcv) { t.Errorf("Expecting call cost: %v, received: %v", cc, ccRcv) } + // UPDATE test here + cc.Category = "premium_call" + if err := mysqlDb.LogCallCost(cgrId, TEST_SQL, utils.DEFAULT_RUNID, cc); err != nil { + t.Error(err.Error()) + } + if ccRcv, err := mysqlDb.GetCallCostLog(cgrId, TEST_SQL, utils.DEFAULT_RUNID); err != nil { + t.Error(err.Error()) + } else if !reflect.DeepEqual(cc, ccRcv) { + t.Errorf("Expecting call cost: %v, received: %v", cc, ccRcv) + } } func TestMySQLGetStoredCdrs(t *testing.T) { diff --git a/engine/storage_postgres.go b/engine/storage_postgres.go index 338a0ac00..26a3ab6e8 100644 --- a/engine/storage_postgres.go +++ b/engine/storage_postgres.go @@ -19,6 +19,7 @@ along with this program. If not, see package engine import ( + "encoding/json" "fmt" "github.com/cgrates/cgrates/config" "github.com/cgrates/cgrates/utils" @@ -70,13 +71,51 @@ func (self *PostgresStorage) SetTPTiming(tm *utils.ApierTPTiming) error { return nil //Nothing to set } tx := self.db.Begin() - if err := tx.Where(&TpTiming{Tpid: tm.TPid, Tag: tm.TimingId}).Delete(TpTiming{}).Error; err != nil { - tx.Rollback() - return err - } if err := tx.Save(&TpTiming{Tpid: tm.TPid, Tag: tm.TimingId, Years: tm.Years, Months: tm.Months, MonthDays: tm.MonthDays, WeekDays: tm.WeekDays, Time: tm.Time, CreatedAt: time.Now()}).Error; err != nil { tx.Rollback() - return err + tx = self.db.Begin() + updated := tx.Model(TpTiming{}).Where(&TpTiming{Tpid: tm.TPid, Tag: tm.TimingId}).Updates(&TpTiming{Years: tm.Years, Months: tm.Months, MonthDays: tm.MonthDays, WeekDays: tm.WeekDays, Time: tm.Time}) + if updated.Error != nil { + tx.Rollback() + return updated.Error + } + } + tx.Commit() + return nil +} + +func (self *PostgresStorage) LogCallCost(cgrid, source, runid string, cc *CallCost) (err error) { + tss, err := json.Marshal(cc.Timespans) + if err != nil { + Logger.Err(fmt.Sprintf("Error marshalling timespans to json: %v", err)) + return err + } + tx := self.db.Begin() + cd := &TblCostDetail{ + Cgrid: cgrid, + Runid: runid, + Tor: cc.TOR, + Direction: cc.Direction, + Tenant: cc.Tenant, + Category: cc.Category, + Account: cc.Account, + Subject: cc.Subject, + Destination: cc.Destination, + Cost: cc.Cost, + Timespans: string(tss), + CostSource: source, + CreatedAt: time.Now(), + } + + if tx.Save(cd).Error != nil { // Check further since error does not properly reflect duplicates here (sql: no rows in result set) + tx.Rollback() + tx = self.db.Begin() + updated := tx.Model(TblCostDetail{}).Where(&TblCostDetail{Cgrid: cgrid, Runid: runid}).Updates(&TblCostDetail{Tor: cc.TOR, Direction: cc.Direction, Tenant: cc.Tenant, Category: cc.Category, + Account: cc.Account, Subject: cc.Subject, Destination: cc.Destination, Cost: cc.Cost, Timespans: string(tss), CostSource: source, UpdatedAt: time.Now()}) + if updated.Error != nil { + tx.Rollback() + return updated.Error + } } tx.Commit() return nil diff --git a/engine/storage_psql_local_test.go b/engine/storage_psql_local_test.go index 6e5084591..8f404d1b0 100644 --- a/engine/storage_psql_local_test.go +++ b/engine/storage_psql_local_test.go @@ -69,6 +69,16 @@ func TestPSQLSetGetTPTiming(t *testing.T) { } else if !reflect.DeepEqual(tm, tmgs[tm.TimingId]) { t.Errorf("Expecting: %+v, received: %+v", tmgs[tm.TimingId]) } + // Update + tm.Time = "00:00:01" + if err := psqlDb.SetTPTiming(tm); err != nil { + t.Error(err.Error()) + } + if tmgs, err := psqlDb.GetTpTimings(TEST_SQL, tm.TimingId); err != nil { + t.Error(err.Error()) + } else if !reflect.DeepEqual(tm, tmgs[tm.TimingId]) { + t.Errorf("Expecting: %+v, received: %+v", tmgs[tm.TimingId]) + } } func TestPSQLSetGetTPDestination(t *testing.T) { @@ -472,38 +482,6 @@ func TestPSQLSetCdr(t *testing.T) { } } -/* -func TestPSQLSetRatedCdr(t *testing.T) { - if !*testLocal { - return - } - strCdr1 := &utils.StoredCdr{TOR: utils.VOICE, AccId: "bbb1", CdrHost: "192.168.1.1", CdrSource: "UNKNOWN", ReqType: "rated", - Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1002", - SetupTime: time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC), AnswerTime: time.Date(2013, 12, 7, 8, 42, 26, 0, time.UTC), - Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, - MediationRunId: utils.DEFAULT_RUNID, Cost: 1.201} - strCdr1.CgrId = utils.Sha1(strCdr1.AccId, strCdr1.SetupTime.String()) - strCdr2 := &utils.StoredCdr{TOR: utils.VOICE, AccId: "bbb2", CdrHost: "192.168.1.2", CdrSource: "UNKNOWN", ReqType: "prepaid", - Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1002", - SetupTime: time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC), AnswerTime: time.Date(2013, 12, 7, 8, 42, 26, 0, time.UTC), - Usage: time.Duration(12) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, - MediationRunId: utils.DEFAULT_RUNID, Cost: 0.201} - strCdr2.CgrId = utils.Sha1(strCdr2.AccId, strCdr2.SetupTime.String()) - strCdr3 := &utils.StoredCdr{TOR: utils.VOICE, AccId: "bbb3", CdrHost: "192.168.1.1", CdrSource: TEST_SQL, ReqType: "rated", - Direction: "*out", Tenant: "itsyscom.com", Category: "call", Account: "1002", Subject: "1002", Destination: "+4986517174964", - SetupTime: time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC), AnswerTime: time.Date(2013, 12, 7, 8, 42, 26, 0, time.UTC), - Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, - MediationRunId: "wholesale_run", Cost: 1.201} - strCdr3.CgrId = utils.Sha1(strCdr3.AccId, strCdr3.SetupTime.String()) - - for _, cdr := range []*utils.StoredCdr{strCdr1, strCdr2, strCdr3} { - if err := psqlDb.SetRatedCdr(cdr, ""); err != nil { - t.Error(err.Error()) - } - } -} - - func TestPSQLCallCost(t *testing.T) { if !*testLocal { return @@ -536,9 +514,51 @@ func TestPSQLCallCost(t *testing.T) { } else if !reflect.DeepEqual(cc, ccRcv) { t.Errorf("Expecting call cost: %v, received: %v", cc, ccRcv) } + // UPDATE test here + cc.Category = "premium_call" + if err := psqlDb.LogCallCost(cgrId, TEST_SQL, utils.DEFAULT_RUNID, cc); err != nil { + t.Error(err.Error()) + } + if ccRcv, err := psqlDb.GetCallCostLog(cgrId, TEST_SQL, utils.DEFAULT_RUNID); err != nil { + t.Error(err.Error()) + } else if !reflect.DeepEqual(cc, ccRcv) { + t.Errorf("Expecting call cost: %v, received: %v", cc, ccRcv) + } } +/* +func TestPSQLSetRatedCdr(t *testing.T) { + if !*testLocal { + return + } + strCdr1 := &utils.StoredCdr{TOR: utils.VOICE, AccId: "bbb1", CdrHost: "192.168.1.1", CdrSource: "UNKNOWN", ReqType: "rated", + Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1002", + SetupTime: time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC), AnswerTime: time.Date(2013, 12, 7, 8, 42, 26, 0, time.UTC), + Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, + MediationRunId: utils.DEFAULT_RUNID, Cost: 1.201} + strCdr1.CgrId = utils.Sha1(strCdr1.AccId, strCdr1.SetupTime.String()) + strCdr2 := &utils.StoredCdr{TOR: utils.VOICE, AccId: "bbb2", CdrHost: "192.168.1.2", CdrSource: "UNKNOWN", ReqType: "prepaid", + Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1002", + SetupTime: time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC), AnswerTime: time.Date(2013, 12, 7, 8, 42, 26, 0, time.UTC), + Usage: time.Duration(12) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, + MediationRunId: utils.DEFAULT_RUNID, Cost: 0.201} + strCdr2.CgrId = utils.Sha1(strCdr2.AccId, strCdr2.SetupTime.String()) + strCdr3 := &utils.StoredCdr{TOR: utils.VOICE, AccId: "bbb3", CdrHost: "192.168.1.1", CdrSource: TEST_SQL, ReqType: "rated", + Direction: "*out", Tenant: "itsyscom.com", Category: "call", Account: "1002", Subject: "1002", Destination: "+4986517174964", + SetupTime: time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC), AnswerTime: time.Date(2013, 12, 7, 8, 42, 26, 0, time.UTC), + Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, + MediationRunId: "wholesale_run", Cost: 1.201} + strCdr3.CgrId = utils.Sha1(strCdr3.AccId, strCdr3.SetupTime.String()) + for _, cdr := range []*utils.StoredCdr{strCdr1, strCdr2, strCdr3} { + if err := psqlDb.SetRatedCdr(cdr, ""); err != nil { + t.Error(err.Error()) + } + } +} +*/ + +/* func TestPSQLGetStoredCdrs(t *testing.T) { if !*testLocal { return diff --git a/engine/storage_sql.go b/engine/storage_sql.go index ae97b9dd6..60aa4bfb0 100644 --- a/engine/storage_sql.go +++ b/engine/storage_sql.go @@ -647,7 +647,7 @@ func (self *SQLStorage) SetTPAccountActions(tpid string, aas map[string]*utils.T tx.Rollback() return err } - saved := tx.Save(TpAccountAction{ + saved := tx.Save(&TpAccountAction{ Tpid: aa.TPid, Loadid: aa.LoadId, Tenant: aa.Tenant, @@ -668,33 +668,7 @@ func (self *SQLStorage) SetTPAccountActions(tpid string, aas map[string]*utils.T } func (self *SQLStorage) LogCallCost(cgrid, source, runid string, cc *CallCost) (err error) { - //ToDo: Add cgrid to logCallCost - if self.Db == nil { - //timespans.Logger.Warning("Cannot write log to database.") - return - } - tss, err := json.Marshal(cc.Timespans) - if err != nil { - Logger.Err(fmt.Sprintf("Error marshalling timespans to json: %v", err)) - } - _, err = self.Db.Exec(fmt.Sprintf("INSERT INTO %s (cost_time,cost_source,cgrid,runid,tor,direction,tenant,category,account,subject,destination,cost,timespans) VALUES (now(),'%s','%s','%s','%s','%s','%s','%s','%s','%s','%s',%f,'%s') ON DUPLICATE KEY UPDATE cost_time=now(),cost_source=values(cost_source),tor=values(tor),direction=values(direction),tenant=values(tenant),category=values(category),account=values(account),subject=values(subject),destination=values(destination),cost=values(cost),timespans=values(timespans)", - utils.TBL_COST_DETAILS, - source, - cgrid, - runid, - cc.TOR, - cc.Direction, - cc.Tenant, - cc.Category, - cc.Account, - cc.Subject, - cc.Destination, - cc.Cost, - tss)) - if err != nil { - Logger.Err(fmt.Sprintf("failed to execute insert statement: %v", err)) - } - return + return errors.New(utils.ERR_NOT_IMPLEMENTED) } func (self *SQLStorage) GetCallCostLog(cgrid, source, runid string) (cc *CallCost, err error) { @@ -763,7 +737,7 @@ func (self *SQLStorage) SetCdr(cdr *utils.StoredCdr) (err error) { } func (self *SQLStorage) SetRatedCdr(storedCdr *utils.StoredCdr, extraInfo string) (err error) { - _, err = self.Db.Exec(fmt.Sprintf("INSERT INTO %s (mediation_time,cgrid,runid,reqtype,direction,tenant,category,account,subject,destination,setup_time,answer_time,`usage`,cost,extra_info) VALUES (now(),'%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s',%v,%f,'%s') ON DUPLICATE KEY UPDATE mediation_time=now(),reqtype=values(reqtype),direction=values(direction),tenant=values(tenant),category=values(category),account=values(account),subject=values(subject),destination=values(destination),setup_time=values(setup_time),answer_time=values(answer_time),`usage`=values(`usage`),cost=values(cost),extra_info=values(extra_info)", + _, err = self.Db.Exec(fmt.Sprintf("INSERT INTO %s (cgrid,runid,reqtype,direction,tenant,category,account,subject,destination,setup_time,answer_time,`usage`,cost,extra_info,created_at) VALUES ('%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s',%v,%f,'%s',%d) ON DUPLICATE KEY UPDATE reqtype=values(reqtype),direction=values(direction),tenant=values(tenant),category=values(category),account=values(account),subject=values(subject),destination=values(destination),setup_time=values(setup_time),answer_time=values(answer_time),`usage`=values(`usage`),cost=values(cost),extra_info=values(extra_info), updated_at=%d", utils.TBL_RATED_CDRS, storedCdr.CgrId, storedCdr.MediationRunId, @@ -778,7 +752,9 @@ func (self *SQLStorage) SetRatedCdr(storedCdr *utils.StoredCdr, extraInfo string storedCdr.AnswerTime, storedCdr.Usage.Seconds(), storedCdr.Cost, - extraInfo)) + extraInfo, + time.Now().Unix(), + time.Now().Unix())) if err != nil { Logger.Err(fmt.Sprintf("failed to execute cdr insert statement: %s", err.Error())) }