From c464c023017fc2b258e3bf496a2d78b6032896d7 Mon Sep 17 00:00:00 2001 From: Daniel Goc Date: Tue, 24 Mar 2026 14:46:38 +0100 Subject: [PATCH 1/2] add carts --- app/delivery/web/api/restricted/carts.go | 99 +++++++++++++++++++ app/delivery/web/init.go | 4 + app/model/cart.go | 24 +++++ app/repos/cartsRepo/cartsRepo.go | 82 +++++++++++++++ app/service/cartsService/cartsService.go | 59 +++++++++++ app/utils/const_data/consts.go | 2 + app/utils/responseErrors/responseErrors.go | 13 ++- .../20260302163122_create_tables.sql | 33 ++++--- 8 files changed, 303 insertions(+), 13 deletions(-) create mode 100644 app/delivery/web/api/restricted/carts.go create mode 100644 app/model/cart.go create mode 100644 app/repos/cartsRepo/cartsRepo.go create mode 100644 app/service/cartsService/cartsService.go diff --git a/app/delivery/web/api/restricted/carts.go b/app/delivery/web/api/restricted/carts.go new file mode 100644 index 0000000..7fd7a5f --- /dev/null +++ b/app/delivery/web/api/restricted/carts.go @@ -0,0 +1,99 @@ +package restricted + +import ( + "strconv" + + "git.ma-al.com/goc_daniel/b2b/app/service/cartsService" + "git.ma-al.com/goc_daniel/b2b/app/utils/i18n" + "git.ma-al.com/goc_daniel/b2b/app/utils/nullable" + "git.ma-al.com/goc_daniel/b2b/app/utils/response" + "git.ma-al.com/goc_daniel/b2b/app/utils/responseErrors" + "github.com/gofiber/fiber/v3" +) + +// CartsHandler handles endpoints that modify carts. +type CartsHandler struct { + cartsService *cartsService.CartsService +} + +// CartsHandler creates a new CartsHandler instance +func NewCartsHandler() *CartsHandler { + cartsService := cartsService.New() + return &CartsHandler{ + cartsService: cartsService, + } +} + +func CartsHandlerRoutes(r fiber.Router) fiber.Router { + handler := NewCartsHandler() + + r.Get("/add-new-cart", handler.AddNewCart) + r.Get("/change-cart-name", handler.ChangeCartName) + r.Get("/retrieve-cart", handler.RetrieveCart) + + return r +} + +func (h *CartsHandler) AddNewCart(c fiber.Ctx) error { + userID, ok := c.Locals("userID").(uint) + if !ok { + return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrInvalidBody)). + JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrInvalidBody))) + } + + new_cart, err := h.cartsService.CreateNewCart(userID) + if err != nil { + return c.Status(responseErrors.GetErrorStatus(err)). + JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) + } + + return c.JSON(response.Make(&new_cart, 0, i18n.T_(c, response.Message_OK))) +} + +func (h *CartsHandler) ChangeCartName(c fiber.Ctx) error { + userID, ok := c.Locals("userID").(uint) + if !ok { + return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrInvalidBody)). + JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrInvalidBody))) + } + + new_name := c.Query("new_name") + + cart_id_attribute := c.Query("cart_id") + cart_id, err := strconv.Atoi(cart_id_attribute) + if err != nil { + return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)). + JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute))) + } + + err = h.cartsService.UpdateCartName(userID, uint(cart_id), new_name) + if err != nil { + return c.Status(responseErrors.GetErrorStatus(err)). + JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) + } + + return c.JSON(response.Make(nullable.GetNil(""), 0, i18n.T_(c, response.Message_OK))) +} + +func (h *CartsHandler) RetrieveCart(c fiber.Ctx) error { + userID, ok := c.Locals("userID").(uint) + if !ok { + return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrInvalidBody)). + JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrInvalidBody))) + } + + cart_id_attribute := c.Query("cart_id") + cart_id, err := strconv.Atoi(cart_id_attribute) + if err != nil { + return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)). + JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute))) + } + + cart, err := h.cartsService.RetrieveCart(userID, uint(cart_id)) + if err != nil { + return c.Status(responseErrors.GetErrorStatus(err)). + JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) + } + + return c.JSON(response.Make(cart, 0, i18n.T_(c, response.Message_OK))) +} diff --git a/app/delivery/web/init.go b/app/delivery/web/init.go index bc61539..9b332cd 100644 --- a/app/delivery/web/init.go +++ b/app/delivery/web/init.go @@ -110,6 +110,10 @@ func (s *Server) Setup() error { meiliSearch := s.restricted.Group("/meili-search") restricted.MeiliSearchHandlerRoutes(meiliSearch) + // carts (restricted) + carts := s.restricted.Group("/carts") + restricted.CartsHandlerRoutes(carts) + // // Restricted routes example // restricted := s.api.Group("/restricted") // restricted.Use(middleware.AuthMiddleware()) diff --git a/app/model/cart.go b/app/model/cart.go new file mode 100644 index 0000000..c7349be --- /dev/null +++ b/app/model/cart.go @@ -0,0 +1,24 @@ +package model + +type CustomerCart struct { + CartID uint64 `gorm:"column:cart_id;primaryKey;autoIncrement" json:"cart_id"` + UserID uint64 `gorm:"column:user_id;not null;index" json:"user_id"` + Name *string `gorm:"column:name;size:255" json:"name,omitempty"` + Products []CartProduct `gorm:"foreignKey:CartID;references:CartID" json:"products,omitempty"` +} + +func (CustomerCart) TableName() string { + return "b2b_customer_carts" +} + +type CartProduct struct { + ID uint64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"` + CartID uint64 `gorm:"column:cart_id;not null;index" json:"cart_id"` + ProductID uint `gorm:"column:product_id;not null" json:"product_id"` + ProductAttributeID *uint64 `gorm:"column:product_attribute_id" json:"product_attribute_id,omitempty"` + Amount int `gorm:"column:amount;not null" json:"amount"` +} + +func (CartProduct) TableName() string { + return "b2b_carts_products" +} diff --git a/app/repos/cartsRepo/cartsRepo.go b/app/repos/cartsRepo/cartsRepo.go new file mode 100644 index 0000000..5db3751 --- /dev/null +++ b/app/repos/cartsRepo/cartsRepo.go @@ -0,0 +1,82 @@ +package cartsRepo + +import ( + "git.ma-al.com/goc_daniel/b2b/app/db" + "git.ma-al.com/goc_daniel/b2b/app/model" + constdata "git.ma-al.com/goc_daniel/b2b/app/utils/const_data" +) + +type UICartsRepo interface { + CartsAmount(user_id uint) (uint, error) + CreateNewCart(user_id uint) (model.CustomerCart, error) + UserHasCart(user_id uint, cart_id uint) (uint, error) + UpdateCartName(user_id uint, cart_id uint, new_name string) error + RetrieveCart(user_id uint, cart_id uint) (*model.CustomerCart, error) +} + +type CartsRepo struct{} + +func New() UICartsRepo { + return &CartsRepo{} +} + +func (repo *CartsRepo) CartsAmount(user_id uint) (uint, error) { + var amt uint + + err := db.DB. + Table("b2b_customer_carts"). + Select("COUNT(*) AS amt"). + Where("user_id = ?", user_id). + Scan(&amt). + Error + + return amt, err +} + +func (repo *CartsRepo) CreateNewCart(user_id uint) (model.CustomerCart, error) { + var name string + name = constdata.DEFAULT_NEW_CART_NAME + + cart := model.CustomerCart{ + UserID: uint64(user_id), + Name: &name, + } + err := db.DB.Create(&cart).Error + + return cart, err +} + +func (repo *CartsRepo) UserHasCart(user_id uint, cart_id uint) (uint, error) { + var amt uint + + err := db.DB. + Table("b2b_customer_carts"). + Select("COUNT(*) AS amt"). + Where("user_id = ? AND cart_id = ?", user_id, cart_id). + Scan(&amt). + Error + + return amt, err +} + +func (repo *CartsRepo) UpdateCartName(user_id uint, cart_id uint, new_name string) error { + err := db.DB. + Table("b2b_customer_carts"). + Where("user_id = ? AND cart_id = ?", user_id, cart_id). + Update("name", new_name). + Error + + return err +} + +func (repo *CartsRepo) RetrieveCart(user_id uint, cart_id uint) (*model.CustomerCart, error) { + var cart model.CustomerCart + + err := db.DB. + Preload("b2b_carts_products"). + Where("user_id = ? AND cart_id = ?", user_id, cart_id). + First(&cart). + Error + + return &cart, err +} diff --git a/app/service/cartsService/cartsService.go b/app/service/cartsService/cartsService.go new file mode 100644 index 0000000..7fe0568 --- /dev/null +++ b/app/service/cartsService/cartsService.go @@ -0,0 +1,59 @@ +package cartsService + +import ( + "git.ma-al.com/goc_daniel/b2b/app/model" + "git.ma-al.com/goc_daniel/b2b/app/repos/cartsRepo" + constdata "git.ma-al.com/goc_daniel/b2b/app/utils/const_data" + "git.ma-al.com/goc_daniel/b2b/app/utils/responseErrors" +) + +type CartsService struct { + repo cartsRepo.UICartsRepo +} + +func New() *CartsService { + return &CartsService{ + repo: cartsRepo.New(), + } +} + +func (s *CartsService) CreateNewCart(user_id uint) (model.CustomerCart, error) { + var cart model.CustomerCart + + customers_carts_amount, err := s.repo.CartsAmount(user_id) + if err != nil { + return cart, err + } + if customers_carts_amount >= constdata.MAX_AMOUNT_OF_CARTS_PER_USER { + return cart, responseErrors.ErrMaxAmtOfCartsReached + } + + // create new cart for customer + cart, err = s.repo.CreateNewCart(user_id) + + return cart, nil +} + +func (s *CartsService) UpdateCartName(user_id uint, cart_id uint, new_name string) error { + amt, err := s.repo.UserHasCart(user_id, cart_id) + if err != nil { + return err + } + if amt != 1 { + return responseErrors.ErrUserHasNoSuchCart + } + + return s.repo.UpdateCartName(user_id, cart_id, new_name) +} + +func (s *CartsService) RetrieveCart(user_id uint, cart_id uint) (*model.CustomerCart, error) { + amt, err := s.repo.UserHasCart(user_id, cart_id) + if err != nil { + return nil, err + } + if amt != 1 { + return nil, responseErrors.ErrUserHasNoSuchCart + } + + return s.repo.RetrieveCart(user_id, cart_id) +} diff --git a/app/utils/const_data/consts.go b/app/utils/const_data/consts.go index 8bdce27..e8fa951 100644 --- a/app/utils/const_data/consts.go +++ b/app/utils/const_data/consts.go @@ -3,3 +3,5 @@ package constdata // PASSWORD_VALIDATION_REGEX is used by the frontend (JavaScript supports lookaheads). const PASSWORD_VALIDATION_REGEX = `^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{10,}$` const SHOP_ID = 1 +const MAX_AMOUNT_OF_CARTS_PER_USER = 10 +const DEFAULT_NEW_CART_NAME = "new cart" diff --git a/app/utils/responseErrors/responseErrors.go b/app/utils/responseErrors/responseErrors.go index fffa66b..b0c1172 100644 --- a/app/utils/responseErrors/responseErrors.go +++ b/app/utils/responseErrors/responseErrors.go @@ -51,6 +51,10 @@ var ( // Typed errors for menu handler ErrNoRootFound = errors.New("no root found in categories table") + + // Typed errors for carts handler + ErrMaxAmtOfCartsReached = errors.New("maximal amount of carts reached") + ErrUserHasNoSuchCart = errors.New("user does not have cart with given id") ) // Error represents an error with HTTP status code @@ -141,6 +145,11 @@ func GetErrorCode(c fiber.Ctx, err error) string { case errors.Is(err, ErrNoRootFound): return i18n.T_(c, "error.no_root_found") + case errors.Is(err, ErrMaxAmtOfCartsReached): + return i18n.T_(c, "error.max_amt_of_carts_reached") + case errors.Is(err, ErrUserHasNoSuchCart): + return i18n.T_(c, "error.max_amt_of_carts_reached") + default: return i18n.T_(c, "error.err_internal_server_error") } @@ -176,7 +185,9 @@ func GetErrorStatus(err error) int { errors.Is(err, ErrBadField), errors.Is(err, ErrInvalidXHTML), errors.Is(err, ErrBadPaging), - errors.Is(err, ErrNoRootFound): + errors.Is(err, ErrNoRootFound), + errors.Is(err, ErrMaxAmtOfCartsReached), + errors.Is(err, ErrUserHasNoSuchCart): return fiber.StatusBadRequest case errors.Is(err, ErrEmailExists): return fiber.StatusConflict diff --git a/i18n/migrations/20260302163122_create_tables.sql b/i18n/migrations/20260302163122_create_tables.sql index 6113dfe..903e36a 100644 --- a/i18n/migrations/20260302163122_create_tables.sql +++ b/i18n/migrations/20260302163122_create_tables.sql @@ -86,18 +86,27 @@ CREATE INDEX IF NOT EXISTS idx_customers_deleted_at ON b2b_customers (deleted_at); --- customer_repo_accesses --- CREATE TABLE IF NOT EXISTS b2b_customer_repo_accesses ( --- user_id BIGINT NOT NULL, --- repo_id BIGINT NOT NULL, --- PRIMARY KEY (user_id, repo_id), --- CONSTRAINT fk_customer_repo_user --- FOREIGN KEY (user_id) REFERENCES b2b_customers(id) --- ON DELETE CASCADE, --- CONSTRAINT fk_customer_repo_repo --- FOREIGN KEY (repo_id) REFERENCES repository(id) --- ON DELETE CASCADE --- ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +-- customer_carts +CREATE TABLE IF NOT EXISTS b2b_customer_carts ( + cart_id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, + user_id BIGINT UNSIGNED NOT NULL, + name VARCHAR(255) NULL, + CONSTRAINT fk_customer_carts_customers FOREIGN KEY (user_id) REFERENCES b2b_customers(id) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4; +CREATE INDEX IF NOT EXISTS idx_customer_carts_user_id ON b2b_customer_carts (user_id); + + +-- carts_products +CREATE TABLE IF NOT EXISTS b2b_carts_products ( + id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, + cart_id BIGINT UNSIGNED NOT NULL, + product_id INT UNSIGNED NOT NULL, + product_attribute_id BIGINT NULL, + amount INT NOT NULL, + CONSTRAINT fk_carts_products_customer_carts FOREIGN KEY (cart_id) REFERENCES b2b_customer_carts (cart_id) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT fk_carts_products_product FOREIGN KEY (product_id) REFERENCES ps_product (id_product) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4; +CREATE INDEX IF NOT EXISTS idx_carts_products_cart_id ON b2b_carts_products (cart_id); -- refresh_tokens From d6fa655c21f32d4c41c4136eebeaddb2d130b0a7 Mon Sep 17 00:00:00 2001 From: Daniel Goc Date: Tue, 24 Mar 2026 15:44:31 +0100 Subject: [PATCH 2/2] add new endpoints --- app/delivery/web/api/restricted/carts.go | 69 +++++++++++++++++++ app/model/cart.go | 14 ++-- app/repos/cartsRepo/cartsRepo.go | 55 ++++++++++++++- app/service/cartsService/cartsService.go | 24 +++++++ app/utils/responseErrors/responseErrors.go | 12 ++-- .../20260302163122_create_tables.sql | 2 +- 6 files changed, 162 insertions(+), 14 deletions(-) diff --git a/app/delivery/web/api/restricted/carts.go b/app/delivery/web/api/restricted/carts.go index 7fd7a5f..aeed1ee 100644 --- a/app/delivery/web/api/restricted/carts.go +++ b/app/delivery/web/api/restricted/carts.go @@ -29,7 +29,9 @@ func CartsHandlerRoutes(r fiber.Router) fiber.Router { r.Get("/add-new-cart", handler.AddNewCart) r.Get("/change-cart-name", handler.ChangeCartName) + r.Get("/retrieve-carts-info", handler.RetrieveCartsInfo) r.Get("/retrieve-cart", handler.RetrieveCart) + r.Get("/add-product-to-cart", handler.AddProduct) return r } @@ -75,6 +77,22 @@ func (h *CartsHandler) ChangeCartName(c fiber.Ctx) error { return c.JSON(response.Make(nullable.GetNil(""), 0, i18n.T_(c, response.Message_OK))) } +func (h *CartsHandler) RetrieveCartsInfo(c fiber.Ctx) error { + userID, ok := c.Locals("userID").(uint) + if !ok { + return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrInvalidBody)). + JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrInvalidBody))) + } + + carts_info, err := h.cartsService.RetrieveCartsInfo(userID) + if err != nil { + return c.Status(responseErrors.GetErrorStatus(err)). + JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) + } + + return c.JSON(response.Make(&carts_info, 0, i18n.T_(c, response.Message_OK))) +} + func (h *CartsHandler) RetrieveCart(c fiber.Ctx) error { userID, ok := c.Locals("userID").(uint) if !ok { @@ -97,3 +115,54 @@ func (h *CartsHandler) RetrieveCart(c fiber.Ctx) error { return c.JSON(response.Make(cart, 0, i18n.T_(c, response.Message_OK))) } + +func (h *CartsHandler) AddProduct(c fiber.Ctx) error { + userID, ok := c.Locals("userID").(uint) + if !ok { + return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrInvalidBody)). + JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrInvalidBody))) + } + + cart_id_attribute := c.Query("cart_id") + cart_id, err := strconv.Atoi(cart_id_attribute) + if err != nil { + return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)). + JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute))) + } + + product_id_attribute := c.Query("product_id") + product_id, err := strconv.Atoi(product_id_attribute) + if err != nil { + return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)). + JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute))) + } + + product_attribute_id_attribute := c.Query("product_attribute_id") + var product_attribute_id *uint + if product_attribute_id_attribute == "" { + product_attribute_id = nil + } else { + val, err := strconv.Atoi(product_attribute_id_attribute) + if err != nil { + return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)). + JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute))) + } + uval := uint(val) + product_attribute_id = &uval + } + + amount_attribute := c.Query("amount") + amount, err := strconv.Atoi(amount_attribute) + if err != nil { + return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)). + JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute))) + } + + err = h.cartsService.AddProduct(userID, uint(cart_id), uint(product_id), product_attribute_id, uint(amount)) + if err != nil { + return c.Status(responseErrors.GetErrorStatus(err)). + JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err))) + } + + return c.JSON(response.Make(nullable.GetNil(""), 0, i18n.T_(c, response.Message_OK))) +} diff --git a/app/model/cart.go b/app/model/cart.go index c7349be..7e30f0f 100644 --- a/app/model/cart.go +++ b/app/model/cart.go @@ -1,8 +1,8 @@ package model type CustomerCart struct { - CartID uint64 `gorm:"column:cart_id;primaryKey;autoIncrement" json:"cart_id"` - UserID uint64 `gorm:"column:user_id;not null;index" json:"user_id"` + CartID uint `gorm:"column:cart_id;primaryKey;autoIncrement" json:"cart_id"` + UserID uint `gorm:"column:user_id;not null;index" json:"-"` Name *string `gorm:"column:name;size:255" json:"name,omitempty"` Products []CartProduct `gorm:"foreignKey:CartID;references:CartID" json:"products,omitempty"` } @@ -12,11 +12,11 @@ func (CustomerCart) TableName() string { } type CartProduct struct { - ID uint64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"` - CartID uint64 `gorm:"column:cart_id;not null;index" json:"cart_id"` - ProductID uint `gorm:"column:product_id;not null" json:"product_id"` - ProductAttributeID *uint64 `gorm:"column:product_attribute_id" json:"product_attribute_id,omitempty"` - Amount int `gorm:"column:amount;not null" json:"amount"` + ID uint `gorm:"column:id;primaryKey;autoIncrement" json:"-"` + CartID uint `gorm:"column:cart_id;not null;index" json:"-"` + ProductID uint `gorm:"column:product_id;not null" json:"product_id"` + ProductAttributeID *uint `gorm:"column:product_attribute_id" json:"product_attribute_id,omitempty"` + Amount uint `gorm:"column:amount;not null" json:"amount"` } func (CartProduct) TableName() string { diff --git a/app/repos/cartsRepo/cartsRepo.go b/app/repos/cartsRepo/cartsRepo.go index 5db3751..b15700c 100644 --- a/app/repos/cartsRepo/cartsRepo.go +++ b/app/repos/cartsRepo/cartsRepo.go @@ -11,7 +11,10 @@ type UICartsRepo interface { CreateNewCart(user_id uint) (model.CustomerCart, error) UserHasCart(user_id uint, cart_id uint) (uint, error) UpdateCartName(user_id uint, cart_id uint, new_name string) error + RetrieveCartsInfo(user_id uint) ([]model.CustomerCart, error) RetrieveCart(user_id uint, cart_id uint) (*model.CustomerCart, error) + CheckProductExists(product_id uint, product_attribute_id *uint) (uint, error) + AddProduct(user_id uint, cart_id uint, product_id uint, product_attribute_id *uint, amount uint) error } type CartsRepo struct{} @@ -38,7 +41,7 @@ func (repo *CartsRepo) CreateNewCart(user_id uint) (model.CustomerCart, error) { name = constdata.DEFAULT_NEW_CART_NAME cart := model.CustomerCart{ - UserID: uint64(user_id), + UserID: user_id, Name: &name, } err := db.DB.Create(&cart).Error @@ -69,14 +72,62 @@ func (repo *CartsRepo) UpdateCartName(user_id uint, cart_id uint, new_name strin return err } +func (repo *CartsRepo) RetrieveCartsInfo(user_id uint) ([]model.CustomerCart, error) { + var carts []model.CustomerCart + + err := db.DB. + Table("b2b_customer_carts"). + Where("user_id = ?", user_id). + Scan(&carts). + Error + + return carts, err +} + func (repo *CartsRepo) RetrieveCart(user_id uint, cart_id uint) (*model.CustomerCart, error) { var cart model.CustomerCart err := db.DB. - Preload("b2b_carts_products"). + Preload("Products"). Where("user_id = ? AND cart_id = ?", user_id, cart_id). First(&cart). Error return &cart, err } + +func (repo *CartsRepo) CheckProductExists(product_id uint, product_attribute_id *uint) (uint, error) { + var amt uint + + if product_attribute_id == nil { + err := db.DB. + Table("ps_product_shop"). + Select("COUNT(*) AS amt"). + Where("id_product = ?", product_id). + Scan(&amt). + Error + return amt, err + + } else { + err := db.DB. + Table("ps_product_shop AS ps"). + Joins("INNER JOIN ps_product_attribute_shop AS pas ON pas.id_product = ps.id_product"). + Select("COUNT(*) AS amt"). + Where("ps.id_product = ? AND pas.id_product_attribute = ?", product_id, *product_attribute_id). + Scan(&amt). + Error + return amt, err + } +} + +func (repo *CartsRepo) AddProduct(user_id uint, cart_id uint, product_id uint, product_attribute_id *uint, amount uint) error { + product := model.CartProduct{ + CartID: cart_id, + ProductID: product_id, + ProductAttributeID: product_attribute_id, + Amount: amount, + } + err := db.DB.Create(&product).Error + + return err +} diff --git a/app/service/cartsService/cartsService.go b/app/service/cartsService/cartsService.go index 7fe0568..c82e7b2 100644 --- a/app/service/cartsService/cartsService.go +++ b/app/service/cartsService/cartsService.go @@ -46,6 +46,10 @@ func (s *CartsService) UpdateCartName(user_id uint, cart_id uint, new_name strin return s.repo.UpdateCartName(user_id, cart_id, new_name) } +func (s *CartsService) RetrieveCartsInfo(user_id uint) ([]model.CustomerCart, error) { + return s.repo.RetrieveCartsInfo(user_id) +} + func (s *CartsService) RetrieveCart(user_id uint, cart_id uint) (*model.CustomerCart, error) { amt, err := s.repo.UserHasCart(user_id, cart_id) if err != nil { @@ -57,3 +61,23 @@ func (s *CartsService) RetrieveCart(user_id uint, cart_id uint) (*model.Customer return s.repo.RetrieveCart(user_id, cart_id) } + +func (s *CartsService) AddProduct(user_id uint, cart_id uint, product_id uint, product_attribute_id *uint, amount uint) error { + amt, err := s.repo.UserHasCart(user_id, cart_id) + if err != nil { + return err + } + if amt != 1 { + return responseErrors.ErrUserHasNoSuchCart + } + + amt, err = s.repo.CheckProductExists(product_id, product_attribute_id) + if err != nil { + return err + } + if amt != 1 { + return responseErrors.ErrProductOrItsVariationDoesNotExist + } + + return s.repo.AddProduct(user_id, cart_id, product_id, product_attribute_id, amount) +} diff --git a/app/utils/responseErrors/responseErrors.go b/app/utils/responseErrors/responseErrors.go index b0c1172..f658430 100644 --- a/app/utils/responseErrors/responseErrors.go +++ b/app/utils/responseErrors/responseErrors.go @@ -53,8 +53,9 @@ var ( ErrNoRootFound = errors.New("no root found in categories table") // Typed errors for carts handler - ErrMaxAmtOfCartsReached = errors.New("maximal amount of carts reached") - ErrUserHasNoSuchCart = errors.New("user does not have cart with given id") + ErrMaxAmtOfCartsReached = errors.New("maximal amount of carts reached") + ErrUserHasNoSuchCart = errors.New("user does not have cart with given id") + ErrProductOrItsVariationDoesNotExist = errors.New("product or its variation with given ids does not exist") ) // Error represents an error with HTTP status code @@ -148,7 +149,9 @@ func GetErrorCode(c fiber.Ctx, err error) string { case errors.Is(err, ErrMaxAmtOfCartsReached): return i18n.T_(c, "error.max_amt_of_carts_reached") case errors.Is(err, ErrUserHasNoSuchCart): - return i18n.T_(c, "error.max_amt_of_carts_reached") + return i18n.T_(c, "error.user_has_no_such_cart") + case errors.Is(err, ErrProductOrItsVariationDoesNotExist): + return i18n.T_(c, "error.product_or_its_variation_does_not_exist") default: return i18n.T_(c, "error.err_internal_server_error") @@ -187,7 +190,8 @@ func GetErrorStatus(err error) int { errors.Is(err, ErrBadPaging), errors.Is(err, ErrNoRootFound), errors.Is(err, ErrMaxAmtOfCartsReached), - errors.Is(err, ErrUserHasNoSuchCart): + errors.Is(err, ErrUserHasNoSuchCart), + errors.Is(err, ErrProductOrItsVariationDoesNotExist): return fiber.StatusBadRequest case errors.Is(err, ErrEmailExists): return fiber.StatusConflict diff --git a/i18n/migrations/20260302163122_create_tables.sql b/i18n/migrations/20260302163122_create_tables.sql index 903e36a..c2e104b 100644 --- a/i18n/migrations/20260302163122_create_tables.sql +++ b/i18n/migrations/20260302163122_create_tables.sql @@ -102,7 +102,7 @@ CREATE TABLE IF NOT EXISTS b2b_carts_products ( cart_id BIGINT UNSIGNED NOT NULL, product_id INT UNSIGNED NOT NULL, product_attribute_id BIGINT NULL, - amount INT NOT NULL, + amount INT UNSIGNED NOT NULL, CONSTRAINT fk_carts_products_customer_carts FOREIGN KEY (cart_id) REFERENCES b2b_customer_carts (cart_id) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT fk_carts_products_product FOREIGN KEY (product_id) REFERENCES ps_product (id_product) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;