Enhance Tender Management with Company-Based Matching and API Updates
- Updated the tender service to include company-based matching for tenders, allowing for more relevant results based on company CPV codes. - Modified the ListTenders endpoint to accept a company ID parameter for calculating match percentages and sorting tenders accordingly. - Refactored the tender response structure to include match percentage and days until deadline for better client-side handling. - Updated API documentation to reflect changes in the tender listing functionality and added new endpoints for public tender access. - Removed deprecated customer-related methods and streamlined customer listing functionality for improved clarity and performance.
This commit is contained in:
@@ -207,7 +207,7 @@ func (h *Handler) ListCustomers(c echo.Context) error {
|
||||
return response.ValidationError(c, "Invalid request data", err.Error())
|
||||
}
|
||||
|
||||
result, err := h.service.ListCustomersWithCompanies(c.Request().Context(), form)
|
||||
result, err := h.service.ListCustomers(c.Request().Context(), form)
|
||||
if err != nil {
|
||||
return response.InternalServerError(c, "Failed to list customers")
|
||||
}
|
||||
@@ -215,45 +215,6 @@ func (h *Handler) ListCustomers(c echo.Context) error {
|
||||
return response.Success(c, result, "Customers retrieved successfully")
|
||||
}
|
||||
|
||||
// ListCustomersWithCompanies lists customers with companies and filters
|
||||
// @Summary List customers with companies and filters
|
||||
// @Description Retrieve a paginated list of customers with their assigned companies and advanced filtering options including search, type, status, company, industry, verification status, and compliance status. Supports sorting and pagination.
|
||||
// @Tags Admin-Customers
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param search query string false "Search term to filter customers by name, email, or company name"
|
||||
// @Param type query string false "Filter by customer type" Enums(individual, company, government)
|
||||
// @Param status query string false "Filter by customer status" Enums(active, inactive, suspended, pending)
|
||||
// @Param company_id query string false "Filter by company ID" format(uuid)
|
||||
// @Param industry query string false "Filter by industry"
|
||||
// @Param is_verified query boolean false "Filter by verification status"
|
||||
// @Param is_compliant query boolean false "Filter by compliance status"
|
||||
// @Param language query string false "Filter by preferred language"
|
||||
// @Param currency query string false "Filter by preferred currency"
|
||||
// @Param limit query integer false "Number of customers per page (1-100)" minimum(1) maximum(100) default(20)
|
||||
// @Param offset query integer false "Number of customers to skip for pagination" minimum(0) default(0)
|
||||
// @Param sort_by query string false "Field to sort by" Enums(email, company_name, created_at, updated_at, status) default(created_at)
|
||||
// @Param sort_order query string false "Sort order" Enums(asc, desc) default(desc)
|
||||
// @Success 200 {object} response.APIResponse{data=CustomerListResponse} "Customers with companies retrieved successfully"
|
||||
// @Failure 400 {object} response.APIResponse "Bad request - Invalid query parameters"
|
||||
// @Failure 422 {object} response.APIResponse "Validation error - Invalid query parameters"
|
||||
// @Failure 500 {object} response.APIResponse "Internal server error"
|
||||
// @Security BearerAuth
|
||||
// @Router /admin/v1/customers/with-companies [get]
|
||||
func (h *Handler) ListCustomersWithCompanies(c echo.Context) error {
|
||||
form, err := response.Parse[ListCustomersForm](c)
|
||||
if err != nil {
|
||||
return response.ValidationError(c, "Invalid request data", err.Error())
|
||||
}
|
||||
|
||||
result, err := h.service.ListCustomersWithCompanies(c.Request().Context(), form)
|
||||
if err != nil {
|
||||
return response.InternalServerError(c, "Failed to list customers with companies")
|
||||
}
|
||||
|
||||
return response.Success(c, result, "Customers with companies retrieved successfully")
|
||||
}
|
||||
|
||||
// UpdateCustomerStatus updates customer status (Web Panel)
|
||||
// @Summary Update customer status
|
||||
// @Description Update customer account status (active, inactive, suspended, pending)
|
||||
|
||||
@@ -20,13 +20,11 @@ type Repository interface {
|
||||
GetByRegistrationNumber(ctx context.Context, registrationNumber string) (*Customer, error)
|
||||
GetByTaxID(ctx context.Context, taxID string) (*Customer, error)
|
||||
GetByCompanyID(ctx context.Context, companyID string, limit, offset int) ([]*Customer, error)
|
||||
GetByCompanyIDs(ctx context.Context, companyIDs []string, limit, offset int) ([]*Customer, error)
|
||||
Update(ctx context.Context, customer *Customer) error
|
||||
Delete(ctx context.Context, id string) error
|
||||
List(ctx context.Context, limit, offset int) ([]*Customer, error)
|
||||
Search(ctx context.Context, search string, customerType *string, status *string, companyID *string, industry *string, isVerified *bool, isCompliant *bool, language *string, currency *string, limit, offset int, sortBy, sortOrder string) ([]*Customer, error)
|
||||
CountByCompanyID(ctx context.Context, companyID string) (int64, error)
|
||||
CountByCompanyIDs(ctx context.Context, companyIDs []string) (int64, error)
|
||||
CountByType(ctx context.Context, customerType CustomerType) (int64, error)
|
||||
CountByStatus(ctx context.Context, status CustomerStatus) (int64, error)
|
||||
UpdateStatus(ctx context.Context, id string, status CustomerStatus) error
|
||||
@@ -247,46 +245,6 @@ func (r *customerRepository) GetByCompanyID(ctx context.Context, companyID strin
|
||||
return customers, nil
|
||||
}
|
||||
|
||||
// GetByCompanyIDs retrieves customers by multiple company IDs with pagination
|
||||
func (r *customerRepository) GetByCompanyIDs(ctx context.Context, companyIDs []string, limit, offset int) ([]*Customer, error) {
|
||||
if len(companyIDs) == 0 {
|
||||
return nil, errors.New("no company IDs provided")
|
||||
}
|
||||
|
||||
// Build pagination
|
||||
pagination := mongopkg.NewPaginationBuilder().
|
||||
Limit(limit).
|
||||
Skip(offset).
|
||||
SortDesc("created_at").
|
||||
Build()
|
||||
|
||||
// Filter by company IDs and active status
|
||||
filter := bson.M{
|
||||
"company_ids": bson.M{"$in": companyIDs},
|
||||
"status": bson.M{"$ne": CustomerStatusInactive},
|
||||
}
|
||||
|
||||
// Use ORM to find customers
|
||||
result, err := r.ormRepo.FindAll(ctx, filter, pagination)
|
||||
if err != nil {
|
||||
r.logger.Error("Failed to get customers by multiple company IDs", map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
"company_ids": companyIDs,
|
||||
"limit": limit,
|
||||
"offset": offset,
|
||||
})
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Convert []Customer to []*Customer
|
||||
customers := make([]*Customer, len(result.Items))
|
||||
for i := range result.Items {
|
||||
customers[i] = &result.Items[i]
|
||||
}
|
||||
|
||||
return customers, nil
|
||||
}
|
||||
|
||||
// Update updates a customer
|
||||
func (r *customerRepository) Update(ctx context.Context, customer *Customer) error {
|
||||
// Set updated timestamp using Unix timestamp
|
||||
@@ -466,29 +424,6 @@ func (r *customerRepository) CountByCompanyID(ctx context.Context, companyID str
|
||||
return count, nil
|
||||
}
|
||||
|
||||
// CountByCompanyIDs counts customers by multiple company IDs
|
||||
func (r *customerRepository) CountByCompanyIDs(ctx context.Context, companyIDs []string) (int64, error) {
|
||||
if len(companyIDs) == 0 {
|
||||
return 0, errors.New("no company IDs provided")
|
||||
}
|
||||
|
||||
filter := bson.M{
|
||||
"company_ids": bson.M{"$in": companyIDs},
|
||||
"status": bson.M{"$ne": CustomerStatusInactive},
|
||||
}
|
||||
|
||||
count, err := r.ormRepo.Count(ctx, filter)
|
||||
if err != nil {
|
||||
r.logger.Error("Failed to count customers by multiple company IDs", map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
"company_ids": companyIDs,
|
||||
})
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return count, nil
|
||||
}
|
||||
|
||||
// CountByType counts customers by type
|
||||
func (r *customerRepository) CountByType(ctx context.Context, customerType CustomerType) (int64, error) {
|
||||
filter := bson.M{
|
||||
|
||||
@@ -25,7 +25,6 @@ type Service interface {
|
||||
|
||||
// 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)
|
||||
@@ -471,79 +470,6 @@ func (s *customerService) ListCustomers(ctx context.Context, form *ListCustomers
|
||||
}, 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)
|
||||
|
||||
Reference in New Issue
Block a user