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 }