change product list to retrieve its data from product outlets

This commit is contained in:
ryan 2026-05-13 23:15:09 +07:00
parent 3b62504798
commit 5f379faf17
6 changed files with 49 additions and 3 deletions

View File

@ -347,7 +347,7 @@ func (a *App) initProcessors(cfg *config.Config, repos *repositories) *processor
outletProcessor: processor.NewOutletProcessorImpl(repos.outletRepo), outletProcessor: processor.NewOutletProcessorImpl(repos.outletRepo),
outletSettingProcessor: processor.NewOutletSettingProcessorImpl(repos.outletSettingRepo, repos.outletRepo), outletSettingProcessor: processor.NewOutletSettingProcessorImpl(repos.outletSettingRepo, repos.outletRepo),
categoryProcessor: processor.NewCategoryProcessorImpl(repos.categoryRepo), categoryProcessor: processor.NewCategoryProcessorImpl(repos.categoryRepo),
productProcessor: processor.NewProductProcessorImpl(repos.productRepo, repos.categoryRepo, repos.productVariantRepo, repos.inventoryRepo, repos.outletRepo), productProcessor: processor.NewProductProcessorImpl(repos.productRepo, repos.categoryRepo, repos.productVariantRepo, repos.inventoryRepo, repos.outletRepo, repos.productOutletPriceRepo),
productVariantProcessor: processor.NewProductVariantProcessorImpl(repos.productVariantRepo, repos.productRepo), productVariantProcessor: processor.NewProductVariantProcessorImpl(repos.productVariantRepo, repos.productRepo),
inventoryProcessor: processor.NewInventoryProcessorImpl(repos.inventoryRepo, repos.productRepo, repos.outletRepo, repos.ingredientRepo, repos.inventoryMovementRepo), inventoryProcessor: processor.NewInventoryProcessorImpl(repos.inventoryRepo, repos.productRepo, repos.outletRepo, repos.ingredientRepo, repos.inventoryMovementRepo),
orderProcessor: processor.NewOrderProcessorImpl(repos.orderRepo, repos.orderItemRepo, repos.paymentRepo, repos.paymentOrderItemRepo, repos.productRepo, repos.paymentMethodRepo, repos.inventoryRepo, repos.inventoryMovementRepo, repos.productVariantRepo, repos.outletRepo, repos.customerRepo, repos.txManager, repos.productRecipeRepo, repos.ingredientRepo, inventoryMovementService, repos.productOutletPriceRepo), orderProcessor: processor.NewOrderProcessorImpl(repos.orderRepo, repos.orderItemRepo, repos.paymentRepo, repos.paymentOrderItemRepo, repos.productRepo, repos.paymentMethodRepo, repos.inventoryRepo, repos.inventoryMovementRepo, repos.productVariantRepo, repos.outletRepo, repos.customerRepo, repos.txManager, repos.productRecipeRepo, repos.ingredientRepo, inventoryMovementService, repos.productOutletPriceRepo),
@ -382,7 +382,7 @@ func (a *App) initProcessors(cfg *config.Config, repos *repositories) *processor
inventoryMovementService: inventoryMovementService, inventoryMovementService: inventoryMovementService,
userDeviceProcessor: processor.NewUserDeviceProcessorImpl(repos.userDeviceRepo), userDeviceProcessor: processor.NewUserDeviceProcessorImpl(repos.userDeviceRepo),
notificationProcessor: buildNotificationProcessor(cfg, repos), notificationProcessor: buildNotificationProcessor(cfg, repos),
productOutletPriceProcessor: processor.NewProductOutletPriceProcessorImpl(repos.productOutletPriceRepo), productOutletPriceProcessor: processor.NewProductOutletPriceProcessorImpl(repos.productOutletPriceRepo, repos.productRepo, repos.outletRepo),
} }
} }

View File

