From 150339628e483d0c2d088ea2b200a6a152d8f236 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=20Jurmanovi=C4=87?= Date: Wed, 16 Jun 2021 21:33:26 +0200 Subject: [PATCH 1/2] subscription controller, service and model --- pkg/api/routes.go | 6 ++ pkg/controllers/subscriptionTypes.go | 42 +++++++++++++ pkg/controllers/subscriptions.go | 47 +++++++++++++++ pkg/migrate/migrate.go | 7 +++ pkg/migrate/migrations/subscriptionTypes.go | 67 +++++++++++++++++++++ pkg/migrate/migrations/subscriptions.go | 34 +++++++++++ pkg/migrate/migrations/transactionTypes.go | 19 ++++++ pkg/models/subscriptionTypes.go | 13 ++++ pkg/models/subscriptions.go | 32 ++++++++++ pkg/models/transactions.go | 2 + pkg/services/subscriptionTypes.go | 33 ++++++++++ pkg/services/subscriptions.go | 45 ++++++++++++++ 12 files changed, 347 insertions(+) create mode 100644 pkg/controllers/subscriptionTypes.go create mode 100644 pkg/controllers/subscriptions.go create mode 100644 pkg/migrate/migrations/subscriptionTypes.go create mode 100644 pkg/migrate/migrations/subscriptions.go create mode 100644 pkg/models/subscriptionTypes.go create mode 100644 pkg/models/subscriptions.go create mode 100644 pkg/services/subscriptionTypes.go create mode 100644 pkg/services/subscriptions.go diff --git a/pkg/api/routes.go b/pkg/api/routes.go index 24967f0..c27c3ed 100644 --- a/pkg/api/routes.go +++ b/pkg/api/routes.go @@ -19,12 +19,16 @@ func Routes(s *gin.Engine, db *pg.DB) { walletHeader := ver.Group("wallet/wallet-header", middleware.Auth) transaction := ver.Group("transaction", middleware.Auth) transactionType := ver.Group("transaction-type", middleware.Auth) + subscription := ver.Group("subscription", middleware.Auth) + subscriptionType := ver.Group("subscription-type", middleware.Auth) apiService := services.ApiService{Db: db} usersService := services.UsersService{Db: db} walletService := services.WalletService{Db: db} transactionService := services.TransactionService{Db: db} transactionTypeService := services.TransactionTypeService{Db: db} + subscriptionService := services.SubscriptionService{Db: db} + subscriptionTypeService := services.SubscriptionTypeService{Db: db} controllers.NewApiController(&apiService, api) controllers.NewAuthController(&usersService, auth) @@ -32,4 +36,6 @@ func Routes(s *gin.Engine, db *pg.DB) { controllers.NewWalletsHeaderController(&walletService, walletHeader) controllers.NewTransactionController(&transactionService, transaction) controllers.NewTransactionTypeController(&transactionTypeService, transactionType) + controllers.NewSubscriptionController(&subscriptionService, subscription) + controllers.NewSubscriptionTypeController(&subscriptionTypeService, subscriptionType) } diff --git a/pkg/controllers/subscriptionTypes.go b/pkg/controllers/subscriptionTypes.go new file mode 100644 index 0000000..a71c9fa --- /dev/null +++ b/pkg/controllers/subscriptionTypes.go @@ -0,0 +1,42 @@ +package controllers + +import ( + "net/http" + "wallet-api/pkg/models" + "wallet-api/pkg/services" + + "github.com/gin-gonic/gin" +) + +type SubscriptionTypeController struct { + SubscriptionTypeService *services.SubscriptionTypeService +} + +func NewSubscriptionTypeController(as *services.SubscriptionTypeService, s *gin.RouterGroup) *SubscriptionTypeController { + wc := new(SubscriptionTypeController) + wc.SubscriptionTypeService = as + + s.POST("", wc.New) + s.GET("", wc.GetAll) + + return wc +} + +func (wc *SubscriptionTypeController) New(c *gin.Context) { + body := new(models.NewSubscriptionTypeBody) + if err := c.ShouldBind(body); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + wm := wc.SubscriptionTypeService.New(body) + c.JSON(200, wm) +} + +func (wc *SubscriptionTypeController) GetAll(c *gin.Context) { + embed, _ := c.GetQuery("embed") + + wm := wc.SubscriptionTypeService.GetAll(embed) + + c.JSON(200, wm) +} diff --git a/pkg/controllers/subscriptions.go b/pkg/controllers/subscriptions.go new file mode 100644 index 0000000..d11a316 --- /dev/null +++ b/pkg/controllers/subscriptions.go @@ -0,0 +1,47 @@ +package controllers + +import ( + "net/http" + "wallet-api/pkg/models" + "wallet-api/pkg/services" + + "github.com/gin-gonic/gin" +) + +type SubscriptionController struct { + SubscriptionService *services.SubscriptionService +} + +func NewSubscriptionController(as *services.SubscriptionService, s *gin.RouterGroup) *SubscriptionController { + wc := new(SubscriptionController) + wc.SubscriptionService = as + + s.POST("", wc.New) + s.GET("", wc.GetAll) + + return wc +} + +func (wc *SubscriptionController) New(c *gin.Context) { + body := new(models.NewSubscriptionBody) + if err := c.ShouldBind(body); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + wm := wc.SubscriptionService.New(body) + c.JSON(200, wm) +} + +func (wc *SubscriptionController) GetAll(c *gin.Context) { + body := new(models.Auth) + auth := c.MustGet("auth") + body.Id = auth.(*models.Auth).Id + + fr := FilteredResponse(c) + wallet, _ := c.GetQuery("walletId") + + wc.SubscriptionService.GetAll(body, wallet, fr) + + c.JSON(200, fr) +} diff --git a/pkg/migrate/migrate.go b/pkg/migrate/migrate.go index 1444d6e..dfaa984 100644 --- a/pkg/migrate/migrate.go +++ b/pkg/migrate/migrate.go @@ -12,12 +12,19 @@ func Start(conn *pg.DB) error { walletsMigration := migrations.WalletsMigration{Db: conn} transactionTypesMigration := migrations.TransactionTypesMigration{Db: conn} transactionsMigration := migrations.TransactionsMigration{Db: conn} + subscriptionTypesMigration := migrations.SubscriptionTypesMigration{Db: conn} + subscriptionsMigration := migrations.SubscriptionsMigration{Db: conn} err := apiMigration.Create() err = usersMigration.Create() err = walletsMigration.Create() err = transactionTypesMigration.Create() err = transactionsMigration.Create() + err = subscriptionTypesMigration.Create() + err = subscriptionsMigration.Create() + + err = transactionTypesMigration.Populate() + err = subscriptionTypesMigration.Populate() return err } diff --git a/pkg/migrate/migrations/subscriptionTypes.go b/pkg/migrate/migrations/subscriptionTypes.go new file mode 100644 index 0000000..670d7f1 --- /dev/null +++ b/pkg/migrate/migrations/subscriptionTypes.go @@ -0,0 +1,67 @@ +package migrations + +import ( + "fmt" + "log" + "wallet-api/pkg/models" + + "github.com/go-pg/pg/v10" + "github.com/go-pg/pg/v10/orm" +) + +type SubscriptionTypesMigration struct { + Db *pg.DB +} + +func (am *SubscriptionTypesMigration) Create() error { + models := []interface{}{ + (*models.SubscriptionType)(nil), + } + + for _, model := range models { + err := am.Db.Model(model).CreateTable(&orm.CreateTableOptions{ + IfNotExists: false, + FKConstraints: true, + }) + if err != nil { + log.Printf("Error Creating Table: %s", err) + return err + } else { + fmt.Println("Table created successfully") + } + } + return nil +} + +func (am *SubscriptionTypesMigration) Populate() error { + daily := new(models.SubscriptionType) + weekly := new(models.SubscriptionType) + monthly := new(models.SubscriptionType) + yearly := new(models.SubscriptionType) + + daily.Init() + daily.Name = "Daily" + daily.Type = "daily" + + weekly.Init() + weekly.Name = "Weekly" + weekly.Type = "weekly" + + monthly.Init() + monthly.Name = "Monthly" + monthly.Type = "monthly" + + yearly.Init() + yearly.Name = "Yearly" + yearly.Type = "yearly" + + _, err := am.Db.Model(daily).Where("? = ?", pg.Ident("type"), daily.Type).SelectOrInsert() + + _, err = am.Db.Model(weekly).Where("? = ?", pg.Ident("type"), weekly.Type).SelectOrInsert() + + _, err = am.Db.Model(monthly).Where("? = ?", pg.Ident("type"), monthly.Type).SelectOrInsert() + + _, err = am.Db.Model(yearly).Where("? = ?", pg.Ident("type"), yearly.Type).SelectOrInsert() + + return err +} diff --git a/pkg/migrate/migrations/subscriptions.go b/pkg/migrate/migrations/subscriptions.go new file mode 100644 index 0000000..f7e73c8 --- /dev/null +++ b/pkg/migrate/migrations/subscriptions.go @@ -0,0 +1,34 @@ +package migrations + +import ( + "fmt" + "log" + "wallet-api/pkg/models" + + "github.com/go-pg/pg/v10" + "github.com/go-pg/pg/v10/orm" +) + +type SubscriptionsMigration struct { + Db *pg.DB +} + +func (am *SubscriptionsMigration) Create() error { + models := []interface{}{ + (*models.Subscription)(nil), + } + + for _, model := range models { + err := am.Db.Model(model).CreateTable(&orm.CreateTableOptions{ + IfNotExists: false, + FKConstraints: true, + }) + if err != nil { + log.Printf("Error Creating Table: %s", err) + return err + } else { + fmt.Println("Table created successfully") + } + } + return nil +} diff --git a/pkg/migrate/migrations/transactionTypes.go b/pkg/migrate/migrations/transactionTypes.go index 2146178..714e76b 100644 --- a/pkg/migrate/migrations/transactionTypes.go +++ b/pkg/migrate/migrations/transactionTypes.go @@ -32,3 +32,22 @@ func (am *TransactionTypesMigration) Create() error { } return nil } + +func (am *TransactionTypesMigration) Populate() error { + gain := new(models.TransactionType) + expense := new(models.TransactionType) + + gain.Init() + gain.Name = "Gain" + gain.Type = "gain" + + expense.Init() + expense.Name = "Expense" + expense.Type = "expense" + + _, err := am.Db.Model(gain).Where("? = ?", pg.Ident("type"), gain.Type).SelectOrInsert() + + _, err = am.Db.Model(expense).Where("? = ?", pg.Ident("type"), expense.Type).SelectOrInsert() + + return err +} diff --git a/pkg/models/subscriptionTypes.go b/pkg/models/subscriptionTypes.go new file mode 100644 index 0000000..eb1c053 --- /dev/null +++ b/pkg/models/subscriptionTypes.go @@ -0,0 +1,13 @@ +package models + +type SubscriptionType struct { + tableName struct{} `pg:"subscriptionTypes,alias:subscriptionTypes"` + BaseModel + Name string `json:"name" pg:"name"` + Type string `json:"type" pg:"type"` +} + +type NewSubscriptionTypeBody struct { + Name string `json:"name" form:"name"` + Type string `json:"type" form:"type"` +} diff --git a/pkg/models/subscriptions.go b/pkg/models/subscriptions.go new file mode 100644 index 0000000..e5dd3ab --- /dev/null +++ b/pkg/models/subscriptions.go @@ -0,0 +1,32 @@ +package models + +import ( + "encoding/json" + "time" +) + +type Subscription struct { + tableName struct{} `pg:"subscriptions,alias:subscriptions"` + BaseModel + Description string `json:"description" pg:"description"` + StartDate time.Time `json:"startDate" pg:"start_date"` + SubscriptionTypeID string `json:"subscriptionTypeId" pg:"subscription_type_id"` + SubscriptionType *SubscriptionType `json:"subscriptionType", pg:"rel:has-one, fk:subscription_type_id"` + CustomRange int `json:"customRange", pg:"custom_range"` + WalletID string `json:"walletId", pg:"wallet_id"` + Wallet *Wallet `json:"wallet" pg:"rel:has-one, fk:wallet_id"` + TransactionTypeID string `json:"transactionTypeId", pg:"transaction_type_id"` + TransactionType *TransactionType `json:"transactionType", pg:"rel:has-one, fk:transaction_type_id"` + LastTransactionDate time.Time `json:"lastTransactionDate", pg:"last_transaction_date"` + Amount int `json:"amount", pg:"amount"` +} + +type NewSubscriptionBody struct { + WalletID string `json:"walletId" form:"walletId"` + TransactionTypeID string `json:"transactionTypeId" form:"transactionTypeId"` + SubscriptionTypeID string `json:"subscriptionTypeId" pg:"subscription_type_id"` + CustomRange int `json:"customRange", pg:"custom_range"` + StartDate time.Time `json:"startDate" pg:"start_date"` + Description string `json:"description" form:"description"` + Amount json.Number `json:"amount" form:"amount"` +} diff --git a/pkg/models/transactions.go b/pkg/models/transactions.go index 9bed283..3854d46 100644 --- a/pkg/models/transactions.go +++ b/pkg/models/transactions.go @@ -15,6 +15,8 @@ type Transaction struct { Amount int `json:"amount", pg:"amount"` Wallet *Wallet `json:"wallet" pg:"rel:has-one, fk:wallet_id"` TransactionDate time.Time `json:"transactionDate" pg:"transaction_date"` + SubscriptionID string `json:"subscriptionId", pg:"subscription_id"` + Subscription *Subscription `json:"subscription", pg:"rel:has-one, fk:subscription_id"` } type NewTransactionBody struct { diff --git a/pkg/services/subscriptionTypes.go b/pkg/services/subscriptionTypes.go new file mode 100644 index 0000000..3a1a1d1 --- /dev/null +++ b/pkg/services/subscriptionTypes.go @@ -0,0 +1,33 @@ +package services + +import ( + "wallet-api/pkg/models" + "wallet-api/pkg/utl/common" + + "github.com/go-pg/pg/v10" +) + +type SubscriptionTypeService struct { + Db *pg.DB +} + +func (as *SubscriptionTypeService) New(body *models.NewSubscriptionTypeBody) *models.SubscriptionType { + tm := new(models.SubscriptionType) + + tm.Init() + tm.Name = body.Name + tm.Type = body.Type + + as.Db.Model(tm).Insert() + + return tm +} + +func (as *SubscriptionTypeService) GetAll(embed string) *[]models.SubscriptionType { + wm := new([]models.SubscriptionType) + + query := as.Db.Model(wm) + common.GenerateEmbed(query, embed).Select() + + return wm +} diff --git a/pkg/services/subscriptions.go b/pkg/services/subscriptions.go new file mode 100644 index 0000000..b64d31d --- /dev/null +++ b/pkg/services/subscriptions.go @@ -0,0 +1,45 @@ +package services + +import ( + "time" + "wallet-api/pkg/models" + + "github.com/go-pg/pg/v10" +) + +type SubscriptionService struct { + Db *pg.DB +} + +func (as *SubscriptionService) New(body *models.NewSubscriptionBody) *models.Subscription { + tm := new(models.Subscription) + + amount, _ := body.Amount.Int64() + + tm.Init() + tm.WalletID = body.WalletID + tm.TransactionTypeID = body.TransactionTypeID + tm.SubscriptionTypeID = body.SubscriptionTypeID + tm.CustomRange = body.CustomRange + tm.Description = body.Description + tm.StartDate = body.StartDate + tm.Amount = int(amount) + + if body.StartDate.IsZero() { + tm.StartDate = time.Now() + } + + as.Db.Model(tm).Insert() + + return tm +} + +func (as *SubscriptionService) GetAll(am *models.Auth, walletId string, filtered *models.FilteredResponse) { + wm := new([]models.Subscription) + + query := as.Db.Model(wm).Relation("Wallet").Where("wallet.? = ?", pg.Ident("user_id"), am.Id) + if walletId != "" { + query = query.Where("? = ?", pg.Ident("wallet_id"), walletId) + } + FilteredResponse(query, wm, filtered) +} From 356b93daa855553bf74f757ffb13a324724e329a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=20Jurmanovi=C4=87?= Date: Fri, 18 Jun 2021 22:07:30 +0200 Subject: [PATCH 2/2] added subscriptions calc to wallet header --- pkg/services/wallets.go | 84 +++++++++++++++++++++++++++++++++++------ 1 file changed, 73 insertions(+), 11 deletions(-) diff --git a/pkg/services/wallets.go b/pkg/services/wallets.go index 37907c2..877b947 100644 --- a/pkg/services/wallets.go +++ b/pkg/services/wallets.go @@ -44,19 +44,37 @@ func (as *WalletService) GetHeader(am *models.Auth, embed string, walletId strin var wallets []models.WalletTransactions var wg sync.WaitGroup transactions := new([]models.Transaction) + subscriptions := new([]models.Subscription) - query := as.Db.Model(transactions).Relation("Wallet").Where("wallet.? = ?", pg.Ident("user_id"), am.Id).Relation("TransactionType") + wg.Add(1) + go func() { + defer wg.Done() + query := as.Db.Model(transactions).Relation("Wallet").Where("wallet.? = ?", pg.Ident("user_id"), am.Id).Relation("TransactionType") + if walletId != "" { + query.Where("? = ?", pg.Ident("wallet_id"), walletId) + } + query.Select() + }() + wg.Add(1) + go func() { + defer wg.Done() + query2 := as.Db.Model(subscriptions).Relation("Wallet").Where("wallet.? = ?", pg.Ident("user_id"), am.Id).Relation("TransactionType").Relation("SubscriptionType") + if walletId != "" { + query2.Where("? = ?", pg.Ident("wallet_id"), walletId) + } + query2.Select() + }() - if walletId != "" { - query.Where("? = ?", pg.Ident("wallet_id"), walletId) - } - - query.Select() + wg.Wait() currentBalance := 0 lastMonthBalance := 0 nextMonth := 0 + subCurrentBalance := 0 + subLastMonthBalance := 0 + subNextMonth := 0 + now := time.Now() currentYear, currentMonth, _ := now.Date() @@ -70,11 +88,11 @@ func (as *WalletService) GetHeader(am *models.Auth, embed string, walletId strin addWhere(&wallets, trans.WalletID, trans) } - for range wallets { + for _, wallet := range wallets { wg.Add(1) go func() { defer wg.Done() - for _, trans := range *transactions { + for _, trans := range wallet.Transactions { if trans.TransactionDate.Before(firstOfNextMonth) && trans.TransactionDate.After(firstOfMonth) { if trans.TransactionType.Type == "expense" { currentBalance -= trans.Amount @@ -98,11 +116,55 @@ func (as *WalletService) GetHeader(am *models.Auth, embed string, walletId strin }() } + for _, sub := range *subscriptions { + wg.Add(1) + go func() { + defer wg.Done() + startDate := sub.StartDate + now := time.Now() + for startDate.Before(now) { + if startDate.Before(firstOfNextMonth) && startDate.After(firstOfMonth) { + if sub.TransactionType.Type == "expense" { + subCurrentBalance -= sub.Amount + } else { + subCurrentBalance += sub.Amount + } + } else if startDate.Before(firstOfMonthAfterNext) && startDate.After(firstOfNextMonth) { + if sub.TransactionType.Type == "expense" { + subNextMonth -= sub.Amount + } else { + subNextMonth += sub.Amount + } + } else if startDate.Before(firstOfMonth) { + if sub.TransactionType.Type == "expense" { + subLastMonthBalance -= sub.Amount + } else { + subLastMonthBalance += sub.Amount + } + } + + if sub.SubscriptionType.Type == "monthly" { + startDate = startDate.AddDate(0, sub.CustomRange, 0) + } else if sub.SubscriptionType.Type == "weekly" { + startDate = startDate.AddDate(0, 0, 7*sub.CustomRange) + } else if sub.SubscriptionType.Type == "daily" { + startDate = startDate.AddDate(0, 0, sub.CustomRange) + } else { + startDate = startDate.AddDate(sub.CustomRange, 0, 0) + } + } + }() + } + wg.Wait() - wm.LastMonth = lastMonthBalance - wm.CurrentBalance = currentBalance + lastMonthBalance - wm.NextMonth = currentBalance + nextMonth + combinedCurrent := currentBalance + subCurrentBalance + combinedLast := lastMonthBalance + subLastMonthBalance + combinedNext := nextMonth + subNextMonth + + wm.LastMonth = combinedLast + wm.CurrentBalance = combinedCurrent + combinedLast + wm.NextMonth = combinedLast + combinedCurrent + combinedNext wm.Currency = "USD" wm.WalletId = walletId