Compare commits

...

3 Commits

Author SHA1 Message Date
efrilm
54dc8662d6 merge 2025-10-07 00:07:38 +07:00
efrilm
27a2535dde update product analytic 2025-10-07 00:02:15 +07:00
efrilm
f55ea1ceb0 add order at categories 2025-10-06 22:46:01 +07:00
15 changed files with 35 additions and 22 deletions

View File

@ -107,6 +107,7 @@ type ProductAnalyticsData struct {
ProductName string `json:"product_name"`
CategoryID uuid.UUID `json:"category_id"`
CategoryName string `json:"category_name"`
CategoryOrder int `json:"category_order"`
QuantitySold int64 `json:"quantity_sold"`
Revenue float64 `json:"revenue"`
AveragePrice float64 `json:"average_price"`

View File

@ -10,6 +10,7 @@ type CreateCategoryRequest struct {
Name string `json:"name" validate:"required,min=1,max=255"`
Description *string `json:"description,omitempty"`
BusinessType *string `json:"business_type,omitempty"`
Order *int `json:"order,omitempty"`
Metadata map[string]interface{} `json:"metadata,omitempty"`
}
@ -17,6 +18,7 @@ type UpdateCategoryRequest struct {
Name *string `json:"name,omitempty" validate:"omitempty,min=1,max=255"`
Description *string `json:"description,omitempty"`
BusinessType *string `json:"business_type,omitempty"`
Order *int `json:"order,omitempty"`
Metadata map[string]interface{} `json:"metadata,omitempty"`
}
@ -35,6 +37,7 @@ type CategoryResponse struct {
Name string `json:"name"`
Description *string `json:"description"`
BusinessType string `json:"business_type"`
Order int `json:"order"`
Metadata map[string]interface{} `json:"metadata"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`

View File

@ -33,6 +33,7 @@ type ProductAnalytics struct {
ProductName string `json:"product_name"`
CategoryID uuid.UUID `json:"category_id"`
CategoryName string `json:"category_name"`
CategoryOrder int `json:"category_order"`
QuantitySold int64 `json:"quantity_sold"`
Revenue float64 `json:"revenue"`
AveragePrice float64 `json:"average_price"`

View File

@ -35,6 +35,7 @@ type Category struct {
OrganizationID uuid.UUID `gorm:"type:uuid;not null;index" json:"organization_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"`
BusinessType string `gorm:"size:50;default:'restaurant'" json:"business_type"`
Metadata Metadata `gorm:"type:jsonb;default:'{}'" json:"metadata"`
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`

View File

@ -1,6 +1,7 @@
package handler
import (
"fmt"
"strconv"
"apskel-pos-be/internal/appcontext"
@ -35,6 +36,7 @@ func (h *CategoryHandler) CreateCategory(c *gin.Context) {
contextInfo := appcontext.FromGinContext(ctx)
var req contract.CreateCategoryRequest
fmt.Printf("CategoryHandler::CreateCategory -> Request: %+v\n", req)
if err := c.ShouldBindJSON(&req); err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("CategoryHandler::CreateCategory -> request binding failed")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error())
@ -71,6 +73,7 @@ func (h *CategoryHandler) UpdateCategory(c *gin.Context) {
}
var req contract.UpdateCategoryRequest
fmt.Printf("CategoryHandler::UpdateCategory -> Request: %+v\n", req)
if err := c.ShouldBindJSON(&req); err != nil {
logger.FromContext(ctx).WithError(err).Error("CategoryHandler::UpdateCategory -> request binding failed")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, "Invalid request body")

View File

@ -16,7 +16,7 @@ func CategoryEntityToModel(entity *entities.Category) *models.Category {
Name: entity.Name,
Description: entity.Description,
ImageURL: nil, // Entity doesn't have ImageURL, model does
SortOrder: 0, // Entity doesn't have SortOrder, model does
Order: entity.Order, // Entity doesn't have SortOrder, model does
IsActive: true, // Entity doesn't have IsActive, default to true
CreatedAt: entity.CreatedAt,
UpdatedAt: entity.UpdatedAt,
@ -32,7 +32,7 @@ func CategoryModelToEntity(model *models.Category) *entities.Category {
if model.ImageURL != nil {
metadata["image_url"] = *model.ImageURL
}
metadata["sort_order"] = model.SortOrder
// metadata["sort_order"] = model.SortOrder
return &entities.Category{
ID: model.ID,
@ -40,6 +40,7 @@ func CategoryModelToEntity(model *models.Category) *entities.Category {
Name: model.Name,
Description: model.Description,
BusinessType: "restaurant", // Default business type
Order: model.Order,
Metadata: metadata,
CreatedAt: model.CreatedAt,
UpdatedAt: model.UpdatedAt,
@ -55,12 +56,13 @@ func CreateCategoryRequestToEntity(req *models.CreateCategoryRequest) *entities.
if req.ImageURL != nil {
metadata["image_url"] = *req.ImageURL
}
metadata["sort_order"] = req.SortOrder
// metadata["sort_order"] = req.SortOrder
return &entities.Category{
OrganizationID: req.OrganizationID,
Name: req.Name,
Description: req.Description,
Order: req.Order,
BusinessType: "restaurant", // Default business type
Metadata: metadata,
}
@ -73,7 +75,6 @@ func CategoryEntityToResponse(entity *entities.Category) *models.CategoryRespons
// Extract image URL and sort order from metadata
var imageURL *string
var sortOrder int
if entity.Metadata != nil {
if imgURL, exists := entity.Metadata["image_url"]; exists {
@ -81,13 +82,6 @@ func CategoryEntityToResponse(entity *entities.Category) *models.CategoryRespons
imageURL = &imgURLStr
}
}
if sort, exists := entity.Metadata["sort_order"]; exists {
if sortInt, ok := sort.(int); ok {
sortOrder = sortInt
} else if sortFloat, ok := sort.(float64); ok {
sortOrder = int(sortFloat)
}
}
}
return &models.CategoryResponse{
@ -96,7 +90,7 @@ func CategoryEntityToResponse(entity *entities.Category) *models.CategoryRespons
Name: entity.Name,
Description: entity.Description,
ImageURL: imageURL,
SortOrder: sortOrder,
Order: entity.Order,
IsActive: true, // Default to true since entity doesn't have this field
CreatedAt: entity.CreatedAt,
UpdatedAt: entity.UpdatedAt,
@ -124,8 +118,8 @@ func UpdateCategoryEntityFromRequest(entity *entities.Category, req *models.Upda
entity.Metadata["image_url"] = *req.ImageURL
}
if req.SortOrder != nil {
entity.Metadata["sort_order"] = *req.SortOrder
if req.Order != nil {
entity.Order = *req.Order
}
}

View File

@ -111,6 +111,7 @@ type ProductAnalyticsData struct {
ProductName string `json:"product_name"`
CategoryID uuid.UUID `json:"category_id"`
CategoryName string `json:"category_name"`
CategoryOrder int `json:"category_order"`
QuantitySold int64 `json:"quantity_sold"`
Revenue float64 `json:"revenue"`
AveragePrice float64 `json:"average_price"`

View File

@ -12,7 +12,7 @@ type Category struct {
Name string
Description *string
ImageURL *string
SortOrder int
Order int
IsActive bool
CreatedAt time.Time
UpdatedAt time.Time
@ -23,14 +23,14 @@ type CreateCategoryRequest struct {
Name string `validate:"required,min=1,max=255"`
Description *string `validate:"omitempty,max=1000"`
ImageURL *string `validate:"omitempty,url"`
SortOrder int `validate:"min=0"`
Order int `validate:"min=0"`
}
type UpdateCategoryRequest struct {
Name *string `validate:"omitempty,min=1,max=255"`
Description *string `validate:"omitempty,max=1000"`
ImageURL *string `validate:"omitempty,url"`
SortOrder *int `validate:"omitempty,min=0"`
Order *int `validate:"omitempty,min=0"`
IsActive *bool
}
@ -40,7 +40,7 @@ type CategoryResponse struct {
Name string
Description *string
ImageURL *string
SortOrder int
Order int
IsActive bool
CreatedAt time.Time
UpdatedAt time.Time

View File

@ -189,6 +189,7 @@ func (p *AnalyticsProcessorImpl) GetProductAnalytics(ctx context.Context, req *m
ProductName: data.ProductName,
CategoryID: data.CategoryID,
CategoryName: data.CategoryName,
CategoryOrder: data.CategoryOrder,
QuantitySold: data.QuantitySold,
Revenue: data.Revenue,
AveragePrice: data.AveragePrice,

View File

@ -117,6 +117,7 @@ func (r *AnalyticsRepositoryImpl) GetProductAnalytics(ctx context.Context, organ
p.name as product_name,
c.id as category_id,
c.name as category_name,
c.order as category_order,
COALESCE(SUM(CASE WHEN oi.is_fully_refunded = false THEN oi.quantity - COALESCE(oi.refund_quantity, 0) ELSE 0 END), 0) as quantity_sold,
COALESCE(SUM(CASE WHEN oi.is_fully_refunded = false THEN oi.total_price - COALESCE(oi.refund_amount, 0) ELSE 0 END), 0) as revenue,
CASE
@ -142,7 +143,7 @@ func (r *AnalyticsRepositoryImpl) GetProductAnalytics(ctx context.Context, organ
err := query.
Group("p.id, p.name, c.id, c.name").
Order("p.name ASC").
Order("revenue DESC").
Limit(limit).
Scan(&results).Error

View File

@ -81,7 +81,7 @@ func (r *CategoryRepositoryImpl) List(ctx context.Context, filters map[string]in
return nil, 0, err
}
err := query.Limit(limit).Offset(offset).Find(&categories).Error
err := query.Order("\"order\" ASC").Limit(limit).Offset(offset).Find(&categories).Error
return categories, total, err
}

View File

@ -159,6 +159,7 @@ func ProductAnalyticsModelToContract(resp *models.ProductAnalyticsResponse) *con
ProductName: item.ProductName,
CategoryID: item.CategoryID,
CategoryName: item.CategoryName,
CategoryOrder: item.CategoryOrder,
QuantitySold: item.QuantitySold,
Revenue: item.Revenue,
AveragePrice: item.AveragePrice,

View File

@ -12,7 +12,7 @@ func CreateCategoryRequestToModel(apctx *appcontext.ContextInfo, req *contract.C
Name: req.Name,
Description: req.Description,
ImageURL: nil,
SortOrder: 0,
Order: *req.Order,
}
}
@ -21,7 +21,7 @@ func UpdateCategoryRequestToModel(req *contract.UpdateCategoryRequest) *models.U
Name: req.Name,
Description: req.Description,
ImageURL: nil,
SortOrder: nil,
Order: req.Order,
IsActive: nil,
}
}
@ -37,6 +37,7 @@ func CategoryModelResponseToResponse(cat *models.CategoryResponse) *contract.Cat
Name: cat.Name,
Description: cat.Description,
BusinessType: "restaurant", // Default business type
Order: cat.Order,
Metadata: map[string]interface{}{},
CreatedAt: cat.CreatedAt,
UpdatedAt: cat.UpdatedAt,

View File

@ -0,0 +1,2 @@
ALTER TABLE categories
DROP COLUMN "order";

View File

@ -0,0 +1,3 @@
ALTER TABLE categories
ADD COLUMN "order" INT DEFAULT 0;