Files
tm_back/internal/company/service.go
T
2026-04-21 19:07:51 +03:30

506 lines
14 KiB
Go

package company
import (
"context"
"errors"
"tm/internal/company_category"
"tm/pkg/logger"
"tm/pkg/response"
)
// Service defines business logic for company operations
type Service interface {
// Core company operations
Create(ctx context.Context, form *CompanyForm) (*CompanyResponse, error)
Update(ctx context.Context, id string, form *CompanyForm) (*CompanyResponse, error)
GetByID(ctx context.Context, id string) (*CompanyResponse, error)
GetByIDs(ctx context.Context, ids []string) ([]*CompanyResponse, error)
Delete(ctx context.Context, id string) error
// Company listing and search
SearchCompanies(ctx context.Context, form *SearchForm, pagination *response.Pagination) (*CompanyListResponse, error)
// Company management
UpdateStatus(ctx context.Context, id string, form *StatusForm) error
UpdateVerification(ctx context.Context, id string, form *VerificationForm) error
// Get my company
GetMyCompany(ctx context.Context, id string) (*CompanyProfileResponse, error)
}
// companyService implements the Service interface
type companyService struct {
repository Repository
categoryService company_category.Service
logger logger.Logger
}
// NewService creates a new company service
func NewService(repository Repository, categoryService company_category.Service, logger logger.Logger) Service {
return &companyService{
repository: repository,
categoryService: categoryService,
logger: logger,
}
}
// Create creates a new company
func (s *companyService) Create(ctx context.Context, form *CompanyForm) (*CompanyResponse, error) {
// Validate categories if provided
if form.Tags != nil && len(form.Tags.Categories) > 0 {
validCategories, err := s.categoryService.GetByIDs(ctx, form.Tags.Categories)
if err != nil {
s.logger.Error("Failed to validate categories", map[string]interface{}{
"error": err.Error(),
"categories": form.Tags.Categories,
})
return nil, errors.New("failed to validate categories")
}
// Check if all provided categories exist
if len(validCategories) != len(form.Tags.Categories) {
return nil, errors.New("one or more categories are invalid")
}
}
// Check if company name already exists
existingCompany, _ := s.repository.GetByName(ctx, form.Name)
if existingCompany != nil {
return nil, errors.New("company with this name already exists")
}
// Check if registration number already exists
existingCompany, _ = s.repository.GetByRegistrationNumber(ctx, form.RegistrationNumber)
if existingCompany != nil {
return nil, errors.New("company with this registration number already exists")
}
// Check if tax ID already exists
existingCompany, _ = s.repository.GetByTaxID(ctx, form.TaxID)
if existingCompany != nil {
return nil, errors.New("company with this tax ID already exists")
}
// Create company
company := &Company{
Name: form.Name,
Type: CompanyType(form.Type),
Status: CompanyStatusActive,
RegistrationNumber: form.RegistrationNumber,
TaxID: form.TaxID,
Industry: form.Industry,
Description: form.Description,
Website: form.Website,
EmployeeCount: form.EmployeeCount,
AnnualRevenue: form.AnnualRevenue,
FoundedYear: form.FoundedYear,
Address: s.convertAddressForm(form.Address),
Phone: form.Phone,
Email: form.Email,
DocumentFileIDs: form.DocumentFileIDs,
Tags: s.convertCompanyTagsForm(form.Tags),
IsVerified: false,
IsCompliant: false,
Language: s.getDefaultLanguage(form.Language),
Currency: s.getDefaultCurrency(form.Currency),
Timezone: s.getDefaultTimezone(form.Timezone),
}
// Save to database
err := s.repository.Create(ctx, company)
if err != nil {
s.logger.Error("Failed to create company", map[string]interface{}{
"error": err.Error(),
"name": form.Name,
"registration_number": form.RegistrationNumber,
})
return nil, err
}
s.logger.Info("Company created successfully", map[string]interface{}{
"company_id": company.ID,
"name": company.Name,
"type": company.Type,
})
return company.ToResponse(nil), nil
}
// GetCompanyByID retrieves a company by ID
func (s *companyService) GetByID(ctx context.Context, id string) (*CompanyResponse, error) {
company, err := s.repository.GetByID(ctx, id)
if err != nil {
s.logger.Error("Failed to get company by ID", map[string]interface{}{
"error": err.Error(),
"company_id": id,
})
return nil, err
}
s.logger.Info("Company retrieved successfully", map[string]interface{}{
"company_id": company.ID,
"name": company.Name,
})
// Get category details for response
categoryDetails, err := s.categoryService.GetByIDs(ctx, company.Tags.Categories)
if err != nil {
s.logger.Warn("Failed to fetch category details for response", map[string]interface{}{
"error": err.Error(),
"company_id": company.ID,
})
}
var convertedCategories []*CategoryResponse
for _, cat := range categoryDetails {
convertedCategories = append(convertedCategories, &CategoryResponse{
ID: cat.ID,
Name: cat.Name,
})
}
return company.ToResponse(convertedCategories), nil
}
// UpdateCompany updates a company
func (s *companyService) Update(ctx context.Context, id string, form *CompanyForm) (*CompanyResponse, error) {
// Validate categories if provided
if form.Tags != nil && len(form.Tags.Categories) > 0 {
validCategories, err := s.categoryService.GetByIDs(ctx, form.Tags.Categories)
if err != nil {
s.logger.Error("Failed to validate categories", map[string]interface{}{
"error": err.Error(),
"categories": form.Tags.Categories,
})
return nil, errors.New("failed to validate categories")
}
// Check if all provided categories exist
if len(validCategories) != len(form.Tags.Categories) {
return nil, errors.New("one or more categories are invalid")
}
}
// Get existing company
company, err := s.repository.GetByID(ctx, id)
if err != nil {
s.logger.Error("Failed to get company for update", map[string]interface{}{
"error": err.Error(),
"company_id": id,
})
return nil, errors.New("company not found")
}
// Check if name already exists (if changing name)
if form.Name != "" && form.Name != company.Name {
existingCompany, _ := s.repository.GetByName(ctx, form.Name)
if existingCompany != nil {
return nil, errors.New("company with this name already exists")
}
company.Name = form.Name
}
// Check if registration number already exists (if changing)
if form.RegistrationNumber != "" && form.RegistrationNumber != company.RegistrationNumber {
existingCompany, _ := s.repository.GetByRegistrationNumber(ctx, form.RegistrationNumber)
if existingCompany != nil {
return nil, errors.New("company with this registration number already exists")
}
company.RegistrationNumber = form.RegistrationNumber
}
// Check if tax ID already exists (if changing)
if form.TaxID != "" && form.TaxID != company.TaxID {
existingCompany, _ := s.repository.GetByTaxID(ctx, form.TaxID)
if existingCompany != nil {
return nil, errors.New("company with this tax ID already exists")
}
company.TaxID = form.TaxID
}
// Update fields if provided
if form.Type != "" {
company.Type = CompanyType(form.Type)
}
if form.Industry != "" {
company.Industry = form.Industry
}
if form.Description != nil {
company.Description = form.Description
}
if form.Website != nil {
company.Website = form.Website
}
if form.EmployeeCount != nil {
company.EmployeeCount = form.EmployeeCount
}
if form.AnnualRevenue != nil {
company.AnnualRevenue = form.AnnualRevenue
}
if form.FoundedYear != nil {
company.FoundedYear = form.FoundedYear
}
if form.Address != nil {
company.Address = s.convertAddressForm(form.Address)
}
if form.Phone != nil {
company.Phone = form.Phone
}
if form.Email != nil {
company.Email = form.Email
}
if form.DocumentFileIDs != nil {
company.DocumentFileIDs = form.DocumentFileIDs
}
if form.Tags != nil {
company.Tags = s.convertCompanyTagsForm(form.Tags)
}
if form.Language != nil {
company.Language = *form.Language
}
if form.Currency != nil {
company.Currency = *form.Currency
}
if form.Timezone != nil {
company.Timezone = *form.Timezone
}
// Update in database
err = s.repository.Update(ctx, company)
if err != nil {
s.logger.Error("Failed to update company", map[string]interface{}{
"error": err.Error(),
"company_id": id,
})
return nil, errors.New("failed to update company")
}
s.logger.Info("Company updated successfully", map[string]interface{}{
"company_id": company.ID,
"name": company.Name,
})
return company.ToResponse(nil), nil
}
// DeleteCompany deletes a company (soft delete)
func (s *companyService) Delete(ctx context.Context, id string) error {
// Delete company (soft delete)
err := s.repository.Delete(ctx, id)
if err != nil {
s.logger.Error("Failed to delete company", map[string]interface{}{
"error": err.Error(),
"company_id": id,
})
return errors.New("failed to delete company")
}
s.logger.Info("Company deleted successfully", map[string]interface{}{
"company_id": id,
})
return nil
}
// SearchCompanies searches companies with advanced filters
func (s *companyService) SearchCompanies(ctx context.Context, form *SearchForm, pagination *response.Pagination) (*CompanyListResponse, error) {
// Get companies using search
companies, total, err := s.repository.Search(ctx, form, pagination)
if err != nil {
s.logger.Error("Failed to search companies", map[string]interface{}{
"error": err.Error(),
})
return nil, errors.New("failed to search companies")
}
// Convert to responses
var companyResponses []*CompanyResponse
for _, company := range companies {
// Get category details for response
categoryDetails, err := s.categoryService.GetByIDs(ctx, company.Tags.Categories)
if err != nil {
s.logger.Warn("Failed to fetch category details for response", map[string]interface{}{
"error": err.Error(),
"company_id": company.ID,
})
}
var convertedCategories []*CategoryResponse
for _, cat := range categoryDetails {
convertedCategories = append(convertedCategories, &CategoryResponse{
ID: cat.ID,
Name: cat.Name,
})
}
companyResponses = append(companyResponses, company.ToResponse(convertedCategories))
}
return &CompanyListResponse{
Companies: companyResponses,
Meta: pagination.Response(total),
}, nil
}
// GetByIDs retrieves companies by IDs
func (s *companyService) GetByIDs(ctx context.Context, ids []string) ([]*CompanyResponse, error) {
companies, err := s.repository.GetByIDs(ctx, ids)
if err != nil {
return nil, errors.New("failed to get companies by IDs")
}
var companyResponses []*CompanyResponse
for _, company := range companies {
// Get category details for response
categoryDetails, err := s.categoryService.GetByIDs(ctx, company.Tags.Categories)
if err != nil {
s.logger.Warn("Failed to fetch category details for response", map[string]interface{}{
"error": err.Error(),
"company_id": company.ID,
})
}
var convertedCategories []*CategoryResponse
for _, cat := range categoryDetails {
convertedCategories = append(convertedCategories, &CategoryResponse{
ID: cat.ID,
Name: cat.Name,
})
}
companyResponses = append(companyResponses, company.ToResponse(convertedCategories))
}
return companyResponses, nil
}
// UpdateCompanyStatus updates company status
func (s *companyService) UpdateStatus(ctx context.Context, id string, form *StatusForm) error {
// Get company to check if exists
_, err := s.repository.GetByID(ctx, id)
if err != nil {
return errors.New("company not found")
}
// Update status
err = s.repository.UpdateStatus(ctx, id, CompanyStatus(form.Status))
if err != nil {
s.logger.Error("Failed to update company status", map[string]interface{}{
"error": err.Error(),
"company_id": id,
"status": CompanyStatus(form.Status),
})
return errors.New("failed to update company status")
}
s.logger.Info("Company status updated successfully", map[string]interface{}{
"company_id": id,
"status": form.Status,
})
return nil
}
// UpdateCompanyVerification updates company verification status
func (s *companyService) UpdateVerification(ctx context.Context, id string, form *VerificationForm) error {
// Get company to check if exists
_, err := s.repository.GetByID(ctx, id)
if err != nil {
return errors.New("company not found")
}
// Update verification
err = s.repository.UpdateVerification(ctx, id, form.IsVerified, form.IsCompliant, form.ComplianceNotes)
if err != nil {
s.logger.Error("Failed to update company verification", map[string]interface{}{
"error": err.Error(),
"company_id": id,
})
return errors.New("failed to update company verification")
}
s.logger.Info("Company verification updated successfully", map[string]interface{}{
"company_id": id,
"is_verified": form.IsVerified,
"is_compliant": form.IsCompliant,
})
return nil
}
// GetMyCompany retrieves the company for the authenticated user
func (s *companyService) GetMyCompany(ctx context.Context, id string) (*CompanyProfileResponse, error) {
company, err := s.repository.GetByID(ctx, id)
if err != nil {
return nil, errors.New("company not found")
}
return company.ToProfileResponse(), nil
}
// Helper methods for converting forms to domain objects
func (s *companyService) convertAddressForm(form *AddressForm) *Address {
if form == nil {
return nil
}
return &Address{
Street: form.Street,
City: form.City,
State: form.State,
PostalCode: form.PostalCode,
Country: form.Country,
}
}
func (s *companyService) convertCompanyTagsForm(form *CompanyTagsForm) *CompanyTags {
if form == nil {
return nil
}
return &CompanyTags{
CPVCodes: form.CPVCodes,
Categories: form.Categories,
Keywords: form.Keywords,
Specializations: form.Specializations,
Certifications: form.Certifications,
}
}
func (s *companyService) getDefaultLanguage(language *string) string {
if language != nil {
return *language
}
return "en"
}
func (s *companyService) getDefaultCurrency(currency *string) string {
if currency != nil {
return *currency
}
return "USD"
}
func (s *companyService) getDefaultTimezone(timezone *string) string {
if timezone != nil {
return *timezone
}
return "UTC"
}