package processor import ( "apskel-pos-be/internal/entities" "apskel-pos-be/internal/mappers" "apskel-pos-be/internal/models" "context" "time" "github.com/google/uuid" ) type IngredientProcessorImpl struct { ingredientRepo IngredientRepository unitRepo UnitRepository } func NewIngredientProcessor(ingredientRepo IngredientRepository, unitRepo UnitRepository) *IngredientProcessorImpl { return &IngredientProcessorImpl{ ingredientRepo: ingredientRepo, unitRepo: unitRepo, } } func (p *IngredientProcessorImpl) CreateIngredient(ctx context.Context, req *models.CreateIngredientRequest) (*models.IngredientResponse, error) { _, err := p.unitRepo.GetByID(ctx, req.UnitID, req.OrganizationID) if err != nil { return nil, err } ingredient := &entities.Ingredient{ ID: uuid.New(), OrganizationID: req.OrganizationID, OutletID: req.OutletID, Name: req.Name, UnitID: req.UnitID, Cost: req.Cost, Stock: req.Stock, IsSemiFinished: req.IsSemiFinished, IsActive: req.IsActive, Metadata: req.Metadata, CreatedAt: time.Now(), UpdatedAt: time.Now(), } err = p.ingredientRepo.Create(ctx, ingredient) if err != nil { return nil, err } ingredientWithUnit, err := p.ingredientRepo.GetByID(ctx, ingredient.ID, req.OrganizationID) if err != nil { return nil, err } ingredientModel := mappers.MapIngredientEntityToModel(ingredientWithUnit) response := &models.IngredientResponse{ ID: ingredientModel.ID, OrganizationID: ingredientModel.OrganizationID, OutletID: ingredientModel.OutletID, Name: ingredientModel.Name, UnitID: ingredientModel.UnitID, Cost: ingredientModel.Cost, Stock: ingredientModel.Stock, IsSemiFinished: ingredientModel.IsSemiFinished, IsActive: ingredientModel.IsActive, Metadata: ingredientModel.Metadata, CreatedAt: ingredientModel.CreatedAt, UpdatedAt: ingredientModel.UpdatedAt, Unit: ingredientModel.Unit, } return response, nil } func (p *IngredientProcessorImpl) GetIngredientByID(ctx context.Context, id uuid.UUID) (*models.IngredientResponse, error) { // For now, we'll need to get organizationID from context or request // This is a limitation of the current interface design organizationID := uuid.Nil // This should come from context ingredient, err := p.ingredientRepo.GetByID(ctx, id, organizationID) if err != nil { return nil, err } ingredientModel := mappers.MapIngredientEntityToModel(ingredient) response := &models.IngredientResponse{ ID: ingredientModel.ID, OrganizationID: ingredientModel.OrganizationID, OutletID: ingredientModel.OutletID, Name: ingredientModel.Name, UnitID: ingredientModel.UnitID, Cost: ingredientModel.Cost, Stock: ingredientModel.Stock, IsSemiFinished: ingredientModel.IsSemiFinished, IsActive: ingredientModel.IsActive, Metadata: ingredientModel.Metadata, CreatedAt: ingredientModel.CreatedAt, UpdatedAt: ingredientModel.UpdatedAt, Unit: ingredientModel.Unit, } return response, nil } func (p *IngredientProcessorImpl) ListIngredients(ctx context.Context, organizationID uuid.UUID, outletID *uuid.UUID, page, limit int, search string) (*models.PaginatedResponse[models.IngredientResponse], error) { // Set default values if page < 1 { page = 1 } if limit < 1 { limit = 10 } if limit > 100 { limit = 100 } ingredients, total, err := p.ingredientRepo.GetAll(ctx, organizationID, outletID, page, limit, search, nil) if err != nil { return nil, err } // Map to response models ingredientModels := mappers.MapIngredientEntitiesToModels(ingredients) ingredientResponses := make([]models.IngredientResponse, len(ingredientModels)) for i, ingredientModel := range ingredientModels { ingredientResponses[i] = models.IngredientResponse{ ID: ingredientModel.ID, OrganizationID: ingredientModel.OrganizationID, OutletID: ingredientModel.OutletID, Name: ingredientModel.Name, UnitID: ingredientModel.UnitID, Cost: ingredientModel.Cost, Stock: ingredientModel.Stock, IsSemiFinished: ingredientModel.IsSemiFinished, IsActive: ingredientModel.IsActive, Metadata: ingredientModel.Metadata, CreatedAt: ingredientModel.CreatedAt, UpdatedAt: ingredientModel.UpdatedAt, Unit: ingredientModel.Unit, } } // Create paginated response paginatedResponse := &models.PaginatedResponse[models.IngredientResponse]{ Data: ingredientResponses, Pagination: models.Pagination{ Page: page, Limit: limit, Total: int64(total), TotalPages: (total + limit - 1) / limit, }, } return paginatedResponse, nil } func (p *IngredientProcessorImpl) UpdateIngredient(ctx context.Context, id uuid.UUID, req *models.UpdateIngredientRequest) (*models.IngredientResponse, error) { // For now, we'll need to get organizationID from context or request // This is a limitation of the current interface design organizationID := uuid.Nil // This should come from context // Get existing ingredient existingIngredient, err := p.ingredientRepo.GetByID(ctx, id, organizationID) if err != nil { return nil, err } // Validate unit exists if changed if req.UnitID != existingIngredient.UnitID { _, err := p.unitRepo.GetByID(ctx, req.UnitID, organizationID) if err != nil { return nil, err } } // Update fields existingIngredient.OutletID = req.OutletID existingIngredient.Name = req.Name existingIngredient.UnitID = req.UnitID existingIngredient.Cost = req.Cost existingIngredient.Stock = req.Stock existingIngredient.IsSemiFinished = req.IsSemiFinished existingIngredient.IsActive = req.IsActive existingIngredient.Metadata = req.Metadata existingIngredient.UpdatedAt = time.Now() // Save to database err = p.ingredientRepo.Update(ctx, existingIngredient) if err != nil { return nil, err } // Get with relations ingredientWithUnit, err := p.ingredientRepo.GetByID(ctx, id, organizationID) if err != nil { return nil, err } // Map to response ingredientModel := mappers.MapIngredientEntityToModel(ingredientWithUnit) response := &models.IngredientResponse{ ID: ingredientModel.ID, OrganizationID: ingredientModel.OrganizationID, OutletID: ingredientModel.OutletID, Name: ingredientModel.Name, UnitID: ingredientModel.UnitID, Cost: ingredientModel.Cost, Stock: ingredientModel.Stock, IsSemiFinished: ingredientModel.IsSemiFinished, IsActive: ingredientModel.IsActive, Metadata: ingredientModel.Metadata, CreatedAt: ingredientModel.CreatedAt, UpdatedAt: ingredientModel.UpdatedAt, Unit: ingredientModel.Unit, } return response, nil } func (p *IngredientProcessorImpl) DeleteIngredient(ctx context.Context, id uuid.UUID) error { // For now, we'll need to get organizationID from context or request // This is a limitation of the current interface design organizationID := uuid.Nil // This should come from context err := p.ingredientRepo.Delete(ctx, id, organizationID) if err != nil { return err } return nil }