diff --git a/.air.toml b/.air.toml index c274c62..e70a5fd 100644 --- a/.air.toml +++ b/.air.toml @@ -28,7 +28,7 @@ tmp_dir = "tmp" rerun = false rerun_delay = 500 send_interrupt = false - stop_on_error = false + stop_on_error = true [color] app = "" diff --git a/app/delivery/web/api/restricted/currency.go b/app/delivery/web/api/restricted/currency.go index 10a08d3..f888286 100644 --- a/app/delivery/web/api/restricted/currency.go +++ b/app/delivery/web/api/restricted/currency.go @@ -9,8 +9,10 @@ import ( "git.ma-al.com/goc_daniel/b2b/app/model" "git.ma-al.com/goc_daniel/b2b/app/service/currencyService" "git.ma-al.com/goc_daniel/b2b/app/utils/i18n" + "git.ma-al.com/goc_daniel/b2b/app/utils/localeExtractor" "git.ma-al.com/goc_daniel/b2b/app/utils/logger" "git.ma-al.com/goc_daniel/b2b/app/utils/nullable" + "git.ma-al.com/goc_daniel/b2b/app/utils/query/query_params" "git.ma-al.com/goc_daniel/b2b/app/utils/response" "git.ma-al.com/goc_daniel/b2b/app/utils/responseErrors" @@ -33,8 +35,9 @@ func NewCurrencyHandler() *CurrencyHandler { func CurrencyHandlerRoutes(r fiber.Router) fiber.Router { handler := NewCurrencyHandler() - r.Post("/currency-rate", middleware.Require(perms.CurrencyWrite), handler.PostCurrencyRate) - r.Get("/currency-rate/:id", handler.GetCurrencyRate) + r.Patch("", middleware.Require(perms.CurrencyWrite), handler.PostCurrencyRate) + r.Get("/list", handler.List) + r.Get("/:id", handler.GetCurrencyRate) return r } @@ -50,7 +53,8 @@ func (h *CurrencyHandler) PostCurrencyRate(c fiber.Ctx) error { logger.Error("failed to create currency rate", "handler", "CurrencyHandler.PostCurrencyRate", - + "b2b_id_currency", currencyRate.B2bIdCurrency, + "conversion_rate", currencyRate.ConversionRate, "error", err.Error(), ) return c.Status(responseErrors.GetErrorStatus(err)). @@ -65,16 +69,13 @@ func (h *CurrencyHandler) GetCurrencyRate(c fiber.Ctx) error { id, err := strconv.Atoi(idStr) if err != nil { return c.Status(responseErrors.GetErrorStatus(err)).JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) - } - currency, err := h.CurrencyService.GetCurrency(uint(id)) + currency, err := h.CurrencyService.Get(uint(id)) if err != nil { - logger.Error("failed to get currency", "handler", "CurrencyHandler.GetCurrencyRate", - - "currency_id", id, + "b2b_id_currency", id, "error", err.Error(), ) return c.Status(responseErrors.GetErrorStatus(err)).JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) @@ -82,3 +83,37 @@ func (h *CurrencyHandler) GetCurrencyRate(c fiber.Ctx) error { return c.JSON(response.Make(currency, 0, i18n.T_(c, response.Message_OK))) } + +func (h *CurrencyHandler) List(c fiber.Ctx) error { + langId, ok := localeExtractor.GetLangID(c) + if !ok { + return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrInvalidBody)). + JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrInvalidBody))) + } + + p, filt, err := query_params.ParseFilters[model.Currency](c, columnMappingCurrencies) + if err != nil { + return c.Status(responseErrors.GetErrorStatus(err)). + JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) + } + + list, err := h.CurrencyService.Find(langId, p, filt) + if err != nil { + logger.Error("failed to get currency list", + "handler", "CurrencyHandler.List", + "lang_id", langId, + "error", err.Error(), + ) + return c.Status(responseErrors.GetErrorStatus(err)).JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) + } + + return c.JSON(response.Make(&list.Items, int(list.Count), i18n.T_(c, response.Message_OK))) +} + +var columnMappingCurrencies map[string]string = map[string]string{ + "id": "c.id", + "ps_id_currency": "c.ps_id_currency", + "is_default": "c.is_default", + "is_active": "c.is_active", + "conversion_rate": "r.conversion_rate", +} diff --git a/app/delivery/web/init.go b/app/delivery/web/init.go index 73559aa..5b5ca5c 100644 --- a/app/delivery/web/init.go +++ b/app/delivery/web/init.go @@ -150,7 +150,8 @@ func (s *Server) Setup() error { restricted.StorageHandlerRoutes(restrictedStorage) webdav.StorageHandlerRoutes(webdavStorage) - restricted.CurrencyHandlerRoutes(s.restricted) + restrictedCurrency := s.restricted.Group("/currency-rate") + restricted.CurrencyHandlerRoutes(restrictedCurrency) s.api.All("*", func(c fiber.Ctx) error { return c.SendStatus(fiber.StatusNotFound) diff --git a/app/model/currency.go b/app/model/currency.go index 18ca8ce..1a508bc 100644 --- a/app/model/currency.go +++ b/app/model/currency.go @@ -7,7 +7,7 @@ type Currency struct { PsIDCurrency uint `json:"ps_id_currency"` IsDefault bool `json:"is_default"` IsActive bool `json:"is_active"` - ConversionRate *float64 `json:"conversion_rate,omitempty"` + ConversionRate *float64 `json:"conversion_rate,omitempty" gorm:"column:conversion_rate"` } func (Currency) TableName() string { diff --git a/app/repos/currencyRepo/currencyRepo.go b/app/repos/currencyRepo/currencyRepo.go index 9cc153c..a2ac91a 100644 --- a/app/repos/currencyRepo/currencyRepo.go +++ b/app/repos/currencyRepo/currencyRepo.go @@ -5,11 +5,13 @@ import ( "git.ma-al.com/goc_daniel/b2b/app/model" "git.ma-al.com/goc_daniel/b2b/app/utils/query/filters" "git.ma-al.com/goc_daniel/b2b/app/utils/query/find" + "gorm.io/gorm" ) type UICurrencyRepo interface { CreateConversionRate(currencyRate *model.CurrencyRate) error Get(id uint) (*model.Currency, error) + Find(langId uint, p find.Paging, filt *filters.FiltersList) (*find.Found[model.Currency], error) } type CurrencyRepo struct{} @@ -25,19 +27,12 @@ func (repo *CurrencyRepo) CreateConversionRate(currencyRate *model.CurrencyRate) func (repo *CurrencyRepo) Get(id uint) (*model.Currency, error) { var currency model.Currency - err := db.DB.Table("b2b_currencies c"). - Select("c.*, r.conversion_rate"). - Joins(` - LEFT JOIN b2b_currency_rates r - ON r.b2b_id_currency = c.id - AND r.created_at = ( - SELECT MAX(created_at) - FROM b2b_currency_rates - WHERE b2b_id_currency = c.id - ) - `). - Where("c.id = ?", id). - Scan(¤cy).Error + err := db.DB. + Model(&model.Currency{}). + Scopes(WithLatestRate()). + Select("b2b_currencies.*, r.conversion_rate"). + Where("b2b_currencies.id = ?", id). + First(¤cy).Error return ¤cy, err } @@ -46,8 +41,24 @@ func (repo *CurrencyRepo) Find(langId uint, p find.Paging, filt *filters.Filters found, err := find.Paginate[model.Currency](langId, p, db.DB. Model(&model.Currency{}). + Scopes(WithLatestRate()). + Select("b2b_currencies.*, r.conversion_rate"). Scopes(filt.All()...), ) return &found, err } + +func WithLatestRate() func(db *gorm.DB) *gorm.DB { + return func(db *gorm.DB) *gorm.DB { + return db.Joins(` + LEFT JOIN b2b_currency_rates r + ON r.b2b_id_currency = b2b_currencies.id + AND r.created_at = ( + SELECT MAX(created_at) + FROM b2b_currency_rates + WHERE b2b_id_currency = b2b_currencies.id + ) + `) + } +} diff --git a/app/service/currencyService/currencyService.go b/app/service/currencyService/currencyService.go index d4924d8..6c4107e 100644 --- a/app/service/currencyService/currencyService.go +++ b/app/service/currencyService/currencyService.go @@ -3,16 +3,22 @@ package currencyService import ( "git.ma-al.com/goc_daniel/b2b/app/model" "git.ma-al.com/goc_daniel/b2b/app/repos/currencyRepo" + "git.ma-al.com/goc_daniel/b2b/app/utils/query/filters" + "git.ma-al.com/goc_daniel/b2b/app/utils/query/find" ) type CurrencyService struct { repo currencyRepo.UICurrencyRepo } -func (s *CurrencyService) GetCurrency(id uint) (*model.Currency, error) { +func (s *CurrencyService) Get(id uint) (*model.Currency, error) { return s.repo.Get(id) } +func (s *CurrencyService) Find(langId uint, p find.Paging, filt *filters.FiltersList) (*find.Found[model.Currency], error) { + return s.repo.Find(langId, p, filt) +} + func (s *CurrencyService) CreateCurrencyRate(currency *model.CurrencyRate) error { return s.repo.CreateConversionRate(currency) } diff --git a/app/utils/query/find/find.go b/app/utils/query/find/find.go index 487c1d1..4549473 100644 --- a/app/utils/query/find/find.go +++ b/app/utils/query/find/find.go @@ -31,7 +31,11 @@ func Paginate[T any](langID uint, paging Paging, stmt *gorm.DB) (Found[T], error var items []T var count int64 - stmt.Count(&count) + countStmt := stmt.Session(&gorm.Session{}).Select("count(*)").Offset(-1).Limit(-1) + + if err := countStmt.Count(&count).Error; err != nil { + return Found[T]{}, err + } err := stmt. Offset(paging.Offset()). @@ -42,15 +46,10 @@ func Paginate[T any](langID uint, paging Paging, stmt *gorm.DB) (Found[T], error return Found[T]{}, err } - // columnsSpec := GetColumnsSpec[T](langID) - return Found[T]{ Items: items, Count: uint(count), - // Spec: map[string]interface{}{ - // "columns": columnsSpec, - // }, - }, err + }, nil } // GetColumnsSpec[T any] generates a column specification map for a given struct type T. diff --git a/bruno/api_v1/currency/currency.yml b/bruno/api_v1/currency/Currency.yml similarity index 93% rename from bruno/api_v1/currency/currency.yml rename to bruno/api_v1/currency/Currency.yml index 4c444d8..17360a5 100644 --- a/bruno/api_v1/currency/currency.yml +++ b/bruno/api_v1/currency/Currency.yml @@ -1,5 +1,5 @@ info: - name: currency + name: Currency type: http seq: 1 diff --git a/bruno/api_v1/currency/List.yml b/bruno/api_v1/currency/List.yml new file mode 100644 index 0000000..ccc269c --- /dev/null +++ b/bruno/api_v1/currency/List.yml @@ -0,0 +1,15 @@ +info: + name: List + type: http + seq: 3 + +http: + method: GET + url: "{{bas_url}}/restricted/currency-rate/list" + auth: inherit + +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 diff --git a/bruno/api_v1/currency/currency-rate.yml b/bruno/api_v1/currency/Update currency rate.yml similarity index 86% rename from bruno/api_v1/currency/currency-rate.yml rename to bruno/api_v1/currency/Update currency rate.yml index 269dea4..ee713fa 100644 --- a/bruno/api_v1/currency/currency-rate.yml +++ b/bruno/api_v1/currency/Update currency rate.yml @@ -1,10 +1,10 @@ info: - name: currency-rate + name: Update currency rate type: http seq: 2 http: - method: POST + method: PATCH url: "{{bas_url}}/restricted/currency-rate" body: type: json