e3ee3d64ce
- Deleted the `/admin/v1/customers/{id}/with-companies` endpoint from the API documentation and Swagger files to streamline the API structure.
- Updated the router to remove the corresponding route registration for the deprecated endpoint.
- Enhanced the company repository and service to support batch retrieval of companies by IDs, improving efficiency in data handling.
- Adjusted customer service methods to utilize the new batch retrieval functionality for loading companies associated with customers.
- Improved logging practices to provide better traceability for company retrieval operations.
1315 lines
40 KiB
Go
1315 lines
40 KiB
Go
package customer
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"time"
|
|
|
|
"tm/internal/company"
|
|
"tm/pkg/logger"
|
|
|
|
"tm/pkg/authorization"
|
|
|
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
|
"golang.org/x/crypto/bcrypt"
|
|
)
|
|
|
|
// Service defines business logic for customer operations
|
|
type Service interface {
|
|
// Core customer operations
|
|
CreateCustomer(ctx context.Context, form *CreateCustomerForm, createdBy *string) (*Customer, error)
|
|
GetCustomerByID(ctx context.Context, id string) (*Customer, error)
|
|
GetCustomerByIDWithCompanies(ctx context.Context, id string) (*CustomerResponse, error)
|
|
UpdateCustomer(ctx context.Context, id string, form *UpdateCustomerForm, updatedBy *string) (*Customer, error)
|
|
DeleteCustomer(ctx context.Context, id string, deletedBy *string) error
|
|
|
|
// Customer listing and search
|
|
ListCustomers(ctx context.Context, form *ListCustomersForm) (*CustomerListResponse, error)
|
|
ListCustomersWithCompanies(ctx context.Context, form *ListCustomersForm) (*CustomerListResponse, error)
|
|
GetCustomersByCompanyID(ctx context.Context, companyID string, limit, offset int) ([]*Customer, int64, error)
|
|
GetCustomersByType(ctx context.Context, customerType CustomerType, limit, offset int) ([]*Customer, int64, error)
|
|
GetCustomersByStatus(ctx context.Context, status CustomerStatus, limit, offset int) ([]*Customer, int64, error)
|
|
|
|
// Customer management
|
|
UpdateCustomerStatus(ctx context.Context, id string, form *UpdateCustomerStatusForm, updatedBy *string) error
|
|
UpdateCustomerVerification(ctx context.Context, id string, form *UpdateCustomerVerificationForm, updatedBy *string) error
|
|
|
|
// Company assignments
|
|
AssignCompaniesToCustomer(ctx context.Context, customerID string, companyIDs []string, updatedBy *string) error
|
|
RemoveCompaniesFromCustomer(ctx context.Context, customerID string, companyIDs []string, updatedBy *string) error
|
|
|
|
// Mobile-specific operations (simplified)
|
|
CreateCustomerMobile(ctx context.Context, form *CreateCustomerMobileForm, createdBy *string) (*Customer, error)
|
|
UpdateCustomerMobile(ctx context.Context, id string, form *UpdateCustomerMobileForm, updatedBy *string) (*Customer, error)
|
|
|
|
// Business operations
|
|
VerifyCustomer(ctx context.Context, id string, verifiedBy *string) error
|
|
SuspendCustomer(ctx context.Context, id string, reason string, suspendedBy *string) error
|
|
ActivateCustomer(ctx context.Context, id string, activatedBy *string) error
|
|
|
|
// Authentication operations
|
|
Login(ctx context.Context, form *LoginForm) (*AuthResponse, error)
|
|
RefreshToken(ctx context.Context, refreshToken string) (*AuthResponse, error)
|
|
GetProfile(ctx context.Context, customerID string) (*Customer, error)
|
|
GetProfileWithCompanies(ctx context.Context, customerID string) (*CustomerResponse, error)
|
|
Logout(ctx context.Context, customerID string, accessToken string) error
|
|
}
|
|
|
|
// customerService implements the Service interface
|
|
type customerService struct {
|
|
repository Repository
|
|
logger logger.Logger
|
|
authService authorization.AuthorizationService
|
|
companyService company.Service
|
|
}
|
|
|
|
// NewCustomerService creates a new customer service
|
|
func NewCustomerService(repository Repository, logger logger.Logger, authService authorization.AuthorizationService, companyService company.Service) Service {
|
|
return &customerService{
|
|
repository: repository,
|
|
logger: logger,
|
|
authService: authService,
|
|
companyService: companyService,
|
|
}
|
|
}
|
|
|
|
// CreateCustomer creates a new customer
|
|
func (s *customerService) CreateCustomer(ctx context.Context, form *CreateCustomerForm, createdBy *string) (*Customer, error) {
|
|
// Check if email already exists
|
|
existingCustomer, _ := s.repository.GetByEmail(ctx, form.Email)
|
|
if existingCustomer != nil {
|
|
return nil, errors.New("customer with this email already exists")
|
|
}
|
|
|
|
// Check if registration number already exists (for company customers)
|
|
if form.Type == string(CustomerTypeCompany) && form.RegistrationNumber != nil {
|
|
existingCustomer, _ = s.repository.GetByRegistrationNumber(ctx, *form.RegistrationNumber)
|
|
if existingCustomer != nil {
|
|
return nil, errors.New("company with this registration number already exists")
|
|
}
|
|
}
|
|
|
|
// Check if tax ID already exists (for company customers)
|
|
if form.Type == string(CustomerTypeCompany) && form.TaxID != nil {
|
|
existingCustomer, _ = s.repository.GetByTaxID(ctx, *form.TaxID)
|
|
if existingCustomer != nil {
|
|
return nil, errors.New("company with this tax ID already exists")
|
|
}
|
|
}
|
|
|
|
// Prepare company assignments
|
|
var companyIDs []string
|
|
if len(form.CompanyIDs) > 0 {
|
|
// Verify that all company IDs exist
|
|
for _, companyID := range form.CompanyIDs {
|
|
_, err := s.companyService.GetCompanyByID(ctx, companyID)
|
|
if err != nil {
|
|
s.logger.Error("Invalid company ID provided", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"company_id": companyID,
|
|
})
|
|
return nil, errors.New("invalid company ID: " + companyID)
|
|
}
|
|
}
|
|
companyIDs = form.CompanyIDs
|
|
}
|
|
|
|
// Hash password
|
|
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(form.Password), bcrypt.DefaultCost)
|
|
if err != nil {
|
|
s.logger.Error("Failed to hash password", map[string]interface{}{
|
|
"error": err.Error(),
|
|
})
|
|
return nil, errors.New("failed to process password")
|
|
}
|
|
|
|
// Create customer
|
|
customer := &Customer{
|
|
Type: CustomerType(form.Type),
|
|
Status: CustomerStatusPending,
|
|
CompanyIDs: companyIDs,
|
|
FirstName: form.FirstName,
|
|
LastName: form.LastName,
|
|
FullName: form.FullName,
|
|
Username: form.Username,
|
|
Email: form.Email,
|
|
Password: string(hashedPassword),
|
|
Phone: form.Phone,
|
|
Mobile: form.Mobile,
|
|
RegistrationNumber: form.RegistrationNumber,
|
|
TaxID: form.TaxID,
|
|
Industry: form.Industry,
|
|
Address: s.convertAddressForm(form.Address),
|
|
BusinessType: form.BusinessType,
|
|
EmployeeCount: form.EmployeeCount,
|
|
AnnualRevenue: form.AnnualRevenue,
|
|
FoundedYear: form.FoundedYear,
|
|
ContactPerson: s.convertContactPersonForm(form.ContactPerson),
|
|
IsVerified: false,
|
|
IsCompliant: false,
|
|
Language: s.getDefaultLanguage(form.Language),
|
|
Currency: s.getDefaultCurrency(form.Currency),
|
|
Timezone: s.getDefaultTimezone(form.Timezone),
|
|
CreatedBy: createdBy,
|
|
}
|
|
|
|
// Save to database
|
|
err = s.repository.Create(ctx, customer)
|
|
if err != nil {
|
|
s.logger.Error("Failed to create customer", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"email": form.Email,
|
|
"username": form.Username,
|
|
})
|
|
return nil, err
|
|
}
|
|
|
|
s.logger.Info("Customer created successfully", map[string]interface{}{
|
|
"customer_id": customer.ID,
|
|
"email": customer.Email,
|
|
"type": customer.Type,
|
|
"company_ids": companyIDs,
|
|
"created_by": *createdBy,
|
|
})
|
|
|
|
return customer, nil
|
|
}
|
|
|
|
// GetCustomerByID retrieves a customer by ID
|
|
func (s *customerService) GetCustomerByID(ctx context.Context, id string) (*Customer, error) {
|
|
customer, err := s.repository.GetByID(ctx, id)
|
|
if err != nil {
|
|
s.logger.Error("Failed to get customer by ID", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"customer_id": id,
|
|
})
|
|
return nil, err
|
|
}
|
|
|
|
s.logger.Info("Customer retrieved successfully", map[string]interface{}{
|
|
"customer_id": customer.ID,
|
|
"email": customer.Email,
|
|
})
|
|
|
|
return customer, nil
|
|
}
|
|
|
|
// GetCustomerByIDWithCompanies retrieves a customer by ID and loads associated companies
|
|
func (s *customerService) GetCustomerByIDWithCompanies(ctx context.Context, id string) (*CustomerResponse, error) {
|
|
customer, err := s.repository.GetByID(ctx, id)
|
|
if err != nil {
|
|
s.logger.Error("Failed to get customer by ID with companies", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"customer_id": id,
|
|
})
|
|
return nil, err
|
|
}
|
|
|
|
// Load companies for the customer
|
|
companies, err := s.loadCompaniesForCustomer(ctx, customer)
|
|
if err != nil {
|
|
s.logger.Error("Failed to load companies for customer", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"customer_id": customer.ID,
|
|
})
|
|
// Continue without companies if loading fails
|
|
companies = []*CompanySummary{}
|
|
}
|
|
|
|
customerResponse := customer.ToResponseWithCompanies(companies)
|
|
|
|
s.logger.Info("Customer retrieved with companies successfully", map[string]interface{}{
|
|
"customer_id": customer.ID,
|
|
"email": customer.Email,
|
|
})
|
|
|
|
return customerResponse, nil
|
|
}
|
|
|
|
// UpdateCustomer updates a customer
|
|
func (s *customerService) UpdateCustomer(ctx context.Context, id string, form *UpdateCustomerForm, updatedBy *string) (*Customer, error) {
|
|
// Get existing customer
|
|
customer, err := s.repository.GetByID(ctx, id)
|
|
if err != nil {
|
|
s.logger.Error("Failed to get customer for update", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"customer_id": id,
|
|
})
|
|
return nil, errors.New("customer not found")
|
|
}
|
|
|
|
// Check if email already exists (if changing email)
|
|
if form.Email != nil && *form.Email != customer.Email {
|
|
existingCustomer, _ := s.repository.GetByEmail(ctx, *form.Email)
|
|
if existingCustomer != nil {
|
|
return nil, errors.New("customer with this email already exists")
|
|
}
|
|
customer.Email = *form.Email
|
|
}
|
|
|
|
// Check if username already exists (if changing username)
|
|
if form.Username != nil && *form.Username != customer.Username {
|
|
existingCustomer, _ := s.repository.GetByUsername(ctx, *form.Username)
|
|
if existingCustomer != nil {
|
|
return nil, errors.New("customer with this username already exists")
|
|
}
|
|
customer.Username = *form.Username
|
|
}
|
|
|
|
// Check if registration number already exists (if changing)
|
|
if form.RegistrationNumber != nil && *form.RegistrationNumber != *customer.RegistrationNumber {
|
|
existingCustomer, _ := s.repository.GetByRegistrationNumber(ctx, *form.RegistrationNumber)
|
|
if existingCustomer != nil {
|
|
return nil, errors.New("company with this registration number already exists")
|
|
}
|
|
customer.RegistrationNumber = form.RegistrationNumber
|
|
}
|
|
|
|
// Check if tax ID already exists (if changing)
|
|
if form.TaxID != nil && *form.TaxID != *customer.TaxID {
|
|
existingCustomer, _ := s.repository.GetByTaxID(ctx, *form.TaxID)
|
|
if existingCustomer != nil {
|
|
return nil, errors.New("company with this tax ID already exists")
|
|
}
|
|
customer.TaxID = form.TaxID
|
|
}
|
|
|
|
// Update fields if provided
|
|
if form.Type != nil {
|
|
customer.Type = CustomerType(*form.Type)
|
|
}
|
|
|
|
// Handle company assignments
|
|
if len(form.CompanyIDs) > 0 {
|
|
// Verify that all company IDs exist
|
|
var validCompanyIDs []string
|
|
for _, companyID := range form.CompanyIDs {
|
|
_, err := s.companyService.GetCompanyByID(ctx, companyID)
|
|
if err != nil {
|
|
s.logger.Error("Invalid company ID provided in update", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"company_id": companyID,
|
|
})
|
|
return nil, errors.New("invalid company ID: " + companyID)
|
|
}
|
|
validCompanyIDs = append(validCompanyIDs, companyID)
|
|
}
|
|
customer.CompanyIDs = validCompanyIDs
|
|
}
|
|
|
|
if form.FirstName != nil {
|
|
customer.FirstName = form.FirstName
|
|
}
|
|
|
|
if form.LastName != nil {
|
|
customer.LastName = form.LastName
|
|
}
|
|
|
|
if form.FullName != nil {
|
|
customer.FullName = form.FullName
|
|
}
|
|
|
|
if form.Phone != nil {
|
|
customer.Phone = form.Phone
|
|
}
|
|
|
|
if form.Mobile != nil {
|
|
customer.Mobile = form.Mobile
|
|
}
|
|
|
|
if form.Industry != nil {
|
|
customer.Industry = form.Industry
|
|
}
|
|
|
|
if form.Address != nil {
|
|
customer.Address = s.convertAddressForm(form.Address)
|
|
}
|
|
|
|
if form.BusinessType != nil {
|
|
customer.BusinessType = form.BusinessType
|
|
}
|
|
|
|
if form.EmployeeCount != nil {
|
|
customer.EmployeeCount = form.EmployeeCount
|
|
}
|
|
|
|
if form.AnnualRevenue != nil {
|
|
customer.AnnualRevenue = form.AnnualRevenue
|
|
}
|
|
|
|
if form.FoundedYear != nil {
|
|
customer.FoundedYear = form.FoundedYear
|
|
}
|
|
|
|
if form.ContactPerson != nil {
|
|
customer.ContactPerson = s.convertContactPersonForm(form.ContactPerson)
|
|
}
|
|
|
|
if form.Language != nil {
|
|
customer.Language = *form.Language
|
|
}
|
|
|
|
if form.Currency != nil {
|
|
customer.Currency = *form.Currency
|
|
}
|
|
|
|
if form.Timezone != nil {
|
|
customer.Timezone = *form.Timezone
|
|
}
|
|
|
|
// Set updated by
|
|
customer.UpdatedBy = updatedBy
|
|
customer.UpdatedAt = time.Now().Unix()
|
|
|
|
// Update in database
|
|
err = s.repository.Update(ctx, customer)
|
|
if err != nil {
|
|
s.logger.Error("Failed to update customer", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"customer_id": id,
|
|
})
|
|
return nil, errors.New("failed to update customer")
|
|
}
|
|
|
|
s.logger.Info("Customer updated successfully", map[string]interface{}{
|
|
"customer_id": customer.ID,
|
|
"email": customer.Email,
|
|
"updated_by": *updatedBy,
|
|
})
|
|
|
|
return customer, nil
|
|
}
|
|
|
|
// DeleteCustomer deletes a customer (soft delete)
|
|
func (s *customerService) DeleteCustomer(ctx context.Context, id string, deletedBy *string) error {
|
|
// Get customer to check if exists
|
|
customer, err := s.repository.GetByID(ctx, id)
|
|
if err != nil {
|
|
return errors.New("customer not found")
|
|
}
|
|
|
|
// Set updated by before deletion
|
|
customer.UpdatedBy = deletedBy
|
|
|
|
// Delete customer (soft delete)
|
|
err = s.repository.Delete(ctx, id)
|
|
if err != nil {
|
|
s.logger.Error("Failed to delete customer", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"customer_id": id,
|
|
})
|
|
return errors.New("failed to delete customer")
|
|
}
|
|
|
|
s.logger.Info("Customer deleted successfully", map[string]interface{}{
|
|
"customer_id": id,
|
|
"deleted_by": deletedBy,
|
|
})
|
|
|
|
return nil
|
|
}
|
|
|
|
// ListCustomers lists customers with search and filters
|
|
func (s *customerService) ListCustomers(ctx context.Context, form *ListCustomersForm) (*CustomerListResponse, error) {
|
|
// Set defaults
|
|
limit := 20
|
|
if form.Limit != nil {
|
|
limit = *form.Limit
|
|
}
|
|
|
|
offset := 0
|
|
if form.Offset != nil {
|
|
offset = *form.Offset
|
|
}
|
|
|
|
search := ""
|
|
if form.Search != nil {
|
|
search = *form.Search
|
|
}
|
|
|
|
sortBy := "created_at"
|
|
if form.SortBy != nil {
|
|
sortBy = *form.SortBy
|
|
}
|
|
|
|
sortOrder := "desc"
|
|
if form.SortOrder != nil {
|
|
sortOrder = *form.SortOrder
|
|
}
|
|
|
|
// Parse company ID if provided
|
|
var companyID *string
|
|
if form.CompanyID != nil {
|
|
companyID = form.CompanyID
|
|
}
|
|
|
|
// Get customers
|
|
customers, err := s.repository.Search(ctx, search, form.Type, form.Status, companyID, form.Industry, form.IsVerified, form.IsCompliant, form.Language, form.Currency, limit, offset, sortBy, sortOrder)
|
|
if err != nil {
|
|
s.logger.Error("Failed to list customers", map[string]interface{}{
|
|
"error": err.Error(),
|
|
})
|
|
return nil, errors.New("failed to list customers")
|
|
}
|
|
|
|
// Convert to responses
|
|
var customerResponses []*CustomerResponse
|
|
for _, customer := range customers {
|
|
customerResponses = append(customerResponses, customer.ToResponse())
|
|
}
|
|
|
|
// TODO: Implement proper count for pagination metadata
|
|
total := int64(len(customers)) // This should be a separate count query
|
|
totalPages := (total + int64(limit) - 1) / int64(limit)
|
|
|
|
return &CustomerListResponse{
|
|
Customers: customerResponses,
|
|
Total: total,
|
|
Limit: limit,
|
|
Offset: offset,
|
|
TotalPages: int(totalPages),
|
|
}, nil
|
|
}
|
|
|
|
// ListCustomersWithCompanies lists customers with search and filters, including companies
|
|
func (s *customerService) ListCustomersWithCompanies(ctx context.Context, form *ListCustomersForm) (*CustomerListResponse, error) {
|
|
// Set defaults
|
|
limit := 20
|
|
if form.Limit != nil {
|
|
limit = *form.Limit
|
|
}
|
|
|
|
offset := 0
|
|
if form.Offset != nil {
|
|
offset = *form.Offset
|
|
}
|
|
|
|
search := ""
|
|
if form.Search != nil {
|
|
search = *form.Search
|
|
}
|
|
|
|
sortBy := "created_at"
|
|
if form.SortBy != nil {
|
|
sortBy = *form.SortBy
|
|
}
|
|
|
|
sortOrder := "desc"
|
|
if form.SortOrder != nil {
|
|
sortOrder = *form.SortOrder
|
|
}
|
|
|
|
// Parse company ID if provided
|
|
var companyID *string
|
|
if form.CompanyID != nil {
|
|
companyID = form.CompanyID
|
|
}
|
|
|
|
// Get customers
|
|
customers, err := s.repository.Search(ctx, search, form.Type, form.Status, companyID, form.Industry, form.IsVerified, form.IsCompliant, form.Language, form.Currency, limit, offset, sortBy, sortOrder)
|
|
if err != nil {
|
|
s.logger.Error("Failed to list customers with companies", map[string]interface{}{
|
|
"error": err.Error(),
|
|
})
|
|
return nil, errors.New("failed to list customers with companies")
|
|
}
|
|
|
|
// Convert to responses
|
|
var customerResponses []*CustomerResponse
|
|
for _, customer := range customers {
|
|
// Load companies for the customer
|
|
companies, err := s.loadCompaniesForCustomer(ctx, customer)
|
|
if err != nil {
|
|
s.logger.Error("Failed to load companies for customer in list", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"customer_id": customer.ID,
|
|
})
|
|
// Continue without companies if loading fails
|
|
companies = []*CompanySummary{}
|
|
}
|
|
customerResponse := customer.ToResponseWithCompanies(companies)
|
|
customerResponses = append(customerResponses, customerResponse)
|
|
}
|
|
|
|
// TODO: Implement proper count for pagination metadata
|
|
total := int64(len(customers)) // This should be a separate count query
|
|
totalPages := (total + int64(limit) - 1) / int64(limit)
|
|
|
|
return &CustomerListResponse{
|
|
Customers: customerResponses,
|
|
Total: total,
|
|
Limit: limit,
|
|
Offset: offset,
|
|
TotalPages: int(totalPages),
|
|
}, nil
|
|
}
|
|
|
|
// GetCustomersByCompanyID retrieves customers by company ID with pagination
|
|
func (s *customerService) GetCustomersByCompanyID(ctx context.Context, companyID string, limit, offset int) ([]*Customer, int64, error) {
|
|
customers, err := s.repository.GetByCompanyID(ctx, companyID, limit, offset)
|
|
if err != nil {
|
|
s.logger.Error("Failed to get customers by company ID", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"company_id": companyID,
|
|
})
|
|
return nil, 0, errors.New("failed to get customers by company")
|
|
}
|
|
|
|
// Get total count
|
|
total, err := s.repository.CountByCompanyID(ctx, companyID)
|
|
if err != nil {
|
|
s.logger.Error("Failed to count customers by company ID", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"company_id": companyID,
|
|
})
|
|
return customers, 0, nil // Return customers even if count fails
|
|
}
|
|
|
|
return customers, total, nil
|
|
}
|
|
|
|
// GetCustomersByType retrieves customers by type with pagination
|
|
func (s *customerService) GetCustomersByType(ctx context.Context, customerType CustomerType, limit, offset int) ([]*Customer, int64, error) {
|
|
// This would need to be implemented in the repository
|
|
// For now, we'll use the search method
|
|
customerTypeStr := string(customerType)
|
|
customers, err := s.repository.Search(ctx, "", &customerTypeStr, nil, nil, nil, nil, nil, nil, nil, limit, offset, "created_at", "desc")
|
|
if err != nil {
|
|
s.logger.Error("Failed to get customers by type", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"customer_type": customerType,
|
|
})
|
|
return nil, 0, errors.New("failed to get customers by type")
|
|
}
|
|
|
|
// Get total count
|
|
total, err := s.repository.CountByType(ctx, customerType)
|
|
if err != nil {
|
|
s.logger.Error("Failed to count customers by type", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"customer_type": customerType,
|
|
})
|
|
return customers, 0, nil
|
|
}
|
|
|
|
return customers, total, nil
|
|
}
|
|
|
|
// GetCustomersByStatus retrieves customers by status with pagination
|
|
func (s *customerService) GetCustomersByStatus(ctx context.Context, status CustomerStatus, limit, offset int) ([]*Customer, int64, error) {
|
|
// This would need to be implemented in the repository
|
|
// For now, we'll use the search method
|
|
statusStr := string(status)
|
|
customers, err := s.repository.Search(ctx, "", nil, &statusStr, nil, nil, nil, nil, nil, nil, limit, offset, "created_at", "desc")
|
|
if err != nil {
|
|
s.logger.Error("Failed to get customers by status", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"status": status,
|
|
})
|
|
return nil, 0, errors.New("failed to get customers by status")
|
|
}
|
|
|
|
// Get total count
|
|
total, err := s.repository.CountByStatus(ctx, status)
|
|
if err != nil {
|
|
s.logger.Error("Failed to count customers by status", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"status": status,
|
|
})
|
|
return customers, 0, nil
|
|
}
|
|
|
|
return customers, total, nil
|
|
}
|
|
|
|
// UpdateCustomerStatus updates customer status
|
|
func (s *customerService) UpdateCustomerStatus(ctx context.Context, id string, form *UpdateCustomerStatusForm, updatedBy *string) error {
|
|
// Get customer to check if exists
|
|
_, err := s.repository.GetByID(ctx, id)
|
|
if err != nil {
|
|
return errors.New("customer not found")
|
|
}
|
|
|
|
// Update status
|
|
status := CustomerStatus(form.Status)
|
|
err = s.repository.UpdateStatus(ctx, id, status)
|
|
if err != nil {
|
|
s.logger.Error("Failed to update customer status", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"customer_id": id,
|
|
"status": form.Status,
|
|
})
|
|
return errors.New("failed to update customer status")
|
|
}
|
|
|
|
s.logger.Info("Customer status updated successfully", map[string]interface{}{
|
|
"customer_id": id,
|
|
"status": form.Status,
|
|
"updated_by": updatedBy,
|
|
})
|
|
|
|
return nil
|
|
}
|
|
|
|
// UpdateCustomerVerification updates customer verification status
|
|
func (s *customerService) UpdateCustomerVerification(ctx context.Context, id string, form *UpdateCustomerVerificationForm, updatedBy *string) error {
|
|
// Get customer to check if exists
|
|
_, err := s.repository.GetByID(ctx, id)
|
|
if err != nil {
|
|
return errors.New("customer 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 customer verification", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"customer_id": id,
|
|
})
|
|
return errors.New("failed to update customer verification")
|
|
}
|
|
|
|
s.logger.Info("Customer verification updated successfully", map[string]interface{}{
|
|
"customer_id": id,
|
|
"is_verified": form.IsVerified,
|
|
"is_compliant": form.IsCompliant,
|
|
"updated_by": updatedBy,
|
|
})
|
|
|
|
return nil
|
|
}
|
|
|
|
// AssignCompaniesToCustomer assigns companies to a customer
|
|
func (s *customerService) AssignCompaniesToCustomer(ctx context.Context, customerID string, companyIDs []string, updatedBy *string) error {
|
|
// Get customer to check if exists
|
|
customer, err := s.repository.GetByID(ctx, customerID)
|
|
if err != nil {
|
|
return errors.New("customer not found")
|
|
}
|
|
|
|
// Verify companies exist and update customer's company IDs
|
|
var validCompanyIDs []string
|
|
for _, companyID := range companyIDs {
|
|
_, err = s.companyService.GetCompanyByID(ctx, companyID)
|
|
if err != nil {
|
|
s.logger.Error("Company not found during assignment", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"customer_id": customerID,
|
|
"company_id": companyID,
|
|
})
|
|
continue // Skip invalid company IDs
|
|
}
|
|
validCompanyIDs = append(validCompanyIDs, companyID)
|
|
}
|
|
|
|
// Update customer's company IDs
|
|
customer.CompanyIDs = append(customer.CompanyIDs, validCompanyIDs...)
|
|
customer.UpdatedBy = updatedBy
|
|
customer.UpdatedAt = time.Now().Unix()
|
|
|
|
err = s.repository.Update(ctx, customer)
|
|
if err != nil {
|
|
s.logger.Error("Failed to update customer with company assignments", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"customer_id": customerID,
|
|
})
|
|
return errors.New("failed to assign companies to customer")
|
|
}
|
|
|
|
s.logger.Info("Companies assigned to customer successfully", map[string]interface{}{
|
|
"customer_id": customerID,
|
|
"company_ids": validCompanyIDs,
|
|
"updated_by": updatedBy,
|
|
})
|
|
|
|
return nil
|
|
}
|
|
|
|
// RemoveCompaniesFromCustomer removes companies from a customer
|
|
func (s *customerService) RemoveCompaniesFromCustomer(ctx context.Context, customerID string, companyIDs []string, updatedBy *string) error {
|
|
// Get customer to check if exists
|
|
customer, err := s.repository.GetByID(ctx, customerID)
|
|
if err != nil {
|
|
return errors.New("customer not found")
|
|
}
|
|
|
|
// Remove company IDs from customer's list
|
|
var updatedCompanyIDs []string
|
|
for _, existingID := range customer.CompanyIDs {
|
|
shouldRemove := false
|
|
for _, removeID := range companyIDs {
|
|
if existingID == removeID {
|
|
shouldRemove = true
|
|
break
|
|
}
|
|
}
|
|
if !shouldRemove {
|
|
updatedCompanyIDs = append(updatedCompanyIDs, existingID)
|
|
}
|
|
}
|
|
|
|
// Update customer's company IDs
|
|
customer.CompanyIDs = updatedCompanyIDs
|
|
customer.UpdatedBy = updatedBy
|
|
customer.UpdatedAt = time.Now().Unix()
|
|
|
|
err = s.repository.Update(ctx, customer)
|
|
if err != nil {
|
|
s.logger.Error("Failed to update customer after removing company assignments", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"customer_id": customerID,
|
|
})
|
|
return errors.New("failed to remove companies from customer")
|
|
}
|
|
|
|
s.logger.Info("Companies removed from customer successfully", map[string]interface{}{
|
|
"customer_id": customerID,
|
|
"company_ids": companyIDs,
|
|
"updated_by": updatedBy,
|
|
})
|
|
|
|
return nil
|
|
}
|
|
|
|
// CreateCustomerMobile creates a new customer via mobile app (simplified)
|
|
func (s *customerService) CreateCustomerMobile(ctx context.Context, form *CreateCustomerMobileForm, createdBy *string) (*Customer, error) {
|
|
// Check if email already exists
|
|
existingCustomer, _ := s.repository.GetByEmail(ctx, form.Email)
|
|
if existingCustomer != nil {
|
|
return nil, errors.New("customer with this email already exists")
|
|
}
|
|
|
|
// Prepare company assignments
|
|
var companyIDs []string
|
|
if len(form.CompanyIDs) > 0 {
|
|
// Verify that all company IDs exist
|
|
for _, companyID := range form.CompanyIDs {
|
|
_, err := s.companyService.GetCompanyByID(ctx, companyID)
|
|
if err != nil {
|
|
s.logger.Error("Invalid company ID provided in mobile form", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"company_id": companyID,
|
|
})
|
|
return nil, errors.New("invalid company ID: " + companyID)
|
|
}
|
|
}
|
|
companyIDs = form.CompanyIDs
|
|
}
|
|
|
|
// Create customer with mobile form
|
|
customer := &Customer{
|
|
Type: CustomerType(form.Type),
|
|
Status: CustomerStatusActive,
|
|
CompanyIDs: companyIDs,
|
|
FirstName: form.FirstName,
|
|
LastName: form.LastName,
|
|
FullName: form.FullName,
|
|
Username: form.Username,
|
|
Email: form.Email,
|
|
Password: "", // Will be set after hashing
|
|
Phone: form.Phone,
|
|
Mobile: form.Mobile,
|
|
Industry: form.Industry,
|
|
IsVerified: false,
|
|
IsCompliant: false,
|
|
Language: "en",
|
|
Currency: "USD",
|
|
Timezone: "UTC",
|
|
CreatedBy: createdBy,
|
|
}
|
|
|
|
// Hash password
|
|
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(form.Password), bcrypt.DefaultCost)
|
|
if err != nil {
|
|
s.logger.Error("Failed to hash password for mobile customer", map[string]interface{}{
|
|
"error": err.Error(),
|
|
})
|
|
return nil, errors.New("failed to process password")
|
|
}
|
|
customer.Password = string(hashedPassword)
|
|
|
|
// Save to database
|
|
err = s.repository.Create(ctx, customer)
|
|
if err != nil {
|
|
s.logger.Error("Failed to create customer via mobile", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"email": form.Email,
|
|
"type": form.Type,
|
|
"created_by": createdBy,
|
|
})
|
|
return nil, err
|
|
}
|
|
|
|
s.logger.Info("Customer created via mobile successfully", map[string]interface{}{
|
|
"customer_id": customer.ID,
|
|
"email": customer.Email,
|
|
"type": customer.Type,
|
|
"company_ids": companyIDs,
|
|
"created_by": *createdBy,
|
|
})
|
|
|
|
return customer, nil
|
|
}
|
|
|
|
// UpdateCustomerMobile updates a customer via mobile app (simplified)
|
|
func (s *customerService) UpdateCustomerMobile(ctx context.Context, id string, form *UpdateCustomerMobileForm, updatedBy *string) (*Customer, error) {
|
|
// Get existing customer
|
|
customer, err := s.repository.GetByID(ctx, id)
|
|
if err != nil {
|
|
return nil, errors.New("customer not found")
|
|
}
|
|
|
|
// Update fields if provided
|
|
if form.FirstName != nil {
|
|
customer.FirstName = form.FirstName
|
|
}
|
|
|
|
if form.LastName != nil {
|
|
customer.LastName = form.LastName
|
|
}
|
|
|
|
if form.FullName != nil {
|
|
customer.FullName = form.FullName
|
|
}
|
|
|
|
if form.Phone != nil {
|
|
customer.Phone = form.Phone
|
|
}
|
|
|
|
if form.Mobile != nil {
|
|
customer.Mobile = form.Mobile
|
|
}
|
|
|
|
// Handle company assignments
|
|
if len(form.CompanyIDs) > 0 {
|
|
// Verify that all company IDs exist
|
|
var validCompanyIDs []string
|
|
for _, companyID := range form.CompanyIDs {
|
|
_, err := s.companyService.GetCompanyByID(ctx, companyID)
|
|
if err != nil {
|
|
s.logger.Error("Invalid company ID provided in mobile update", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"company_id": companyID,
|
|
})
|
|
return nil, errors.New("invalid company ID: " + companyID)
|
|
}
|
|
validCompanyIDs = append(validCompanyIDs, companyID)
|
|
}
|
|
customer.CompanyIDs = validCompanyIDs
|
|
}
|
|
|
|
if form.Industry != nil {
|
|
customer.Industry = form.Industry
|
|
}
|
|
|
|
// Set updated by
|
|
customer.UpdatedBy = updatedBy
|
|
|
|
// Update in database
|
|
err = s.repository.Update(ctx, customer)
|
|
if err != nil {
|
|
s.logger.Error("Failed to update customer via mobile", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"customer_id": id,
|
|
})
|
|
return nil, errors.New("failed to update customer")
|
|
}
|
|
|
|
s.logger.Info("Customer updated via mobile successfully", map[string]interface{}{
|
|
"customer_id": customer.ID,
|
|
"email": customer.Email,
|
|
"company_ids": customer.CompanyIDs,
|
|
"updated_by": *updatedBy,
|
|
})
|
|
|
|
return customer, nil
|
|
}
|
|
|
|
// VerifyCustomer verifies a customer
|
|
func (s *customerService) VerifyCustomer(ctx context.Context, id string, verifiedBy *string) error {
|
|
// Get customer to check if exists
|
|
customer, err := s.repository.GetByID(ctx, id)
|
|
if err != nil {
|
|
return errors.New("customer not found")
|
|
}
|
|
|
|
// Update verification
|
|
err = s.repository.UpdateVerification(ctx, id, true, customer.IsCompliant, customer.ComplianceNotes)
|
|
if err != nil {
|
|
s.logger.Error("Failed to verify customer", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"customer_id": id,
|
|
})
|
|
return errors.New("failed to verify customer")
|
|
}
|
|
|
|
s.logger.Info("Customer verified successfully", map[string]interface{}{
|
|
"customer_id": id,
|
|
"verified_by": verifiedBy,
|
|
})
|
|
|
|
return nil
|
|
}
|
|
|
|
// SuspendCustomer suspends a customer
|
|
func (s *customerService) SuspendCustomer(ctx context.Context, id string, reason string, suspendedBy *string) error {
|
|
// Get customer to check if exists
|
|
_, err := s.repository.GetByID(ctx, id)
|
|
if err != nil {
|
|
return errors.New("customer not found")
|
|
}
|
|
|
|
// Update status to suspended
|
|
err = s.repository.UpdateStatus(ctx, id, CustomerStatusSuspended)
|
|
if err != nil {
|
|
s.logger.Error("Failed to suspend customer", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"customer_id": id,
|
|
})
|
|
return errors.New("failed to suspend customer")
|
|
}
|
|
|
|
s.logger.Info("Customer suspended successfully", map[string]interface{}{
|
|
"customer_id": id,
|
|
"reason": reason,
|
|
"suspended_by": suspendedBy,
|
|
})
|
|
|
|
return nil
|
|
}
|
|
|
|
// ActivateCustomer activates a customer
|
|
func (s *customerService) ActivateCustomer(ctx context.Context, id string, activatedBy *string) error {
|
|
// Get customer to check if exists
|
|
_, err := s.repository.GetByID(ctx, id)
|
|
if err != nil {
|
|
return errors.New("customer not found")
|
|
}
|
|
|
|
// Update status to active
|
|
err = s.repository.UpdateStatus(ctx, id, CustomerStatusActive)
|
|
if err != nil {
|
|
s.logger.Error("Failed to activate customer", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"customer_id": id,
|
|
})
|
|
return errors.New("failed to activate customer")
|
|
}
|
|
|
|
s.logger.Info("Customer activated successfully", map[string]interface{}{
|
|
"customer_id": id,
|
|
"activated_by": activatedBy,
|
|
})
|
|
|
|
return nil
|
|
}
|
|
|
|
// Login handles customer authentication
|
|
func (s *customerService) Login(ctx context.Context, form *LoginForm) (*AuthResponse, error) {
|
|
s.logger.Info("Customer login attempt", map[string]interface{}{
|
|
"username": form.Username,
|
|
})
|
|
|
|
// Get customer by username
|
|
customer, err := s.repository.GetByUsername(ctx, form.Username)
|
|
if err != nil {
|
|
s.logger.Warn("Customer login failed - username not found", map[string]interface{}{
|
|
"username": form.Username,
|
|
})
|
|
return nil, errors.New("invalid credentials")
|
|
}
|
|
|
|
// Check if customer is active
|
|
if customer.Status != CustomerStatusActive {
|
|
s.logger.Warn("Customer login failed - account not active", map[string]interface{}{
|
|
"username": form.Username,
|
|
"customer_id": customer.ID,
|
|
"status": string(customer.Status),
|
|
})
|
|
return nil, errors.New("account is not active")
|
|
}
|
|
|
|
// Verify password
|
|
err = bcrypt.CompareHashAndPassword([]byte(customer.Password), []byte(form.Password))
|
|
if err != nil {
|
|
s.logger.Warn("Customer login failed - wrong password", map[string]interface{}{
|
|
"customer_id": customer.ID,
|
|
"email": customer.Email,
|
|
})
|
|
return nil, errors.New("invalid credentials")
|
|
}
|
|
|
|
// Generate JWT tokens using authorization service
|
|
companyID := ""
|
|
if len(customer.CompanyIDs) > 0 {
|
|
companyID = customer.CompanyIDs[0] // Use first company ID for JWT token
|
|
}
|
|
|
|
tokenPair, err := s.authService.GenerateTokenPair(
|
|
customer.ID.Hex(),
|
|
customer.Email,
|
|
"customer", // Role for customers
|
|
companyID,
|
|
)
|
|
if err != nil {
|
|
s.logger.Error("Failed to generate JWT tokens", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"customer_id": customer.ID,
|
|
})
|
|
return nil, errors.New("failed to generate authentication tokens")
|
|
}
|
|
|
|
s.logger.Info("Customer login successful", map[string]interface{}{
|
|
"username": form.Username,
|
|
"customer_id": customer.ID,
|
|
"customer_type": string(customer.Type),
|
|
})
|
|
|
|
return &AuthResponse{
|
|
Customer: customer.ToResponse(),
|
|
AccessToken: tokenPair.AccessToken,
|
|
RefreshToken: tokenPair.RefreshToken,
|
|
ExpiresAt: time.Now().Add(15 * time.Minute).Unix(), // 15 minutes from now
|
|
}, nil
|
|
}
|
|
|
|
// RefreshToken handles token refresh for customers
|
|
func (s *customerService) RefreshToken(ctx context.Context, refreshToken string) (*AuthResponse, error) {
|
|
s.logger.Info("Customer token refresh attempt", map[string]interface{}{
|
|
"refresh_token": refreshToken[:10] + "...", // Log only first 10 chars for security
|
|
})
|
|
|
|
// Validate refresh token and generate new access token
|
|
newAccessToken, err := s.authService.RefreshAccessToken(refreshToken)
|
|
if err != nil {
|
|
s.logger.Warn("Failed to refresh access token", map[string]interface{}{
|
|
"error": err.Error(),
|
|
})
|
|
return nil, errors.New("invalid or expired refresh token")
|
|
}
|
|
|
|
// Parse the new access token to get customer information
|
|
validationResult, err := s.authService.ValidateAccessToken(newAccessToken)
|
|
if err != nil {
|
|
s.logger.Error("Failed to validate new access token", map[string]interface{}{
|
|
"error": err.Error(),
|
|
})
|
|
return nil, errors.New("failed to generate valid access token")
|
|
}
|
|
|
|
if !validationResult.Valid {
|
|
s.logger.Error("New access token validation failed", map[string]interface{}{
|
|
"error": validationResult.Error,
|
|
})
|
|
return nil, errors.New("failed to generate valid access token")
|
|
}
|
|
|
|
// Get customer information
|
|
customerID, err := primitive.ObjectIDFromHex(validationResult.Claims.UserID)
|
|
if err != nil {
|
|
s.logger.Error("Failed to parse customer ID from token", map[string]interface{}{
|
|
"error": err.Error(),
|
|
})
|
|
return nil, errors.New("invalid token format")
|
|
}
|
|
|
|
customer, err := s.repository.GetByID(ctx, customerID.Hex())
|
|
if err != nil {
|
|
s.logger.Error("Failed to get customer for token refresh", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"customer_id": customerID.Hex(),
|
|
})
|
|
return nil, errors.New("customer not found")
|
|
}
|
|
|
|
// Check if customer is still active
|
|
if customer.Status != CustomerStatusActive {
|
|
return nil, errors.New("customer account is inactive")
|
|
}
|
|
|
|
s.logger.Info("Access token refreshed successfully", map[string]interface{}{
|
|
"customer_id": customer.ID,
|
|
"customer_type": string(customer.Type),
|
|
})
|
|
|
|
return &AuthResponse{
|
|
Customer: customer.ToResponse(),
|
|
AccessToken: newAccessToken,
|
|
RefreshToken: refreshToken, // Return the same refresh token
|
|
ExpiresAt: time.Now().Add(15 * time.Minute).Unix(), // 15 minutes from now
|
|
}, nil
|
|
}
|
|
|
|
// GetProfile retrieves customer profile information
|
|
func (s *customerService) GetProfile(ctx context.Context, customerID string) (*Customer, error) {
|
|
s.logger.Info("Getting customer profile", map[string]interface{}{
|
|
"customer_id": customerID,
|
|
})
|
|
|
|
customer, err := s.repository.GetByID(ctx, customerID)
|
|
if err != nil {
|
|
s.logger.Error("Failed to get customer profile", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"customer_id": customerID,
|
|
})
|
|
return nil, errors.New("customer not found")
|
|
}
|
|
|
|
s.logger.Info("Customer profile retrieved successfully", map[string]interface{}{
|
|
"customer_id": customerID,
|
|
"customer_type": string(customer.Type),
|
|
})
|
|
|
|
return customer, nil
|
|
}
|
|
|
|
// GetProfileWithCompanies retrieves customer profile information including companies
|
|
func (s *customerService) GetProfileWithCompanies(ctx context.Context, customerID string) (*CustomerResponse, error) {
|
|
s.logger.Info("Getting customer profile with companies", map[string]interface{}{
|
|
"customer_id": customerID,
|
|
})
|
|
|
|
customer, err := s.repository.GetByID(ctx, customerID)
|
|
if err != nil {
|
|
s.logger.Error("Failed to get customer profile with companies", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"customer_id": customerID,
|
|
})
|
|
return nil, errors.New("customer not found")
|
|
}
|
|
|
|
// Load companies for the customer
|
|
companies, err := s.loadCompaniesForCustomer(ctx, customer)
|
|
if err != nil {
|
|
s.logger.Error("Failed to load companies for customer profile", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"customer_id": customer.ID,
|
|
})
|
|
// Continue without companies if loading fails
|
|
companies = []*CompanySummary{}
|
|
}
|
|
|
|
customerResponse := customer.ToResponseWithCompanies(companies)
|
|
|
|
s.logger.Info("Customer profile retrieved with companies successfully", map[string]interface{}{
|
|
"customer_id": customerID,
|
|
"customer_type": string(customer.Type),
|
|
})
|
|
|
|
return customerResponse, nil
|
|
}
|
|
|
|
// Logout handles customer logout
|
|
func (s *customerService) Logout(ctx context.Context, customerID string, accessToken string) error {
|
|
s.logger.Info("Customer logout", map[string]interface{}{
|
|
"customer_id": customerID,
|
|
"access_token": accessToken[:10] + "...", // Log only first 10 chars for security
|
|
})
|
|
|
|
// Invalidate the access token
|
|
err := s.authService.InvalidateToken(accessToken)
|
|
if err != nil {
|
|
s.logger.Error("Failed to invalidate access token", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"customer_id": customerID,
|
|
})
|
|
// Don't return error here as the customer should still be logged out
|
|
}
|
|
|
|
s.logger.Info("Customer logout successful", map[string]interface{}{
|
|
"customer_id": customerID,
|
|
})
|
|
|
|
return nil
|
|
}
|
|
|
|
// Helper methods for converting forms to domain objects
|
|
func (s *customerService) 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,
|
|
AddressType: form.AddressType,
|
|
IsDefault: form.IsDefault,
|
|
}
|
|
}
|
|
|
|
func (s *customerService) convertContactPersonForm(form *ContactPersonForm) *ContactPerson {
|
|
if form == nil {
|
|
return nil
|
|
}
|
|
|
|
return &ContactPerson{
|
|
FirstName: form.FirstName,
|
|
LastName: form.LastName,
|
|
FullName: form.FullName,
|
|
Position: form.Position,
|
|
Email: form.Email,
|
|
Phone: form.Phone,
|
|
Mobile: form.Mobile,
|
|
IsPrimary: form.IsPrimary,
|
|
}
|
|
}
|
|
|
|
func (s *customerService) getDefaultLanguage(language *string) string {
|
|
if language != nil {
|
|
return *language
|
|
}
|
|
return "en"
|
|
}
|
|
|
|
func (s *customerService) getDefaultCurrency(currency *string) string {
|
|
if currency != nil {
|
|
return *currency
|
|
}
|
|
return "USD"
|
|
}
|
|
|
|
func (s *customerService) getDefaultTimezone(timezone *string) string {
|
|
if timezone != nil {
|
|
return *timezone
|
|
}
|
|
return "UTC"
|
|
}
|
|
|
|
// loadCompaniesForCustomer loads company summaries for a customer
|
|
func (s *customerService) loadCompaniesForCustomer(ctx context.Context, customer *Customer) ([]*CompanySummary, error) {
|
|
if len(customer.CompanyIDs) == 0 {
|
|
return []*CompanySummary{}, nil
|
|
}
|
|
|
|
// Load companies in batch using the new GetCompaniesByIDs method
|
|
companies, err := s.companyService.GetCompaniesByIDs(ctx, customer.CompanyIDs)
|
|
if err != nil {
|
|
s.logger.Error("Failed to load companies for customer", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"customer_id": customer.ID,
|
|
"company_ids": customer.CompanyIDs,
|
|
})
|
|
return []*CompanySummary{}, err
|
|
}
|
|
|
|
// Convert to CompanySummary format
|
|
var companySummaries []*CompanySummary
|
|
for _, company := range companies {
|
|
companySummaries = append(companySummaries, &CompanySummary{
|
|
ID: company.ID.Hex(),
|
|
Name: company.Name,
|
|
})
|
|
}
|
|
|
|
s.logger.Info("Companies loaded for customer", map[string]interface{}{
|
|
"customer_id": customer.ID,
|
|
"requested_count": len(customer.CompanyIDs),
|
|
"loaded_count": len(companySummaries),
|
|
})
|
|
|
|
return companySummaries, nil
|
|
}
|