@ -89,6 +89,7 @@ type ProductVariantResponse struct {
type ListProductsRequest struct { type ListProductsRequest struct {
OrganizationID *uuid.UUID `json:"organization_id,omitempty"` OrganizationID *uuid.UUID `json:"organization_id,omitempty"`
OutletID *uuid.UUID `json:"outlet_id,omitempty"`
CategoryID *uuid.UUID `json:"category_id,omitempty"` CategoryID *uuid.UUID `json:"category_id,omitempty"`
BusinessType string `json:"business_type,omitempty"` BusinessType string `json:"business_type,omitempty"`
IsActive *bool `json:"is_active,omitempty"` IsActive *bool `json:"is_active,omitempty"`

View File

@ -184,6 +184,14 @@ func (h *ProductHandler) ListProducts(c *gin.Context) {
} }
} }
if outletIDStr := c.Query("outlet_id"); outletIDStr != "" {
if outletID, err := uuid.Parse(outletIDStr); err == nil {
req.OutletID = &outletID
}
} else if contextInfo.OutletID != uuid.Nil {
req.OutletID = &contextInfo.OutletID
}
if minPriceStr := c.Query("min_price"); minPriceStr != "" { if minPriceStr := c.Query("min_price"); minPriceStr != "" {
if minPrice, err := strconv.ParseFloat(minPriceStr, 64); err == nil { if minPrice, err := strconv.ParseFloat(minPriceStr, 64); err == nil {
req.MinPrice = &minPrice req.MinPrice = &minPrice

View File

@ -47,15 +47,17 @@ type ProductProcessorImpl struct {
productVariantRepo repository.ProductVariantRepository productVariantRepo repository.ProductVariantRepository
inventoryRepo repository.InventoryRepository inventoryRepo repository.InventoryRepository
outletRepo OutletRepository outletRepo OutletRepository
outletPriceRepo repository.ProductOutletPriceRepository
} }
func NewProductProcessorImpl(productRepo ProductRepository, categoryRepo CategoryRepository, productVariantRepo repository.ProductVariantRepository, inventoryRepo repository.InventoryRepository, outletRepo OutletRepository) *ProductProcessorImpl { func NewProductProcessorImpl(productRepo ProductRepository, categoryRepo CategoryRepository, productVariantRepo repository.ProductVariantRepository, inventoryRepo repository.InventoryRepository, outletRepo OutletRepository, outletPriceRepo repository.ProductOutletPriceRepository) *ProductProcessorImpl {
return &ProductProcessorImpl{ return &ProductProcessorImpl{
productRepo: productRepo, productRepo: productRepo,
categoryRepo: categoryRepo, categoryRepo: categoryRepo,
productVariantRepo: productVariantRepo, productVariantRepo: productVariantRepo,
inventoryRepo: inventoryRepo, inventoryRepo: inventoryRepo,
outletRepo: outletRepo, outletRepo: outletRepo,
outletPriceRepo: outletPriceRepo,
} }
} }
@ -227,11 +229,36 @@ func (p *ProductProcessorImpl) GetProductByID(ctx context.Context, id uuid.UUID)
func (p *ProductProcessorImpl) ListProducts(ctx context.Context, filters map[string]interface{}, page, limit int) ([]models.ProductResponse, int, error) { func (p *ProductProcessorImpl) ListProducts(ctx context.Context, filters map[string]interface{}, page, limit int) ([]models.ProductResponse, int, error) {
offset := (page - 1) * limit offset := (page - 1) * limit
var outletID uuid.UUID
if oid, ok := filters["outlet_id"]; ok {
outletID = oid.(uuid.UUID)
delete(filters, "outlet_id")
}
productEntities, total, err := p.productRepo.List(ctx, filters, limit, offset) productEntities, total, err := p.productRepo.List(ctx, filters, limit, offset)
if err != nil { if err != nil {
return nil, 0, fmt.Errorf("failed to list products: %w", err) return nil, 0, fmt.Errorf("failed to list products: %w", err)
} }
if outletID != uuid.Nil && len(productEntities) > 0 {
productIDs := make([]uuid.UUID, len(productEntities))
for i, pe := range productEntities {
productIDs[i] = pe.ID
}
outletPrices, err := p.outletPriceRepo.GetByProductsAndOutlet(ctx, productIDs, outletID)
if err == nil {
priceMap := make(map[uuid.UUID]float64, len(outletPrices))
for _, op := range outletPrices {
priceMap[op.ProductID] = op.Price
}
for _, pe := range productEntities {
if price, ok := priceMap[pe.ID]; ok {
pe.Price = price
}
}
}
}
responses := make([]models.ProductResponse, len(productEntities)) responses := make([]models.ProductResponse, len(productEntities))
for i, entity := range productEntities { for i, entity := range productEntities {
response := mappers.ProductEntityToResponse(entity) response := mappers.ProductEntityToResponse(entity)

View File

@ -14,6 +14,7 @@ type ProductOutletPriceRepository interface {
GetByProductAndOutlet(ctx context.Context, productID, outletID uuid.UUID) (*entities.ProductOutletPrice, error) GetByProductAndOutlet(ctx context.Context, productID, outletID uuid.UUID) (*entities.ProductOutletPrice, error)
GetByProduct(ctx context.Context, productID uuid.UUID) ([]*entities.ProductOutletPrice, error) GetByProduct(ctx context.Context, productID uuid.UUID) ([]*entities.ProductOutletPrice, error)
GetByOutlet(ctx context.Context, outletID uuid.UUID) ([]*entities.ProductOutletPrice, error) GetByOutlet(ctx context.Context, outletID uuid.UUID) ([]*entities.ProductOutletPrice, error)
GetByProductsAndOutlet(ctx context.Context, productIDs []uuid.UUID, outletID uuid.UUID) ([]*entities.ProductOutletPrice, error)
Upsert(ctx context.Context, price *entities.ProductOutletPrice) error Upsert(ctx context.Context, price *entities.ProductOutletPrice) error
Delete(ctx context.Context, id uuid.UUID) error Delete(ctx context.Context, id uuid.UUID) error
GetByID(ctx context.Context, id uuid.UUID) (*entities.ProductOutletPrice, error) GetByID(ctx context.Context, id uuid.UUID) (*entities.ProductOutletPrice, error)
@ -69,3 +70,9 @@ func (r *ProductOutletPriceRepositoryImpl) GetByID(ctx context.Context, id uuid.
} }
return &price, nil return &price, nil
} }
func (r *ProductOutletPriceRepositoryImpl) GetByProductsAndOutlet(ctx context.Context, productIDs []uuid.UUID, outletID uuid.UUID) ([]*entities.ProductOutletPrice, error) {
var prices []*entities.ProductOutletPrice
err := r.db.WithContext(ctx).Where("product_id IN ? AND outlet_id = ?", productIDs, outletID).Find(&prices).Error
return prices, err
}

View File

@ -85,6 +85,9 @@ func (s *ProductServiceImpl) ListProducts(ctx context.Context, req *contract.Lis
if req.OrganizationID != nil { if req.OrganizationID != nil {
filters["organization_id"] = *req.OrganizationID filters["organization_id"] = *req.OrganizationID
} }
if req.OutletID != nil {
filters["outlet_id"] = *req.OutletID
}
if req.CategoryID != nil { if req.CategoryID != nil {
filters["category_id"] = *req.CategoryID filters["category_id"] = *req.CategoryID
} }