feat(category): update list filter category
This commit is contained in:
@@ -30,6 +30,7 @@ type ListCategoriesRequest struct {
|
|||||||
OrganizationID *uuid.UUID `json:"organization_id,omitempty"`
|
OrganizationID *uuid.UUID `json:"organization_id,omitempty"`
|
||||||
OutletID *uuid.UUID `json:"outlet_id,omitempty"`
|
OutletID *uuid.UUID `json:"outlet_id,omitempty"`
|
||||||
ParentID *uuid.UUID `json:"parent_id,omitempty"`
|
ParentID *uuid.UUID `json:"parent_id,omitempty"`
|
||||||
|
Type string `json:"type,omitempty" validate:"omitempty,oneof=parent child"`
|
||||||
BusinessType string `json:"business_type,omitempty"`
|
BusinessType string `json:"business_type,omitempty"`
|
||||||
Search string `json:"search,omitempty"`
|
Search string `json:"search,omitempty"`
|
||||||
Page int `json:"page" validate:"required,min=1"`
|
Page int `json:"page" validate:"required,min=1"`
|
||||||
|
|||||||
@@ -196,6 +196,13 @@ func (h *CategoryHandler) ListCategories(c *gin.Context) {
|
|||||||
req.ParentID = &parentID
|
req.ParentID = &parentID
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// type=parent -> top level categories only
|
||||||
|
// type=child -> leaf categories (sub categories + top level ones without children)
|
||||||
|
if categoryType := c.Query("type"); categoryType != "" {
|
||||||
|
req.Type = categoryType
|
||||||
|
}
|
||||||
|
|
||||||
validationError, validationErrorCode := h.categoryValidator.ValidateListCategoriesRequest(req)
|
validationError, validationErrorCode := h.categoryValidator.ValidateListCategoriesRequest(req)
|
||||||
if validationError != nil {
|
if validationError != nil {
|
||||||
logger.FromContext(ctx).WithError(validationError).Error("CategoryHandler::ListCategories -> request validation failed")
|
logger.FromContext(ctx).WithError(validationError).Error("CategoryHandler::ListCategories -> request validation failed")
|
||||||
|
|||||||
@@ -63,6 +63,21 @@ func (r *CategoryRepositoryImpl) Delete(ctx context.Context, id uuid.UUID) error
|
|||||||
return r.db.WithContext(ctx).Delete(&entities.Category{}, "id = ?", id).Error
|
return r.db.WithContext(ctx).Delete(&entities.Category{}, "id = ?", id).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// applyCategoryTypeFilter narrows the query by position in the category tree.
|
||||||
|
// - "parent": top level categories only (no parent of their own)
|
||||||
|
// - "child": leaf categories — sub categories plus top level categories that
|
||||||
|
// have no sub categories, i.e. everything a product can be assigned to
|
||||||
|
func applyCategoryTypeFilter(query *gorm.DB, value interface{}) *gorm.DB {
|
||||||
|
switch value {
|
||||||
|
case "parent":
|
||||||
|
return query.Where("parent_id IS NULL")
|
||||||
|
case "child":
|
||||||
|
return query.Where("NOT EXISTS (SELECT 1 FROM categories AS sub WHERE sub.parent_id = categories.id)")
|
||||||
|
default:
|
||||||
|
return query
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (r *CategoryRepositoryImpl) List(ctx context.Context, filters map[string]interface{}, limit, offset int) ([]*entities.Category, int64, error) {
|
func (r *CategoryRepositoryImpl) List(ctx context.Context, filters map[string]interface{}, limit, offset int) ([]*entities.Category, int64, error) {
|
||||||
var categories []*entities.Category
|
var categories []*entities.Category
|
||||||
var total int64
|
var total int64
|
||||||
@@ -77,6 +92,8 @@ func (r *CategoryRepositoryImpl) List(ctx context.Context, filters map[string]in
|
|||||||
case "outlet_id":
|
case "outlet_id":
|
||||||
// Include outlet-specific categories AND global categories (outlet_id IS NULL)
|
// Include outlet-specific categories AND global categories (outlet_id IS NULL)
|
||||||
query = query.Where("outlet_id = ? OR outlet_id IS NULL", value)
|
query = query.Where("outlet_id = ? OR outlet_id IS NULL", value)
|
||||||
|
case "type":
|
||||||
|
query = applyCategoryTypeFilter(query, value)
|
||||||
default:
|
default:
|
||||||
query = query.Where(key+" = ?", value)
|
query = query.Where(key+" = ?", value)
|
||||||
}
|
}
|
||||||
@@ -101,6 +118,8 @@ func (r *CategoryRepositoryImpl) Count(ctx context.Context, filters map[string]i
|
|||||||
query = query.Where("name ILIKE ? OR description ILIKE ?", searchValue, searchValue)
|
query = query.Where("name ILIKE ? OR description ILIKE ?", searchValue, searchValue)
|
||||||
case "outlet_id":
|
case "outlet_id":
|
||||||
query = query.Where("outlet_id = ? OR outlet_id IS NULL", value)
|
query = query.Where("outlet_id = ? OR outlet_id IS NULL", value)
|
||||||
|
case "type":
|
||||||
|
query = applyCategoryTypeFilter(query, value)
|
||||||
default:
|
default:
|
||||||
query = query.Where(key+" = ?", value)
|
query = query.Where(key+" = ?", value)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -94,6 +94,9 @@ func (s *CategoryServiceImpl) ListCategories(ctx context.Context, req *contract.
|
|||||||
if req.ParentID != nil {
|
if req.ParentID != nil {
|
||||||
filters["parent_id"] = *req.ParentID
|
filters["parent_id"] = *req.ParentID
|
||||||
}
|
}
|
||||||
|
if req.Type != "" {
|
||||||
|
filters["type"] = req.Type
|
||||||
|
}
|
||||||
if req.Search != "" {
|
if req.Search != "" {
|
||||||
filters["search"] = req.Search
|
filters["search"] = req.Search
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -118,5 +118,9 @@ func (v *CategoryValidatorImpl) ValidateListCategoriesRequest(req *contract.List
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if req.Type != "" && req.Type != "parent" && req.Type != "child" {
|
||||||
|
return errors.New("type must be either 'parent' or 'child'"), constants.MalformedFieldErrorCode
|
||||||
|
}
|
||||||
|
|
||||||
return nil, ""
|
return nil, ""
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user