orders #58

Merged
goc_daniel merged 13 commits from orders into main 2026-04-14 11:20:06 +00:00
9 changed files with 131 additions and 44 deletions
Showing only changes of commit 134bc4ea53 - Show all commits

View File

@@ -3,9 +3,10 @@ package perms
type Permission string
const (
UserReadAny Permission = "user.read.any"
UserWriteAny Permission = "user.write.any"
UserDeleteAny Permission = "user.delete.any"
CurrencyWrite Permission = "currency.write"
ViewAllOrders Permission = "orders.view"
UserReadAny Permission = "user.read.any"
UserWriteAny Permission = "user.write.any"
UserDeleteAny Permission = "user.delete.any"
CurrencyWrite Permission = "currency.write"
ViewAllOrders Permission = "orders.view"
ModifyAllOrders Permission = "orders.modify"
)

View File

@@ -61,8 +61,9 @@ func (h *OrdersHandler) ListOrders(c fiber.Ctx) error {
}
var columnMappingListOrders map[string]string = map[string]string{
"order_id": "b2b_customer_orders.id",
"order_id": "b2b_customer_orders.order_id",
"user_id": "b2b_customer_orders.user_id",
"name": "b2b_customer_orders.name",
"country_id": "b2b_customer_orders.country_id",
"status": "b2b_customer_orders.status",
}
@@ -81,6 +82,7 @@ func (h *OrdersHandler) PlaceNewOrder(c fiber.Ctx) error {
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute)))
}
address_info := c.Query("address_info")
country_id_attribute := c.Query("country_id")
country_id, err := strconv.Atoi(country_id_attribute)
if err != nil {
@@ -88,9 +90,9 @@ func (h *OrdersHandler) PlaceNewOrder(c fiber.Ctx) error {
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute)))
}
address_info := c.Query("address_info")
name := c.Query("name")
err = h.ordersService.PlaceNewOrder(userID, uint(cart_id), uint(country_id), address_info)
err = h.ordersService.PlaceNewOrder(userID, uint(cart_id), name, uint(country_id), address_info)
if err != nil {
return c.Status(responseErrors.GetErrorStatus(err)).
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))

View File

