0cce9ef1b5
- Introduced the `tender_submission` domain, including entity, repository, service, and handler implementations for managing tender submissions. - Added new routes for both admin and public access to tender submissions, allowing for listing, ensuring, and retrieving submissions by ID and tender. - Enhanced the tender approval service to synchronize submission workflows with approval changes, ensuring proper state management. - Implemented validation and response structures for tender submission operations, improving API consistency and usability. - Added unit tests for tender submission status transitions and workflow logic, ensuring robust functionality. This update enhances the tender management system by providing comprehensive support for tender submissions, improving overall workflow and user experience.
111 lines
3.3 KiB
Go
111 lines
3.3 KiB
Go
package tender_submission
|
|
|
|
import (
|
|
"context"
|
|
|
|
"tm/internal/tender"
|
|
)
|
|
|
|
func listTenderDetailsFromResponse(resp *tender.TenderResponse) *TenderDetails {
|
|
if resp == nil {
|
|
return nil
|
|
}
|
|
|
|
details := &TenderDetails{
|
|
ID: resp.ID,
|
|
Title: resp.Title,
|
|
Description: resp.Description,
|
|
EstimatedValue: resp.EstimatedValue,
|
|
Currency: resp.Currency,
|
|
TenderDeadline: resp.TenderDeadline,
|
|
SubmissionDeadline: resp.SubmissionDeadline,
|
|
CountryCode: resp.CountryCode,
|
|
Status: string(resp.Status),
|
|
}
|
|
if resp.BuyerOrganization != nil && resp.BuyerOrganization.Name != "" {
|
|
details.BuyerOrganization = &OrganizationResponse{Name: resp.BuyerOrganization.Name}
|
|
}
|
|
return details
|
|
}
|
|
|
|
func fullTenderDetailsFromResponse(resp *tender.TenderResponse) *TenderDetails {
|
|
if resp == nil {
|
|
return nil
|
|
}
|
|
|
|
details := &TenderDetails{
|
|
ID: resp.ID,
|
|
NoticePublicationID: resp.NoticePublicationID,
|
|
PublicationDate: resp.PublicationDate,
|
|
Title: resp.Title,
|
|
Description: resp.Description,
|
|
ProcurementTypeCode: resp.ProcurementTypeCode,
|
|
ProcedureCode: resp.ProcedureCode,
|
|
EstimatedValue: resp.EstimatedValue,
|
|
Currency: resp.Currency,
|
|
Duration: resp.Duration,
|
|
DurationUnit: resp.DurationUnit,
|
|
TenderDeadline: resp.TenderDeadline,
|
|
SubmissionDeadline: resp.SubmissionDeadline,
|
|
CountryCode: resp.CountryCode,
|
|
Status: string(resp.Status),
|
|
}
|
|
if resp.BuyerOrganization != nil && resp.BuyerOrganization.Name != "" {
|
|
details.BuyerOrganization = &OrganizationResponse{Name: resp.BuyerOrganization.Name}
|
|
}
|
|
return details
|
|
}
|
|
|
|
func uniqueTenderIDs(submissions []*TenderSubmission) []string {
|
|
if len(submissions) == 0 {
|
|
return nil
|
|
}
|
|
|
|
seen := make(map[string]struct{}, len(submissions))
|
|
ids := make([]string, 0, len(submissions))
|
|
for _, submission := range submissions {
|
|
if submission == nil || submission.TenderID == "" {
|
|
continue
|
|
}
|
|
if _, ok := seen[submission.TenderID]; ok {
|
|
continue
|
|
}
|
|
seen[submission.TenderID] = struct{}{}
|
|
ids = append(ids, submission.TenderID)
|
|
}
|
|
return ids
|
|
}
|
|
|
|
func (s *tenderSubmissionService) loadTendersByIDs(ctx context.Context, tenderIDs []string) map[string]*tender.TenderResponse {
|
|
if len(tenderIDs) == 0 {
|
|
return map[string]*tender.TenderResponse{}
|
|
}
|
|
|
|
tenders, err := s.tenderService.GetByIDs(ctx, tenderIDs)
|
|
if err != nil {
|
|
s.logger.Error("Failed to batch load tenders for submissions", map[string]interface{}{
|
|
"count": len(tenderIDs),
|
|
"error": err.Error(),
|
|
})
|
|
return map[string]*tender.TenderResponse{}
|
|
}
|
|
return tenders
|
|
}
|
|
|
|
func (s *tenderSubmissionService) enrichSubmissions(ctx context.Context, submissions []*TenderSubmission, fullDetails bool) []*TenderSubmissionWithTenderResponse {
|
|
tenderMap := s.loadTendersByIDs(ctx, uniqueTenderIDs(submissions))
|
|
responses := make([]*TenderSubmissionWithTenderResponse, len(submissions))
|
|
for i, submission := range submissions {
|
|
var details *TenderDetails
|
|
if tenderResp, ok := tenderMap[submission.TenderID]; ok {
|
|
if fullDetails {
|
|
details = fullTenderDetailsFromResponse(tenderResp)
|
|
} else {
|
|
details = listTenderDetailsFromResponse(tenderResp)
|
|
}
|
|
}
|
|
responses[i] = submission.ToResponseWithTender(details)
|
|
}
|
|
return responses
|
|
}
|