From 8ca38173a8048526cee14ec1272378ac5f14a24e Mon Sep 17 00:00:00 2001 From: Cristian Dean Date: Mon, 3 Jan 2022 01:00:35 -0300 Subject: [PATCH] Wallet API - Refactor / Bug Fixes --- api/certificates_of_deposit_operations.go | 2 +- api/ficfi_operations.go | 2 +- api/fiis_operations.go | 2 +- api/portfolios.go | 8 +--- api/stocks_funds_operations.go | 2 +- api/treasuries_direct_operations.go | 2 +- config/config.go | 8 ++-- db/operations.go | 46 ++++++++++++----------- wallet/certificate_of_deposit.go | 2 +- wallet/ficfi.go | 2 +- wallet/fii.go | 2 +- wallet/portfolio.go | 4 +- wallet/position.go | 2 +- wallet/stock.go | 2 +- wallet/stock_fund.go | 2 +- wallet/tradable.go | 9 +++++ wallet/treasury_direct.go | 23 ++++++------ 17 files changed, 64 insertions(+), 56 deletions(-) diff --git a/api/certificates_of_deposit_operations.go b/api/certificates_of_deposit_operations.go index 272f80b..6cee4b2 100644 --- a/api/certificates_of_deposit_operations.go +++ b/api/certificates_of_deposit_operations.go @@ -35,7 +35,7 @@ func (s *server) getAllCertificatesOfDepositOperations(c echo.Context) error { func (s *server) getCertificateOfDepositOperationByID(c echo.Context) error { id := c.Param("id") log.Debugf("[API] Retrieving certificate of deposit operation with id: %s", id) - result := &wallet.CertificateOfDeposit{} + result := wallet.NewCertificateOfDeposit() if err := s.db.Get(id, result); err != nil { errMsg := fmt.Sprintf("Error on retrieve '%s' operations: %v", id, err) return logAndReturnError(c, errMsg) diff --git a/api/ficfi_operations.go b/api/ficfi_operations.go index fa9916b..617490b 100644 --- a/api/ficfi_operations.go +++ b/api/ficfi_operations.go @@ -35,7 +35,7 @@ func (s *server) getAllFICFIOperations(c echo.Context) error { func (s *server) getFICFIOperationByID(c echo.Context) error { id := c.Param("id") log.Debugf("[API] Retrieving FICFI operation with id: %s", id) - result := &wallet.FICFI{} + result := wallet.NewFICFI() if err := s.db.Get(id, result); err != nil { errMsg := fmt.Sprintf("Error on retrieve '%s' operations: %v", id, err) return logAndReturnError(c, errMsg) diff --git a/api/fiis_operations.go b/api/fiis_operations.go index 83f7a02..3feaee6 100644 --- a/api/fiis_operations.go +++ b/api/fiis_operations.go @@ -35,7 +35,7 @@ func (s *server) getAllFIIOperations(c echo.Context) error { func (s *server) getFIIOperationByID(c echo.Context) error { id := c.Param("id") log.Debugf("[API] Retrieving stock operation with id: %s", id) - result := &wallet.FII{} + result := wallet.NewFII() if err := s.db.Get(id, result); err != nil { errMsg := fmt.Sprintf("Error on retrieve '%s' operations: %v", id, err) return logAndReturnError(c, errMsg) diff --git a/api/portfolios.go b/api/portfolios.go index bce644e..8446573 100644 --- a/api/portfolios.go +++ b/api/portfolios.go @@ -16,14 +16,10 @@ import ( ) func getYear(c echo.Context) (int, error) { - var year int - yearString := c.QueryParam("year") - if yearString != "" { + if yearString := c.QueryParam("year"); yearString != "" { return strconv.Atoi(yearString) - } else { - t := time.Now() - year = t.Year() } + year := time.Now().Year() return year, nil } diff --git a/api/stocks_funds_operations.go b/api/stocks_funds_operations.go index d2e0616..28417bf 100644 --- a/api/stocks_funds_operations.go +++ b/api/stocks_funds_operations.go @@ -35,7 +35,7 @@ func (s *server) getAllStockFundsOperations(c echo.Context) error { func (s *server) getStockFundOperationByID(c echo.Context) error { id := c.Param("id") log.Debugf("[API] Retrieving stock fund operation with id: %s", id) - result := &wallet.StockFund{} + result := wallet.NewStockFund() if err := s.db.Get(id, result); err != nil { errMsg := fmt.Sprintf("Error on retrieve '%s' operations: %v", id, err) return logAndReturnError(c, errMsg) diff --git a/api/treasuries_direct_operations.go b/api/treasuries_direct_operations.go index 59ec35f..1796a3c 100644 --- a/api/treasuries_direct_operations.go +++ b/api/treasuries_direct_operations.go @@ -35,7 +35,7 @@ func (s *server) getAllTreasuriesDirectOperations(c echo.Context) error { func (s *server) getTreasuryDirectOperationByID(c echo.Context) error { id := c.Param("id") log.Debugf("[API] Retrieving treasury direct operation with id: %s", id) - result := &wallet.TreasuryDirect{} + result := wallet.NewTreasuryDirect() if err := s.db.Get(id, result); err != nil { errMsg := fmt.Sprintf("Error on retrieve '%s' operations: %v", id, err) return logAndReturnError(c, errMsg) diff --git a/config/config.go b/config/config.go index bbe0671..7bcd8d2 100644 --- a/config/config.go +++ b/config/config.go @@ -19,6 +19,10 @@ func init() { viper.SetDefault("mongodb.name", "finance-wallet") viper.SetDefault("port", 8889) viper.SetDefault("debug", false) + viper.SetDefault("db.operation.timeout", 3) + viper.SetDefault("collection.operation.timeout", 3) + viper.SetDefault("financeapi.operation.timeout", 3) + viper.SetDefault("financeapi.url", "https://mfinance.com.br/api/v1") logLevel := log.InfoLevel if viper.GetBool("debug") { logLevel = log.DebugLevel @@ -28,8 +32,4 @@ func init() { DisableColors: true, FullTimestamp: true, }) - viper.SetDefault("db.operation.timeout", 3) - viper.SetDefault("collection.operation.timeout", 3) - viper.SetDefault("financeapi.operation.timeout", 3) - viper.SetDefault("financeapi.url", "https://mfinance.com.br/api/v1") } diff --git a/db/operations.go b/db/operations.go index 464eb09..006fa3a 100644 --- a/db/operations.go +++ b/db/operations.go @@ -4,6 +4,8 @@ package db import ( + "errors" + "fmt" "time" "github.com/mfinancecombr/finance-wallet-api/wallet" @@ -12,6 +14,15 @@ import ( "go.mongodb.org/mongo-driver/mongo/options" ) +var operationTypes = map[string]func() wallet.Tradable{ + wallet.ItemTypeStocks: func() wallet.Tradable { return wallet.NewStock() }, + wallet.ItemTypeFIIS: func() wallet.Tradable { return wallet.NewFII() }, + wallet.ItemTypeCertificateOfDeposit: func() wallet.Tradable { return wallet.NewCertificateOfDeposit() }, + wallet.ItemTypeStocksTreasuriesDirect: func() wallet.Tradable { return wallet.NewTreasuryDirect() }, + wallet.ItemTypeStocksStocksFunds: func() wallet.Tradable { return wallet.NewStock() }, + wallet.ItemTypeStocksFICFI: func() wallet.Tradable { return wallet.NewFICFI() }, +} + func (m *mongoSession) getOperationsSymbols(filter bson.M) ([]interface{}, error) { log.Debug("[DB] getOperationSymbols") return m.collection.Distinct(operationsCollection, "symbol", filter) @@ -23,35 +34,26 @@ func (m *mongoSession) getItemTypes() ([]interface{}, error) { } func (m *mongoSession) getAllOperationsBySymbol(symbol, itemType string, year int) (wallet.OperationsList, error) { - log.Debug("[DB] getAllOperationsBySymbol") - date := time.Date(year, 12, 31, 23, 59, 59, 0, time.UTC) - query := bson.M{"symbol": symbol, "date": bson.M{"$lte": date}} - opts := options.Find().SetSort(bson.D{{"date", 1}}) + log.Debug("[DB] getAllOperationsBySymbol", "year", year) + date := time.Date(year+1, 1, 1, 0, 0, 0, 0, time.UTC) + query := bson.M{"symbol": symbol, "date": bson.M{"$lt": date}} + opts := options.Find().SetSort(bson.D{{Key: "date", Value: 1}}) results, err := m.collection.FindAll(operationsCollection, query, opts) if err != nil { return nil, err } - // FIXME operationsList := wallet.OperationsList{} for _, result := range results { - var operation wallet.Tradable - switch itemType { - case "stocks": - operation = &wallet.Stock{} - case "fiis": - operation = &wallet.FII{} - case "certificates-of-deposit": - operation = &wallet.CertificateOfDeposit{} - case "treasuries-direct": - operation = &wallet.TreasuryDirect{} - case "stocks-funds": - operation = &wallet.StockFund{} - case "ficfi": - operation = &wallet.FICFI{} - default: - log.Errorf("Item type '%s' not found", itemType) + newOperationType, ok := operationTypes[itemType] + if !ok { + errMsg := fmt.Sprintf("operation type)Item type '%s' not found", itemType) + return nil, errors.New(errMsg) + } + operation := newOperationType() + bsonBytes, err := bson.Marshal(result) + if err != nil { + return nil, err } - bsonBytes, _ := bson.Marshal(result) bson.Unmarshal(bsonBytes, operation) operationsList = append(operationsList, operation) } diff --git a/wallet/certificate_of_deposit.go b/wallet/certificate_of_deposit.go index 5555b63..e4f46eb 100644 --- a/wallet/certificate_of_deposit.go +++ b/wallet/certificate_of_deposit.go @@ -24,7 +24,7 @@ type CertificateOfDeposit struct { type CertificateOfDepositList []CertificateOfDeposit -const CertificateOfDepositItemType = "certificate-of-deposit" +const CertificateOfDepositItemType = ItemTypeCertificateOfDeposit func NewCertificateOfDeposit() *CertificateOfDeposit { return &CertificateOfDeposit{ItemType: CertificateOfDepositItemType} diff --git a/wallet/ficfi.go b/wallet/ficfi.go index 70dc3b5..be9753f 100644 --- a/wallet/ficfi.go +++ b/wallet/ficfi.go @@ -22,7 +22,7 @@ type FICFI struct { type FICFIList []FICFI -const FICFIItemType = "ficfi" +const FICFIItemType = ItemTypeStocksFICFI func NewFICFI() *FICFI { return &FICFI{ItemType: FICFIItemType} diff --git a/wallet/fii.go b/wallet/fii.go index e5bbf40..18e5b52 100644 --- a/wallet/fii.go +++ b/wallet/fii.go @@ -22,7 +22,7 @@ type FII struct { type FIIList []FII -const FIIItemType = "fiis" +const FIIItemType = ItemTypeFIIS func NewFII() *FII { return &FII{ItemType: FIIItemType} diff --git a/wallet/portfolio.go b/wallet/portfolio.go index 9da23ec..4f666c1 100644 --- a/wallet/portfolio.go +++ b/wallet/portfolio.go @@ -45,5 +45,7 @@ func (p *Portfolio) Recalculate() { p.CostBasis = roundFloatTwoDecimalPlaces(costBasis) p.Gain = roundFloatTwoDecimalPlaces(gain) - p.OverallReturn = roundFloatTwoDecimalPlaces(p.Gain * 100 / p.CostBasis) + if p.CostBasis > 0 { + p.OverallReturn = roundFloatTwoDecimalPlaces(p.Gain * 100 / p.CostBasis) + } } diff --git a/wallet/position.go b/wallet/position.go index 9641945..3ea4ba9 100644 --- a/wallet/position.go +++ b/wallet/position.go @@ -56,7 +56,7 @@ func (pi *Position) Recalculate() { pi.AveragePrice = roundFloatTwoDecimalPlaces(pi.CostBasis / pi.Shares) // FIXME - if pi.ItemType == "stocks" || pi.ItemType == "fiis" { + if pi.ItemType == ItemTypeStocks || pi.ItemType == ItemTypeFIIS { gain := (pi.Shares * pi.LastPrice) - pi.CostBasis pi.Gain = roundFloatTwoDecimalPlaces(gain) pi.OverallReturn = roundFloatTwoDecimalPlaces((gain * 100) / pi.CostBasis) diff --git a/wallet/stock.go b/wallet/stock.go index 4a6a520..094b5bd 100644 --- a/wallet/stock.go +++ b/wallet/stock.go @@ -22,7 +22,7 @@ type Stock struct { type StockList []Stock -const StockItemType = "stocks" +const StockItemType = ItemTypeStocks func NewStock() *Stock { return &Stock{ItemType: StockItemType} diff --git a/wallet/stock_fund.go b/wallet/stock_fund.go index 8479560..0c2b5d1 100644 --- a/wallet/stock_fund.go +++ b/wallet/stock_fund.go @@ -22,7 +22,7 @@ type StockFund struct { type StockFundList []StockFund -const StockFundItemType = "stocks-funds" +const StockFundItemType = ItemTypeStocksStocksFunds func NewStockFund() *StockFund { return &StockFund{ItemType: StockFundItemType} diff --git a/wallet/tradable.go b/wallet/tradable.go index 67e5870..5d4b8bf 100644 --- a/wallet/tradable.go +++ b/wallet/tradable.go @@ -10,3 +10,12 @@ type Tradable interface { GetType() string GetBrokerSlug() string } + +const ( + ItemTypeStocks = "stocks" + ItemTypeFIIS = "fiis" + ItemTypeCertificateOfDeposit = "certificate-of-deposit" + ItemTypeStocksTreasuriesDirect = "treasury-direct" + ItemTypeStocksStocksFunds = "stocks-funds" + ItemTypeStocksFICFI = "ficfi" +) diff --git a/wallet/treasury_direct.go b/wallet/treasury_direct.go index ce4d63d..160a782 100644 --- a/wallet/treasury_direct.go +++ b/wallet/treasury_direct.go @@ -8,18 +8,17 @@ import ( ) type TreasuryDirect struct { - BrokerSlug string `json:"brokerSlug" bson:"brokerSlug" validate:"required"` - Commission float64 `json:"commission" bson:"commission"` - Date *time.Time `json:"date" bson:"date" validate:"required"` - //DueDate *time.Time `json:"dueDate" bson:"dueDate" validate:"required"` - FixedInterestRate float64 `json:"fixedInterestRate" bson:"fixedInterestRate" validate:"required"` - ID string `json:"id,omitempty" bson:"_id,omitempty"` - ItemType string `json:"itemType" bson:"itemType" validate:"required"` - PortfolioSlug string `json:"portfolioSlug" bson:"portfolioSlug" validate:"required"` - Price float64 `json:"price" bson:"price" validate:"required"` - Shares float64 `json:"shares" bson:"shares" validate:"required"` - Symbol string `json:"symbol" bson:"symbol" validate:"required"` - Type string `json:"type" bson:"type" validate:"required"` + BrokerSlug string `json:"brokerSlug" bson:"brokerSlug" validate:"required"` + Commission float64 `json:"commission" bson:"commission"` + Date *time.Time `json:"date" bson:"date" validate:"required"` + FixedInterestRate float64 `json:"fixedInterestRate" bson:"fixedInterestRate" validate:"required"` + ID string `json:"id,omitempty" bson:"_id,omitempty"` + ItemType string `json:"itemType" bson:"itemType" validate:"required"` + PortfolioSlug string `json:"portfolioSlug" bson:"portfolioSlug" validate:"required"` + Price float64 `json:"price" bson:"price" validate:"required"` + Shares float64 `json:"shares" bson:"shares" validate:"required"` + Symbol string `json:"symbol" bson:"symbol" validate:"required"` + Type string `json:"type" bson:"type" validate:"required"` } type TreasuryDirectList []TreasuryDirect