@@ -1,12 +1,13 @@
package model
type CustomerOrder struct {
ID uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"`
UserID uint `gorm:"column:user_id;not null;index" json:"-"`
OrderID uint `gorm:"column:order_id;primaryKey;autoIncrement" json:"order_id"`
UserID uint `gorm:"column:user_id;not null;index" json:"user_id"`
Name string `gorm:"column:name;not null" json:"name"`
CountryID uint `gorm:"column:country_id;not null" json:"country_id"`
AddressJSON *string `gorm:"column:address_json" json:"address_json,omitempty"`
Status *string `gorm:"column:status;size:50" json:"status,omitempty"`
Products []OrderProduct `gorm:"foreignKey:OrderID;references:ID" json:"products,omitempty"`
AddressJSON string `gorm:"column:address_json;not null" json:"address_json"`
Status string `gorm:"column:status;size:50;not null" json:"status"`
Products []OrderProduct `gorm:"foreignKey:OrderID;references:ID" json:"products"`
}
func (CustomerOrder) TableName() string {
@@ -14,7 +15,6 @@ func (CustomerOrder) TableName() string {
}
type OrderProduct struct {
ID uint `gorm:"column:id;primaryKey;autoIncrement" json:"-"`
OrderID uint `gorm:"column:order_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"`

View File

@@ -9,11 +9,11 @@ import (
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)
UserHasCart(user_id uint, cart_id uint) (bool, 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)
CheckProductExists(product_id uint, product_attribute_id *uint) (bool, error)
AddProduct(user_id uint, cart_id uint, product_id uint, product_attribute_id *uint, amount uint) error
}
@@ -49,7 +49,7 @@ func (repo *CartsRepo) CreateNewCart(user_id uint) (model.CustomerCart, error) {
return cart, err
}
func (repo *CartsRepo) UserHasCart(user_id uint, cart_id uint) (uint, error) {
func (repo *CartsRepo) UserHasCart(user_id uint, cart_id uint) (bool, error) {
var amt uint
err := db.DB.
@@ -59,7 +59,7 @@ func (repo *CartsRepo) UserHasCart(user_id uint, cart_id uint) (uint, error) {
Scan(&amt).
Error
return amt, err
return amt >= 1, err
}
func (repo *CartsRepo) UpdateCartName(user_id uint, cart_id uint, new_name string) error {
@@ -96,7 +96,7 @@ func (repo *CartsRepo) RetrieveCart(user_id uint, cart_id uint) (*model.Customer
return &cart, err
}
func (repo *CartsRepo) CheckProductExists(product_id uint, product_attribute_id *uint) (uint, error) {
func (repo *CartsRepo) CheckProductExists(product_id uint, product_attribute_id *uint) (bool, error) {
var amt uint
if product_attribute_id == nil {
@@ -106,7 +106,7 @@ func (repo *CartsRepo) CheckProductExists(product_id uint, product_attribute_id
Where("id_product = ?", product_id).
Scan(&amt).
Error
return amt, err
return amt >= 1, err
} else {
err := db.DB.
@@ -116,7 +116,7 @@ func (repo *CartsRepo) CheckProductExists(product_id uint, product_attribute_id
Where("ps.id_product = ? AND pas.id_product_attribute = ?", product_id, *product_attribute_id).
Scan(&amt).
Error
return amt, err
return amt >= 1, err
}
}

View File

@@ -3,15 +3,17 @@ package ordersRepo
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"
"git.ma-al.com/goc_daniel/b2b/app/utils/query/filters"
"git.ma-al.com/goc_daniel/b2b/app/utils/query/find"
)
type UIOrdersRepo interface {
UserHasOrder(user_id uint, order_id uint) (bool, error)
Find(user_id uint, p find.Paging, filt *filters.FiltersList) (find.Found[model.CustomerOrder], error)
PlaceNewOrder(user_id uint, cart_id uint, country_id uint, address_info string) error
ChangeOrderAddress(user_id uint, order_id uint, country_id uint, address_info string) error
ChangeOrderStatus(user_id uint, order_id uint, status string) error
PlaceNewOrder(cart *model.CustomerCart, name string, country_id uint, address_info string) error
ChangeOrderAddress(order_id uint, country_id uint, address_info string) error
ChangeOrderStatus(order_id uint, status string) error
}
type OrdersRepo struct{}
@@ -20,6 +22,19 @@ func New() UIOrdersRepo {
return &OrdersRepo{}
}
func (repo *OrdersRepo) UserHasOrder(user_id uint, order_id uint) (bool, error) {
var amt uint
err := db.DB.
Table("b2b_customer_orders").
Select("COUNT(*) AS amt").
Where("user_id = ? AND order_id = ?", user_id, order_id).
Scan(&amt).
Error
return amt >= 1, err
}
func (repo *OrdersRepo) Find(user_id uint, p find.Paging, filt *filters.FiltersList) (find.Found[model.CustomerOrder], error) {
var list []model.CustomerOrder
var total int64
@@ -54,15 +69,42 @@ func (repo *OrdersRepo) Find(user_id uint, p find.Paging, filt *filters.FiltersL
}, nil
}
func (repo *OrdersRepo) PlaceNewOrder(user_id uint, cart_id uint, country_id uint, address_info string) error {
func (repo *OrdersRepo) PlaceNewOrder(cart *model.CustomerCart, name string, country_id uint, address_info string) error {
order := model.CustomerOrder{
UserID: cart.UserID,
Name: name,
CountryID: country_id,
AddressJSON: address_info,
Status: constdata.NEW_ORDER_STATUS,
Products: make([]model.OrderProduct, 0, len(cart.Products)),
}
return nil
for _, product := range cart.Products {
order.Products = append(order.Products, model.OrderProduct{
ProductID: product.ProductID,
ProductAttributeID: product.ProductAttributeID,
Amount: product.Amount,
})
}
return db.DB.Create(&order).Error
}
func (repo *OrdersRepo) ChangeOrderAddress(user_id uint, order_id uint, country_id uint, address_info string) error {
return nil
func (repo *OrdersRepo) ChangeOrderAddress(order_id uint, country_id uint, address_info string) error {
return db.DB.
Table("b2b_customer_orders").
Where("order_id = ?", order_id).
Updates(map[string]interface{}{
"country_id": country_id,
"address_info": address_info,
}).
Error
}
func (repo *OrdersRepo) ChangeOrderStatus(user_id uint, order_id uint, status string) error {
return nil
func (repo *OrdersRepo) ChangeOrderStatus(order_id uint, status string) error {
return db.DB.
Table("b2b_customer_orders").
Where("order_id = ?", order_id).
Update("status", status).
Error
}

View File

@@ -37,17 +37,17 @@ func (s *OrderService) Find(user *model.Customer, p find.Paging, filt *filters.F
return s.ordersRepo.Find(user.ID, p, filt)
}
func (s *OrderService) PlaceNewOrder(user_id uint, cart_id uint, country_id uint, address_info string) error {
func (s *OrderService) PlaceNewOrder(user_id uint, cart_id uint, name string, country_id uint, address_info string) error {
_, err := s.addressesService.ValidateAddressJson(address_info, country_id)
if err != nil {
return err
}
amt, err := s.cartsRepo.UserHasCart(user_id, cart_id)
exists, err := s.cartsRepo.UserHasCart(user_id, cart_id)
if err != nil {
return err
}
if amt <= 0 {
if !exists {
return responseErrors.ErrUserHasNoSuchCart
}
@@ -59,14 +59,45 @@ func (s *OrderService) PlaceNewOrder(user_id uint, cart_id uint, country_id uint
return responseErrors.ErrEmptyCart
}
if name == "" && cart.Name != nil {
name = *cart.Name
}
// all checks passed
return s.ordersRepo.PlaceNewOrder(user_id, cart_id, country_id, address_info)
return s.ordersRepo.PlaceNewOrder(cart, name, country_id, address_info)
}
func (s *OrderService) ChangeOrderAddress(user *model.Customer, order_id uint, country_id uint, address_info string) error {
return nil
_, err := s.addressesService.ValidateAddressJson(address_info, country_id)
if err != nil {
return err
}
if !user.HasPermission(perms.ModifyAllOrders) {
exists, err := s.ordersRepo.UserHasOrder(user.ID, order_id)
if err != nil {
return err
}
if !exists {
return responseErrors.ErrUserHasNoSuchOrder
}
}
return s.ordersRepo.ChangeOrderAddress(order_id, country_id, address_info)
}
func (s *OrderService) ChangeOrderStatus(user *model.Customer, order_id uint, status string) error {
return nil
if !user.HasPermission(perms.ModifyAllOrders) {
exists, err := s.ordersRepo.UserHasOrder(user.ID, order_id)
if err != nil {
return err
}
if !exists {
return responseErrors.ErrUserHasNoSuchOrder
}
}
return s.ordersRepo.ChangeOrderStatus(order_id, status)
}

View File

@@ -16,6 +16,9 @@ const MAX_AMOUNT_OF_ADDRESSES_PER_USER = 10
const USER_LOCALE = "user"
// ORDERS
const NEW_ORDER_STATUS = "PENDING"
// WEBDAV
const NBYTES_IN_WEBDAV_TOKEN = 32
const WEBDAV_HREF_ROOT = "http://localhost:3000/api/v1/webdav/storage"

View File

@@ -65,7 +65,10 @@ var (
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")
ErrEmptyCart = errors.New("the cart is empty")
// Typed errors for orders handler
ErrEmptyCart = errors.New("the cart is empty")
ErrUserHasNoSuchOrder = errors.New("user does not have order with given id")
// Typed errors for storage
ErrAccessDenied = errors.New("access denied!")
@@ -196,8 +199,11 @@ func GetErrorCode(c fiber.Ctx, err error) string {
return i18n.T_(c, "error.err_user_has_no_such_cart")
case errors.Is(err, ErrProductOrItsVariationDoesNotExist):
return i18n.T_(c, "error.err_product_or_its_variation_does_not_exist")
case errors.Is(err, ErrEmptyCart):
return i18n.T_(c, "error.err_cart_is_empty")
case errors.Is(err, ErrUserHasNoSuchOrder):
return i18n.T_(c, "error.err_user_has_no_such_order")
case errors.Is(err, ErrAccessDenied):
return i18n.T_(c, "error.err_access_denied")
@@ -272,6 +278,7 @@ func GetErrorStatus(err error) int {
errors.Is(err, ErrUserHasNoSuchCart),
errors.Is(err, ErrProductOrItsVariationDoesNotExist),
errors.Is(err, ErrEmptyCart),
errors.Is(err, ErrUserHasNoSuchOrder),
errors.Is(err, ErrAccessDenied),
errors.Is(err, ErrFolderDoesNotExist),
errors.Is(err, ErrFileDoesNotExist),

View File

@@ -130,7 +130,7 @@ FOREIGN KEY (role_id) REFERENCES b2b_roles(id);
-- customer_carts
CREATE TABLE IF NOT EXISTS b2b_customer_carts (
cart_id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
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
@@ -140,8 +140,8 @@ CREATE INDEX IF NOT EXISTS idx_customer_carts_user_id ON b2b_customer_carts (use
-- carts_products
CREATE TABLE IF NOT EXISTS b2b_carts_products (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
cart_id INT UNSIGNED NOT NULL,
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
cart_id BIGINT UNSIGNED NOT NULL,
product_id INT UNSIGNED NOT NULL,
product_attribute_id INT NULL,
amount INT UNSIGNED NOT NULL,
@@ -153,11 +153,12 @@ CREATE INDEX IF NOT EXISTS idx_carts_products_cart_id ON b2b_carts_products (car
-- customer_orders
CREATE TABLE IF NOT EXISTS b2b_customer_orders (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
order_id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
user_id BIGINT UNSIGNED NOT NULL,
name TEXT NOT NULL,
country_id BIGINT UNSIGNED NOT NULL,
address_json TEXT NULL,
status VARCHAR(50) NULL,
address_json TEXT NOT NULL,
status VARCHAR(50) NOT NULL,
CONSTRAINT fk_customer_orders_customers FOREIGN KEY (user_id) REFERENCES b2b_customers(id) ON DELETE NO ACTION ON UPDATE CASCADE,
CONSTRAINT fk_customer_orders_countries FOREIGN KEY (country_id) REFERENCES b2b_countries(id) ON DELETE NO ACTION ON UPDATE CASCADE
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;
@@ -167,7 +168,7 @@ CREATE INDEX idx_customer_orders_country_id ON b2b_customer_orders (country_id);
-- orders_products
CREATE TABLE IF NOT EXISTS b2b_orders_products (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
order_id BIGINT UNSIGNED NOT NULL,
product_id INT UNSIGNED NOT NULL,
product_attribute_id INT NULL,