Enhance Company Management with Category Integration and API Documentation Updates
- Updated the Company service to include category validation during creation and updates, ensuring that only valid categories are associated with companies. - Introduced a new CategoryResponse structure to encapsulate category details in API responses, improving the clarity of the data returned. - Enhanced the CompanyResponse structure to include category details, providing more comprehensive information in API responses. - Updated Swagger and YAML documentation to reflect the new category integration, including detailed descriptions and examples for the updated response structures. - Improved error handling for invalid category data during company creation and updates, enhancing the robustness of the API. - Refactored existing API handlers to accommodate the new category-related changes, ensuring a seamless user experience across the application.
This commit is contained in:
+54
-28
@@ -98,36 +98,52 @@ type CompanyListResponse struct {
|
||||
Meta *response.Meta `json:"-"`
|
||||
}
|
||||
|
||||
// CompanyResponse represents the company data sent in API responses
|
||||
type CompanyResponse struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Status string `json:"status"`
|
||||
RegistrationNumber string `json:"registration_number"`
|
||||
TaxID string `json:"tax_id"`
|
||||
Industry string `json:"industry"`
|
||||
Description *string `json:"description,omitempty"`
|
||||
Website *string `json:"website,omitempty"`
|
||||
EmployeeCount *int `json:"employee_count,omitempty"`
|
||||
AnnualRevenue *float64 `json:"annual_revenue,omitempty"`
|
||||
FoundedYear *int `json:"founded_year,omitempty"`
|
||||
Address *Address `json:"address,omitempty"`
|
||||
Phone *string `json:"phone,omitempty"`
|
||||
Email *string `json:"email,omitempty"`
|
||||
Tags *CompanyTags `json:"tags,omitempty"`
|
||||
IsVerified bool `json:"is_verified"`
|
||||
IsCompliant bool `json:"is_compliant"`
|
||||
ComplianceNotes *string `json:"compliance_notes,omitempty"`
|
||||
Language string `json:"language"`
|
||||
Currency string `json:"currency"`
|
||||
Timezone string `json:"timezone"`
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
UpdatedAt int64 `json:"updated_at"`
|
||||
// CategoryResponse represents category data for company responses
|
||||
type CategoryResponse struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// CompanyResponse represents the company data sent in API responses
|
||||
type (
|
||||
CompanyResponse struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Status string `json:"status"`
|
||||
RegistrationNumber string `json:"registration_number"`
|
||||
TaxID string `json:"tax_id"`
|
||||
Industry string `json:"industry"`
|
||||
Description *string `json:"description,omitempty"`
|
||||
Website *string `json:"website,omitempty"`
|
||||
EmployeeCount *int `json:"employee_count,omitempty"`
|
||||
AnnualRevenue *float64 `json:"annual_revenue,omitempty"`
|
||||
FoundedYear *int `json:"founded_year,omitempty"`
|
||||
Address *Address `json:"address,omitempty"`
|
||||
Phone *string `json:"phone,omitempty"`
|
||||
Email *string `json:"email,omitempty"`
|
||||
Tags *CompanyTagsResponse `json:"tags,omitempty"`
|
||||
IsVerified bool `json:"is_verified"`
|
||||
IsCompliant bool `json:"is_compliant"`
|
||||
ComplianceNotes *string `json:"compliance_notes,omitempty"`
|
||||
Language string `json:"language"`
|
||||
Currency string `json:"currency"`
|
||||
Timezone string `json:"timezone"`
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
UpdatedAt int64 `json:"updated_at"`
|
||||
}
|
||||
|
||||
CompanyTagsResponse struct {
|
||||
CPVCodes []string `json:"cpv_codes"`
|
||||
Categories []*CategoryResponse `json:"categories"`
|
||||
Keywords []string `json:"keywords"`
|
||||
Specializations []string `json:"specializations"`
|
||||
Certifications []string `json:"certifications"`
|
||||
}
|
||||
)
|
||||
|
||||
// ToResponse converts Company to CompanyResponse
|
||||
func (c *Company) ToResponse() *CompanyResponse {
|
||||
func (c *Company) ToResponse(categories []*CategoryResponse) *CompanyResponse {
|
||||
return &CompanyResponse{
|
||||
ID: c.ID.Hex(),
|
||||
Name: c.Name,
|
||||
@@ -144,7 +160,7 @@ func (c *Company) ToResponse() *CompanyResponse {
|
||||
Address: c.Address,
|
||||
Phone: c.Phone,
|
||||
Email: c.Email,
|
||||
Tags: c.Tags,
|
||||
Tags: c.Tags.ToResponse(categories),
|
||||
IsVerified: c.IsVerified,
|
||||
IsCompliant: c.IsCompliant,
|
||||
ComplianceNotes: c.ComplianceNotes,
|
||||
@@ -156,6 +172,16 @@ func (c *Company) ToResponse() *CompanyResponse {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CompanyTags) ToResponse(categories []*CategoryResponse) *CompanyTagsResponse {
|
||||
return &CompanyTagsResponse{
|
||||
CPVCodes: c.CPVCodes,
|
||||
Categories: categories,
|
||||
Keywords: c.Keywords,
|
||||
Specializations: c.Specializations,
|
||||
Certifications: c.Certifications,
|
||||
}
|
||||
}
|
||||
|
||||
// CompanyProfileResponse represents the company profile data sent in API responses
|
||||
type CompanyProfileResponse struct {
|
||||
ID string `json:"id"`
|
||||
|
||||
@@ -80,6 +80,10 @@ func (h *Handler) Create(c echo.Context) error {
|
||||
err.Error() == "company with this tax ID already exists" {
|
||||
return response.Conflict(c, err.Error())
|
||||
}
|
||||
if err.Error() == "one or more categories are invalid" ||
|
||||
err.Error() == "failed to validate categories" {
|
||||
return response.BadRequest(c, err.Error(), "Invalid category data")
|
||||
}
|
||||
return response.InternalServerError(c, "Failed to create company")
|
||||
}
|
||||
|
||||
@@ -146,6 +150,10 @@ func (h *Handler) Update(c echo.Context) error {
|
||||
err.Error() == "company with this tax ID already exists" {
|
||||
return response.Conflict(c, err.Error())
|
||||
}
|
||||
if err.Error() == "one or more categories are invalid" ||
|
||||
err.Error() == "failed to validate categories" {
|
||||
return response.BadRequest(c, err.Error(), "Invalid category data")
|
||||
}
|
||||
return response.InternalServerError(c, "Failed to update company")
|
||||
}
|
||||
|
||||
|
||||
+98
-10
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"tm/internal/company_category"
|
||||
"tm/pkg/logger"
|
||||
"tm/pkg/response"
|
||||
)
|
||||
@@ -30,20 +31,39 @@ type Service interface {
|
||||
|
||||
// companyService implements the Service interface
|
||||
type companyService struct {
|
||||
repository Repository
|
||||
logger logger.Logger
|
||||
repository Repository
|
||||
categoryService company_category.Service
|
||||
logger logger.Logger
|
||||
}
|
||||
|
||||
// NewCompanyService creates a new company service
|
||||
func NewCompanyService(repository Repository, logger logger.Logger) Service {
|
||||
func NewCompanyService(repository Repository, categoryService company_category.Service, logger logger.Logger) Service {
|
||||
return &companyService{
|
||||
repository: repository,
|
||||
logger: logger,
|
||||
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 {
|
||||
@@ -103,7 +123,7 @@ func (s *companyService) Create(ctx context.Context, form *CompanyForm) (*Compan
|
||||
"type": company.Type,
|
||||
})
|
||||
|
||||
return company.ToResponse(), nil
|
||||
return company.ToResponse(nil), nil
|
||||
}
|
||||
|
||||
// GetCompanyByID retrieves a company by ID
|
||||
@@ -122,11 +142,45 @@ func (s *companyService) GetByID(ctx context.Context, id string) (*CompanyRespon
|
||||
"name": company.Name,
|
||||
})
|
||||
|
||||
return company.ToResponse(), nil
|
||||
// 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 {
|
||||
@@ -236,7 +290,7 @@ func (s *companyService) Update(ctx context.Context, id string, form *CompanyFor
|
||||
"name": company.Name,
|
||||
})
|
||||
|
||||
return company.ToResponse(), nil
|
||||
return company.ToResponse(nil), nil
|
||||
}
|
||||
|
||||
// DeleteCompany deletes a company (soft delete)
|
||||
@@ -273,7 +327,24 @@ func (s *companyService) SearchCompanies(ctx context.Context, form *SearchForm,
|
||||
// Convert to responses
|
||||
var companyResponses []*CompanyResponse
|
||||
for _, company := range companies {
|
||||
companyResponses = append(companyResponses, company.ToResponse())
|
||||
// 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{
|
||||
@@ -291,7 +362,24 @@ func (s *companyService) GetByIDs(ctx context.Context, ids []string) ([]*Company
|
||||
|
||||
var companyResponses []*CompanyResponse
|
||||
for _, company := range companies {
|
||||
companyResponses = append(companyResponses, company.ToResponse())
|
||||
// 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
|
||||
|
||||
Reference in New Issue
Block a user