From 691e2ea614a2b1d5a382a791fc5bfdd24d63c19e Mon Sep 17 00:00:00 2001 From: ryan Date: Wed, 13 May 2026 15:00:34 +0700 Subject: [PATCH] add outlet association to category entity and related layers --- internal/entities/category.go | 2 ++ internal/handler/category_handler.go | 6 ++++++ internal/models/category.go | 6 +++++- internal/repository/category_repository.go | 6 +++--- internal/service/category_service.go | 3 +++ internal/transformer/category_transformer.go | 3 +++ internal/validator/category_validator.go | 8 +++++++- 7 files changed, 29 insertions(+), 5 deletions(-) diff --git a/internal/entities/category.go b/internal/entities/category.go index c5accc6..7b186a6 100644 --- a/internal/entities/category.go +++ b/internal/entities/category.go @@ -33,6 +33,7 @@ func (m *Metadata) Scan(value interface{}) error { type Category struct { ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"` OrganizationID uuid.UUID `gorm:"type:uuid;not null;index" json:"organization_id" validate:"required"` + OutletID uuid.UUID `gorm:"type:uuid;not null;index" json:"outlet_id" validate:"required"` Name string `gorm:"not null;size:255" json:"name" validate:"required,min=1,max=255"` Description *string `gorm:"type:text" json:"description"` Order int `gorm:"default:0" json:"order"` @@ -42,6 +43,7 @@ type Category struct { UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"` Organization Organization `gorm:"foreignKey:OrganizationID" json:"organization,omitempty"` + Outlet Outlet `gorm:"foreignKey:OutletID" json:"outlet,omitempty"` Products []Product `gorm:"foreignKey:CategoryID" json:"products,omitempty"` } diff --git a/internal/handler/category_handler.go b/internal/handler/category_handler.go index ddfa8a3..420c9c2 100644 --- a/internal/handler/category_handler.go +++ b/internal/handler/category_handler.go @@ -170,6 +170,12 @@ func (h *CategoryHandler) ListCategories(c *gin.Context) { req.BusinessType = businessType } + if outletIDStr := c.Query("outlet_id"); outletIDStr != "" { + if outletID, err := uuid.Parse(outletIDStr); err == nil { + req.OutletID = &outletID + } + } + if organizationIDStr := c.Query("organization_id"); organizationIDStr != "" { if organizationID, err := uuid.Parse(organizationIDStr); err == nil { req.OrganizationID = &organizationID diff --git a/internal/models/category.go b/internal/models/category.go index eff397a..00982c1 100644 --- a/internal/models/category.go +++ b/internal/models/category.go @@ -9,6 +9,7 @@ import ( type Category struct { ID uuid.UUID OrganizationID uuid.UUID + OutletID uuid.UUID Name string Description *string ImageURL *string @@ -20,6 +21,7 @@ type Category struct { type CreateCategoryRequest struct { OrganizationID uuid.UUID `validate:"required"` + OutletID uuid.UUID `validate:"required"` Name string `validate:"required,min=1,max=255"` Description *string `validate:"omitempty,max=1000"` ImageURL *string `validate:"omitempty,url"` @@ -27,7 +29,8 @@ type CreateCategoryRequest struct { } type UpdateCategoryRequest struct { - Name *string `validate:"omitempty,min=1,max=255"` + OutletID *uuid.UUID `validate:"omitempty,required"` + Name *string `validate:"omitempty,min=1,max=255"` Description *string `validate:"omitempty,max=1000"` ImageURL *string `validate:"omitempty,url"` Order *int `validate:"omitempty,min=0"` @@ -37,6 +40,7 @@ type UpdateCategoryRequest struct { type CategoryResponse struct { ID uuid.UUID OrganizationID uuid.UUID + OutletID uuid.UUID Name string Description *string ImageURL *string diff --git a/internal/repository/category_repository.go b/internal/repository/category_repository.go index a592a49..aefeb76 100644 --- a/internal/repository/category_repository.go +++ b/internal/repository/category_repository.go @@ -25,7 +25,7 @@ func (r *CategoryRepositoryImpl) Create(ctx context.Context, category *entities. func (r *CategoryRepositoryImpl) GetByID(ctx context.Context, id uuid.UUID) (*entities.Category, error) { var category entities.Category - err := r.db.WithContext(ctx).First(&category, "id = ?", id).Error + err := r.db.WithContext(ctx).Preload("Outlet").First(&category, "id = ?", id).Error if err != nil { return nil, err } @@ -34,7 +34,7 @@ func (r *CategoryRepositoryImpl) GetByID(ctx context.Context, id uuid.UUID) (*en func (r *CategoryRepositoryImpl) GetWithProducts(ctx context.Context, id uuid.UUID) (*entities.Category, error) { var category entities.Category - err := r.db.WithContext(ctx).Preload("Products").First(&category, "id = ?", id).Error + err := r.db.WithContext(ctx).Preload("Products").Preload("Outlet").First(&category, "id = ?", id).Error if err != nil { return nil, err } @@ -81,7 +81,7 @@ func (r *CategoryRepositoryImpl) List(ctx context.Context, filters map[string]in return nil, 0, err } - err := query.Order("\"order\" ASC").Limit(limit).Offset(offset).Find(&categories).Error + err := query.Preload("Outlet").Order("\"order\" ASC").Limit(limit).Offset(offset).Find(&categories).Error return categories, total, err } diff --git a/internal/service/category_service.go b/internal/service/category_service.go index cfec8aa..0abc755 100644 --- a/internal/service/category_service.go +++ b/internal/service/category_service.go @@ -88,6 +88,9 @@ func (s *CategoryServiceImpl) ListCategories(ctx context.Context, req *contract. if req.BusinessType != "" { filters["business_type"] = req.BusinessType } + if req.OutletID != nil { + filters["outlet_id"] = *req.OutletID + } if req.Search != "" { filters["search"] = req.Search } diff --git a/internal/transformer/category_transformer.go b/internal/transformer/category_transformer.go index 962eee6..465a04b 100644 --- a/internal/transformer/category_transformer.go +++ b/internal/transformer/category_transformer.go @@ -9,6 +9,7 @@ import ( func CreateCategoryRequestToModel(apctx *appcontext.ContextInfo, req *contract.CreateCategoryRequest) *models.CreateCategoryRequest { return &models.CreateCategoryRequest{ OrganizationID: apctx.OrganizationID, + OutletID: req.OutletID, Name: req.Name, Description: req.Description, ImageURL: nil, @@ -18,6 +19,7 @@ func CreateCategoryRequestToModel(apctx *appcontext.ContextInfo, req *contract.C func UpdateCategoryRequestToModel(req *contract.UpdateCategoryRequest) *models.UpdateCategoryRequest { return &models.UpdateCategoryRequest{ + OutletID: req.OutletID, Name: req.Name, Description: req.Description, ImageURL: nil, @@ -34,6 +36,7 @@ func CategoryModelResponseToResponse(cat *models.CategoryResponse) *contract.Cat return &contract.CategoryResponse{ ID: cat.ID, OrganizationID: cat.OrganizationID, + OutletID: cat.OutletID, Name: cat.Name, Description: cat.Description, BusinessType: "restaurant", // Default business type diff --git a/internal/validator/category_validator.go b/internal/validator/category_validator.go index cb28905..3b6ec64 100644 --- a/internal/validator/category_validator.go +++ b/internal/validator/category_validator.go @@ -6,6 +6,8 @@ import ( "apskel-pos-be/internal/constants" "apskel-pos-be/internal/contract" + + "github.com/google/uuid" ) type CategoryValidator interface { @@ -25,6 +27,10 @@ func (v *CategoryValidatorImpl) ValidateCreateCategoryRequest(req *contract.Crea return errors.New("request body is required"), constants.MissingFieldErrorCode } + if req.OutletID == uuid.Nil { + return errors.New("outlet_id is required"), constants.MissingFieldErrorCode + } + if strings.TrimSpace(req.Name) == "" { return errors.New("name is required"), constants.MissingFieldErrorCode } @@ -59,7 +65,7 @@ func (v *CategoryValidatorImpl) ValidateUpdateCategoryRequest(req *contract.Upda } // At least one field should be provided for update - if req.Name == nil && req.Description == nil && req.BusinessType == nil && req.Metadata == nil { + if req.Name == nil && req.Description == nil && req.BusinessType == nil && req.Metadata == nil && req.OutletID == nil { return errors.New("at least one field must be provided for update"), constants.MissingFieldErrorCode }