3911180486
- Renamed and restructured tender-related API endpoints for improved clarity and consistency, including changing the route from `/admin/tenders` to `/admin/v1/companies` and updating the associated methods. - Introduced a new SearchForm structure for listing tenders with advanced filtering capabilities, enhancing the API's search functionality. - Updated the service and repository layers to align with the new endpoint structure, ensuring proper handling of tender data. - Enhanced Swagger and YAML documentation to reflect the changes in endpoint structure, including detailed descriptions and examples for the new search functionality. - Improved error handling and response structures to provide clearer feedback to API consumers, ensuring a more robust and user-friendly experience.
1081 lines
40 KiB
Go
1081 lines
40 KiB
Go
package tender_approval
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"math"
|
|
"time"
|
|
"tm/internal/tender"
|
|
"tm/pkg/logger"
|
|
"tm/pkg/response"
|
|
)
|
|
|
|
// Service defines business logic for tender approval operations
|
|
type Service interface {
|
|
// Core tender approval operations
|
|
CreateTenderApproval(ctx context.Context, form *CreateTenderApprovalForm, companyID string) (*TenderApproval, error)
|
|
GetTenderApprovalByID(ctx context.Context, id string) (*TenderApproval, error)
|
|
GetTenderApprovalByIDWithTender(ctx context.Context, id string) (*TenderApprovalWithTenderResponse, error)
|
|
GetTenderApprovalByTenderAndCompany(ctx context.Context, tenderID, companyID string) (*TenderApproval, error)
|
|
GetTenderApprovalByTenderAndCompanyWithTender(ctx context.Context, tenderID, companyID string) (*TenderApprovalWithTenderResponse, error)
|
|
GetTenderApprovalsByTenderID(ctx context.Context, tenderID string) ([]*TenderApproval, error)
|
|
GetTenderApprovalsByCompanyID(ctx context.Context, companyID string, submissionMode *SubmissionMode, status *ApprovalStatus, from *int64, to *int64, limit int, offset int) ([]*TenderApproval, int64, error)
|
|
GetTenderApprovalsByCompanyIDWithTender(ctx context.Context, companyID string, submissionMode *SubmissionMode, status *ApprovalStatus, from *int64, to *int64, limit int, offset int) (*TenderApprovalWithTenderListResponse, error)
|
|
GetTenderApprovalsByIDs(ctx context.Context, ids []string) ([]*TenderApproval, error)
|
|
UpdateTenderApproval(ctx context.Context, id string, form *UpdateTenderApprovalForm) (*TenderApproval, error)
|
|
UpdateTenderApprovalWithTender(ctx context.Context, id string, form *UpdateTenderApprovalForm) (*TenderApprovalWithTenderResponse, error)
|
|
DeleteTenderApproval(ctx context.Context, id string) error
|
|
|
|
// Tender approval listing and search
|
|
ListTenderApprovals(ctx context.Context, form *ListTenderApprovalsForm) (*TenderApprovalListResponse, error)
|
|
ListTenderApprovalsWithTender(ctx context.Context, form *ListTenderApprovalsForm) (*TenderApprovalListResponseWithTender, error)
|
|
SearchTenderApprovals(ctx context.Context, form *TenderApprovalSearchForm) (*TenderApprovalListResponse, error)
|
|
SearchTenderApprovalsByCriteria(ctx context.Context, criteria *TenderApprovalSearchCriteria) ([]*TenderApproval, error)
|
|
|
|
// Business operations
|
|
ToggleTenderApproval(ctx context.Context, req *ToggleTenderApprovalForm, companyID string) (*TenderApprovalWithTenderResponse, error)
|
|
|
|
// Statistics and analytics
|
|
GetTenderApprovalStats(ctx context.Context) (*TenderApprovalStatsResponse, error)
|
|
GetCompanyTenderApprovalStats(ctx context.Context, companyID string) (*CompanyTenderApprovalStatsResponse, error)
|
|
GetMultipleCompanyTenderApprovalStats(ctx context.Context, companyIDs []string) ([]*CompanyTenderApprovalStatsResponse, error)
|
|
GetAggregatedCompanyStats(ctx context.Context) (*AggregatedCompanyStatsResponse, error)
|
|
|
|
// Search and filtering helpers
|
|
GetTenderApprovalsByStatus(ctx context.Context, status ApprovalStatus, limit, offset int) ([]*TenderApproval, int64, error)
|
|
GetTenderApprovalsByStatusWithTender(ctx context.Context, status ApprovalStatus, limit, offset int) ([]*TenderApprovalWithTenderResponse, int64, error)
|
|
GetTenderApprovalsBySubmissionMode(ctx context.Context, submissionMode SubmissionMode, limit, offset int) ([]*TenderApproval, int64, error)
|
|
GetTenderApprovalsBySubmissionModeWithTender(ctx context.Context, submissionMode SubmissionMode, limit, offset int) ([]*TenderApprovalWithTenderResponse, int64, error)
|
|
}
|
|
|
|
// tenderApprovalService implements the Service interface
|
|
type tenderApprovalService struct {
|
|
repository Repository
|
|
tenderService tender.Service
|
|
logger logger.Logger
|
|
}
|
|
|
|
// NewTenderApprovalService creates a new tender approval service
|
|
func NewTenderApprovalService(repository Repository, tenderService tender.Service, logger logger.Logger) Service {
|
|
return &tenderApprovalService{
|
|
repository: repository,
|
|
tenderService: tenderService,
|
|
logger: logger,
|
|
}
|
|
}
|
|
|
|
// CreateTenderApproval creates a new tender approval
|
|
func (s *tenderApprovalService) CreateTenderApproval(ctx context.Context, form *CreateTenderApprovalForm, companyID string) (*TenderApproval, error) {
|
|
// Check if tender approval already exists for this tender and company
|
|
existingApproval, _ := s.repository.GetByTenderAndCompany(ctx, form.TenderID, companyID)
|
|
if existingApproval != nil {
|
|
return nil, errors.New("tender approval already exists for this tender and company")
|
|
}
|
|
|
|
// Create tender approval
|
|
tenderApproval := &TenderApproval{
|
|
TenderID: form.TenderID,
|
|
CompanyID: companyID,
|
|
SubmissionMode: SubmissionMode(form.SubmissionMode),
|
|
Status: ApprovalStatus(form.Status),
|
|
}
|
|
|
|
// Save to database
|
|
err := s.repository.Create(ctx, tenderApproval)
|
|
if err != nil {
|
|
s.logger.Error("Failed to create tender approval", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"tender_id": form.TenderID,
|
|
"company_id": companyID,
|
|
})
|
|
return nil, err
|
|
}
|
|
|
|
s.logger.Info("Tender approval created successfully", map[string]interface{}{
|
|
"tender_approval_id": tenderApproval.ID,
|
|
"tender_id": tenderApproval.TenderID,
|
|
"company_id": tenderApproval.CompanyID,
|
|
"submission_mode": tenderApproval.SubmissionMode,
|
|
"status": tenderApproval.Status,
|
|
})
|
|
|
|
return tenderApproval, nil
|
|
}
|
|
|
|
// GetTenderApprovalByID retrieves a tender approval by ID
|
|
func (s *tenderApprovalService) GetTenderApprovalByID(ctx context.Context, id string) (*TenderApproval, error) {
|
|
tenderApproval, err := s.repository.GetByID(ctx, id)
|
|
if err != nil {
|
|
s.logger.Error("Failed to get tender approval by ID", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"tender_approval_id": id,
|
|
})
|
|
return nil, err
|
|
}
|
|
|
|
s.logger.Info("Tender approval retrieved successfully", map[string]interface{}{
|
|
"tender_approval_id": tenderApproval.ID,
|
|
"tender_id": tenderApproval.TenderID,
|
|
"company_id": tenderApproval.CompanyID,
|
|
})
|
|
|
|
return tenderApproval, nil
|
|
}
|
|
|
|
// GetTenderApprovalByIDWithTender retrieves a tender approval by ID with tender details
|
|
func (s *tenderApprovalService) GetTenderApprovalByIDWithTender(ctx context.Context, id string) (*TenderApprovalWithTenderResponse, error) {
|
|
tenderApproval, err := s.repository.GetByID(ctx, id)
|
|
if err != nil {
|
|
s.logger.Error("Failed to get tender approval by ID", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"tender_approval_id": id,
|
|
})
|
|
return nil, err
|
|
}
|
|
|
|
// Get tender details
|
|
tenderResponse, err := s.tenderService.GetByID(ctx, tenderApproval.TenderID)
|
|
if err != nil {
|
|
s.logger.Warn("Failed to get tender details", map[string]interface{}{
|
|
"tender_id": tenderApproval.TenderID,
|
|
"error": err.Error(),
|
|
})
|
|
// Return approval without tender details rather than failing completely
|
|
return tenderApproval.ToResponseWithTender(nil), nil
|
|
}
|
|
|
|
// Convert tender response to tender details
|
|
tenderDetails := &TenderDetails{
|
|
ID: tenderResponse.ID,
|
|
NoticePublicationID: tenderResponse.NoticePublicationID,
|
|
PublicationDate: tenderResponse.PublicationDate,
|
|
Title: tenderResponse.Title,
|
|
Description: tenderResponse.Description,
|
|
ProcurementTypeCode: tenderResponse.ProcurementTypeCode,
|
|
ProcedureCode: tenderResponse.ProcedureCode,
|
|
EstimatedValue: tenderResponse.EstimatedValue,
|
|
Currency: tenderResponse.Currency,
|
|
Duration: tenderResponse.Duration,
|
|
DurationUnit: tenderResponse.DurationUnit,
|
|
TenderDeadline: tenderResponse.TenderDeadline,
|
|
SubmissionDeadline: tenderResponse.SubmissionDeadline,
|
|
CountryCode: tenderResponse.CountryCode,
|
|
BuyerOrganization: &OrganizationResponse{Name: tenderResponse.BuyerOrganization.Name},
|
|
Status: string(tenderResponse.Status),
|
|
}
|
|
|
|
s.logger.Info("Tender approval with tender details retrieved successfully", map[string]interface{}{
|
|
"tender_approval_id": id,
|
|
})
|
|
|
|
return tenderApproval.ToResponseWithTender(tenderDetails), nil
|
|
}
|
|
|
|
// GetTenderApprovalByTenderAndCompany retrieves a tender approval by tender ID and company ID
|
|
func (s *tenderApprovalService) GetTenderApprovalByTenderAndCompany(ctx context.Context, tenderID, companyID string) (*TenderApproval, error) {
|
|
tenderApproval, err := s.repository.GetByTenderAndCompany(ctx, tenderID, companyID)
|
|
if err != nil {
|
|
s.logger.Error("Failed to get tender approval by tender and company", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"tender_id": tenderID,
|
|
"company_id": companyID,
|
|
})
|
|
return nil, err
|
|
}
|
|
|
|
s.logger.Info("Tender approval retrieved successfully", map[string]interface{}{
|
|
"tender_approval_id": tenderApproval.ID,
|
|
"tender_id": tenderApproval.TenderID,
|
|
"company_id": tenderApproval.CompanyID,
|
|
})
|
|
|
|
return tenderApproval, nil
|
|
}
|
|
|
|
// GetTenderApprovalByTenderAndCompanyWithTender retrieves a tender approval by tender ID and company ID with tender details
|
|
func (s *tenderApprovalService) GetTenderApprovalByTenderAndCompanyWithTender(ctx context.Context, tenderID, companyID string) (*TenderApprovalWithTenderResponse, error) {
|
|
tenderApproval, err := s.repository.GetByTenderAndCompany(ctx, tenderID, companyID)
|
|
if err != nil {
|
|
s.logger.Error("Failed to get tender approval by tender and company", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"tender_id": tenderID,
|
|
"company_id": companyID,
|
|
})
|
|
return nil, err
|
|
}
|
|
|
|
// Get tender details
|
|
tenderResponse, err := s.tenderService.GetByID(ctx, tenderApproval.TenderID)
|
|
if err != nil {
|
|
s.logger.Warn("Failed to get tender details", map[string]interface{}{
|
|
"tender_id": tenderApproval.TenderID,
|
|
})
|
|
// Return approval without tender details rather than failing completely
|
|
return tenderApproval.ToResponseWithTender(nil), nil
|
|
}
|
|
|
|
// Convert tender response to tender details
|
|
tenderDetails := &TenderDetails{
|
|
ID: tenderResponse.ID,
|
|
NoticePublicationID: tenderResponse.NoticePublicationID,
|
|
PublicationDate: tenderResponse.PublicationDate,
|
|
Title: tenderResponse.Title,
|
|
Description: tenderResponse.Description,
|
|
ProcurementTypeCode: tenderResponse.ProcurementTypeCode,
|
|
ProcedureCode: tenderResponse.ProcedureCode,
|
|
EstimatedValue: tenderResponse.EstimatedValue,
|
|
Currency: tenderResponse.Currency,
|
|
Duration: tenderResponse.Duration,
|
|
DurationUnit: tenderResponse.DurationUnit,
|
|
TenderDeadline: tenderResponse.TenderDeadline,
|
|
SubmissionDeadline: tenderResponse.SubmissionDeadline,
|
|
CountryCode: tenderResponse.CountryCode,
|
|
BuyerOrganization: &OrganizationResponse{Name: tenderResponse.BuyerOrganization.Name},
|
|
Status: string(tenderResponse.Status),
|
|
}
|
|
|
|
s.logger.Info("Tender approval with tender details retrieved successfully", map[string]interface{}{
|
|
"tender_approval_id": tenderApproval.ID,
|
|
"tender_id": tenderApproval.TenderID,
|
|
"company_id": tenderApproval.CompanyID,
|
|
})
|
|
|
|
return tenderApproval.ToResponseWithTender(tenderDetails), nil
|
|
}
|
|
|
|
// GetTenderApprovalsByTenderID retrieves all tender approvals for a specific tender
|
|
func (s *tenderApprovalService) GetTenderApprovalsByTenderID(ctx context.Context, tenderID string) ([]*TenderApproval, error) {
|
|
tenderApprovals, err := s.repository.GetByTenderID(ctx, tenderID)
|
|
if err != nil {
|
|
s.logger.Error("Failed to get tender approvals by tender ID", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"tender_id": tenderID,
|
|
})
|
|
return nil, err
|
|
}
|
|
|
|
s.logger.Info("Tender approvals retrieved successfully", map[string]interface{}{
|
|
"tender_id": tenderID,
|
|
"count": len(tenderApprovals),
|
|
})
|
|
|
|
return tenderApprovals, nil
|
|
}
|
|
|
|
// GetTenderApprovalsByCompanyID retrieves all tender approvals for a specific company with optional filters
|
|
func (s *tenderApprovalService) GetTenderApprovalsByCompanyID(ctx context.Context, companyID string, submissionMode *SubmissionMode, status *ApprovalStatus, from *int64, to *int64, limit int, offset int) ([]*TenderApproval, int64, error) {
|
|
tenderApprovals, total, err := s.repository.GetByCompanyID(ctx, companyID, submissionMode, status, limit, offset, from, to)
|
|
if err != nil {
|
|
s.logger.Error("Failed to get tender approvals by company ID", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"company_id": companyID,
|
|
})
|
|
return nil, 0, err
|
|
}
|
|
|
|
s.logger.Info("Tender approvals retrieved successfully", map[string]interface{}{
|
|
"company_id": companyID,
|
|
"count": len(tenderApprovals),
|
|
})
|
|
|
|
return tenderApprovals, total, nil
|
|
}
|
|
|
|
// GetTenderApprovalsByCompanyIDWithTender retrieves all tender approvals for a specific company with tender details
|
|
func (s *tenderApprovalService) GetTenderApprovalsByCompanyIDWithTender(ctx context.Context, companyID string, submissionMode *SubmissionMode, status *ApprovalStatus, from *int64, to *int64, limit, offset int) (*TenderApprovalWithTenderListResponse, error) {
|
|
tenderApprovals, total, err := s.repository.GetByCompanyID(ctx, companyID, submissionMode, status, limit, offset, from, to)
|
|
if err != nil {
|
|
s.logger.Error("Failed to get tender approvals by company ID", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"company_id": companyID,
|
|
})
|
|
return nil, err
|
|
}
|
|
|
|
// Convert to response with tender details
|
|
responses := &TenderApprovalWithTenderListResponse{
|
|
Approvals: []*TenderApprovalWithTenderResponse{},
|
|
Metadata: &response.Meta{},
|
|
}
|
|
for _, approval := range tenderApprovals {
|
|
// Get tender details
|
|
tenderResponse, err := s.tenderService.GetByID(ctx, approval.TenderID)
|
|
if err != nil {
|
|
s.logger.Warn("Failed to get tender details", map[string]interface{}{
|
|
"tender_id": approval.TenderID,
|
|
"error": err.Error(),
|
|
})
|
|
// Continue without tender details rather than failing completely
|
|
responses.Approvals = append(responses.Approvals, approval.ToResponseWithTender(nil))
|
|
continue
|
|
}
|
|
|
|
// Convert tender response to tender details
|
|
tenderDetails := &TenderDetails{
|
|
ID: tenderResponse.ID,
|
|
Title: tenderResponse.Title,
|
|
Description: tenderResponse.Description,
|
|
EstimatedValue: tenderResponse.EstimatedValue,
|
|
Currency: tenderResponse.Currency,
|
|
TenderDeadline: tenderResponse.TenderDeadline,
|
|
SubmissionDeadline: tenderResponse.SubmissionDeadline,
|
|
CountryCode: tenderResponse.CountryCode,
|
|
BuyerOrganization: &OrganizationResponse{Name: tenderResponse.BuyerOrganization.Name},
|
|
Status: string(tenderResponse.Status),
|
|
}
|
|
|
|
responses.Approvals = append(responses.Approvals, approval.ToResponseWithTender(tenderDetails))
|
|
}
|
|
|
|
responses.Metadata = &response.Meta{
|
|
Total: int(total),
|
|
Limit: limit,
|
|
Offset: offset,
|
|
Pages: int(math.Ceil(float64(total) / float64(limit))),
|
|
Page: int(math.Ceil(float64(offset) / float64(limit))),
|
|
}
|
|
|
|
s.logger.Info("Tender approvals with tender details retrieved successfully", map[string]interface{}{
|
|
"company_id": companyID,
|
|
"count": len(responses.Approvals),
|
|
})
|
|
|
|
return responses, nil
|
|
}
|
|
|
|
// GetTenderApprovalsByIDs retrieves multiple tender approvals by their IDs
|
|
func (s *tenderApprovalService) GetTenderApprovalsByIDs(ctx context.Context, ids []string) ([]*TenderApproval, error) {
|
|
if len(ids) == 0 {
|
|
return []*TenderApproval{}, nil
|
|
}
|
|
|
|
tenderApprovals, err := s.repository.GetByIDs(ctx, ids)
|
|
if err != nil {
|
|
s.logger.Error("Failed to get tender approvals by IDs", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"ids": ids,
|
|
})
|
|
return nil, err
|
|
}
|
|
|
|
s.logger.Info("Tender approvals retrieved successfully", map[string]interface{}{
|
|
"requested_count": len(ids),
|
|
"found_count": len(tenderApprovals),
|
|
})
|
|
|
|
return tenderApprovals, nil
|
|
}
|
|
|
|
// UpdateTenderApproval updates a tender approval
|
|
func (s *tenderApprovalService) UpdateTenderApproval(ctx context.Context, id string, form *UpdateTenderApprovalForm) (*TenderApproval, error) {
|
|
// Get existing tender approval
|
|
tenderApproval, err := s.repository.GetByID(ctx, id)
|
|
if err != nil {
|
|
s.logger.Error("Failed to get tender approval for update", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"tender_approval_id": id,
|
|
})
|
|
return nil, errors.New("tender approval not found")
|
|
}
|
|
|
|
// Update fields if provided
|
|
if form.SubmissionMode != nil {
|
|
tenderApproval.SubmissionMode = SubmissionMode(*form.SubmissionMode)
|
|
}
|
|
|
|
if form.Status != nil {
|
|
tenderApproval.Status = ApprovalStatus(*form.Status)
|
|
}
|
|
|
|
// Set updated timestamp
|
|
tenderApproval.UpdatedAt = time.Now().Unix()
|
|
|
|
// Update in database
|
|
err = s.repository.Update(ctx, tenderApproval)
|
|
if err != nil {
|
|
s.logger.Error("Failed to update tender approval", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"tender_approval_id": id,
|
|
})
|
|
return nil, errors.New("failed to update tender approval")
|
|
}
|
|
|
|
s.logger.Info("Tender approval updated successfully", map[string]interface{}{
|
|
"tender_approval_id": tenderApproval.ID,
|
|
"tender_id": tenderApproval.TenderID,
|
|
"company_id": tenderApproval.CompanyID,
|
|
})
|
|
|
|
return tenderApproval, nil
|
|
}
|
|
|
|
// UpdateTenderApprovalWithTender updates a tender approval and returns it with tender details
|
|
func (s *tenderApprovalService) UpdateTenderApprovalWithTender(ctx context.Context, id string, form *UpdateTenderApprovalForm) (*TenderApprovalWithTenderResponse, error) {
|
|
// Get existing tender approval
|
|
tenderApproval, err := s.repository.GetByID(ctx, id)
|
|
if err != nil {
|
|
s.logger.Error("Failed to get tender approval for update", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"tender_approval_id": id,
|
|
})
|
|
return nil, errors.New("tender approval not found")
|
|
}
|
|
|
|
// Update fields if provided
|
|
if form.SubmissionMode != nil {
|
|
tenderApproval.SubmissionMode = SubmissionMode(*form.SubmissionMode)
|
|
}
|
|
|
|
if form.Status != nil {
|
|
tenderApproval.Status = ApprovalStatus(*form.Status)
|
|
}
|
|
|
|
// Set updated timestamp
|
|
tenderApproval.UpdatedAt = time.Now().Unix()
|
|
|
|
// Update in database
|
|
err = s.repository.Update(ctx, tenderApproval)
|
|
if err != nil {
|
|
s.logger.Error("Failed to update tender approval", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"tender_approval_id": id,
|
|
})
|
|
return nil, errors.New("failed to update tender approval")
|
|
}
|
|
|
|
// Get tender details
|
|
tenderResponse, err := s.tenderService.GetByID(ctx, tenderApproval.TenderID)
|
|
if err != nil {
|
|
s.logger.Warn("Failed to get tender details", map[string]interface{}{
|
|
"tender_id": tenderApproval.TenderID,
|
|
"error": err.Error(),
|
|
})
|
|
// Return approval without tender details rather than failing completely
|
|
return tenderApproval.ToResponseWithTender(nil), nil
|
|
}
|
|
|
|
// Convert tender response to tender details
|
|
tenderDetails := &TenderDetails{
|
|
ID: tenderResponse.ID,
|
|
NoticePublicationID: tenderResponse.NoticePublicationID,
|
|
PublicationDate: tenderResponse.PublicationDate,
|
|
Title: tenderResponse.Title,
|
|
Description: tenderResponse.Description,
|
|
ProcurementTypeCode: tenderResponse.ProcurementTypeCode,
|
|
ProcedureCode: tenderResponse.ProcedureCode,
|
|
EstimatedValue: tenderResponse.EstimatedValue,
|
|
Currency: tenderResponse.Currency,
|
|
Duration: tenderResponse.Duration,
|
|
DurationUnit: tenderResponse.DurationUnit,
|
|
TenderDeadline: tenderResponse.TenderDeadline,
|
|
SubmissionDeadline: tenderResponse.SubmissionDeadline,
|
|
CountryCode: tenderResponse.CountryCode,
|
|
BuyerOrganization: &OrganizationResponse{Name: tenderResponse.BuyerOrganization.Name},
|
|
Status: string(tenderResponse.Status),
|
|
}
|
|
|
|
s.logger.Info("Tender approval updated successfully", map[string]interface{}{
|
|
"tender_approval_id": tenderApproval.ID,
|
|
"tender_id": tenderApproval.TenderID,
|
|
"company_id": tenderApproval.CompanyID,
|
|
})
|
|
|
|
return tenderApproval.ToResponseWithTender(tenderDetails), nil
|
|
}
|
|
|
|
// DeleteTenderApproval deletes a tender approval
|
|
func (s *tenderApprovalService) DeleteTenderApproval(ctx context.Context, id string) error {
|
|
// Get tender approval to check if exists
|
|
tenderApproval, err := s.repository.GetByID(ctx, id)
|
|
if err != nil {
|
|
return errors.New("tender approval not found")
|
|
}
|
|
|
|
// Delete tender approval
|
|
err = s.repository.Delete(ctx, id)
|
|
if err != nil {
|
|
s.logger.Error("Failed to delete tender approval", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"tender_approval_id": id,
|
|
})
|
|
return errors.New("failed to delete tender approval")
|
|
}
|
|
|
|
s.logger.Info("Tender approval deleted successfully", map[string]interface{}{
|
|
"tender_approval_id": id,
|
|
"tender_id": tenderApproval.TenderID,
|
|
"company_id": tenderApproval.CompanyID,
|
|
})
|
|
|
|
return nil
|
|
}
|
|
|
|
// ListTenderApprovals lists tender approvals with search and filters
|
|
func (s *tenderApprovalService) ListTenderApprovals(ctx context.Context, form *ListTenderApprovalsForm) (*TenderApprovalListResponse, error) {
|
|
// Set defaults
|
|
limit := 20
|
|
if form.Limit != nil {
|
|
limit = *form.Limit
|
|
}
|
|
|
|
offset := 0
|
|
if form.Offset != nil {
|
|
offset = *form.Offset
|
|
}
|
|
|
|
sortBy := "created_at"
|
|
if form.SortBy != nil {
|
|
sortBy = *form.SortBy
|
|
}
|
|
|
|
sortOrder := "desc"
|
|
if form.SortOrder != nil {
|
|
sortOrder = *form.SortOrder
|
|
}
|
|
|
|
// Convert status strings to ApprovalStatus
|
|
var status []ApprovalStatus
|
|
if form.Status != nil {
|
|
status = make([]ApprovalStatus, len(form.Status))
|
|
for i, s := range form.Status {
|
|
status[i] = ApprovalStatus(s)
|
|
}
|
|
}
|
|
|
|
// Convert submission mode strings to SubmissionMode
|
|
var submissionMode []SubmissionMode
|
|
if form.SubmissionMode != nil {
|
|
submissionMode = make([]SubmissionMode, len(form.SubmissionMode))
|
|
for i, m := range form.SubmissionMode {
|
|
submissionMode[i] = SubmissionMode(m)
|
|
}
|
|
}
|
|
|
|
// Get tender approvals
|
|
tenderApprovals, err := s.repository.Search(ctx, form.TenderID, form.CompanyID, status, submissionMode, form.CreatedAtFrom, form.CreatedAtTo, limit, offset, sortBy, sortOrder)
|
|
if err != nil {
|
|
s.logger.Error("Failed to list tender approvals", map[string]interface{}{
|
|
"error": err.Error(),
|
|
})
|
|
return nil, errors.New("failed to list tender approvals")
|
|
}
|
|
|
|
// Convert to responses
|
|
var tenderApprovalResponses []*TenderApprovalResponse
|
|
for _, approval := range tenderApprovals {
|
|
tenderApprovalResponses = append(tenderApprovalResponses, approval.ToResponse())
|
|
}
|
|
|
|
// TODO: Implement proper count for pagination metadata
|
|
total := int64(len(tenderApprovals)) // This should be a separate count query
|
|
totalPages := (total + int64(limit) - 1) / int64(limit)
|
|
|
|
return &TenderApprovalListResponse{
|
|
TenderApprovals: tenderApprovalResponses,
|
|
Total: total,
|
|
Limit: limit,
|
|
Offset: offset,
|
|
TotalPages: int(totalPages),
|
|
}, nil
|
|
}
|
|
|
|
// SearchTenderApprovals searches tender approvals with advanced filters
|
|
func (s *tenderApprovalService) SearchTenderApprovals(ctx context.Context, form *TenderApprovalSearchForm) (*TenderApprovalListResponse, error) {
|
|
// Set defaults
|
|
limit := 20
|
|
if form.Limit != nil {
|
|
limit = *form.Limit
|
|
}
|
|
|
|
offset := 0
|
|
if form.Offset != nil {
|
|
offset = *form.Offset
|
|
}
|
|
|
|
// Convert status strings to ApprovalStatus
|
|
var status []ApprovalStatus
|
|
if form.Status != nil {
|
|
status = make([]ApprovalStatus, len(form.Status))
|
|
for i, s := range form.Status {
|
|
status[i] = ApprovalStatus(s)
|
|
}
|
|
}
|
|
|
|
// Convert submission mode strings to SubmissionMode
|
|
var submissionMode []SubmissionMode
|
|
if form.SubmissionMode != nil {
|
|
submissionMode = make([]SubmissionMode, len(form.SubmissionMode))
|
|
for i, m := range form.SubmissionMode {
|
|
submissionMode[i] = SubmissionMode(m)
|
|
}
|
|
}
|
|
|
|
// Get tender approvals using search
|
|
tenderApprovals, err := s.repository.Search(ctx, form.TenderID, form.CompanyID, status, submissionMode, form.CreatedAtFrom, form.CreatedAtTo, limit, offset, "created_at", "desc")
|
|
if err != nil {
|
|
s.logger.Error("Failed to search tender approvals", map[string]interface{}{
|
|
"error": err.Error(),
|
|
})
|
|
return nil, errors.New("failed to search tender approvals")
|
|
}
|
|
|
|
// Convert to responses
|
|
var tenderApprovalResponses []*TenderApprovalResponse
|
|
for _, approval := range tenderApprovals {
|
|
tenderApprovalResponses = append(tenderApprovalResponses, approval.ToResponse())
|
|
}
|
|
|
|
// TODO: Implement proper count for pagination metadata
|
|
total := int64(len(tenderApprovals)) // This should be a separate count query
|
|
totalPages := (total + int64(limit) - 1) / int64(limit)
|
|
|
|
return &TenderApprovalListResponse{
|
|
TenderApprovals: tenderApprovalResponses,
|
|
Total: total,
|
|
Limit: limit,
|
|
Offset: offset,
|
|
TotalPages: int(totalPages),
|
|
}, nil
|
|
}
|
|
|
|
// SearchTenderApprovalsByCriteria searches tender approvals using search criteria
|
|
func (s *tenderApprovalService) SearchTenderApprovalsByCriteria(ctx context.Context, criteria *TenderApprovalSearchCriteria) ([]*TenderApproval, error) {
|
|
tenderApprovals, err := s.repository.SearchByCriteria(ctx, criteria)
|
|
if err != nil {
|
|
s.logger.Error("Failed to search tender approvals by criteria", map[string]interface{}{
|
|
"error": err.Error(),
|
|
})
|
|
return nil, errors.New("failed to search tender approvals by criteria")
|
|
}
|
|
|
|
return tenderApprovals, nil
|
|
}
|
|
|
|
// GetTenderApprovalStats returns tender approval statistics
|
|
func (s *tenderApprovalService) GetTenderApprovalStats(ctx context.Context) (*TenderApprovalStatsResponse, error) {
|
|
stats, err := s.repository.GetTenderApprovalStats(ctx)
|
|
if err != nil {
|
|
s.logger.Error("Failed to get tender approval stats", map[string]interface{}{
|
|
"error": err.Error(),
|
|
})
|
|
return nil, errors.New("failed to get tender approval statistics")
|
|
}
|
|
|
|
return stats, nil
|
|
}
|
|
|
|
// GetMultipleCompanyTenderApprovalStats returns tender approval statistics for multiple companies
|
|
func (s *tenderApprovalService) GetMultipleCompanyTenderApprovalStats(ctx context.Context, companyIDs []string) ([]*CompanyTenderApprovalStatsResponse, error) {
|
|
if len(companyIDs) == 0 {
|
|
return []*CompanyTenderApprovalStatsResponse{}, nil
|
|
}
|
|
|
|
var allStats []*CompanyTenderApprovalStatsResponse
|
|
for _, companyID := range companyIDs {
|
|
stats, err := s.GetCompanyTenderApprovalStats(ctx, companyID)
|
|
if err != nil {
|
|
s.logger.Warn("Failed to get stats for company", map[string]interface{}{
|
|
"company_id": companyID,
|
|
"error": err.Error(),
|
|
})
|
|
// Continue with other companies rather than failing completely
|
|
continue
|
|
}
|
|
allStats = append(allStats, stats)
|
|
}
|
|
|
|
s.logger.Info("Multiple company tender approval stats retrieved successfully", map[string]interface{}{
|
|
"requested_count": len(companyIDs),
|
|
"retrieved_count": len(allStats),
|
|
})
|
|
|
|
return allStats, nil
|
|
}
|
|
|
|
// GetAggregatedCompanyStats returns aggregated statistics across all companies
|
|
func (s *tenderApprovalService) GetAggregatedCompanyStats(ctx context.Context) (*AggregatedCompanyStatsResponse, error) {
|
|
// Get general stats first
|
|
generalStats, err := s.GetTenderApprovalStats(ctx)
|
|
if err != nil {
|
|
s.logger.Error("Failed to get general tender approval stats", map[string]interface{}{
|
|
"error": err.Error(),
|
|
})
|
|
return nil, errors.New("failed to get general tender approval statistics")
|
|
}
|
|
|
|
// For now, we'll return basic aggregated stats
|
|
// In a real implementation, you might want to get stats for all companies
|
|
// and aggregate them properly
|
|
stats := &AggregatedCompanyStatsResponse{
|
|
TotalCompanies: 1, // This should be calculated from actual company data
|
|
TotalApprovals: generalStats.TotalApprovals,
|
|
TotalSubmittedTenders: generalStats.SubmittedTenders,
|
|
TotalRejectedTenders: generalStats.RejectedTenders,
|
|
CompanyStats: []*CompanyTenderApprovalStatsResponse{}, // Empty for now
|
|
LastUpdated: time.Now().Unix(),
|
|
}
|
|
|
|
s.logger.Info("Aggregated company stats retrieved successfully", map[string]interface{}{
|
|
"total_approvals": stats.TotalApprovals,
|
|
"total_companies": stats.TotalCompanies,
|
|
})
|
|
|
|
return stats, nil
|
|
}
|
|
|
|
// GetTenderApprovalsByStatus retrieves tender approvals by status with pagination
|
|
func (s *tenderApprovalService) GetTenderApprovalsByStatus(ctx context.Context, status ApprovalStatus, limit, offset int) ([]*TenderApproval, int64, error) {
|
|
// Use search method with status filter
|
|
tenderApprovals, err := s.repository.Search(ctx, nil, nil, []ApprovalStatus{status}, nil, nil, nil, limit, offset, "created_at", "desc")
|
|
if err != nil {
|
|
s.logger.Error("Failed to get tender approvals by status", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"status": status,
|
|
})
|
|
return nil, 0, errors.New("failed to get tender approvals by status")
|
|
}
|
|
|
|
// Get total count
|
|
total, err := s.repository.CountByStatus(ctx, status)
|
|
if err != nil {
|
|
s.logger.Error("Failed to count tender approvals by status", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"status": status,
|
|
})
|
|
return tenderApprovals, 0, nil // Return approvals even if count fails
|
|
}
|
|
|
|
return tenderApprovals, total, nil
|
|
}
|
|
|
|
// GetTenderApprovalsBySubmissionMode retrieves tender approvals by submission mode with pagination
|
|
func (s *tenderApprovalService) GetTenderApprovalsBySubmissionMode(ctx context.Context, submissionMode SubmissionMode, limit, offset int) ([]*TenderApproval, int64, error) {
|
|
// Use search method with submission mode filter
|
|
tenderApprovals, err := s.repository.Search(ctx, nil, nil, nil, []SubmissionMode{submissionMode}, nil, nil, limit, offset, "created_at", "desc")
|
|
if err != nil {
|
|
s.logger.Error("Failed to get tender approvals by submission mode", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"submission_mode": submissionMode,
|
|
})
|
|
return nil, 0, errors.New("failed to get tender approvals by submission mode")
|
|
}
|
|
|
|
// Get total count
|
|
total, err := s.repository.CountBySubmissionMode(ctx, submissionMode)
|
|
if err != nil {
|
|
s.logger.Error("Failed to count tender approvals by submission mode", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"submission_mode": submissionMode,
|
|
})
|
|
return tenderApprovals, 0, nil
|
|
}
|
|
|
|
return tenderApprovals, total, nil
|
|
}
|
|
|
|
// GetTenderApprovalsByStatusWithTender retrieves tender approvals by status with tender details
|
|
func (s *tenderApprovalService) GetTenderApprovalsByStatusWithTender(ctx context.Context, status ApprovalStatus, limit, offset int) ([]*TenderApprovalWithTenderResponse, int64, error) {
|
|
tenderApprovals, total, err := s.GetTenderApprovalsByStatus(ctx, status, limit, offset)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
|
|
// Convert to response with tender details
|
|
var responses []*TenderApprovalWithTenderResponse
|
|
for _, approval := range tenderApprovals {
|
|
// Get tender details
|
|
tenderResponse, err := s.tenderService.GetByID(ctx, approval.TenderID)
|
|
if err != nil {
|
|
s.logger.Warn("Failed to get tender details", map[string]interface{}{
|
|
"tender_id": approval.TenderID,
|
|
"error": err.Error(),
|
|
})
|
|
// Continue without tender details rather than failing completely
|
|
responses = append(responses, approval.ToResponseWithTender(nil))
|
|
continue
|
|
}
|
|
|
|
// Convert tender response to tender details
|
|
tenderDetails := &TenderDetails{
|
|
ID: tenderResponse.ID,
|
|
NoticePublicationID: tenderResponse.NoticePublicationID,
|
|
PublicationDate: tenderResponse.PublicationDate,
|
|
Title: tenderResponse.Title,
|
|
Description: tenderResponse.Description,
|
|
ProcurementTypeCode: tenderResponse.ProcurementTypeCode,
|
|
ProcedureCode: tenderResponse.ProcurementTypeCode,
|
|
EstimatedValue: tenderResponse.EstimatedValue,
|
|
Currency: tenderResponse.Currency,
|
|
Duration: tenderResponse.Duration,
|
|
DurationUnit: tenderResponse.DurationUnit,
|
|
TenderDeadline: tenderResponse.TenderDeadline,
|
|
SubmissionDeadline: tenderResponse.SubmissionDeadline,
|
|
CountryCode: tenderResponse.CountryCode,
|
|
BuyerOrganization: &OrganizationResponse{Name: tenderResponse.BuyerOrganization.Name},
|
|
Status: string(tenderResponse.Status),
|
|
}
|
|
|
|
responses = append(responses, approval.ToResponseWithTender(tenderDetails))
|
|
}
|
|
|
|
return responses, total, nil
|
|
}
|
|
|
|
// GetTenderApprovalsBySubmissionModeWithTender retrieves tender approvals by submission mode with tender details
|
|
func (s *tenderApprovalService) GetTenderApprovalsBySubmissionModeWithTender(ctx context.Context, submissionMode SubmissionMode, limit, offset int) ([]*TenderApprovalWithTenderResponse, int64, error) {
|
|
tenderApprovals, total, err := s.GetTenderApprovalsBySubmissionMode(ctx, submissionMode, limit, offset)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
|
|
// Convert to response with tender details
|
|
var responses []*TenderApprovalWithTenderResponse
|
|
for _, approval := range tenderApprovals {
|
|
// Get tender details
|
|
tenderResponse, err := s.tenderService.GetByID(ctx, approval.TenderID)
|
|
if err != nil {
|
|
s.logger.Warn("Failed to get tender details", map[string]interface{}{
|
|
"tender_id": approval.TenderID,
|
|
"error": err.Error(),
|
|
})
|
|
// Continue without tender details rather than failing completely
|
|
responses = append(responses, approval.ToResponseWithTender(nil))
|
|
continue
|
|
}
|
|
|
|
// Convert tender response to tender details
|
|
tenderDetails := &TenderDetails{
|
|
ID: tenderResponse.ID,
|
|
NoticePublicationID: tenderResponse.NoticePublicationID,
|
|
PublicationDate: tenderResponse.PublicationDate,
|
|
Title: tenderResponse.Title,
|
|
Description: tenderResponse.Description,
|
|
ProcurementTypeCode: tenderResponse.ProcurementTypeCode,
|
|
ProcedureCode: tenderResponse.ProcedureCode,
|
|
EstimatedValue: tenderResponse.EstimatedValue,
|
|
Currency: tenderResponse.Currency,
|
|
Duration: tenderResponse.Duration,
|
|
DurationUnit: tenderResponse.DurationUnit,
|
|
TenderDeadline: tenderResponse.TenderDeadline,
|
|
SubmissionDeadline: tenderResponse.SubmissionDeadline,
|
|
CountryCode: tenderResponse.CountryCode,
|
|
BuyerOrganization: &OrganizationResponse{Name: tenderResponse.BuyerOrganization.Name},
|
|
Status: string(tenderResponse.Status),
|
|
}
|
|
|
|
responses = append(responses, approval.ToResponseWithTender(tenderDetails))
|
|
}
|
|
|
|
return responses, total, nil
|
|
}
|
|
|
|
// ListTenderApprovalsWithTender lists tender approvals with search and filters, including tender details
|
|
func (s *tenderApprovalService) ListTenderApprovalsWithTender(ctx context.Context, form *ListTenderApprovalsForm) (*TenderApprovalListResponseWithTender, error) {
|
|
// Set defaults
|
|
limit := 20
|
|
if form.Limit != nil {
|
|
limit = *form.Limit
|
|
}
|
|
|
|
offset := 0
|
|
if form.Offset != nil {
|
|
offset = *form.Offset
|
|
}
|
|
|
|
sortBy := "created_at"
|
|
if form.SortBy != nil {
|
|
sortBy = *form.SortBy
|
|
}
|
|
|
|
sortOrder := "desc"
|
|
if form.SortOrder != nil {
|
|
sortOrder = *form.SortOrder
|
|
}
|
|
|
|
// Convert status strings to ApprovalStatus
|
|
var status []ApprovalStatus
|
|
if form.Status != nil {
|
|
for _, s := range form.Status {
|
|
status = append(status, ApprovalStatus(s))
|
|
}
|
|
}
|
|
|
|
// Convert submission mode strings to SubmissionMode
|
|
var submissionMode []SubmissionMode
|
|
if form.SubmissionMode != nil {
|
|
for _, sm := range form.SubmissionMode {
|
|
submissionMode = append(submissionMode, SubmissionMode(sm))
|
|
}
|
|
}
|
|
|
|
// Search tender approvals
|
|
tenderApprovals, err := s.repository.Search(ctx, form.TenderID, form.CompanyID, status, submissionMode, form.CreatedAtFrom, form.CreatedAtTo, limit, offset, sortBy, sortOrder)
|
|
if err != nil {
|
|
s.logger.Error("Failed to search tender approvals", map[string]interface{}{
|
|
"error": err.Error(),
|
|
})
|
|
return nil, errors.New("failed to search tender approvals")
|
|
}
|
|
|
|
// Get total count for pagination
|
|
total, err := s.repository.Count(ctx)
|
|
if err != nil {
|
|
s.logger.Error("Failed to count tender approvals", map[string]interface{}{
|
|
"error": err.Error(),
|
|
})
|
|
total = int64(len(tenderApprovals)) // Use actual count if total count fails
|
|
}
|
|
|
|
// Convert to response with tender details
|
|
var responses []*TenderApprovalWithTenderResponse
|
|
for _, approval := range tenderApprovals {
|
|
// Get tender details
|
|
tenderResponse, err := s.tenderService.GetByID(ctx, approval.TenderID)
|
|
if err != nil {
|
|
s.logger.Warn("Failed to get tender details", map[string]interface{}{
|
|
"tender_id": approval.TenderID,
|
|
"error": err.Error(),
|
|
})
|
|
// Continue without tender details rather than failing completely
|
|
responses = append(responses, approval.ToResponseWithTender(nil))
|
|
continue
|
|
}
|
|
|
|
// Convert tender response to tender details
|
|
tenderDetails := &TenderDetails{
|
|
ID: tenderResponse.ID,
|
|
NoticePublicationID: tenderResponse.NoticePublicationID,
|
|
PublicationDate: tenderResponse.PublicationDate,
|
|
Title: tenderResponse.Title,
|
|
Description: tenderResponse.Description,
|
|
ProcurementTypeCode: tenderResponse.ProcurementTypeCode,
|
|
ProcedureCode: tenderResponse.ProcedureCode,
|
|
EstimatedValue: tenderResponse.EstimatedValue,
|
|
Currency: tenderResponse.Currency,
|
|
Duration: tenderResponse.Duration,
|
|
DurationUnit: tenderResponse.DurationUnit,
|
|
TenderDeadline: tenderResponse.TenderDeadline,
|
|
SubmissionDeadline: tenderResponse.SubmissionDeadline,
|
|
CountryCode: tenderResponse.CountryCode,
|
|
BuyerOrganization: &OrganizationResponse{Name: tenderResponse.BuyerOrganization.Name},
|
|
Status: string(tenderResponse.Status),
|
|
}
|
|
|
|
responses = append(responses, approval.ToResponseWithTender(tenderDetails))
|
|
}
|
|
|
|
// Calculate pagination metadata
|
|
pages := int(total) / limit
|
|
if int(total)%limit > 0 {
|
|
pages++
|
|
}
|
|
page := (offset / limit) + 1
|
|
|
|
meta := &TenderApprovalListResponseWithTender{
|
|
TenderApprovals: responses,
|
|
Metadata: &response.Meta{
|
|
Total: int(total),
|
|
Limit: limit,
|
|
Offset: offset,
|
|
Page: page,
|
|
Pages: pages,
|
|
},
|
|
}
|
|
|
|
return meta, nil
|
|
}
|
|
|
|
func (s *tenderApprovalService) ToggleTenderApproval(ctx context.Context, req *ToggleTenderApprovalForm, companyID string) (*TenderApprovalWithTenderResponse, error) {
|
|
existing, err := s.repository.GetByTenderAndCompany(ctx, req.TenderID, companyID)
|
|
if err != nil {
|
|
approval := &TenderApproval{
|
|
TenderID: req.TenderID,
|
|
CompanyID: companyID,
|
|
SubmissionMode: req.SubmissionMode,
|
|
Status: req.Status,
|
|
}
|
|
|
|
err = s.repository.Create(ctx, approval)
|
|
if err != nil {
|
|
s.logger.Error("Failed to create new tender approval", map[string]interface{}{
|
|
"tender_id": req.TenderID,
|
|
"company_id": companyID,
|
|
"status": req.Status,
|
|
"submission_mode": req.SubmissionMode,
|
|
"error": err.Error(),
|
|
})
|
|
return nil, err
|
|
}
|
|
|
|
return approval.ToResponseWithTender(nil), nil
|
|
}
|
|
|
|
if existing.Status == req.Status {
|
|
err = s.repository.Delete(ctx, existing.GetID())
|
|
if err != nil {
|
|
s.logger.Error("Failed to update tender approval", map[string]interface{}{
|
|
"error": err.Error(),
|
|
})
|
|
return nil, err
|
|
}
|
|
|
|
if req.Status == ApprovalStatusSubmitted {
|
|
existing.Status = "unsubmitted"
|
|
} else {
|
|
existing.Status = "unrejected"
|
|
}
|
|
|
|
return existing.ToResponseWithTender(nil), nil
|
|
} else if existing.Status == req.Status && existing.SubmissionMode != req.SubmissionMode {
|
|
existing.SubmissionMode = req.SubmissionMode
|
|
err = s.repository.Update(ctx, existing)
|
|
if err != nil {
|
|
s.logger.Error("Failed to update tender approval", map[string]interface{}{
|
|
"error": err.Error(),
|
|
})
|
|
return nil, err
|
|
}
|
|
|
|
return existing.ToResponseWithTender(nil), nil
|
|
}
|
|
|
|
if existing.Status == ApprovalStatusSubmitted {
|
|
existing.Reject()
|
|
} else {
|
|
existing.Submit()
|
|
}
|
|
|
|
err = s.repository.Update(ctx, existing)
|
|
if err != nil {
|
|
s.logger.Error("Failed to update tender approval", map[string]interface{}{
|
|
"error": err.Error(),
|
|
})
|
|
return nil, err
|
|
}
|
|
|
|
return existing.ToResponseWithTender(nil), nil
|
|
}
|
|
|
|
// GetCompanyTenderApprovalStats returns company-specific tender approval statistics
|
|
func (s *tenderApprovalService) GetCompanyTenderApprovalStats(ctx context.Context, companyID string) (*CompanyTenderApprovalStatsResponse, error) {
|
|
stats, err := s.repository.GetCompanyTenderApprovalStats(ctx, companyID)
|
|
if err != nil {
|
|
s.logger.Error("Failed to get company tender approval stats", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"company_id": companyID,
|
|
})
|
|
return nil, errors.New("failed to get company tender approval statistics")
|
|
}
|
|
|
|
s.logger.Info("Company tender approval stats retrieved successfully", map[string]interface{}{
|
|
"company_id": companyID,
|
|
"total_approvals": stats.TotalApprovals,
|
|
"submitted_tenders": stats.SubmittedTenders,
|
|
"rejected_tenders": stats.RejectedTenders,
|
|
"self_apply_count": stats.SelfApplyCount,
|
|
"partnership_count": stats.PartnershipCount,
|
|
})
|
|
|
|
return stats, nil
|
|
}
|