add outlet association to category entity and related layers

This commit is contained in:
ryan 2026-05-13 15:00:34 +07:00
parent c5f94229a7
commit 691e2ea614
7 changed files with 29 additions and 5 deletions

View File

@ -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"`
}

View File

@ -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

View File

@ -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

View File

@ -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
}

View File

@ -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
}

View File

@ -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

View File

@ -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
}