apskel-pos-backend/internal/processor/category_processor.go

153 lines
5.5 KiB
Go
Raw Normal View History

2025-07-18 20:10:29 +07:00
package processor
import (
"context"
"fmt"
"apskel-pos-be/internal/entities"
"apskel-pos-be/internal/mappers"
"apskel-pos-be/internal/models"
"github.com/google/uuid"
)
type CategoryProcessor interface {
CreateCategory(ctx context.Context, req *models.CreateCategoryRequest) (*models.CategoryResponse, error)
UpdateCategory(ctx context.Context, id uuid.UUID, req *models.UpdateCategoryRequest) (*models.CategoryResponse, error)
DeleteCategory(ctx context.Context, id uuid.UUID) error
GetCategoryByID(ctx context.Context, id uuid.UUID) (*models.CategoryResponse, error)
ListCategories(ctx context.Context, filters map[string]interface{}, page, limit int) ([]models.CategoryResponse, int, error)
}
type CategoryRepository interface {
Create(ctx context.Context, category *entities.Category) error
GetByID(ctx context.Context, id uuid.UUID) (*entities.Category, error)
GetWithProducts(ctx context.Context, id uuid.UUID) (*entities.Category, error)
GetByOrganization(ctx context.Context, organizationID uuid.UUID) ([]*entities.Category, error)
GetByBusinessType(ctx context.Context, businessType string) ([]*entities.Category, error)
Update(ctx context.Context, category *entities.Category) error
Delete(ctx context.Context, id uuid.UUID) error
List(ctx context.Context, filters map[string]interface{}, limit, offset int) ([]*entities.Category, int64, error)
Count(ctx context.Context, filters map[string]interface{}) (int64, error)
GetByName(ctx context.Context, organizationID uuid.UUID, name string) (*entities.Category, error)
ExistsByName(ctx context.Context, organizationID uuid.UUID, name string, excludeID *uuid.UUID) (bool, error)
}
type CategoryProcessorImpl struct {
categoryRepo CategoryRepository
}
func NewCategoryProcessorImpl(categoryRepo CategoryRepository) *CategoryProcessorImpl {
return &CategoryProcessorImpl{
categoryRepo: categoryRepo,
}
}
func (p *CategoryProcessorImpl) CreateCategory(ctx context.Context, req *models.CreateCategoryRequest) (*models.CategoryResponse, error) {
// Check if category with same name exists for this organization
exists, err := p.categoryRepo.ExistsByName(ctx, req.OrganizationID, req.Name, nil)
if err != nil {
return nil, fmt.Errorf("failed to check category name uniqueness: %w", err)
}
if exists {
return nil, fmt.Errorf("category with name '%s' already exists for this organization", req.Name)
}
// Map request to entity
categoryEntity := mappers.CreateCategoryRequestToEntity(req)
// Create category
if err := p.categoryRepo.Create(ctx, categoryEntity); err != nil {
return nil, fmt.Errorf("failed to create category: %w", err)
}
// Map entity to response model
response := mappers.CategoryEntityToResponse(categoryEntity)
return response, nil
}
func (p *CategoryProcessorImpl) UpdateCategory(ctx context.Context, id uuid.UUID, req *models.UpdateCategoryRequest) (*models.CategoryResponse, error) {
// Get existing category
existingCategory, err := p.categoryRepo.GetByID(ctx, id)
if err != nil {
return nil, fmt.Errorf("category not found: %w", err)
}
// Check name uniqueness if name is being updated
if req.Name != nil && *req.Name != existingCategory.Name {
exists, err := p.categoryRepo.ExistsByName(ctx, existingCategory.OrganizationID, *req.Name, &id)
if err != nil {
return nil, fmt.Errorf("failed to check category name uniqueness: %w", err)
}
if exists {
return nil, fmt.Errorf("category with name '%s' already exists for this organization", *req.Name)
}
}
// Apply updates to entity
mappers.UpdateCategoryEntityFromRequest(existingCategory, req)
// Update category
if err := p.categoryRepo.Update(ctx, existingCategory); err != nil {
return nil, fmt.Errorf("failed to update category: %w", err)
}
// Map entity to response model
response := mappers.CategoryEntityToResponse(existingCategory)
return response, nil
}
func (p *CategoryProcessorImpl) DeleteCategory(ctx context.Context, id uuid.UUID) error {
// Check if category exists
_, err := p.categoryRepo.GetByID(ctx, id)
if err != nil {
return fmt.Errorf("category not found: %w", err)
}
// Check if category has products
categoryWithProducts, err := p.categoryRepo.GetWithProducts(ctx, id)
if err != nil {
return fmt.Errorf("failed to check category products: %w", err)
}
if len(categoryWithProducts.Products) > 0 {
return fmt.Errorf("cannot delete category: it has %d products associated with it", len(categoryWithProducts.Products))
}
// Delete category
if err := p.categoryRepo.Delete(ctx, id); err != nil {
return fmt.Errorf("failed to delete category: %w", err)
}
return nil
}
func (p *CategoryProcessorImpl) GetCategoryByID(ctx context.Context, id uuid.UUID) (*models.CategoryResponse, error) {
categoryEntity, err := p.categoryRepo.GetByID(ctx, id)
if err != nil {
return nil, fmt.Errorf("category not found: %w", err)
}
response := mappers.CategoryEntityToResponse(categoryEntity)
return response, nil
}
func (p *CategoryProcessorImpl) ListCategories(ctx context.Context, filters map[string]interface{}, page, limit int) ([]models.CategoryResponse, int, error) {
offset := (page - 1) * limit
categoryEntities, total, err := p.categoryRepo.List(ctx, filters, limit, offset)
if err != nil {
return nil, 0, fmt.Errorf("failed to list categories: %w", err)
}
responses := make([]models.CategoryResponse, len(categoryEntities))
for i, entity := range categoryEntities {
response := mappers.CategoryEntityToResponse(entity)
if response != nil {
responses[i] = *response
}
}
return responses, int(total), nil
}