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

187 lines
5.9 KiB
Go
Raw Normal View History

2025-07-18 20:10:29 +07:00
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
}