551 lines
23 KiB
Go
551 lines
23 KiB
Go
package tender
|
|
|
|
import (
|
|
"strings"
|
|
"time"
|
|
|
|
"tm/pkg/response"
|
|
)
|
|
|
|
// UpdateTenderRequest represents a request to update an existing tender
|
|
type UpdateTenderRequest struct {
|
|
ID string `json:"id" valid:"required~Tender ID is required"`
|
|
Title *string `json:"title,omitempty" valid:"stringlength(1|500)~Title must be between 1 and 500 characters"`
|
|
Description *string `json:"description,omitempty" valid:"stringlength(0|5000)~Description must not exceed 5000 characters"`
|
|
Status *TenderStatus `json:"status,omitempty"`
|
|
EstimatedValue *float64 `json:"estimated_value,omitempty" valid:"range(0|999999999999)~Estimated value must be between 0 and 999999999999"`
|
|
Currency *string `json:"currency,omitempty" valid:"stringlength(3|3)~Currency must be 3 characters"`
|
|
TenderDeadline *int64 `json:"tender_deadline,omitempty"` // Unix milliseconds
|
|
}
|
|
|
|
// SearchForm represents a request to list tenders with pagination and filtering
|
|
type SearchForm struct {
|
|
Search *string `query:"q" valid:"optional"`
|
|
Title *string `query:"title" valid:"optional"`
|
|
Description *string `query:"description" valid:"optional"`
|
|
NoticeType *string `query:"notice_type" valid:"optional"`
|
|
NoticeTypes []string `query:"notice_types" valid:"optional"`
|
|
FormTypes []string `query:"form_types" valid:"optional"`
|
|
ProcurementType *string `query:"procurement_type" valid:"optional"`
|
|
Country *string `query:"country" valid:"optional"`
|
|
CountryCode *string `query:"country_code" valid:"optional"`
|
|
CountryCodes []string `query:"country_codes" valid:"optional"`
|
|
RegionCodes []string `query:"region_codes" valid:"optional"`
|
|
CompanyID *string `query:"company_id" valid:"optional"`
|
|
CustomerID *string `query:"customer_id" valid:"optional"`
|
|
Status []string `query:"status" valid:"optional"`
|
|
MainClassification *string `query:"main_classification" valid:"optional"`
|
|
Classifications []string `query:"classifications" valid:"optional"`
|
|
CpvCodes []string `query:"cpv_codes" valid:"optional"`
|
|
MinEstimatedValue *float64 `query:"min_estimated_value" valid:"optional"`
|
|
MaxEstimatedValue *float64 `query:"max_estimated_value" valid:"optional"`
|
|
Currency string `query:"currency" valid:"optional"`
|
|
CreatedAt *int64 `query:"created_at" valid:"optional"`
|
|
CreatedAtFrom *int64 `query:"created_at_from" valid:"optional"`
|
|
CreatedAtTo *int64 `query:"created_at_to" valid:"optional"`
|
|
TenderDeadline *int64 `query:"tender_deadline" valid:"optional"`
|
|
TenderDeadlineFrom *int64 `query:"tender_deadline_from" valid:"optional"`
|
|
TenderDeadlineTo *int64 `query:"tender_deadline_to" valid:"optional"`
|
|
DeadlineFrom *int64 `query:"deadline_from" valid:"optional"`
|
|
DeadlineTo *int64 `query:"deadline_to" valid:"optional"`
|
|
PublicationDate *int64 `query:"publication_date" valid:"optional"`
|
|
PublicationDateFrom *int64 `query:"publication_date_from" valid:"optional"`
|
|
PublicationDateTo *int64 `query:"publication_date_to" valid:"optional"`
|
|
SubmissionDeadline *int64 `query:"submission_deadline" valid:"optional"`
|
|
SubmissionDateAliasFrom *int64 `query:"submission_deadline_from" valid:"optional"`
|
|
SubmissionDateAliasTo *int64 `query:"submission_deadline_to" valid:"optional"`
|
|
SubmissionDateFrom *int64 `query:"submission_date_from" valid:"optional"`
|
|
SubmissionDateTo *int64 `query:"submission_date_to" valid:"optional"`
|
|
Source []string `query:"source" valid:"optional"`
|
|
BuyerOrganizationID *string `query:"buyer_organization_id" valid:"optional"`
|
|
Languages []string `query:"languages" valid:"optional"`
|
|
OnlyActiveDeadlines bool `query:"-" valid:"optional"`
|
|
Language *string `query:"lang" valid:"optional"`
|
|
}
|
|
|
|
// SearchResponse represents the response for listing tenders
|
|
type SearchResponse struct {
|
|
Tenders []TenderResponse `json:"tenders"`
|
|
Metadata *response.Meta `json:"-"`
|
|
}
|
|
|
|
// AddressResponse is postal / NUTS address data for API consumers.
|
|
type AddressResponse struct {
|
|
StreetName string `json:"street_name,omitempty"`
|
|
CityName string `json:"city_name,omitempty"`
|
|
PostalZone string `json:"postal_zone,omitempty"`
|
|
CountrySubentityCode string `json:"country_subentity_code,omitempty"`
|
|
Department string `json:"department,omitempty"`
|
|
Region string `json:"region,omitempty"`
|
|
CountryCode string `json:"country_code"`
|
|
}
|
|
|
|
// OrganizationResponse is an organization (buyer, winner, etc.) in tender API payloads.
|
|
type OrganizationResponse struct {
|
|
ID string `json:"id,omitempty"`
|
|
Name string `json:"name"`
|
|
CompanyID string `json:"company_id,omitempty"`
|
|
WebsiteURI string `json:"website_uri,omitempty"`
|
|
ContactName string `json:"contact_name,omitempty"`
|
|
ContactTelephone string `json:"contact_telephone,omitempty"`
|
|
ContactEmail string `json:"contact_email,omitempty"`
|
|
Address AddressResponse `json:"address,omitempty"`
|
|
}
|
|
|
|
// TenderLotResponse is one procurement lot for list/detail tender APIs.
|
|
type TenderLotResponse struct {
|
|
LotID string `json:"lot_id,omitempty"`
|
|
Title string `json:"title,omitempty"`
|
|
Description string `json:"description,omitempty"`
|
|
MainNatureOfContract string `json:"main_nature_of_contract,omitempty"`
|
|
MainClassification string `json:"main_classification,omitempty"`
|
|
MainClassificationDescription string `json:"main_classification_description,omitempty"`
|
|
MainClassificationDisplay string `json:"main_classification_display,omitempty"`
|
|
AdditionalClassifications []string `json:"additional_classifications,omitempty"`
|
|
Duration string `json:"duration,omitempty"`
|
|
EstimatedValue float64 `json:"estimated_value,omitempty"`
|
|
Currency string `json:"currency,omitempty"`
|
|
}
|
|
|
|
// TenderWinnerResponse is a contract winner for awarded tenders.
|
|
type TenderWinnerResponse struct {
|
|
OfficialName string `json:"official_name"`
|
|
RegistrationNumber string `json:"registration_number,omitempty"`
|
|
PostalAddress string `json:"postal_address,omitempty"`
|
|
Town string `json:"town,omitempty"`
|
|
Postcode string `json:"postcode,omitempty"`
|
|
CountrySubdivisionNuts string `json:"country_subdivision_nuts,omitempty"`
|
|
Country string `json:"country,omitempty"`
|
|
ContactPoint string `json:"contact_point,omitempty"`
|
|
Email string `json:"email,omitempty"`
|
|
Telephone string `json:"telephone,omitempty"`
|
|
LotID string `json:"lot_id,omitempty"`
|
|
Amount float64 `json:"amount,omitempty"`
|
|
Currency string `json:"currency,omitempty"`
|
|
}
|
|
|
|
// TenderResponse represents a tender in API responses (search, list, and detail).
|
|
type TenderResponse struct {
|
|
ID string `json:"id"`
|
|
NoticePublicationID string `json:"notice_publication_id"`
|
|
RelatedNoticePublicationIDs []string `json:"related_notice_publication_ids,omitempty"`
|
|
ContractFolderID string `json:"contract_folder_id,omitempty"`
|
|
ProcurementProjectID string `json:"procurement_project_id,omitempty"`
|
|
ContractNoticeID string `json:"contract_notice_id,omitempty"`
|
|
|
|
NoticeTypeCode string `json:"notice_type_code"`
|
|
FormType string `json:"form_type,omitempty"`
|
|
NoticeSubTypeCode string `json:"notice_sub_type_code,omitempty"`
|
|
NoticeIdentifier string `json:"notice_identifier,omitempty"`
|
|
NoticeVersion string `json:"notice_version,omitempty"`
|
|
NoticeLanguageCode string `json:"notice_language_code,omitempty"`
|
|
GazetteID string `json:"gazette_id,omitempty"`
|
|
|
|
NoticeDispatchAt int64 `json:"notice_dispatch_at,omitempty"`
|
|
NoticeDispatchDateWithTimezone string `json:"notice_dispatch_date_with_timezone,omitempty"`
|
|
ESenderDispatchAt int64 `json:"esender_dispatch_at,omitempty"`
|
|
IssueDate int64 `json:"issue_date,omitempty"`
|
|
IssueTime int64 `json:"issue_time,omitempty"`
|
|
|
|
Title string `json:"title"`
|
|
Description string `json:"description"`
|
|
|
|
ProcurementTypeCode string `json:"procurement_type_code"`
|
|
ProcedureCode string `json:"procedure_code"`
|
|
MainClassification string `json:"main_classification"`
|
|
MainClassificationDescription string `json:"main_classification_description,omitempty"`
|
|
MainClassificationDisplay string `json:"main_classification_display,omitempty"`
|
|
AdditionalClassifications []string `json:"additional_classifications,omitempty"`
|
|
EstimatedValue float64 `json:"estimated_value"`
|
|
Currency string `json:"currency"`
|
|
EstimatedValueVATBasis string `json:"estimated_value_vat_basis,omitempty"`
|
|
|
|
Duration string `json:"duration"`
|
|
DurationUnit string `json:"duration_unit"`
|
|
|
|
PublicationDate int64 `json:"publication_date"`
|
|
TenderDeadline int64 `json:"tender_deadline"`
|
|
SubmissionDeadline int64 `json:"submission_deadline"`
|
|
ApplicationDeadline int64 `json:"application_deadline"`
|
|
SubmissionURL string `json:"submission_url"`
|
|
|
|
PlaceOfPerformance string `json:"place_of_performance,omitempty"`
|
|
CountryCode string `json:"country_code"`
|
|
RegionCode string `json:"region_code,omitempty"`
|
|
CityName string `json:"city_name,omitempty"`
|
|
PostalCode string `json:"postal_code,omitempty"`
|
|
|
|
DocumentURI string `json:"document_uri,omitempty"`
|
|
DocumentURL string `json:"document_url,omitempty"`
|
|
TenderURL string `json:"tender_url,omitempty"`
|
|
BuyerProfileURL string `json:"buyer_profile_url,omitempty"`
|
|
|
|
BuyerOrganization *OrganizationResponse `json:"buyer_organization,omitempty"`
|
|
WinningTenderer *OrganizationResponse `json:"winning_tenderer,omitempty"`
|
|
Lots []TenderLotResponse `json:"lots,omitempty"`
|
|
Winners []TenderWinnerResponse `json:"winners,omitempty"`
|
|
OfficialLanguages []string `json:"official_languages,omitempty"`
|
|
|
|
AwardDate int64 `json:"award_date,omitempty"`
|
|
AwardedValue float64 `json:"awarded_value,omitempty"`
|
|
ContractNumber string `json:"contract_number,omitempty"`
|
|
|
|
Status TenderStatus `json:"status"`
|
|
Source TenderSource `json:"source,omitempty"`
|
|
TenderID string `json:"tender_id"`
|
|
ProjectName string `json:"project_name,omitempty"`
|
|
CreatedAt int64 `json:"created_at"`
|
|
|
|
OverallSummary string `json:"overall_summary,omitempty"`
|
|
Language string `json:"language,omitempty"`
|
|
}
|
|
|
|
// TenderDocumentResponse represents a scraped tender document available for download.
|
|
type TenderDocumentResponse struct {
|
|
Filename string `json:"filename"`
|
|
Size int64 `json:"size"`
|
|
LastModified int64 `json:"last_modified"`
|
|
DocumentType string `json:"document_type,omitempty"`
|
|
}
|
|
|
|
// TenderDocumentsResponse represents scraped documents for a tender.
|
|
type TenderDocumentsResponse struct {
|
|
TenderID string `json:"tender_id"`
|
|
Documents []TenderDocumentResponse `json:"documents"`
|
|
}
|
|
|
|
const (
|
|
enrichmentMainCPVDescriptionKey = "main_classification_description"
|
|
vatBasisUnspecified = "unspecified"
|
|
)
|
|
|
|
// ToTenderResponse converts a Tender entity to TenderResponse
|
|
func (t *Tender) ToResponse() *TenderResponse {
|
|
return t.ToResponseWithLanguage("")
|
|
}
|
|
|
|
// ToResponseWithLanguage converts a Tender entity to TenderResponse using a preferred language.
|
|
// When language is provided and translation exists, translated title/description are returned.
|
|
func (t *Tender) ToResponseWithLanguage(language string) *TenderResponse {
|
|
title := t.Title
|
|
description := t.Description
|
|
usedLanguage := ""
|
|
if language != "" {
|
|
if entry, ok := t.Translations[language]; ok {
|
|
title = entry.Title
|
|
description = entry.Description
|
|
usedLanguage = language
|
|
}
|
|
}
|
|
|
|
mainDesc := firstNonEmpty(
|
|
strings.TrimSpace(t.ProcessingMetadata.EnrichmentData[enrichmentMainCPVDescriptionKey]),
|
|
strings.TrimSpace(t.MainClassificationDescription),
|
|
)
|
|
mainCode := strings.TrimSpace(t.MainClassification)
|
|
|
|
documentURL := strings.TrimSpace(t.DocumentURI)
|
|
if documentURL == "" {
|
|
documentURL = strings.TrimSpace(t.TenderURL)
|
|
}
|
|
|
|
response := &TenderResponse{
|
|
ID: t.ID.Hex(),
|
|
NoticePublicationID: t.NoticePublicationID,
|
|
RelatedNoticePublicationIDs: append([]string(nil), t.RelatedNoticePublicationIDs...),
|
|
ContractFolderID: strings.TrimSpace(t.ContractFolderID),
|
|
ProcurementProjectID: strings.TrimSpace(t.ProcurementProjectID),
|
|
ContractNoticeID: strings.TrimSpace(t.ContractNoticeID),
|
|
|
|
NoticeTypeCode: strings.TrimSpace(t.NoticeTypeCode),
|
|
FormType: strings.TrimSpace(t.FormType),
|
|
NoticeSubTypeCode: strings.TrimSpace(t.NoticeSubTypeCode),
|
|
NoticeIdentifier: strings.TrimSpace(t.NoticeIdentifier),
|
|
NoticeVersion: strings.TrimSpace(t.NoticeVersion),
|
|
NoticeLanguageCode: strings.TrimSpace(t.NoticeLanguageCode),
|
|
GazetteID: strings.TrimSpace(t.GazetteID),
|
|
|
|
NoticeDispatchAt: t.NoticeDispatchAt,
|
|
NoticeDispatchDateWithTimezone: formatNoticeDispatchRFC3339(t),
|
|
ESenderDispatchAt: t.ESenderDispatchAt,
|
|
IssueDate: t.IssueDate,
|
|
IssueTime: t.IssueTime,
|
|
|
|
Title: title,
|
|
Description: description,
|
|
ProcurementTypeCode: strings.TrimSpace(t.ProcurementTypeCode),
|
|
ProcedureCode: strings.TrimSpace(t.ProcedureCode),
|
|
MainClassification: mainCode,
|
|
MainClassificationDescription: mainDesc,
|
|
MainClassificationDisplay: classificationDisplay(mainCode, mainDesc),
|
|
AdditionalClassifications: append([]string(nil), t.AdditionalClassifications...),
|
|
EstimatedValue: t.EstimatedValue,
|
|
Currency: strings.TrimSpace(t.Currency),
|
|
EstimatedValueVATBasis: vatBasisUnspecified,
|
|
|
|
Duration: t.Duration,
|
|
DurationUnit: t.DurationUnit,
|
|
|
|
PublicationDate: t.PublicationDate,
|
|
TenderDeadline: t.TenderDeadline,
|
|
SubmissionDeadline: t.SubmissionDeadline,
|
|
ApplicationDeadline: t.ApplicationDeadline,
|
|
SubmissionURL: strings.TrimSpace(t.SubmissionURL),
|
|
|
|
PlaceOfPerformance: strings.TrimSpace(t.PlaceOfPerformance),
|
|
CountryCode: strings.TrimSpace(t.CountryCode),
|
|
RegionCode: strings.TrimSpace(t.RegionCode),
|
|
CityName: strings.TrimSpace(t.CityName),
|
|
PostalCode: strings.TrimSpace(t.PostalCode),
|
|
|
|
DocumentURI: strings.TrimSpace(t.DocumentURI),
|
|
DocumentURL: documentURL,
|
|
TenderURL: strings.TrimSpace(t.TenderURL),
|
|
BuyerProfileURL: strings.TrimSpace(t.BuyerProfileURL),
|
|
|
|
BuyerOrganization: organizationToResponse(t.BuyerOrganization),
|
|
WinningTenderer: organizationToResponse(t.WinningTenderer),
|
|
Lots: buildTenderLotsResponse(t, mainDesc),
|
|
Winners: buildTenderWinnersResponse(t),
|
|
OfficialLanguages: append([]string(nil), t.OfficialLanguages...),
|
|
|
|
AwardDate: t.AwardDate,
|
|
AwardedValue: t.AwardedValue,
|
|
ContractNumber: strings.TrimSpace(t.ContractNumber),
|
|
|
|
Status: t.Status,
|
|
Source: t.Source,
|
|
TenderID: t.TenderID,
|
|
ProjectName: strings.TrimSpace(t.ProjectName),
|
|
CreatedAt: t.CreatedAt,
|
|
Language: usedLanguage,
|
|
|
|
OverallSummary: strings.TrimSpace(t.AIOverallSummary),
|
|
}
|
|
|
|
return response
|
|
}
|
|
|
|
func organizationToResponse(o *Organization) *OrganizationResponse {
|
|
if o == nil {
|
|
return nil
|
|
}
|
|
return &OrganizationResponse{
|
|
ID: strings.TrimSpace(o.ID),
|
|
Name: strings.TrimSpace(o.Name),
|
|
CompanyID: strings.TrimSpace(o.CompanyID),
|
|
WebsiteURI: strings.TrimSpace(o.WebsiteURI),
|
|
ContactName: strings.TrimSpace(o.ContactName),
|
|
ContactTelephone: strings.TrimSpace(o.ContactTelephone),
|
|
ContactEmail: strings.TrimSpace(o.ContactEmail),
|
|
Address: AddressResponse{
|
|
StreetName: strings.TrimSpace(o.Address.StreetName),
|
|
CityName: strings.TrimSpace(o.Address.CityName),
|
|
PostalZone: strings.TrimSpace(o.Address.PostalZone),
|
|
CountrySubentityCode: strings.TrimSpace(o.Address.CountrySubentityCode),
|
|
Department: strings.TrimSpace(o.Address.Department),
|
|
Region: strings.TrimSpace(o.Address.Region),
|
|
CountryCode: strings.TrimSpace(o.Address.CountryCode),
|
|
},
|
|
}
|
|
}
|
|
|
|
func formatNoticeDispatchRFC3339(t *Tender) string {
|
|
if t == nil {
|
|
return ""
|
|
}
|
|
sec := t.NoticeDispatchAt
|
|
if sec == 0 {
|
|
sec = t.ESenderDispatchAt
|
|
}
|
|
if sec == 0 {
|
|
return ""
|
|
}
|
|
return time.Unix(sec, 0).UTC().Format(time.RFC3339)
|
|
}
|
|
|
|
func firstNonEmpty(values ...string) string {
|
|
for _, v := range values {
|
|
if s := strings.TrimSpace(v); s != "" {
|
|
return s
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func classificationDisplay(code, description string) string {
|
|
code = strings.TrimSpace(code)
|
|
description = strings.TrimSpace(description)
|
|
if code != "" && description != "" {
|
|
return code + " - " + description
|
|
}
|
|
if code != "" {
|
|
return code
|
|
}
|
|
return description
|
|
}
|
|
|
|
func buildTenderLotsResponse(t *Tender, globalMainDesc string) []TenderLotResponse {
|
|
if t == nil || len(t.ProcurementLots) == 0 {
|
|
return nil
|
|
}
|
|
out := make([]TenderLotResponse, 0, len(t.ProcurementLots))
|
|
mainCode := strings.TrimSpace(t.MainClassification)
|
|
for i := range t.ProcurementLots {
|
|
lot := t.ProcurementLots[i]
|
|
lotCode := strings.TrimSpace(lot.MainClassification)
|
|
perLotDesc := strings.TrimSpace(lot.MainClassificationDescription)
|
|
if perLotDesc == "" && lotCode != "" && lotCode == mainCode {
|
|
perLotDesc = globalMainDesc
|
|
}
|
|
out = append(out, TenderLotResponse{
|
|
LotID: strings.TrimSpace(lot.LotID),
|
|
Title: strings.TrimSpace(lot.Title),
|
|
Description: strings.TrimSpace(lot.Description),
|
|
MainNatureOfContract: strings.TrimSpace(lot.MainNatureOfContract),
|
|
MainClassification: lotCode,
|
|
MainClassificationDescription: perLotDesc,
|
|
MainClassificationDisplay: classificationDisplay(lotCode, perLotDesc),
|
|
AdditionalClassifications: append([]string(nil), lot.AdditionalClassifications...),
|
|
Duration: strings.TrimSpace(lot.Duration),
|
|
EstimatedValue: lot.EstimatedValue,
|
|
Currency: strings.TrimSpace(lot.Currency),
|
|
})
|
|
}
|
|
return out
|
|
}
|
|
|
|
func buildTenderWinnersResponse(t *Tender) []TenderWinnerResponse {
|
|
if t == nil {
|
|
return nil
|
|
}
|
|
if len(t.AwardedEntities) > 0 {
|
|
out := make([]TenderWinnerResponse, 0, len(t.AwardedEntities))
|
|
for _, a := range t.AwardedEntities {
|
|
w := TenderWinnerResponse{
|
|
OfficialName: strings.TrimSpace(a.Name),
|
|
RegistrationNumber: strings.TrimSpace(a.CompanyID),
|
|
PostalAddress: strings.TrimSpace(a.Address),
|
|
Country: strings.TrimSpace(a.Country),
|
|
LotID: strings.TrimSpace(a.LotID),
|
|
Amount: a.Amount,
|
|
Currency: strings.TrimSpace(a.Currency),
|
|
Town: "",
|
|
Postcode: "",
|
|
CountrySubdivisionNuts: "",
|
|
ContactPoint: "",
|
|
Email: "",
|
|
Telephone: "",
|
|
}
|
|
if org := organizationByID(t, a.OrganizationID); org != nil {
|
|
mergeWinnerFromOrganization(&w, org)
|
|
}
|
|
out = append(out, w)
|
|
}
|
|
return out
|
|
}
|
|
if t.WinningTenderer == nil || strings.TrimSpace(t.WinningTenderer.Name) == "" {
|
|
return nil
|
|
}
|
|
w := t.WinningTenderer
|
|
addr := w.Address
|
|
return []TenderWinnerResponse{{
|
|
OfficialName: strings.TrimSpace(w.Name),
|
|
RegistrationNumber: strings.TrimSpace(w.CompanyID),
|
|
PostalAddress: strings.TrimSpace(addr.StreetName),
|
|
Town: strings.TrimSpace(addr.CityName),
|
|
Postcode: strings.TrimSpace(addr.PostalZone),
|
|
CountrySubdivisionNuts: strings.TrimSpace(addr.CountrySubentityCode),
|
|
Country: strings.TrimSpace(addr.CountryCode),
|
|
ContactPoint: strings.TrimSpace(w.ContactName),
|
|
Email: strings.TrimSpace(w.ContactEmail),
|
|
Telephone: strings.TrimSpace(w.ContactTelephone),
|
|
}}
|
|
}
|
|
|
|
func organizationByID(t *Tender, id string) *Organization {
|
|
id = strings.TrimSpace(id)
|
|
if id == "" || t == nil {
|
|
return nil
|
|
}
|
|
for i := range t.Organizations {
|
|
if strings.TrimSpace(t.Organizations[i].ID) == id {
|
|
return &t.Organizations[i]
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func mergeWinnerFromOrganization(w *TenderWinnerResponse, org *Organization) {
|
|
if w == nil || org == nil {
|
|
return
|
|
}
|
|
if w.OfficialName == "" {
|
|
w.OfficialName = strings.TrimSpace(org.Name)
|
|
}
|
|
if w.RegistrationNumber == "" {
|
|
w.RegistrationNumber = strings.TrimSpace(org.CompanyID)
|
|
}
|
|
if w.PostalAddress == "" {
|
|
w.PostalAddress = strings.TrimSpace(org.Address.StreetName)
|
|
}
|
|
if w.Town == "" {
|
|
w.Town = strings.TrimSpace(org.Address.CityName)
|
|
}
|
|
if w.Postcode == "" {
|
|
w.Postcode = strings.TrimSpace(org.Address.PostalZone)
|
|
}
|
|
if w.CountrySubdivisionNuts == "" {
|
|
w.CountrySubdivisionNuts = strings.TrimSpace(org.Address.CountrySubentityCode)
|
|
}
|
|
if w.Country == "" {
|
|
w.Country = strings.TrimSpace(org.Address.CountryCode)
|
|
}
|
|
if w.ContactPoint == "" {
|
|
w.ContactPoint = strings.TrimSpace(org.ContactName)
|
|
}
|
|
if w.Email == "" {
|
|
w.Email = strings.TrimSpace(org.ContactEmail)
|
|
}
|
|
if w.Telephone == "" {
|
|
w.Telephone = strings.TrimSpace(org.ContactTelephone)
|
|
}
|
|
}
|
|
|
|
// AITranslateRequest represents a request for on-demand AI translation.
|
|
type AITranslateRequest struct {
|
|
Language string `json:"language" valid:"required,stringlength(2|5)~Language must be between 2 and 5 characters"`
|
|
}
|
|
|
|
// AITranslateResponse represents the response for AI translation trigger.
|
|
type AITranslateResponse struct {
|
|
ContractFolderID string `json:"contract_folder_id"`
|
|
NoticePublicationID string `json:"notice_publication_id"`
|
|
Language string `json:"language"`
|
|
TranslatedTitle string `json:"translated_title"`
|
|
TranslatedDescription string `json:"translated_description"`
|
|
}
|
|
|
|
// AISummaryResponse represents the response for getting an AI-generated summary
|
|
type AISummaryResponse struct {
|
|
NoticeID string `json:"notice_id"`
|
|
OverallSummary string `json:"overall_summary"`
|
|
Source string `json:"source"` // storage | pending | unavailable
|
|
}
|
|
|
|
// AISummarizeResponse represents the response for triggering on-demand AI summarization
|
|
type AISummarizeResponse struct {
|
|
NoticeID string `json:"notice_id"`
|
|
Summaries []AIDocumentSummary `json:"summaries"`
|
|
OverallSummary *string `json:"overall_summary"`
|
|
}
|
|
|
|
// AIDocumentSummary represents a single document summary from the AI service
|
|
type AIDocumentSummary struct {
|
|
DocumentName string `json:"document_name"`
|
|
DocumentType string `json:"document_type"`
|
|
Summary string `json:"summary"`
|
|
SummaryModel string `json:"summary_model"`
|
|
Error string `json:"error,omitempty"`
|
|
}
|