package processor import ( "context" "fmt" "golang.org/x/crypto/bcrypt" "apskel-pos-be/internal/entities" "apskel-pos-be/internal/mappers" "apskel-pos-be/internal/models" "github.com/google/uuid" ) type OrganizationProcessor interface { CreateOrganization(ctx context.Context, req *models.CreateOrganizationRequest) (*models.CreateOrganizationResponse, error) UpdateOrganization(ctx context.Context, id uuid.UUID, req *models.UpdateOrganizationRequest) (*models.OrganizationResponse, error) DeleteOrganization(ctx context.Context, id uuid.UUID) error GetOrganizationByID(ctx context.Context, id uuid.UUID) (*models.OrganizationResponse, error) ListOrganizations(ctx context.Context, filters map[string]interface{}, page, limit int) ([]models.OrganizationResponse, int, error) } type OrganizationProcessorImpl struct { organizationRepo OrganizationRepository outletRepo OutletRepository userRepo UserRepository } func NewOrganizationProcessorImpl( organizationRepo OrganizationRepository, outletRepo OutletRepository, userRepo UserRepository, ) *OrganizationProcessorImpl { return &OrganizationProcessorImpl{ organizationRepo: organizationRepo, outletRepo: outletRepo, userRepo: userRepo, } } func (p *OrganizationProcessorImpl) CreateOrganization(ctx context.Context, req *models.CreateOrganizationRequest) (*models.CreateOrganizationResponse, error) { if req.OrganizationEmail != nil && *req.OrganizationEmail != "" { existingOrg, err := p.organizationRepo.GetByEmail(ctx, *req.OrganizationEmail) if err == nil && existingOrg != nil { return nil, fmt.Errorf("organization with email %s already exists", *req.OrganizationEmail) } } existingUser, err := p.userRepo.GetByEmail(ctx, req.AdminEmail) if err == nil && existingUser != nil { return nil, fmt.Errorf("user with email %s already exists", req.AdminEmail) } organizationEntity := &entities.Organization{ Name: req.OrganizationName, Email: req.OrganizationEmail, PhoneNumber: req.OrganizationPhoneNumber, PlanType: string(req.PlanType), } err = p.organizationRepo.Create(ctx, organizationEntity) if err != nil { return nil, fmt.Errorf("failed to create organization: %w", err) } passwordHash, err := bcrypt.GenerateFromPassword([]byte(req.AdminPassword), bcrypt.DefaultCost) if err != nil { return nil, fmt.Errorf("failed to hash password: %w", err) } adminUserEntity := &entities.User{ OrganizationID: organizationEntity.ID, Name: req.AdminName, Email: req.AdminEmail, PasswordHash: string(passwordHash), Role: entities.RoleAdmin, IsActive: true, } err = p.userRepo.Create(ctx, adminUserEntity) if err != nil { return nil, fmt.Errorf("failed to create admin user: %w", err) } defaultOutletEntity := &entities.Outlet{ OrganizationID: organizationEntity.ID, Name: req.OutletName, Address: req.OutletAddress, Timezone: req.OutletTimezone, Currency: req.OutletCurrency, TaxRate: 0.0, IsActive: true, } err = p.outletRepo.Create(ctx, defaultOutletEntity) if err != nil { return nil, fmt.Errorf("failed to create default outlet: %w", err) } organizationResponse := mappers.OrganizationEntityToResponse(organizationEntity) adminUserResponse := mappers.UserEntityToResponse(adminUserEntity) outletResponse := mappers.OutletEntityToResponse(defaultOutletEntity) return &models.CreateOrganizationResponse{ Organization: organizationResponse, AdminUser: adminUserResponse, DefaultOutlet: outletResponse, }, nil } func (p *OrganizationProcessorImpl) UpdateOrganization(ctx context.Context, id uuid.UUID, req *models.UpdateOrganizationRequest) (*models.OrganizationResponse, error) { existingOrg, err := p.organizationRepo.GetByID(ctx, id) if err != nil { return nil, fmt.Errorf("organization not found: %w", err) } if req.Email != nil && (existingOrg.Email == nil || *req.Email != *existingOrg.Email) { existingOrgByEmail, err := p.organizationRepo.GetByEmail(ctx, *req.Email) if err == nil && existingOrgByEmail != nil && existingOrgByEmail.ID != id { return nil, fmt.Errorf("organization with email %s already exists", *req.Email) } } if req.Name != nil { existingOrg.Name = *req.Name } if req.Email != nil { existingOrg.Email = req.Email } if req.PhoneNumber != nil { existingOrg.PhoneNumber = req.PhoneNumber } if req.PlanType != nil { existingOrg.PlanType = string(*req.PlanType) } err = p.organizationRepo.Update(ctx, existingOrg) if err != nil { return nil, fmt.Errorf("failed to update organization: %w", err) } return mappers.OrganizationEntityToResponse(existingOrg), nil } func (p *OrganizationProcessorImpl) DeleteOrganization(ctx context.Context, id uuid.UUID) error { _, err := p.organizationRepo.GetByID(ctx, id) if err != nil { return fmt.Errorf("organization not found: %w", err) } err = p.organizationRepo.Delete(ctx, id) if err != nil { return fmt.Errorf("failed to delete organization: %w", err) } return nil } func (p *OrganizationProcessorImpl) GetOrganizationByID(ctx context.Context, id uuid.UUID) (*models.OrganizationResponse, error) { organization, err := p.organizationRepo.GetByID(ctx, id) if err != nil { return nil, fmt.Errorf("organization not found: %w", err) } return mappers.OrganizationEntityToResponse(organization), nil } func (p *OrganizationProcessorImpl) ListOrganizations(ctx context.Context, filters map[string]interface{}, page, limit int) ([]models.OrganizationResponse, int, error) { offset := (page - 1) * limit organizations, totalCount, err := p.organizationRepo.List(ctx, filters, limit, offset) if err != nil { return nil, 0, fmt.Errorf("failed to get organizations: %w", err) } responses := make([]models.OrganizationResponse, len(organizations)) for i, org := range organizations { response := mappers.OrganizationEntityToResponse(org) if response != nil { responses[i] = *response } } return responses, int(totalCount), nil }