From cc6aa27d5dbafa8e8af6139455d646a61bbdd60f Mon Sep 17 00:00:00 2001 From: Jeffrey Duroyon Date: Tue, 30 Nov 2021 01:46:51 +0100 Subject: [PATCH] feat(expense): import expense --- go.mod | 1 + go.sum | 2 ++ internal/expense/database.go | 9 ++++-- internal/expense/handler.go | 46 ++++++++++++++++++++++++--- internal/expense/service.go | 60 ++++++++++++++++++++++++++++++++++++ 5 files changed, 112 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 6ee910a..6341a88 100644 --- a/go.mod +++ b/go.mod @@ -20,6 +20,7 @@ require ( github.com/go-playground/locales v0.13.0 // indirect github.com/go-playground/universal-translator v0.17.0 // indirect github.com/go-playground/validator/v10 v10.4.1 // indirect + github.com/gocarina/gocsv v0.0.0-20211020200912-82fc2684cc48 // indirect github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/googleapis/gax-go/v2 v2.1.1 // indirect diff --git a/go.sum b/go.sum index 8abbb85..3ae893b 100644 --- a/go.sum +++ b/go.sum @@ -142,6 +142,8 @@ github.com/go-playground/validator/v10 v10.3.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GO github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE= github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gocarina/gocsv v0.0.0-20211020200912-82fc2684cc48 h1:hLeicZW4XBuaISuJPfjkprg0SP0xxsQmb31aJZ6lnIw= +github.com/gocarina/gocsv v0.0.0-20211020200912-82fc2684cc48/go.mod h1:5YoVOkjYAQumqlV356Hj3xeYh4BdZuLE0/nRkf2NKkI= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= diff --git a/internal/expense/database.go b/internal/expense/database.go index ba83669..98e8398 100644 --- a/internal/expense/database.go +++ b/internal/expense/database.go @@ -1,8 +1,10 @@ package expense import ( - "github.com/lib/pq" "nos-comptes/internal/storage/dao/postgresql" + "nos-comptes/internal/utils" + + "github.com/lib/pq" ) type Database struct { @@ -14,13 +16,16 @@ func (db *Database) CreateExpense(expense *Expense) error { INSERT INTO public.expense (account_id, value, type_expense, expense_date, libelle) VALUES - ($1, $2, $3, $4) + ($1, $2, $3, $4, $5) RETURNING id, created_at ` err := db.Session. QueryRow(q, expense.AccountId, expense.Value, expense.TypeExpense, expense.ExpenseDate, expense.Libelle). Scan(&expense.ID, &expense.CreatedAt) + if err != nil { + utils.GetLogger().Info(err) + } if errPq, ok := err.(*pq.Error); ok { return postgresql.HandlePgError(errPq) } diff --git a/internal/expense/handler.go b/internal/expense/handler.go index c5b3858..073456f 100644 --- a/internal/expense/handler.go +++ b/internal/expense/handler.go @@ -1,8 +1,10 @@ package expense import ( + "encoding/csv" "net/http" "nos-comptes/handler" + "nos-comptes/internal/account" "nos-comptes/internal/storage/dao/postgresql" "nos-comptes/internal/storage/model" "nos-comptes/internal/storage/validators" @@ -12,13 +14,48 @@ import ( ) type Context struct { - service *Service - db *Database + service *Service + db *Database + accountService *account.Service *handler.Context } +func (c *Context) ImportExpenseFromCSV(gc *gin.Context) { +} func (c *Context) CreateAnExpense(gc *gin.Context) { accountID := gc.Param("accountId") + userId := gc.Param("userId") + csvHeaderFile, err := gc.FormFile("attachment") + if err != nil { + utils.GetLogger().Info(err) + utils.JSONErrorWithMessage(gc.Writer, model.ErrInternalServer, err.Error()) + return + } + if err == nil { + csvFile, err := csvHeaderFile.Open() + if err != nil { + utils.GetLogger().Info(err) + utils.JSONErrorWithMessage(gc.Writer, model.ErrInternalServer, err.Error()) + return + } + csvr := csv.NewReader(csvFile) + csvr.FieldsPerRecord = -1 + csvr.Comma = ';' + filedata, err := csvr.ReadAll() + + account, err := c.accountService.GetASpecificAccountForUser(userId, accountID) + if err != nil { + utils.GetLogger().Info(err) + utils.JSONErrorWithMessage(gc.Writer, model.ErrInternalServer, err.Error()) + return + } + err = c.service.ProcessCSVFile(filedata, account) + if err != nil { + utils.JSONErrorWithMessage(gc.Writer, model.ErrInternalServer, err.Error()) + return + } + return + } var expense Expense var expenseEditable ExpenseEditable if err := gc.BindJSON(&expenseEditable); err != nil { @@ -26,7 +63,7 @@ func (c *Context) CreateAnExpense(gc *gin.Context) { return } expense = Expense{ExpenseEditable: expenseEditable, AccountId: accountID} - err := c.service.CreateExpense(&expense) + err = c.service.CreateExpense(&expense) if err != nil { utils.GetLogger().Info(err) utils.JSONErrorWithMessage(gc.Writer, model.ErrInternalServer, err.Error()) @@ -65,5 +102,6 @@ func (c *Context) GetAnExpenses(context *gin.Context) { func NewHandler(ctx *handler.Context, db *postgresql.DatabasePostgreSQL) *Context { database := NewDatabase(db) service := NewService(database) - return &Context{service: service, db: database, Context: ctx} + accountService := account.NewService(account.NewDatabase(db)) + return &Context{service: service, db: database, accountService: accountService, Context: ctx} } diff --git a/internal/expense/service.go b/internal/expense/service.go index 1b3f2ec..d2d8556 100644 --- a/internal/expense/service.go +++ b/internal/expense/service.go @@ -1,9 +1,13 @@ package expense import ( + "nos-comptes/internal/account" "nos-comptes/internal/storage/dao" "nos-comptes/internal/storage/model" "nos-comptes/internal/utils" + "strconv" + "strings" + "time" ) type Service struct { @@ -35,6 +39,62 @@ func (s Service) CreateExpense(expense *Expense) error { return s.db.CreateExpense(expense) } +func (s Service) ProcessCSVFile(filedata [][]string, account *account.Account) error { + switch account.Provider { + case "caisse-epargne": + return s.processCaisseEpargne(filedata, account) + case "boursorama": + return s.processBoursorama(filedata, account) + case "bnp": + return s.processBnp(filedata, account) + default: + return nil + } + +} + +func (s Service) processCaisseEpargne(filedata [][]string, account *account.Account) error { + for _, val := range filedata[4:] { + expenseDate, err := time.Parse("02/01/06", val[0]) + if err != nil { + utils.GetLogger().Info(err) + continue + } + amount := val[3] + typeExpense := "D" + if amount == "" { + amount = val[4] + typeExpense = "C" + } + amountParsed, err := strconv.ParseFloat(strings.Trim(strings.ReplaceAll(amount, ",", "."), "+"), 32) + if err != nil { + utils.GetLogger().Info(err) + continue + } + expense := &Expense{ + ExpenseEditable: ExpenseEditable{ + Value: float32(amountParsed), + Libelle: val[2], + TypeExpense: typeExpense, + ExpenseDate: expenseDate, + }, + AccountId: account.ID, + } + s.CreateExpense(expense) + utils.GetLogger().Info(val) + } + + return nil +} + +func (s Service) processBoursorama(filedata [][]string, account *account.Account) error { + return nil +} + +func (s Service) processBnp(filedata [][]string, account *account.Account) error { + return nil +} + func NewService(database *Database) *Service { return &Service{db: database} }