Add Tender Approval Management Functionality and API Enhancements
- Introduced a new tender approval management system, including the creation of tender approval entities, services, and repositories. - Implemented CRUD operations for tender approvals, allowing companies to approve or reject tenders. - Developed administrative and public API endpoints for listing, retrieving, and updating tender approvals with comprehensive filtering options. - Enhanced Swagger documentation to accurately reflect the new endpoints, including detailed descriptions, parameters, and response formats. - Updated existing routes to include tender approval management, ensuring adherence to Clean Architecture principles and maintaining a clear separation of concerns in the handler layer.
This commit is contained in:
@@ -0,0 +1,527 @@
|
||||
package tender_approval
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"tm/pkg/logger"
|
||||
)
|
||||
|
||||
// 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)
|
||||
GetTenderApprovalByTenderAndCompany(ctx context.Context, tenderID, companyID string) (*TenderApproval, error)
|
||||
GetTenderApprovalsByTenderID(ctx context.Context, tenderID string) ([]*TenderApproval, error)
|
||||
GetTenderApprovalsByCompanyID(ctx context.Context, companyID string, submissionMode *SubmissionMode, status *ApprovalStatus) ([]*TenderApproval, error)
|
||||
GetTenderApprovalsByIDs(ctx context.Context, ids []string) ([]*TenderApproval, error)
|
||||
UpdateTenderApproval(ctx context.Context, id string, form *UpdateTenderApprovalForm) (*TenderApproval, error)
|
||||
DeleteTenderApproval(ctx context.Context, id string) error
|
||||
|
||||
// Tender approval listing and search
|
||||
ListTenderApprovals(ctx context.Context, form *ListTenderApprovalsForm) (*TenderApprovalListResponse, error)
|
||||
SearchTenderApprovals(ctx context.Context, form *TenderApprovalSearchForm) (*TenderApprovalListResponse, error)
|
||||
SearchTenderApprovalsByCriteria(ctx context.Context, criteria *TenderApprovalSearchCriteria) ([]*TenderApproval, error)
|
||||
|
||||
// Business operations
|
||||
ApproveTender(ctx context.Context, id string, form *ApproveTenderForm) (*TenderApproval, error)
|
||||
RejectTender(ctx context.Context, id string, form *RejectTenderForm) (*TenderApproval, error)
|
||||
|
||||
// Statistics and analytics
|
||||
GetTenderApprovalStats(ctx context.Context) (*TenderApprovalStatsResponse, error)
|
||||
|
||||
// Search and filtering helpers
|
||||
GetTenderApprovalsByStatus(ctx context.Context, status ApprovalStatus, limit, offset int) ([]*TenderApproval, int64, error)
|
||||
GetTenderApprovalsBySubmissionMode(ctx context.Context, submissionMode SubmissionMode, limit, offset int) ([]*TenderApproval, int64, error)
|
||||
}
|
||||
|
||||
// tenderApprovalService implements the Service interface
|
||||
type tenderApprovalService struct {
|
||||
repository Repository
|
||||
logger logger.Logger
|
||||
}
|
||||
|
||||
// NewTenderApprovalService creates a new tender approval service
|
||||
func NewTenderApprovalService(repository Repository, logger logger.Logger) Service {
|
||||
return &tenderApprovalService{
|
||||
repository: repository,
|
||||
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
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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) ([]*TenderApproval, error) {
|
||||
tenderApprovals, err := s.repository.GetByCompanyID(ctx, companyID, submissionMode, status)
|
||||
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
|
||||
}
|
||||
|
||||
s.logger.Info("Tender approvals retrieved successfully", map[string]interface{}{
|
||||
"company_id": companyID,
|
||||
"count": len(tenderApprovals),
|
||||
})
|
||||
|
||||
return tenderApprovals, 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
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// ApproveTender approves a tender
|
||||
func (s *tenderApprovalService) ApproveTender(ctx context.Context, id string, form *ApproveTenderForm) (*TenderApproval, error) {
|
||||
// Get tender approval to check if exists
|
||||
tenderApproval, err := s.repository.GetByID(ctx, id)
|
||||
if err != nil {
|
||||
return nil, errors.New("tender approval not found")
|
||||
}
|
||||
|
||||
// Approve the tender
|
||||
tenderApproval.Approve(form.Notes)
|
||||
|
||||
// Update in database
|
||||
err = s.repository.Update(ctx, tenderApproval)
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to approve tender", map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
"tender_approval_id": id,
|
||||
})
|
||||
return nil, errors.New("failed to approve tender")
|
||||
}
|
||||
|
||||
s.logger.Info("Tender approved successfully", map[string]interface{}{
|
||||
"tender_approval_id": tenderApproval.ID,
|
||||
"tender_id": tenderApproval.TenderID,
|
||||
"company_id": tenderApproval.CompanyID,
|
||||
})
|
||||
|
||||
return tenderApproval, nil
|
||||
}
|
||||
|
||||
// RejectTender rejects a tender
|
||||
func (s *tenderApprovalService) RejectTender(ctx context.Context, id string, form *RejectTenderForm) (*TenderApproval, error) {
|
||||
// Get tender approval to check if exists
|
||||
tenderApproval, err := s.repository.GetByID(ctx, id)
|
||||
if err != nil {
|
||||
return nil, errors.New("tender approval not found")
|
||||
}
|
||||
|
||||
// Reject the tender
|
||||
tenderApproval.Reject(form.Reason)
|
||||
|
||||
// Update in database
|
||||
err = s.repository.Update(ctx, tenderApproval)
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to reject tender", map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
"tender_approval_id": id,
|
||||
})
|
||||
return nil, errors.New("failed to reject tender")
|
||||
}
|
||||
|
||||
s.logger.Info("Tender rejected successfully", map[string]interface{}{
|
||||
"tender_approval_id": tenderApproval.ID,
|
||||
"tender_id": tenderApproval.TenderID,
|
||||
"company_id": tenderApproval.CompanyID,
|
||||
"reason": form.Reason,
|
||||
})
|
||||
|
||||
return tenderApproval, 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
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
Reference in New Issue
Block a user