diff --git a/internal/handler/product_handler.go b/internal/handler/product_handler.go index b4957d0..9b18f27 100644 --- a/internal/handler/product_handler.go +++ b/internal/handler/product_handler.go @@ -220,3 +220,86 @@ func (h *ProductHandler) ListProducts(c *gin.Context) { util.HandleResponse(c.Writer, c.Request, productsResponse, "ProductHandler::ListProducts") } + +func (h *ProductHandler) ListProductAll(c *gin.Context) { + ctx := c.Request.Context() + contextInfo := appcontext.FromGinContext(ctx) + + req := &contract.ListProductsRequest{ + Page: 1, + Limit: 10, + OrganizationID: &contextInfo.OrganizationID, + } + + if pageStr := c.Query("page"); pageStr != "" { + if page, err := strconv.Atoi(pageStr); err == nil { + req.Page = page + } + } + + if limitStr := c.Query("limit"); limitStr != "" { + if limit, err := strconv.Atoi(limitStr); err == nil { + req.Limit = limit + } + } + + if search := c.Query("search"); search != "" { + req.Search = search + } + + if businessType := c.Query("business_type"); businessType != "" { + req.BusinessType = businessType + } + + if organizationIDStr := c.Query("organization_id"); organizationIDStr != "" { + if organizationID, err := uuid.Parse(organizationIDStr); err == nil { + req.OrganizationID = &organizationID + } + } + + if categoryIDStr := c.Query("category_id"); categoryIDStr != "" { + if categoryID, err := uuid.Parse(categoryIDStr); err == nil { + req.CategoryID = &categoryID + } + } + + if isActiveStr := c.Query("is_active"); isActiveStr != "" { + if isActive, err := strconv.ParseBool(isActiveStr); err == nil { + req.IsActive = &isActive + } + } + + if outletIDStr := c.Query("outlet_id"); outletIDStr != "" { + if outletID, err := uuid.Parse(outletIDStr); err == nil { + req.OutletID = &outletID + } + } + + if minPriceStr := c.Query("min_price"); minPriceStr != "" { + if minPrice, err := strconv.ParseFloat(minPriceStr, 64); err == nil { + req.MinPrice = &minPrice + } + } + + if maxPriceStr := c.Query("max_price"); maxPriceStr != "" { + if maxPrice, err := strconv.ParseFloat(maxPriceStr, 64); err == nil { + req.MaxPrice = &maxPrice + } + } + + validationError, validationErrorCode := h.productValidator.ValidateListProductsRequest(req) + if validationError != nil { + logger.FromContext(ctx).WithError(validationError).Error("ProductHandler::ListProducts -> request validation failed") + validationResponseError := contract.NewResponseError(validationErrorCode, constants.RequestEntity, validationError.Error()) + util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "ProductHandler::ListProducts") + return + } + + productsResponse := h.productService.ListProducts(ctx, req) + if productsResponse.HasErrors() { + errorResp := productsResponse.GetErrors()[0] + logger.FromContext(ctx).WithError(errorResp).Error("ProductHandler::ListProducts -> Failed to list products from service") + } + + util.HandleResponse(c.Writer, c.Request, productsResponse, "ProductHandler::ListProducts") +} diff --git a/internal/processor/product_processor.go b/internal/processor/product_processor.go index 846e74b..2d7e568 100644 --- a/internal/processor/product_processor.go +++ b/internal/processor/product_processor.go @@ -18,6 +18,7 @@ type ProductProcessor interface { DeleteProduct(ctx context.Context, id uuid.UUID) error GetProductByID(ctx context.Context, id uuid.UUID) (*models.ProductResponse, error) ListProducts(ctx context.Context, filters map[string]interface{}, page, limit int) ([]models.ProductResponse, int, error) + ListProductsAll(ctx context.Context, filters map[string]interface{}, page, limit int) ([]models.ProductResponse, int, error) } type ProductRepository interface { @@ -270,6 +271,25 @@ func (p *ProductProcessorImpl) ListProducts(ctx context.Context, filters map[str return responses, int(total), nil } +func (p *ProductProcessorImpl) ListProductsAll(ctx context.Context, filters map[string]interface{}, page, limit int) ([]models.ProductResponse, int, error) { + offset := (page - 1) * limit + + productEntities, total, err := p.productRepo.List(ctx, filters, limit, offset) + if err != nil { + return nil, 0, fmt.Errorf("failed to list products: %w", err) + } + + responses := make([]models.ProductResponse, len(productEntities)) + for i, entity := range productEntities { + response := mappers.ProductEntityToResponse(entity) + if response != nil { + responses[i] = *response + } + } + + return responses, int(total), nil +} + // Helper methods for inventory management // createInventoryForAllOutlets creates inventory records for all outlets of an organization diff --git a/internal/router/router.go b/internal/router/router.go index a67d60b..bebbb89 100644 --- a/internal/router/router.go +++ b/internal/router/router.go @@ -225,6 +225,7 @@ func (r *Router) addAppRoutes(rg *gin.Engine) { { products.POST("", r.productHandler.CreateProduct) products.GET("", r.productHandler.ListProducts) + products.GET("/all", r.productHandler.ListProductAll) products.GET("/:id", r.productHandler.GetProduct) products.PUT("/:id", r.productHandler.UpdateProduct) products.DELETE("/:id", r.productHandler.DeleteProduct) diff --git a/internal/service/product_service.go b/internal/service/product_service.go index de05f5e..38cd1e6 100644 --- a/internal/service/product_service.go +++ b/internal/service/product_service.go @@ -18,6 +18,7 @@ type ProductService interface { DeleteProduct(ctx context.Context, id uuid.UUID) *contract.Response GetProductByID(ctx context.Context, id uuid.UUID) *contract.Response ListProducts(ctx context.Context, req *contract.ListProductsRequest) *contract.Response + ListProductsAll(ctx context.Context, req *contract.ListProductsRequest) *contract.Response } type ProductServiceImpl struct { @@ -132,3 +133,57 @@ func (s *ProductServiceImpl) ListProducts(ctx context.Context, req *contract.Lis return contract.BuildSuccessResponse(listResponse) } + +func (s *ProductServiceImpl) ListProductsAll(ctx context.Context, req *contract.ListProductsRequest) *contract.Response { + // Build filters + filters := make(map[string]interface{}) + if req.OrganizationID != nil { + filters["organization_id"] = *req.OrganizationID + } + if req.OutletID != nil { + filters["outlet_id"] = *req.OutletID + } + if req.CategoryID != nil { + filters["category_id"] = *req.CategoryID + } + if req.BusinessType != "" { + filters["business_type"] = req.BusinessType + } + if req.IsActive != nil { + filters["is_active"] = *req.IsActive + } + if req.Search != "" { + filters["search"] = req.Search + } + if req.MinPrice != nil { + filters["price_min"] = *req.MinPrice + } + if req.MaxPrice != nil { + filters["price_max"] = *req.MaxPrice + } + + products, totalCount, err := s.productProcessor.ListProducts(ctx, filters, req.Page, req.Limit) + if err != nil { + errorResp := contract.NewResponseError(constants.InternalServerErrorCode, constants.ProductServiceEntity, err.Error()) + return contract.BuildErrorResponse([]*contract.ResponseError{errorResp}) + } + + // Convert to contract responses + contractResponses := transformer.ProductsToResponses(products) + + // Calculate total pages + totalPages := totalCount / req.Limit + if totalCount%req.Limit > 0 { + totalPages++ + } + + listResponse := &contract.ListProductsResponse{ + Products: contractResponses, + TotalCount: totalCount, + Page: req.Page, + Limit: req.Limit, + TotalPages: totalPages, + } + + return contract.BuildSuccessResponse(listResponse) +}