ee5414bc10
- Updated the `AggregateProcurementLotEstimatedValue` function to reject mixed currencies and allow empty lot currencies, ensuring accurate aggregation of estimated values. - Introduced new unit tests in `entity_test.go` to validate the behavior of the aggregation function under various scenarios, including mixed currencies and empty lot currencies. - Refactored budget calculation in `budget_test.go` to utilize the new aggregation logic, improving consistency in budget retrieval. This update improves the handling of estimated values in procurement lots, enhancing the reliability of the tender management system.
2319 lines
97 KiB
Go
2319 lines
97 KiB
Go
package ted
|
|
|
|
import (
|
|
"encoding/xml"
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// NoticeTypeCodeField captures cbc:NoticeTypeCode: inner text (notice type, BT-02) and listName (form type, BT-03).
|
|
type NoticeTypeCodeField struct {
|
|
ListName string `xml:"listName,attr,omitempty"`
|
|
Text string `xml:",chardata"`
|
|
}
|
|
|
|
// DocumentType represents the type of TED document
|
|
type DocumentType string
|
|
|
|
// Document type constants for all supported TED XML notice types
|
|
const (
|
|
DocumentTypeContractNotice DocumentType = "ContractNotice"
|
|
DocumentTypeContractAwardNotice DocumentType = "ContractAwardNotice"
|
|
DocumentTypePriorInformationNotice DocumentType = "PriorInformationNotice"
|
|
DocumentTypeBusinessRegistrationInformationNotice DocumentType = "BusinessRegistrationInformationNotice"
|
|
)
|
|
|
|
// ParsedDocument wraps any TED document with type information
|
|
type ParsedDocument struct {
|
|
Type DocumentType
|
|
ContractNotice *ContractNotice `json:"contract_notice,omitempty"`
|
|
ContractAwardNotice *ContractAwardNotice `json:"contract_award_notice,omitempty"`
|
|
PriorInformationNotice *PriorInformationNotice `json:"prior_information_notice,omitempty"`
|
|
// BusinessRegistrationInformationNotice *BusinessRegistrationInformationNotice `json:"business_registration_information_notice,omitempty"`
|
|
}
|
|
|
|
// GetDocument returns the underlying document interface
|
|
func (pd *ParsedDocument) GetDocument() TEDDocument {
|
|
switch pd.Type {
|
|
case DocumentTypeContractNotice:
|
|
return pd.ContractNotice
|
|
case DocumentTypeContractAwardNotice:
|
|
return pd.ContractAwardNotice
|
|
case DocumentTypePriorInformationNotice:
|
|
return pd.PriorInformationNotice
|
|
default:
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// TEDDocument represents the common interface for all TED document types
|
|
type TEDDocument interface {
|
|
GetID() string
|
|
GetNoticeType() string
|
|
GetLanguage() string
|
|
Validate() error
|
|
}
|
|
|
|
// ublEformsExtension returns embedded eForms extension data from UBL extensions (shared across notice root types).
|
|
func ublEformsExtension(ext *UBLExtensions) *EformsExtension {
|
|
if ext == nil || ext.UBLExtension == nil || ext.UBLExtension.ExtensionContent == nil {
|
|
return nil
|
|
}
|
|
return ext.UBLExtension.ExtensionContent.EformsExtension
|
|
}
|
|
|
|
// PriorInformationNotice represents a prior information notice
|
|
type PriorInformationNotice struct {
|
|
XMLName xml.Name `xml:"PriorInformationNotice"`
|
|
Xmlns string `xml:"xmlns,attr,omitempty"`
|
|
XmlnsCac string `xml:"xmlns:cac,attr,omitempty"`
|
|
XmlnsCbc string `xml:"xmlns:cbc,attr,omitempty"`
|
|
XmlnsEfac string `xml:"xmlns:efac,attr,omitempty"`
|
|
XmlnsEfbc string `xml:"xmlns:efbc,attr,omitempty"`
|
|
XmlnsEfext string `xml:"xmlns:efext,attr,omitempty"`
|
|
XmlnsExt string `xml:"xmlns:ext,attr,omitempty"`
|
|
UBLVersionID string `xml:"UBLVersionID"`
|
|
CustomizationID string `xml:"CustomizationID"`
|
|
ID string `xml:"ID"`
|
|
ContractFolderID string `xml:"ContractFolderID"`
|
|
IssueDate string `xml:"IssueDate"`
|
|
IssueTime string `xml:"IssueTime"`
|
|
VersionID string `xml:"VersionID"`
|
|
RegulatoryDomain string `xml:"RegulatoryDomain"`
|
|
NoticeTypeCode NoticeTypeCodeField `xml:"NoticeTypeCode,omitempty"`
|
|
NoticeLanguageCode string `xml:"NoticeLanguageCode"`
|
|
Extensions *UBLExtensions `xml:"UBLExtensions,omitempty"`
|
|
ContractingParty *ContractingParty `xml:"ContractingParty,omitempty"`
|
|
TenderingTerms *TenderingTerms `xml:"TenderingTerms,omitempty"`
|
|
TenderingProcess *TenderingProcess `xml:"TenderingProcess,omitempty"`
|
|
ProcurementProject *ProcurementProject `xml:"ProcurementProject,omitempty"`
|
|
ProcurementProjectLot []ProcurementProjectLot `xml:"ProcurementProjectLot,omitempty"`
|
|
AdditionalDocumentReference []AdditionalDocumentReference `xml:"AdditionalDocumentReference,omitempty"`
|
|
}
|
|
|
|
// Interface methods for PriorInformationNotice
|
|
func (pin PriorInformationNotice) GetID() string {
|
|
return pin.ID
|
|
}
|
|
|
|
func (pin PriorInformationNotice) GetNoticeType() string {
|
|
return strings.TrimSpace(pin.NoticeTypeCode.Text)
|
|
}
|
|
|
|
// GetNoticeFormType returns the form type from listName on cbc:NoticeTypeCode (BT-03), e.g. "planning".
|
|
func (pin PriorInformationNotice) GetNoticeFormType() string {
|
|
return strings.TrimSpace(pin.NoticeTypeCode.ListName)
|
|
}
|
|
|
|
func (pin PriorInformationNotice) firstLot() *ProcurementProjectLot {
|
|
if len(pin.ProcurementProjectLot) == 0 {
|
|
return nil
|
|
}
|
|
return &pin.ProcurementProjectLot[0]
|
|
}
|
|
|
|
// GetPublicationInfo returns publication information if available.
|
|
func (pin PriorInformationNotice) GetPublicationInfo() *Publication {
|
|
if ef := ublEformsExtension(pin.Extensions); ef != nil {
|
|
return ef.Publication
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetOrganizations returns organizations from extensions.
|
|
func (pin PriorInformationNotice) GetOrganizations() *Organizations {
|
|
if ef := ublEformsExtension(pin.Extensions); ef != nil {
|
|
return ef.Organizations
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetNoticeSubType returns the notice subtype code from extensions.
|
|
func (pin PriorInformationNotice) GetNoticeSubType() string {
|
|
if ef := ublEformsExtension(pin.Extensions); ef != nil && ef.NoticeSubType != nil {
|
|
return ef.NoticeSubType.SubTypeCode
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// GetNoticePublicationID returns the notice publication ID from extensions.
|
|
func (pin PriorInformationNotice) GetNoticePublicationID() string {
|
|
pub := pin.GetPublicationInfo()
|
|
if pub != nil {
|
|
return pub.NoticePublicationID
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// GetOJSID returns the OJS gazette ID from extensions.
|
|
func (pin PriorInformationNotice) GetOJSID() string {
|
|
pub := pin.GetPublicationInfo()
|
|
if pub != nil {
|
|
return pub.GazetteID
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// GetPublicationDate returns the publication date from extensions.
|
|
func (pin PriorInformationNotice) GetPublicationDate() string {
|
|
pub := pin.GetPublicationInfo()
|
|
if pub != nil {
|
|
return pub.PublicationDate
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// GetOrganizationByID returns an organization by its ID.
|
|
func (pin PriorInformationNotice) GetOrganizationByID(orgID string) *Organization {
|
|
orgs := pin.GetOrganizations()
|
|
if orgs == nil {
|
|
return nil
|
|
}
|
|
for _, org := range orgs.Organization {
|
|
if org.Company != nil &&
|
|
org.Company.PartyIdentification != nil &&
|
|
org.Company.PartyIdentification.ID == orgID {
|
|
return &org
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetBuyerID returns the buyer organization ID from contracting party.
|
|
func (pin PriorInformationNotice) GetBuyerID() string {
|
|
if pin.ContractingParty != nil &&
|
|
pin.ContractingParty.Party != nil &&
|
|
pin.ContractingParty.Party.PartyIdentification != nil {
|
|
return pin.ContractingParty.Party.PartyIdentification.ID
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// GetReviewOrganizationID returns the review organization ID from appeal terms.
|
|
func (pin PriorInformationNotice) GetReviewOrganizationID() string {
|
|
if pin.firstLot() != nil &&
|
|
pin.firstLot().TenderingTerms != nil &&
|
|
pin.firstLot().TenderingTerms.AppealTerms != nil &&
|
|
pin.firstLot().TenderingTerms.AppealTerms.AppealReceiverParty != nil &&
|
|
pin.firstLot().TenderingTerms.AppealTerms.AppealReceiverParty.PartyIdentification != nil {
|
|
return pin.firstLot().TenderingTerms.AppealTerms.AppealReceiverParty.PartyIdentification.ID
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// GetProcedureCode returns the type of procedure.
|
|
func (pin PriorInformationNotice) GetProcedureCode() string {
|
|
if pin.TenderingProcess != nil {
|
|
return pin.TenderingProcess.ProcedureCode
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// GetProcurementTitle returns the procurement project title.
|
|
func (pin PriorInformationNotice) GetProcurementTitle() string {
|
|
if pin.ProcurementProject != nil && strings.TrimSpace(pin.ProcurementProject.Name) != "" {
|
|
return pin.ProcurementProject.Name
|
|
}
|
|
if pin.firstLot() != nil && pin.firstLot().ProcurementProject != nil {
|
|
return pin.firstLot().ProcurementProject.Name
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// GetProcurementDescription returns the procurement project description.
|
|
func (pin PriorInformationNotice) GetProcurementDescription() string {
|
|
if pin.ProcurementProject != nil && strings.TrimSpace(pin.ProcurementProject.Description) != "" {
|
|
return pin.ProcurementProject.Description
|
|
}
|
|
if pin.firstLot() != nil && pin.firstLot().ProcurementProject != nil {
|
|
return pin.firstLot().ProcurementProject.Description
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// GetContractNature returns the main nature of the contract.
|
|
func (pin PriorInformationNotice) GetContractNature() string {
|
|
if pin.ProcurementProject != nil && strings.TrimSpace(pin.ProcurementProject.ProcurementTypeCode) != "" {
|
|
return pin.ProcurementProject.ProcurementTypeCode
|
|
}
|
|
if pin.firstLot() != nil && pin.firstLot().ProcurementProject != nil {
|
|
return pin.firstLot().ProcurementProject.ProcurementTypeCode
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// GetMainClassification returns the main CPV classification code.
|
|
func (pin PriorInformationNotice) GetMainClassification() string {
|
|
if pin.ProcurementProject != nil &&
|
|
pin.ProcurementProject.MainCommodityClassification != nil {
|
|
return pin.ProcurementProject.MainCommodityClassification.ItemClassificationCode
|
|
}
|
|
if lot := pin.firstLot(); lot != nil && lot.ProcurementProject != nil &&
|
|
lot.ProcurementProject.MainCommodityClassification != nil {
|
|
return lot.ProcurementProject.MainCommodityClassification.ItemClassificationCode
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// GetMainClassificationDescription returns the CPV / item classification description when present in the notice XML.
|
|
func (pin PriorInformationNotice) GetMainClassificationDescription() string {
|
|
if pin.ProcurementProject != nil {
|
|
if s := mainCommodityClassificationLabel(pin.ProcurementProject.MainCommodityClassification); s != "" {
|
|
return s
|
|
}
|
|
}
|
|
if lot := pin.firstLot(); lot != nil && lot.ProcurementProject != nil {
|
|
return mainCommodityClassificationLabel(lot.ProcurementProject.MainCommodityClassification)
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// GetPlaceOfPerformance returns the place of performance.
|
|
func (pin PriorInformationNotice) GetPlaceOfPerformance() string {
|
|
if pin.ProcurementProject != nil &&
|
|
pin.ProcurementProject.RealizedLocation != nil &&
|
|
pin.ProcurementProject.RealizedLocation.Address != nil {
|
|
return pin.ProcurementProject.RealizedLocation.Address.Region
|
|
}
|
|
if lot := pin.firstLot(); lot != nil && lot.ProcurementProject != nil &&
|
|
lot.ProcurementProject.RealizedLocation != nil &&
|
|
lot.ProcurementProject.RealizedLocation.Address != nil {
|
|
return lot.ProcurementProject.RealizedLocation.Address.Region
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// GetSelectionCriteria returns selection criteria from extensions.
|
|
func (pin PriorInformationNotice) GetSelectionCriteria() []SelectionCriteria {
|
|
if ef := ublEformsExtension(pin.Extensions); ef != nil {
|
|
return ef.SelectionCriteria
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetOfficialLanguages returns official languages from tender terms extensions.
|
|
func (pin PriorInformationNotice) GetOfficialLanguages() []string {
|
|
if pin.firstLot() != nil &&
|
|
pin.firstLot().TenderingTerms != nil &&
|
|
len(pin.firstLot().TenderingTerms.CallForTendersDocumentReference) > 0 &&
|
|
pin.firstLot().TenderingTerms.CallForTendersDocumentReference[0].UBLExtensions != nil &&
|
|
pin.firstLot().TenderingTerms.CallForTendersDocumentReference[0].UBLExtensions.UBLExtension != nil &&
|
|
pin.firstLot().TenderingTerms.CallForTendersDocumentReference[0].UBLExtensions.UBLExtension.ExtensionContent != nil &&
|
|
pin.firstLot().TenderingTerms.CallForTendersDocumentReference[0].UBLExtensions.UBLExtension.ExtensionContent.EformsExtension != nil &&
|
|
pin.firstLot().TenderingTerms.CallForTendersDocumentReference[0].UBLExtensions.UBLExtension.ExtensionContent.EformsExtension.OfficialLanguages != nil {
|
|
|
|
langs := pin.firstLot().TenderingTerms.CallForTendersDocumentReference[0].UBLExtensions.UBLExtension.ExtensionContent.EformsExtension.OfficialLanguages.Language
|
|
languages := make([]string, len(langs))
|
|
for i, lang := range langs {
|
|
languages[i] = lang.ID
|
|
}
|
|
return languages
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetDocumentURI returns the document URI from call for tenders document reference.
|
|
func (pin PriorInformationNotice) GetDocumentURI() string {
|
|
if pin.firstLot() != nil &&
|
|
pin.firstLot().TenderingTerms != nil &&
|
|
len(pin.firstLot().TenderingTerms.CallForTendersDocumentReference) > 0 &&
|
|
pin.firstLot().TenderingTerms.CallForTendersDocumentReference[0].Attachment != nil &&
|
|
pin.firstLot().TenderingTerms.CallForTendersDocumentReference[0].Attachment.ExternalReference != nil {
|
|
return pin.firstLot().TenderingTerms.CallForTendersDocumentReference[0].Attachment.ExternalReference.URI
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// GetTenderDeadline returns the tender submission deadline.
|
|
func (pin PriorInformationNotice) GetTenderDeadline() string {
|
|
if pin.firstLot() != nil &&
|
|
pin.firstLot().TenderingProcess != nil &&
|
|
pin.firstLot().TenderingProcess.TenderSubmissionDeadlinePeriod != nil {
|
|
return pin.firstLot().TenderingProcess.TenderSubmissionDeadlinePeriod.EndDate
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// GetBudget returns the estimated contract amount and currency.
|
|
func (pin PriorInformationNotice) GetBudget() (amount string, currency string) {
|
|
if amount, currency = budgetFromProcurementLots(pin.ProcurementProjectLot); amount != "" {
|
|
return amount, currency
|
|
}
|
|
if pin.ProcurementProject != nil && pin.ProcurementProject.RequestedTenderTotal != nil {
|
|
return pin.ProcurementProject.RequestedTenderTotal.AmountAndCurrency()
|
|
}
|
|
return "", ""
|
|
}
|
|
|
|
// GetDuration returns the contract duration and unit.
|
|
func (pin PriorInformationNotice) GetDuration() (duration string, unit string) {
|
|
if pin.firstLot() != nil &&
|
|
pin.firstLot().ProcurementProject != nil &&
|
|
pin.firstLot().ProcurementProject.PlannedPeriod != nil &&
|
|
pin.firstLot().ProcurementProject.PlannedPeriod.DurationMeasure != nil {
|
|
durationMeasure := pin.firstLot().ProcurementProject.PlannedPeriod.DurationMeasure
|
|
return durationMeasure.Text, durationMeasure.UnitCode
|
|
}
|
|
if pin.ProcurementProject != nil &&
|
|
pin.ProcurementProject.PlannedPeriod != nil &&
|
|
pin.ProcurementProject.PlannedPeriod.DurationMeasure != nil {
|
|
dm := pin.ProcurementProject.PlannedPeriod.DurationMeasure
|
|
return dm.Text, dm.UnitCode
|
|
}
|
|
return "", ""
|
|
}
|
|
|
|
func (pin PriorInformationNotice) GetLanguage() string {
|
|
return pin.NoticeLanguageCode
|
|
}
|
|
|
|
func (pin PriorInformationNotice) Validate() error {
|
|
if pin.ID == "" {
|
|
return fmt.Errorf("prior information notice ID is required")
|
|
}
|
|
if pin.IssueDate == "" {
|
|
return fmt.Errorf("issue date is required")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// DesignContest represents a design contest notice
|
|
type DesignContest struct {
|
|
XMLName xml.Name `xml:"DesignContest"`
|
|
Xmlns string `xml:"xmlns,attr,omitempty"`
|
|
XmlnsCac string `xml:"xmlns:cac,attr,omitempty"`
|
|
XmlnsCbc string `xml:"xmlns:cbc,attr,omitempty"`
|
|
XmlnsEfac string `xml:"xmlns:efac,attr,omitempty"`
|
|
XmlnsEfbc string `xml:"xmlns:efbc,attr,omitempty"`
|
|
XmlnsEfext string `xml:"xmlns:efext,attr,omitempty"`
|
|
XmlnsExt string `xml:"xmlns:ext,attr,omitempty"`
|
|
UBLVersionID string `xml:"UBLVersionID"`
|
|
CustomizationID string `xml:"CustomizationID"`
|
|
ID string `xml:"ID"`
|
|
ContractFolderID string `xml:"ContractFolderID"`
|
|
IssueDate string `xml:"IssueDate"`
|
|
IssueTime string `xml:"IssueTime"`
|
|
VersionID string `xml:"VersionID"`
|
|
RegulatoryDomain string `xml:"RegulatoryDomain"`
|
|
NoticeTypeCode string `xml:"NoticeTypeCode"`
|
|
NoticeLanguageCode string `xml:"NoticeLanguageCode"`
|
|
Extensions *UBLExtensions `xml:"UBLExtensions,omitempty"`
|
|
ContractingParty *ContractingParty `xml:"ContractingParty,omitempty"`
|
|
TenderingTerms *TenderingTerms `xml:"TenderingTerms,omitempty"`
|
|
TenderingProcess *TenderingProcess `xml:"TenderingProcess,omitempty"`
|
|
ProcurementProject *ProcurementProject `xml:"ProcurementProject,omitempty"`
|
|
ContestDesignJury *ContestDesignJury `xml:"ContestDesignJury,omitempty"`
|
|
ContestDesignReward []ContestDesignReward `xml:"ContestDesignReward,omitempty"`
|
|
}
|
|
|
|
// Interface methods for DesignContest
|
|
func (dc DesignContest) GetID() string { return dc.ID }
|
|
func (dc DesignContest) GetNoticeType() string { return dc.NoticeTypeCode }
|
|
func (dc DesignContest) GetLanguage() string { return dc.NoticeLanguageCode }
|
|
func (dc DesignContest) Validate() error {
|
|
if dc.ID == "" {
|
|
return fmt.Errorf("design contest ID is required")
|
|
}
|
|
if dc.IssueDate == "" {
|
|
return fmt.Errorf("issue date is required")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// QualificationSystemNotice represents a qualification system notice
|
|
type QualificationSystemNotice struct {
|
|
XMLName xml.Name `xml:"QualificationSystemNotice"`
|
|
Xmlns string `xml:"xmlns,attr,omitempty"`
|
|
XmlnsCac string `xml:"xmlns:cac,attr,omitempty"`
|
|
XmlnsCbc string `xml:"xmlns:cbc,attr,omitempty"`
|
|
XmlnsEfac string `xml:"xmlns:efac,attr,omitempty"`
|
|
XmlnsEfbc string `xml:"xmlns:efbc,attr,omitempty"`
|
|
XmlnsEfext string `xml:"xmlns:efext,attr,omitempty"`
|
|
XmlnsExt string `xml:"xmlns:ext,attr,omitempty"`
|
|
UBLVersionID string `xml:"UBLVersionID"`
|
|
CustomizationID string `xml:"CustomizationID"`
|
|
ID string `xml:"ID"`
|
|
ContractFolderID string `xml:"ContractFolderID"`
|
|
IssueDate string `xml:"IssueDate"`
|
|
IssueTime string `xml:"IssueTime"`
|
|
VersionID string `xml:"VersionID"`
|
|
RegulatoryDomain string `xml:"RegulatoryDomain"`
|
|
NoticeTypeCode string `xml:"NoticeTypeCode"`
|
|
NoticeLanguageCode string `xml:"NoticeLanguageCode"`
|
|
Extensions *UBLExtensions `xml:"UBLExtensions,omitempty"`
|
|
ContractingParty *ContractingParty `xml:"ContractingParty,omitempty"`
|
|
TenderingTerms *TenderingTerms `xml:"TenderingTerms,omitempty"`
|
|
TenderingProcess *TenderingProcess `xml:"TenderingProcess,omitempty"`
|
|
ProcurementProject *ProcurementProject `xml:"ProcurementProject,omitempty"`
|
|
QualificationApplication []QualificationApplication `xml:"QualificationApplication,omitempty"`
|
|
}
|
|
|
|
// Interface methods for QualificationSystemNotice
|
|
func (qsn QualificationSystemNotice) GetID() string { return qsn.ID }
|
|
func (qsn QualificationSystemNotice) GetNoticeType() string { return qsn.NoticeTypeCode }
|
|
func (qsn QualificationSystemNotice) GetLanguage() string { return qsn.NoticeLanguageCode }
|
|
func (qsn QualificationSystemNotice) Validate() error {
|
|
if qsn.ID == "" {
|
|
return fmt.Errorf("qualification system notice ID is required")
|
|
}
|
|
if qsn.IssueDate == "" {
|
|
return fmt.Errorf("issue date is required")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ConcessionNotice represents a concession notice
|
|
type ConcessionNotice struct {
|
|
XMLName xml.Name `xml:"ConcessionNotice"`
|
|
Xmlns string `xml:"xmlns,attr,omitempty"`
|
|
XmlnsCac string `xml:"xmlns:cac,attr,omitempty"`
|
|
XmlnsCbc string `xml:"xmlns:cbc,attr,omitempty"`
|
|
XmlnsEfac string `xml:"xmlns:efac,attr,omitempty"`
|
|
XmlnsEfbc string `xml:"xmlns:efbc,attr,omitempty"`
|
|
XmlnsEfext string `xml:"xmlns:efext,attr,omitempty"`
|
|
XmlnsExt string `xml:"xmlns:ext,attr,omitempty"`
|
|
UBLVersionID string `xml:"UBLVersionID"`
|
|
CustomizationID string `xml:"CustomizationID"`
|
|
ID string `xml:"ID"`
|
|
ContractFolderID string `xml:"ContractFolderID"`
|
|
IssueDate string `xml:"IssueDate"`
|
|
IssueTime string `xml:"IssueTime"`
|
|
VersionID string `xml:"VersionID"`
|
|
RegulatoryDomain string `xml:"RegulatoryDomain"`
|
|
NoticeTypeCode string `xml:"NoticeTypeCode"`
|
|
NoticeLanguageCode string `xml:"NoticeLanguageCode"`
|
|
Extensions *UBLExtensions `xml:"UBLExtensions,omitempty"`
|
|
ContractingParty *ContractingParty `xml:"ContractingParty,omitempty"`
|
|
TenderingTerms *TenderingTerms `xml:"TenderingTerms,omitempty"`
|
|
TenderingProcess *TenderingProcess `xml:"TenderingProcess,omitempty"`
|
|
ProcurementProject *ProcurementProject `xml:"ProcurementProject,omitempty"`
|
|
ProcurementProjectLot []ProcurementProjectLot `xml:"ProcurementProjectLot,omitempty"`
|
|
ConcessionRevenue *ConcessionRevenue `xml:"ConcessionRevenue,omitempty"`
|
|
ConcessionTerm *ConcessionTerm `xml:"ConcessionTerm,omitempty"`
|
|
}
|
|
|
|
// Interface methods for ConcessionNotice
|
|
func (cn ConcessionNotice) GetID() string { return cn.ID }
|
|
func (cn ConcessionNotice) GetNoticeType() string { return cn.NoticeTypeCode }
|
|
func (cn ConcessionNotice) GetLanguage() string { return cn.NoticeLanguageCode }
|
|
func (cn ConcessionNotice) Validate() error {
|
|
if cn.ID == "" {
|
|
return fmt.Errorf("concession notice ID is required")
|
|
}
|
|
if cn.IssueDate == "" {
|
|
return fmt.Errorf("issue date is required")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// PlanningNotice represents a planning notice
|
|
type PlanningNotice struct {
|
|
XMLName xml.Name `xml:"PlanningNotice"`
|
|
Xmlns string `xml:"xmlns,attr,omitempty"`
|
|
XmlnsCac string `xml:"xmlns:cac,attr,omitempty"`
|
|
XmlnsCbc string `xml:"xmlns:cbc,attr,omitempty"`
|
|
XmlnsEfac string `xml:"xmlns:efac,attr,omitempty"`
|
|
XmlnsEfbc string `xml:"xmlns:efbc,attr,omitempty"`
|
|
XmlnsEfext string `xml:"xmlns:efext,attr,omitempty"`
|
|
XmlnsExt string `xml:"xmlns:ext,attr,omitempty"`
|
|
UBLVersionID string `xml:"UBLVersionID"`
|
|
CustomizationID string `xml:"CustomizationID"`
|
|
ID string `xml:"ID"`
|
|
ContractFolderID string `xml:"ContractFolderID"`
|
|
IssueDate string `xml:"IssueDate"`
|
|
IssueTime string `xml:"IssueTime"`
|
|
VersionID string `xml:"VersionID"`
|
|
RegulatoryDomain string `xml:"RegulatoryDomain"`
|
|
NoticeTypeCode string `xml:"NoticeTypeCode"`
|
|
NoticeLanguageCode string `xml:"NoticeLanguageCode"`
|
|
Extensions *UBLExtensions `xml:"UBLExtensions,omitempty"`
|
|
ContractingParty *ContractingParty `xml:"ContractingParty,omitempty"`
|
|
ProcurementProject *ProcurementProject `xml:"ProcurementProject,omitempty"`
|
|
ProcurementProjectLot []ProcurementProjectLot `xml:"ProcurementProjectLot,omitempty"`
|
|
BudgetAccountLine []BudgetAccountLine `xml:"BudgetAccountLine,omitempty"`
|
|
PlannedPeriod *PlannedPeriod `xml:"PlannedPeriod,omitempty"`
|
|
}
|
|
|
|
// Interface methods for PlanningNotice
|
|
func (pn PlanningNotice) GetID() string { return pn.ID }
|
|
func (pn PlanningNotice) GetNoticeType() string { return pn.NoticeTypeCode }
|
|
func (pn PlanningNotice) GetLanguage() string { return pn.NoticeLanguageCode }
|
|
func (pn PlanningNotice) Validate() error {
|
|
if pn.ID == "" {
|
|
return fmt.Errorf("planning notice ID is required")
|
|
}
|
|
if pn.IssueDate == "" {
|
|
return fmt.Errorf("issue date is required")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// CompetitionNotice represents a competition notice
|
|
type CompetitionNotice struct {
|
|
XMLName xml.Name `xml:"CompetitionNotice"`
|
|
Xmlns string `xml:"xmlns,attr,omitempty"`
|
|
XmlnsCac string `xml:"xmlns:cac,attr,omitempty"`
|
|
XmlnsCbc string `xml:"xmlns:cbc,attr,omitempty"`
|
|
XmlnsEfac string `xml:"xmlns:efac,attr,omitempty"`
|
|
XmlnsEfbc string `xml:"xmlns:efbc,attr,omitempty"`
|
|
XmlnsEfext string `xml:"xmlns:efext,attr,omitempty"`
|
|
XmlnsExt string `xml:"xmlns:ext,attr,omitempty"`
|
|
UBLVersionID string `xml:"UBLVersionID"`
|
|
CustomizationID string `xml:"CustomizationID"`
|
|
ID string `xml:"ID"`
|
|
ContractFolderID string `xml:"ContractFolderID"`
|
|
IssueDate string `xml:"IssueDate"`
|
|
IssueTime string `xml:"IssueTime"`
|
|
VersionID string `xml:"VersionID"`
|
|
RegulatoryDomain string `xml:"RegulatoryDomain"`
|
|
NoticeTypeCode string `xml:"NoticeTypeCode"`
|
|
NoticeLanguageCode string `xml:"NoticeLanguageCode"`
|
|
Extensions *UBLExtensions `xml:"UBLExtensions,omitempty"`
|
|
ContractingParty *ContractingParty `xml:"ContractingParty,omitempty"`
|
|
TenderingTerms *TenderingTerms `xml:"TenderingTerms,omitempty"`
|
|
TenderingProcess *TenderingProcess `xml:"TenderingProcess,omitempty"`
|
|
ProcurementProject *ProcurementProject `xml:"ProcurementProject,omitempty"`
|
|
ProcurementProjectLot []ProcurementProjectLot `xml:"ProcurementProjectLot,omitempty"`
|
|
}
|
|
|
|
// Interface methods for CompetitionNotice
|
|
func (cn CompetitionNotice) GetID() string { return cn.ID }
|
|
func (cn CompetitionNotice) GetNoticeType() string { return cn.NoticeTypeCode }
|
|
func (cn CompetitionNotice) GetLanguage() string { return cn.NoticeLanguageCode }
|
|
func (cn CompetitionNotice) Validate() error {
|
|
if cn.ID == "" {
|
|
return fmt.Errorf("competition notice ID is required")
|
|
}
|
|
if cn.IssueDate == "" {
|
|
return fmt.Errorf("issue date is required")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ResultNotice represents a result notice (eForms specific)
|
|
type ResultNotice struct {
|
|
XMLName xml.Name `xml:"ResultNotice"`
|
|
Xmlns string `xml:"xmlns,attr,omitempty"`
|
|
XmlnsCac string `xml:"xmlns:cac,attr,omitempty"`
|
|
XmlnsCbc string `xml:"xmlns:cbc,attr,omitempty"`
|
|
XmlnsEfac string `xml:"xmlns:efac,attr,omitempty"`
|
|
XmlnsEfbc string `xml:"xmlns:efbc,attr,omitempty"`
|
|
XmlnsEfext string `xml:"xmlns:efext,attr,omitempty"`
|
|
XmlnsExt string `xml:"xmlns:ext,attr,omitempty"`
|
|
UBLVersionID string `xml:"UBLVersionID"`
|
|
CustomizationID string `xml:"CustomizationID"`
|
|
ID string `xml:"ID"`
|
|
ContractFolderID string `xml:"ContractFolderID"`
|
|
IssueDate string `xml:"IssueDate"`
|
|
IssueTime string `xml:"IssueTime"`
|
|
VersionID string `xml:"VersionID"`
|
|
RegulatoryDomain string `xml:"RegulatoryDomain"`
|
|
NoticeTypeCode string `xml:"NoticeTypeCode"`
|
|
NoticeLanguageCode string `xml:"NoticeLanguageCode"`
|
|
Extensions *UBLExtensions `xml:"UBLExtensions,omitempty"`
|
|
ContractingParty *ContractingParty `xml:"ContractingParty,omitempty"`
|
|
TenderingProcess *TenderingProcess `xml:"TenderingProcess,omitempty"`
|
|
ProcurementProject *ProcurementProject `xml:"ProcurementProject,omitempty"`
|
|
ProcurementProjectLot []ProcurementProjectLot `xml:"ProcurementProjectLot,omitempty"`
|
|
TenderResult []TenderResult `xml:"TenderResult,omitempty"`
|
|
SettledContract []SettledContract `xml:"SettledContract,omitempty"`
|
|
}
|
|
|
|
// Interface methods for ResultNotice
|
|
func (rn ResultNotice) GetID() string { return rn.ID }
|
|
func (rn ResultNotice) GetNoticeType() string { return rn.NoticeTypeCode }
|
|
func (rn ResultNotice) GetLanguage() string { return rn.NoticeLanguageCode }
|
|
func (rn ResultNotice) Validate() error {
|
|
if rn.ID == "" {
|
|
return fmt.Errorf("result notice ID is required")
|
|
}
|
|
if rn.IssueDate == "" {
|
|
return fmt.Errorf("issue date is required")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ChangeNotice represents a change notice (eForms specific)
|
|
type ChangeNotice struct {
|
|
XMLName xml.Name `xml:"ChangeNotice"`
|
|
Xmlns string `xml:"xmlns,attr,omitempty"`
|
|
XmlnsCac string `xml:"xmlns:cac,attr,omitempty"`
|
|
XmlnsCbc string `xml:"xmlns:cbc,attr,omitempty"`
|
|
XmlnsEfac string `xml:"xmlns:efac,attr,omitempty"`
|
|
XmlnsEfbc string `xml:"xmlns:efbc,attr,omitempty"`
|
|
XmlnsEfext string `xml:"xmlns:efext,attr,omitempty"`
|
|
XmlnsExt string `xml:"xmlns:ext,attr,omitempty"`
|
|
UBLVersionID string `xml:"UBLVersionID"`
|
|
CustomizationID string `xml:"CustomizationID"`
|
|
ID string `xml:"ID"`
|
|
ContractFolderID string `xml:"ContractFolderID"`
|
|
IssueDate string `xml:"IssueDate"`
|
|
IssueTime string `xml:"IssueTime"`
|
|
VersionID string `xml:"VersionID"`
|
|
RegulatoryDomain string `xml:"RegulatoryDomain"`
|
|
NoticeTypeCode string `xml:"NoticeTypeCode"`
|
|
NoticeLanguageCode string `xml:"NoticeLanguageCode"`
|
|
Extensions *UBLExtensions `xml:"UBLExtensions,omitempty"`
|
|
ContractingParty *ContractingParty `xml:"ContractingParty,omitempty"`
|
|
TenderingTerms *TenderingTerms `xml:"TenderingTerms,omitempty"`
|
|
TenderingProcess *TenderingProcess `xml:"TenderingProcess,omitempty"`
|
|
ProcurementProject *ProcurementProject `xml:"ProcurementProject,omitempty"`
|
|
ProcurementProjectLot []ProcurementProjectLot `xml:"ProcurementProjectLot,omitempty"`
|
|
NoticeVersionIdentifier string `xml:"NoticeVersionIdentifier,omitempty"`
|
|
ChangeReason []ChangeReason `xml:"ChangeReason,omitempty"`
|
|
}
|
|
|
|
// Interface methods for ChangeNotice
|
|
func (cn ChangeNotice) GetID() string { return cn.ID }
|
|
func (cn ChangeNotice) GetNoticeType() string { return cn.NoticeTypeCode }
|
|
func (cn ChangeNotice) GetLanguage() string { return cn.NoticeLanguageCode }
|
|
func (cn ChangeNotice) Validate() error {
|
|
if cn.ID == "" {
|
|
return fmt.Errorf("change notice ID is required")
|
|
}
|
|
if cn.IssueDate == "" {
|
|
return fmt.Errorf("issue date is required")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// SubcontractNotice represents a subcontract notice
|
|
type SubcontractNotice struct {
|
|
XMLName xml.Name `xml:"SubcontractNotice"`
|
|
Xmlns string `xml:"xmlns,attr,omitempty"`
|
|
XmlnsCac string `xml:"xmlns:cac,attr,omitempty"`
|
|
XmlnsCbc string `xml:"xmlns:cbc,attr,omitempty"`
|
|
XmlnsEfac string `xml:"xmlns:efac,attr,omitempty"`
|
|
XmlnsEfbc string `xml:"xmlns:efbc,attr,omitempty"`
|
|
XmlnsEfext string `xml:"xmlns:efext,attr,omitempty"`
|
|
XmlnsExt string `xml:"xmlns:ext,attr,omitempty"`
|
|
UBLVersionID string `xml:"UBLVersionID"`
|
|
CustomizationID string `xml:"CustomizationID"`
|
|
ID string `xml:"ID"`
|
|
ContractFolderID string `xml:"ContractFolderID"`
|
|
IssueDate string `xml:"IssueDate"`
|
|
IssueTime string `xml:"IssueTime"`
|
|
VersionID string `xml:"VersionID"`
|
|
RegulatoryDomain string `xml:"RegulatoryDomain"`
|
|
NoticeTypeCode string `xml:"NoticeTypeCode"`
|
|
NoticeLanguageCode string `xml:"NoticeLanguageCode"`
|
|
Extensions *UBLExtensions `xml:"UBLExtensions,omitempty"`
|
|
ContractingParty *ContractingParty `xml:"ContractingParty,omitempty"`
|
|
TenderingTerms *TenderingTerms `xml:"TenderingTerms,omitempty"`
|
|
TenderingProcess *TenderingProcess `xml:"TenderingProcess,omitempty"`
|
|
ProcurementProject *ProcurementProject `xml:"ProcurementProject,omitempty"`
|
|
SubcontractCall []SubcontractCall `xml:"SubcontractCall,omitempty"`
|
|
ParentContract *ParentContract `xml:"ParentContract,omitempty"`
|
|
}
|
|
|
|
// Interface methods for SubcontractNotice
|
|
func (sn SubcontractNotice) GetID() string { return sn.ID }
|
|
func (sn SubcontractNotice) GetNoticeType() string { return sn.NoticeTypeCode }
|
|
func (sn SubcontractNotice) GetLanguage() string { return sn.NoticeLanguageCode }
|
|
func (sn SubcontractNotice) Validate() error {
|
|
if sn.ID == "" {
|
|
return fmt.Errorf("subcontract notice ID is required")
|
|
}
|
|
if sn.IssueDate == "" {
|
|
return fmt.Errorf("issue date is required")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Additional structures for new notice types
|
|
|
|
// ContestDesignJury contains design contest jury information
|
|
type ContestDesignJury struct {
|
|
XMLName xml.Name `xml:"ContestDesignJury"`
|
|
JuryDecisionDate string `xml:"JuryDecisionDate,omitempty"`
|
|
JuryDecision string `xml:"JuryDecision,omitempty"`
|
|
JuryMember []JuryMember `xml:"JuryMember,omitempty"`
|
|
JudgingCriteria *JudgingCriteria `xml:"JudgingCriteria,omitempty"`
|
|
}
|
|
|
|
// ContestDesignReward contains design contest reward information
|
|
type ContestDesignReward struct {
|
|
XMLName xml.Name `xml:"ContestDesignReward"`
|
|
RewardCode string `xml:"RewardCode,omitempty"`
|
|
Benefit *Benefit `xml:"Benefit,omitempty"`
|
|
}
|
|
|
|
// JuryMember contains jury member information
|
|
type JuryMember struct {
|
|
XMLName xml.Name `xml:"JuryMember"`
|
|
Party *Party `xml:"Party,omitempty"`
|
|
}
|
|
|
|
// JudgingCriteria contains judging criteria information
|
|
type JudgingCriteria struct {
|
|
XMLName xml.Name `xml:"JudgingCriteria"`
|
|
Description string `xml:"Description,omitempty"`
|
|
LanguageID string `xml:"languageID,attr,omitempty"`
|
|
}
|
|
|
|
// Benefit contains benefit information
|
|
type Benefit struct {
|
|
XMLName xml.Name `xml:"Benefit"`
|
|
BenefitValue CurrencyText `xml:"BenefitValue,omitempty"`
|
|
}
|
|
|
|
// QualificationApplication contains qualification application information
|
|
type QualificationApplication struct {
|
|
XMLName xml.Name `xml:"QualificationApplication"`
|
|
QualificationTypeCode string `xml:"QualificationTypeCode,omitempty"`
|
|
ApplicationPeriod *ApplicationPeriod `xml:"ApplicationPeriod,omitempty"`
|
|
TendererQualificationRequest *TendererQualificationRequest `xml:"TendererQualificationRequest,omitempty"`
|
|
}
|
|
|
|
// ApplicationPeriod contains application period information
|
|
type ApplicationPeriod struct {
|
|
XMLName xml.Name `xml:"ApplicationPeriod"`
|
|
StartDate string `xml:"StartDate,omitempty"`
|
|
EndDate string `xml:"EndDate,omitempty"`
|
|
StartTime string `xml:"StartTime,omitempty"`
|
|
EndTime string `xml:"EndTime,omitempty"`
|
|
}
|
|
|
|
// ConcessionRevenue contains concession revenue information
|
|
type ConcessionRevenue struct {
|
|
XMLName xml.Name `xml:"ConcessionRevenue"`
|
|
RevenueType string `xml:"RevenueType,omitempty"`
|
|
RevenueAmount CurrencyText `xml:"RevenueAmount,omitempty"`
|
|
}
|
|
|
|
// ConcessionTerm contains concession term information
|
|
type ConcessionTerm struct {
|
|
XMLName xml.Name `xml:"ConcessionTerm"`
|
|
ConcessionValue CurrencyText `xml:"ConcessionValue,omitempty"`
|
|
Duration *DurationMeasure `xml:"DurationMeasure,omitempty"`
|
|
}
|
|
|
|
// BudgetAccountLine contains budget account line information
|
|
type BudgetAccountLine struct {
|
|
XMLName xml.Name `xml:"BudgetAccountLine"`
|
|
ID string `xml:"ID,omitempty"`
|
|
BudgetYear string `xml:"BudgetYear,omitempty"`
|
|
TotalAmount CurrencyText `xml:"TotalAmount,omitempty"`
|
|
BudgetAccount *BudgetAccount `xml:"BudgetAccount,omitempty"`
|
|
}
|
|
|
|
// BudgetAccount contains budget account information
|
|
type BudgetAccount struct {
|
|
XMLName xml.Name `xml:"BudgetAccount"`
|
|
BudgetCode string `xml:"BudgetCode,omitempty"`
|
|
RequiredClassificationScheme *RequiredClassificationScheme `xml:"RequiredClassificationScheme,omitempty"`
|
|
}
|
|
|
|
// RequiredClassificationScheme contains classification scheme information
|
|
type RequiredClassificationScheme struct {
|
|
XMLName xml.Name `xml:"RequiredClassificationScheme"`
|
|
ItemClassificationCode string `xml:"ItemClassificationCode,omitempty"`
|
|
}
|
|
|
|
// SubcontractCall contains subcontract call information
|
|
type SubcontractCall struct {
|
|
XMLName xml.Name `xml:"SubcontractCall"`
|
|
SubcontractingTypeCode string `xml:"SubcontractingTypeCode,omitempty"`
|
|
TenderingTerms *TenderingTerms `xml:"TenderingTerms,omitempty"`
|
|
ProcurementProject *ProcurementProject `xml:"ProcurementProject,omitempty"`
|
|
}
|
|
|
|
// ParentContract contains parent contract information
|
|
type ParentContract struct {
|
|
XMLName xml.Name `xml:"ParentContract"`
|
|
ContractReference *ContractReference `xml:"ContractReference,omitempty"`
|
|
FrameworkAgreement *FrameworkAgreement `xml:"FrameworkAgreement,omitempty"`
|
|
}
|
|
|
|
// FrameworkAgreement contains framework agreement information
|
|
type FrameworkAgreement struct {
|
|
XMLName xml.Name `xml:"FrameworkAgreement"`
|
|
EstimatedMaximumValue CurrencyText `xml:"EstimatedMaximumValue,omitempty"`
|
|
MaximumParticipants string `xml:"MaximumParticipants,omitempty"`
|
|
Duration *DurationMeasure `xml:"DurationMeasure,omitempty"`
|
|
SubsequentProcedureIndicator string `xml:"SubsequentProcedureIndicator,omitempty"`
|
|
}
|
|
|
|
// AdditionalDocumentReference contains additional document reference information
|
|
type AdditionalDocumentReference struct {
|
|
XMLName xml.Name `xml:"AdditionalDocumentReference"`
|
|
ID string `xml:"ID,omitempty"`
|
|
DocumentType string `xml:"DocumentType,omitempty"`
|
|
DocumentDescription string `xml:"DocumentDescription,omitempty"`
|
|
LanguageID string `xml:"LanguageID,omitempty"`
|
|
Attachment *Attachment `xml:"Attachment,omitempty"`
|
|
ValidityPeriod *ValidityPeriod `xml:"ValidityPeriod,omitempty"`
|
|
UBLExtensions *UBLExtensions `xml:"UBLExtensions,omitempty"`
|
|
}
|
|
|
|
// ValidityPeriod contains validity period information
|
|
type ValidityPeriod struct {
|
|
XMLName xml.Name `xml:"ValidityPeriod"`
|
|
StartDate string `xml:"StartDate,omitempty"`
|
|
EndDate string `xml:"EndDate,omitempty"`
|
|
StartTime string `xml:"StartTime,omitempty"`
|
|
EndTime string `xml:"EndTime,omitempty"`
|
|
}
|
|
|
|
// EnhancedTenderResult structure with more comprehensive information
|
|
type EnhancedTenderResult struct {
|
|
XMLName xml.Name `xml:"TenderResult"`
|
|
AwardDate string `xml:"AwardDate"`
|
|
AwardTime string `xml:"AwardTime,omitempty"`
|
|
ReceivedTenderQuantity string `xml:"ReceivedTenderQuantity,omitempty"`
|
|
LowerTenderAmount CurrencyText `xml:"LowerTenderAmount,omitempty"`
|
|
HigherTenderAmount CurrencyText `xml:"HigherTenderAmount,omitempty"`
|
|
AwardedTenderedProject *AwardedTenderedProject `xml:"AwardedTenderedProject,omitempty"`
|
|
ContractAwardNotice *ContractAwardNotice `xml:"ContractAwardNotice,omitempty"`
|
|
SubcontractingTerm []SubcontractingTerm `xml:"SubcontractingTerm,omitempty"`
|
|
WinningTenderLot []WinningTenderLot `xml:"WinningTenderLot,omitempty"`
|
|
ReceivedSubmissionsStatistics []ReceivedSubmissionsStatistics `xml:"ReceivedSubmissionsStatistics,omitempty"`
|
|
Extensions *UBLExtensions `xml:"UBLExtensions,omitempty"`
|
|
}
|
|
|
|
// AwardedTenderedProject contains awarded tendered project information
|
|
type AwardedTenderedProject struct {
|
|
XMLName xml.Name `xml:"AwardedTenderedProject"`
|
|
ProcurementTypeCode string `xml:"ProcurementTypeCode,omitempty"`
|
|
TotalAmount CurrencyText `xml:"TotalAmount,omitempty"`
|
|
ProcurementProject *ProcurementProject `xml:"ProcurementProject,omitempty"`
|
|
LegalMonetaryTotal *LegalMonetaryTotal `xml:"LegalMonetaryTotal,omitempty"`
|
|
TenderLot []TenderLot `xml:"TenderLot,omitempty"`
|
|
}
|
|
|
|
// WinningTenderLot contains winning tender lot information
|
|
type WinningTenderLot struct {
|
|
XMLName xml.Name `xml:"WinningTenderLot"`
|
|
TenderLot *TenderLot `xml:"TenderLot,omitempty"`
|
|
WinningTender []WinningTender `xml:"WinningTender,omitempty"`
|
|
}
|
|
|
|
// WinningTender contains winning tender information
|
|
type WinningTender struct {
|
|
XMLName xml.Name `xml:"WinningTender"`
|
|
ID string `xml:"ID"`
|
|
TenderVariantIndicator string `xml:"TenderVariantIndicator,omitempty"`
|
|
LegalMonetaryTotal *LegalMonetaryTotal `xml:"LegalMonetaryTotal,omitempty"`
|
|
TenderingParty *TenderingParty `xml:"TenderingParty,omitempty"`
|
|
SubcontractingTerm []SubcontractingTerm `xml:"SubcontractingTerm,omitempty"`
|
|
}
|
|
|
|
// Enhanced SubcontractingTerm structure
|
|
type SubcontractingTerm struct {
|
|
XMLName xml.Name `xml:"SubcontractingTerm"`
|
|
TermCode string `xml:"TermCode"`
|
|
Amount CurrencyText `xml:"Amount,omitempty"`
|
|
Rate string `xml:"Rate,omitempty"`
|
|
Description string `xml:"Description,omitempty"`
|
|
UnknownPriceIndicator string `xml:"UnknownPriceIndicator,omitempty"`
|
|
SubcontractingDescription string `xml:"SubcontractingDescription,omitempty"`
|
|
}
|
|
|
|
// ContractNotice represents the main TED XML contract notice structure
|
|
type ContractNotice struct {
|
|
XMLName xml.Name `xml:"ContractNotice"`
|
|
Xmlns string `xml:"xmlns,attr,omitempty"`
|
|
XmlnsCac string `xml:"xmlns:cac,attr,omitempty"`
|
|
XmlnsCbc string `xml:"xmlns:cbc,attr,omitempty"`
|
|
XmlnsEfac string `xml:"xmlns:efac,attr,omitempty"`
|
|
XmlnsEfbc string `xml:"xmlns:efbc,attr,omitempty"`
|
|
XmlnsEfext string `xml:"xmlns:efext,attr,omitempty"`
|
|
XmlnsExt string `xml:"xmlns:ext,attr,omitempty"`
|
|
UBLVersionID string `xml:"UBLVersionID"`
|
|
CustomizationID string `xml:"CustomizationID"`
|
|
ID string `xml:"ID"`
|
|
ContractFolderID string `xml:"ContractFolderID"`
|
|
IssueDate string `xml:"IssueDate"`
|
|
IssueTime string `xml:"IssueTime"`
|
|
VersionID string `xml:"VersionID"`
|
|
RegulatoryDomain string `xml:"RegulatoryDomain"`
|
|
NoticeTypeCode NoticeTypeCodeField `xml:"NoticeTypeCode,omitempty"`
|
|
NoticeLanguageCode string `xml:"NoticeLanguageCode"`
|
|
Extensions *UBLExtensions `xml:"UBLExtensions,omitempty"`
|
|
ContractingParty *ContractingParty `xml:"ContractingParty,omitempty"`
|
|
TenderingTerms *TenderingTerms `xml:"TenderingTerms,omitempty"`
|
|
TenderingProcess *TenderingProcess `xml:"TenderingProcess,omitempty"`
|
|
ProcurementProject *ProcurementProject `xml:"ProcurementProject,omitempty"`
|
|
ProcurementProjectLots []ProcurementProjectLot `xml:"ProcurementProjectLot,omitempty"`
|
|
}
|
|
|
|
func (cn ContractNotice) firstLot() *ProcurementProjectLot {
|
|
if len(cn.ProcurementProjectLots) == 0 {
|
|
return nil
|
|
}
|
|
return &cn.ProcurementProjectLots[0]
|
|
}
|
|
|
|
// ContractAwardNotice represents the main TED XML contract award notice structure
|
|
type ContractAwardNotice struct {
|
|
XMLName xml.Name `xml:"ContractAwardNotice"`
|
|
Xmlns string `xml:"xmlns,attr,omitempty"`
|
|
XmlnsCac string `xml:"xmlns:cac,attr,omitempty"`
|
|
XmlnsCbc string `xml:"xmlns:cbc,attr,omitempty"`
|
|
XmlnsEfac string `xml:"xmlns:efac,attr,omitempty"`
|
|
XmlnsEfbc string `xml:"xmlns:efbc,attr,omitempty"`
|
|
XmlnsEfext string `xml:"xmlns:efext,attr,omitempty"`
|
|
XmlnsExt string `xml:"xmlns:ext,attr,omitempty"`
|
|
Extensions *UBLExtensions `xml:"UBLExtensions,omitempty"`
|
|
UBLVersionID string `xml:"UBLVersionID"`
|
|
CustomizationID string `xml:"CustomizationID"`
|
|
ID string `xml:"ID"`
|
|
ContractFolderID string `xml:"ContractFolderID"`
|
|
IssueDate string `xml:"IssueDate"`
|
|
IssueTime string `xml:"IssueTime"`
|
|
VersionID string `xml:"VersionID"`
|
|
RegulatoryDomain string `xml:"RegulatoryDomain"`
|
|
NoticeTypeCode NoticeTypeCodeField `xml:"NoticeTypeCode,omitempty"`
|
|
NoticeLanguageCode string `xml:"NoticeLanguageCode"`
|
|
ContractingParty *ContractingParty `xml:"ContractingParty,omitempty"`
|
|
TenderingProcess *TenderingProcess `xml:"TenderingProcess,omitempty"`
|
|
ProcurementProject *ProcurementProject `xml:"ProcurementProject,omitempty"`
|
|
ProcurementProjectLot []ProcurementProjectLot `xml:"ProcurementProjectLot,omitempty"`
|
|
TenderResult *TenderResult `xml:"TenderResult,omitempty"`
|
|
}
|
|
|
|
func (can ContractAwardNotice) firstAwardLot() *ProcurementProjectLot {
|
|
if len(can.ProcurementProjectLot) == 0 {
|
|
return nil
|
|
}
|
|
return &can.ProcurementProjectLot[0]
|
|
}
|
|
|
|
func pickWinningLotTender(lotTenders []LotTender) *LotTender {
|
|
if len(lotTenders) == 0 {
|
|
return nil
|
|
}
|
|
for i := range lotTenders {
|
|
if strings.TrimSpace(lotTenders[i].RankCode) == "1" {
|
|
return &lotTenders[i]
|
|
}
|
|
}
|
|
return &lotTenders[0]
|
|
}
|
|
|
|
// NoticeResult contains notice result information from extensions
|
|
type NoticeResult struct {
|
|
XMLName xml.Name `xml:"NoticeResult"`
|
|
TotalAmount *CurrencyText `xml:"TotalAmount,omitempty"`
|
|
LotResult []LotResult `xml:"LotResult,omitempty"`
|
|
}
|
|
|
|
// LotResult contains lot result information
|
|
type LotResult struct {
|
|
XMLName xml.Name `xml:"LotResult"`
|
|
ID string `xml:"ID"`
|
|
TenderResultCode string `xml:"TenderResultCode"`
|
|
LotTender []LotTender `xml:"LotTender,omitempty"`
|
|
ReceivedSubmissionsStatistics []ReceivedSubmissionsStatistics `xml:"ReceivedSubmissionsStatistics,omitempty"`
|
|
SettledContract []SettledContract `xml:"SettledContract,omitempty"`
|
|
TenderLot *TenderLot `xml:"TenderLot,omitempty"`
|
|
}
|
|
|
|
// LotTender contains lot tender information
|
|
type LotTender struct {
|
|
XMLName xml.Name `xml:"LotTender"`
|
|
ID string `xml:"ID"`
|
|
RankCode string `xml:"RankCode,omitempty"`
|
|
TenderRankedIndicator string `xml:"TenderRankedIndicator,omitempty"`
|
|
TenderVariantIndicator string `xml:"TenderVariantIndicator,omitempty"`
|
|
LegalMonetaryTotal *LegalMonetaryTotal `xml:"LegalMonetaryTotal,omitempty"`
|
|
SubcontractingTerm *SubcontractingTerm `xml:"SubcontractingTerm,omitempty"`
|
|
TenderingParty *TenderingParty `xml:"TenderingParty,omitempty"`
|
|
TenderLot *TenderLot `xml:"TenderLot,omitempty"`
|
|
TenderReference *TenderReference `xml:"TenderReference,omitempty"`
|
|
}
|
|
|
|
// LegalMonetaryTotal contains monetary total information
|
|
type LegalMonetaryTotal struct {
|
|
XMLName xml.Name `xml:"LegalMonetaryTotal"`
|
|
PayableAmount CurrencyText `xml:"PayableAmount"`
|
|
}
|
|
|
|
// TenderingParty contains tendering party information
|
|
type TenderingParty struct {
|
|
XMLName xml.Name `xml:"TenderingParty"`
|
|
ID string `xml:"ID"`
|
|
}
|
|
|
|
// TenderLot contains tender lot information
|
|
type TenderLot struct {
|
|
XMLName xml.Name `xml:"TenderLot"`
|
|
ID string `xml:"ID"`
|
|
}
|
|
|
|
// TenderReference contains tender reference information
|
|
type TenderReference struct {
|
|
XMLName xml.Name `xml:"TenderReference"`
|
|
ID string `xml:"ID"`
|
|
}
|
|
|
|
// ReceivedSubmissionsStatistics contains received submissions statistics
|
|
type ReceivedSubmissionsStatistics struct {
|
|
XMLName xml.Name `xml:"ReceivedSubmissionsStatistics"`
|
|
StatisticsCode string `xml:"StatisticsCode"`
|
|
StatisticsNumeric string `xml:"StatisticsNumeric"`
|
|
}
|
|
|
|
// SettledContract contains settled contract information
|
|
type SettledContract struct {
|
|
XMLName xml.Name `xml:"SettledContract"`
|
|
ID string `xml:"ID"`
|
|
IssueDate string `xml:"IssueDate,omitempty"`
|
|
Title string `xml:"Title,omitempty"`
|
|
SignatoryParty *SignatoryParty `xml:"SignatoryParty,omitempty"`
|
|
ContractReference *ContractReference `xml:"ContractReference,omitempty"`
|
|
LotTender *LotTender `xml:"LotTender,omitempty"`
|
|
}
|
|
|
|
// SignatoryParty contains signatory party information
|
|
type SignatoryParty struct {
|
|
XMLName xml.Name `xml:"SignatoryParty"`
|
|
PartyIdentification *PartyIdentification `xml:"PartyIdentification,omitempty"`
|
|
}
|
|
|
|
// ContractReference contains contract reference information
|
|
type ContractReference struct {
|
|
XMLName xml.Name `xml:"ContractReference"`
|
|
ID string `xml:"ID"`
|
|
}
|
|
|
|
// TenderResult contains tender result information
|
|
type TenderResult struct {
|
|
XMLName xml.Name `xml:"TenderResult"`
|
|
AwardDate string `xml:"AwardDate"`
|
|
}
|
|
|
|
// NoticeDocumentReference contains notice document reference information
|
|
type NoticeDocumentReference struct {
|
|
XMLName xml.Name `xml:"NoticeDocumentReference"`
|
|
ID string `xml:"ID"`
|
|
}
|
|
|
|
// PresentationPeriod contains presentation period information
|
|
type PresentationPeriod struct {
|
|
XMLName xml.Name `xml:"PresentationPeriod"`
|
|
Description string `xml:"Description,omitempty"`
|
|
LanguageID string `xml:"languageID,attr,omitempty"`
|
|
}
|
|
|
|
// AppealInformationParty contains appeal information party
|
|
type AppealInformationParty struct {
|
|
XMLName xml.Name `xml:"AppealInformationParty"`
|
|
PartyIdentification *PartyIdentification `xml:"PartyIdentification,omitempty"`
|
|
}
|
|
|
|
// MediationParty contains mediation party information
|
|
type MediationParty struct {
|
|
XMLName xml.Name `xml:"MediationParty"`
|
|
PartyIdentification *PartyIdentification `xml:"PartyIdentification,omitempty"`
|
|
}
|
|
|
|
// UBLExtensions contains eForms extensions
|
|
type UBLExtensions struct {
|
|
XMLName xml.Name `xml:"UBLExtensions"`
|
|
UBLExtension *UBLExtension `xml:"UBLExtension,omitempty"`
|
|
}
|
|
|
|
// UBLExtension contains extension content
|
|
type UBLExtension struct {
|
|
XMLName xml.Name `xml:"UBLExtension"`
|
|
ExtensionContent *ExtensionContent `xml:"ExtensionContent,omitempty"`
|
|
}
|
|
|
|
// ExtensionContent contains eForms extension data
|
|
type ExtensionContent struct {
|
|
XMLName xml.Name `xml:"ExtensionContent"`
|
|
EformsExtension *EformsExtension `xml:"EformsExtension,omitempty"`
|
|
TransmissionDate string `xml:"TransmissionDate,omitempty"`
|
|
TransmissionTime string `xml:"TransmissionTime,omitempty"`
|
|
}
|
|
|
|
// EformsExtension contains eForms specific data
|
|
type EformsExtension struct {
|
|
XMLName xml.Name `xml:"EformsExtension"`
|
|
NoticeResult *NoticeResult `xml:"NoticeResult,omitempty"`
|
|
NoticeSubType *NoticeSubType `xml:"NoticeSubType,omitempty"`
|
|
Organizations *Organizations `xml:"Organizations,omitempty"`
|
|
Publication *Publication `xml:"Publication,omitempty"`
|
|
Changes *Changes `xml:"Changes,omitempty"`
|
|
SelectionCriteria []SelectionCriteria `xml:"SelectionCriteria,omitempty"`
|
|
OfficialLanguages *OfficialLanguages `xml:"OfficialLanguages,omitempty"`
|
|
AwardCriterionParameter *AwardCriterionParameter `xml:"AwardCriterionParameter,omitempty"`
|
|
AccessToolName string `xml:"AccessToolName,omitempty"`
|
|
TransmissionDate string `xml:"TransmissionDate,omitempty"`
|
|
TransmissionTime string `xml:"TransmissionTime,omitempty"`
|
|
}
|
|
|
|
// NoticeSubType contains notice subtype information
|
|
type NoticeSubType struct {
|
|
XMLName xml.Name `xml:"NoticeSubType"`
|
|
SubTypeCode string `xml:"SubTypeCode"`
|
|
}
|
|
|
|
// SelectionCriteria contains tenderer requirement information
|
|
type SelectionCriteria struct {
|
|
XMLName xml.Name `xml:"SelectionCriteria"`
|
|
CriterionTypeCode string `xml:"CriterionTypeCode"`
|
|
TendererRequirementTypeCode string `xml:"TendererRequirementTypeCode"`
|
|
Name string `xml:"Name,omitempty"`
|
|
Description string `xml:"Description,omitempty"`
|
|
CalculationExpressionCode string `xml:"CalculationExpressionCode,omitempty"`
|
|
LanguageID string `xml:"languageID,attr,omitempty"`
|
|
}
|
|
|
|
// OfficialLanguages contains official language information
|
|
type OfficialLanguages struct {
|
|
XMLName xml.Name `xml:"OfficialLanguages"`
|
|
Language []Language `xml:"Language"`
|
|
}
|
|
|
|
// Language represents a language entry
|
|
type Language struct {
|
|
XMLName xml.Name `xml:"Language"`
|
|
ID string `xml:"ID"`
|
|
}
|
|
|
|
// Organizations contains organization information
|
|
type Organizations struct {
|
|
XMLName xml.Name `xml:"Organizations"`
|
|
Organization []Organization `xml:"Organization"`
|
|
}
|
|
|
|
// Organization represents an organization entity
|
|
type Organization struct {
|
|
XMLName xml.Name `xml:"Organization"`
|
|
NaturalPersonIndicator string `xml:"NaturalPersonIndicator,omitempty"`
|
|
Company *Company `xml:"Company,omitempty"`
|
|
}
|
|
|
|
// Company represents company informationf
|
|
type Company struct {
|
|
XMLName xml.Name `xml:"Company"`
|
|
WebsiteURI string `xml:"WebsiteURI,omitempty"`
|
|
PartyIdentification *PartyIdentification `xml:"PartyIdentification,omitempty"`
|
|
PartyName *PartyName `xml:"PartyName,omitempty"`
|
|
PostalAddress *PostalAddress `xml:"PostalAddress,omitempty"`
|
|
PartyLegalEntity *PartyLegalEntity `xml:"PartyLegalEntity,omitempty"`
|
|
Contact *Contact `xml:"Contact,omitempty"`
|
|
}
|
|
|
|
// PartyIdentification contains party ID information
|
|
type PartyIdentification struct {
|
|
XMLName xml.Name `xml:"PartyIdentification"`
|
|
ID string `xml:"ID"`
|
|
}
|
|
|
|
// PartyName contains party name
|
|
type PartyName struct {
|
|
XMLName xml.Name `xml:"PartyName"`
|
|
Name string `xml:"Name"`
|
|
}
|
|
|
|
// PostalAddress contains address information
|
|
type PostalAddress struct {
|
|
XMLName xml.Name `xml:"PostalAddress"`
|
|
StreetName string `xml:"StreetName,omitempty"`
|
|
CityName string `xml:"CityName,omitempty"`
|
|
PostalZone string `xml:"PostalZone,omitempty"`
|
|
CountrySubentity string `xml:"CountrySubentity,omitempty"`
|
|
CountrySubentityCode string `xml:"CountrySubentityCode,omitempty"`
|
|
Department string `xml:"Department,omitempty"`
|
|
Country *Country `xml:"Country,omitempty"`
|
|
}
|
|
|
|
// Country contains country information
|
|
type Country struct {
|
|
XMLName xml.Name `xml:"Country"`
|
|
IdentificationCode string `xml:"IdentificationCode"`
|
|
}
|
|
|
|
// PartyLegalEntity contains legal entity information
|
|
type PartyLegalEntity struct {
|
|
XMLName xml.Name `xml:"PartyLegalEntity"`
|
|
CompanyID string `xml:"CompanyID"`
|
|
}
|
|
|
|
// Contact contains contact information
|
|
type Contact struct {
|
|
XMLName xml.Name `xml:"Contact"`
|
|
Name string `xml:"Name,omitempty"`
|
|
Telephone string `xml:"Telephone,omitempty"`
|
|
Telefax string `xml:"Telefax,omitempty"`
|
|
ElectronicMail string `xml:"ElectronicMail,omitempty"`
|
|
}
|
|
|
|
// Publication contains publication information
|
|
type Publication struct {
|
|
XMLName xml.Name `xml:"Publication"`
|
|
NoticePublicationID string `xml:"NoticePublicationID"`
|
|
GazetteID string `xml:"GazetteID"`
|
|
PublicationDate string `xml:"PublicationDate"`
|
|
}
|
|
|
|
// Changes contains notice change information
|
|
type Changes struct {
|
|
XMLName xml.Name `xml:"Changes"`
|
|
ChangedNoticeIdentifier string `xml:"ChangedNoticeIdentifier"`
|
|
ChangeReason *ChangeReason `xml:"ChangeReason,omitempty"`
|
|
}
|
|
|
|
// ChangeReason contains change reason information
|
|
type ChangeReason struct {
|
|
XMLName xml.Name `xml:"ChangeReason"`
|
|
ReasonCode string `xml:"ReasonCode"`
|
|
}
|
|
|
|
// ContractingParty contains contracting party information
|
|
type ContractingParty struct {
|
|
XMLName xml.Name `xml:"ContractingParty"`
|
|
BuyerProfileURI string `xml:"BuyerProfileURI,omitempty"`
|
|
ContractingPartyType *ContractingPartyType `xml:"ContractingPartyType,omitempty"`
|
|
ContractingActivity *ContractingActivity `xml:"ContractingActivity,omitempty"`
|
|
Party *Party `xml:"Party,omitempty"`
|
|
}
|
|
|
|
// ContractingPartyType contains party type information
|
|
type ContractingPartyType struct {
|
|
XMLName xml.Name `xml:"ContractingPartyType"`
|
|
PartyTypeCode string `xml:"PartyTypeCode"`
|
|
}
|
|
|
|
// ContractingActivity contains contracting activity information
|
|
type ContractingActivity struct {
|
|
XMLName xml.Name `xml:"ContractingActivity"`
|
|
ActivityTypeCode string `xml:"ActivityTypeCode"`
|
|
}
|
|
|
|
// Party contains party information
|
|
type Party struct {
|
|
XMLName xml.Name `xml:"Party"`
|
|
PartyIdentification *PartyIdentification `xml:"PartyIdentification,omitempty"`
|
|
ServiceProviderParty *ServiceProviderParty `xml:"ServiceProviderParty,omitempty"`
|
|
}
|
|
|
|
// ServiceProviderParty contains service provider information
|
|
type ServiceProviderParty struct {
|
|
XMLName xml.Name `xml:"ServiceProviderParty"`
|
|
ServiceTypeCode string `xml:"ServiceTypeCode,omitempty"`
|
|
Party *Party `xml:"Party,omitempty"`
|
|
}
|
|
|
|
// TenderingTerms contains tendering terms
|
|
type TenderingTerms struct {
|
|
XMLName xml.Name `xml:"TenderingTerms"`
|
|
VariantConstraintCode string `xml:"VariantConstraintCode,omitempty"`
|
|
FundingProgramCode string `xml:"FundingProgramCode,omitempty"`
|
|
RecurringProcurementIndicator string `xml:"RecurringProcurementIndicator,omitempty"`
|
|
MultipleTendersCode string `xml:"MultipleTendersCode,omitempty"`
|
|
TendererQualificationRequest []TendererQualificationRequest `xml:"TendererQualificationRequest,omitempty"`
|
|
AppealTerms *AppealTerms `xml:"AppealTerms,omitempty"`
|
|
CallForTendersDocumentReference []CallForTendersDocumentReference `xml:"CallForTendersDocumentReference,omitempty"`
|
|
RequiredFinancialGuarantee *RequiredFinancialGuarantee `xml:"RequiredFinancialGuarantee,omitempty"`
|
|
PaymentTerms *PaymentTerms `xml:"PaymentTerms,omitempty"`
|
|
AwardingTerms *AwardingTerms `xml:"AwardingTerms,omitempty"`
|
|
AdditionalInformationParty *AdditionalInformationParty `xml:"AdditionalInformationParty,omitempty"`
|
|
TenderRecipientParty *TenderRecipientParty `xml:"TenderRecipientParty,omitempty"`
|
|
TenderValidityPeriod *TenderValidityPeriod `xml:"TenderValidityPeriod,omitempty"`
|
|
Language *Language `xml:"Language,omitempty"`
|
|
PostAwardProcess *PostAwardProcess `xml:"PostAwardProcess,omitempty"`
|
|
ContractExecutionRequirement []ContractExecutionRequirement `xml:"ContractExecutionRequirement,omitempty"`
|
|
UBLExtensions *UBLExtensions `xml:"UBLExtensions,omitempty"`
|
|
}
|
|
|
|
// RequiredFinancialGuarantee contains financial guarantee information
|
|
type RequiredFinancialGuarantee struct {
|
|
XMLName xml.Name `xml:"RequiredFinancialGuarantee"`
|
|
GuaranteeTypeCode string `xml:"GuaranteeTypeCode,omitempty"`
|
|
Description string `xml:"Description,omitempty"`
|
|
LanguageID string `xml:"languageID,attr,omitempty"`
|
|
}
|
|
|
|
// PaymentTerms contains payment terms information
|
|
type PaymentTerms struct {
|
|
XMLName xml.Name `xml:"PaymentTerms"`
|
|
Note string `xml:"Note,omitempty"`
|
|
LanguageID string `xml:"languageID,attr,omitempty"`
|
|
}
|
|
|
|
// AwardingTerms contains awarding terms information
|
|
type AwardingTerms struct {
|
|
XMLName xml.Name `xml:"AwardingTerms"`
|
|
AwardingCriterion []AwardingCriterion `xml:"AwardingCriterion,omitempty"`
|
|
}
|
|
|
|
// AwardingCriterion contains awarding criteria information
|
|
type AwardingCriterion struct {
|
|
XMLName xml.Name `xml:"AwardingCriterion"`
|
|
SubordinateAwardingCriterion []SubordinateAwardingCriterion `xml:"SubordinateAwardingCriterion,omitempty"`
|
|
}
|
|
|
|
// SubordinateAwardingCriterion contains subordinate awarding criteria
|
|
type SubordinateAwardingCriterion struct {
|
|
XMLName xml.Name `xml:"SubordinateAwardingCriterion"`
|
|
AwardingCriterionTypeCode string `xml:"AwardingCriterionTypeCode,omitempty"`
|
|
Name string `xml:"Name,omitempty"`
|
|
Description string `xml:"Description,omitempty"`
|
|
UBLExtensions *UBLExtensions `xml:"UBLExtensions,omitempty"`
|
|
}
|
|
|
|
// AwardCriterionParameter contains award criterion parameter information
|
|
type AwardCriterionParameter struct {
|
|
XMLName xml.Name `xml:"AwardCriterionParameter"`
|
|
ParameterCode string `xml:"ParameterCode,omitempty"`
|
|
ParameterNumeric string `xml:"ParameterNumeric,omitempty"`
|
|
}
|
|
|
|
// AdditionalInformationParty contains additional information party
|
|
type AdditionalInformationParty struct {
|
|
XMLName xml.Name `xml:"AdditionalInformationParty"`
|
|
PartyIdentification *PartyIdentification `xml:"PartyIdentification,omitempty"`
|
|
}
|
|
|
|
// TenderRecipientParty contains tender recipient party information
|
|
type TenderRecipientParty struct {
|
|
XMLName xml.Name `xml:"TenderRecipientParty"`
|
|
EndpointID string `xml:"EndpointID,omitempty"`
|
|
}
|
|
|
|
// TenderValidityPeriod contains tender validity period
|
|
type TenderValidityPeriod struct {
|
|
XMLName xml.Name `xml:"TenderValidityPeriod"`
|
|
DurationMeasure *DurationMeasure `xml:"DurationMeasure,omitempty"`
|
|
}
|
|
|
|
// PostAwardProcess contains post award process information
|
|
type PostAwardProcess struct {
|
|
XMLName xml.Name `xml:"PostAwardProcess"`
|
|
ElectronicCatalogueUsageIndicator string `xml:"ElectronicCatalogueUsageIndicator,omitempty"`
|
|
ElectronicOrderUsageIndicator string `xml:"ElectronicOrderUsageIndicator,omitempty"`
|
|
ElectronicPaymentUsageIndicator string `xml:"ElectronicPaymentUsageIndicator,omitempty"`
|
|
}
|
|
|
|
// ContractExecutionRequirement contains contract execution requirements
|
|
type ContractExecutionRequirement struct {
|
|
XMLName xml.Name `xml:"ContractExecutionRequirement"`
|
|
ExecutionRequirementCode string `xml:"ExecutionRequirementCode,omitempty"`
|
|
Description string `xml:"Description,omitempty"`
|
|
LanguageID string `xml:"languageID,attr,omitempty"`
|
|
}
|
|
|
|
// AppealTerms contains appeal terms information
|
|
type AppealTerms struct {
|
|
XMLName xml.Name `xml:"AppealTerms"`
|
|
Description string `xml:"Description,omitempty"`
|
|
LanguageID string `xml:"languageID,attr,omitempty"`
|
|
PresentationPeriod *PresentationPeriod `xml:"PresentationPeriod,omitempty"`
|
|
AppealInformationParty *AppealInformationParty `xml:"AppealInformationParty,omitempty"`
|
|
AppealReceiverParty *AppealReceiverParty `xml:"AppealReceiverParty,omitempty"`
|
|
MediationParty *MediationParty `xml:"MediationParty,omitempty"`
|
|
}
|
|
|
|
// AppealReceiverParty contains appeal receiver party information
|
|
type AppealReceiverParty struct {
|
|
XMLName xml.Name `xml:"AppealReceiverParty"`
|
|
PartyIdentification *PartyIdentification `xml:"PartyIdentification,omitempty"`
|
|
}
|
|
|
|
// CallForTendersDocumentReference contains document reference information
|
|
type CallForTendersDocumentReference struct {
|
|
XMLName xml.Name `xml:"CallForTendersDocumentReference"`
|
|
ID string `xml:"ID,omitempty"`
|
|
DocumentType string `xml:"DocumentType,omitempty"`
|
|
LanguageID string `xml:"LanguageID,omitempty"`
|
|
DocumentStatusCode string `xml:"DocumentStatusCode,omitempty"`
|
|
Attachment *Attachment `xml:"Attachment,omitempty"`
|
|
UBLExtensions *UBLExtensions `xml:"UBLExtensions,omitempty"`
|
|
}
|
|
|
|
// Attachment contains attachment information
|
|
type Attachment struct {
|
|
XMLName xml.Name `xml:"Attachment"`
|
|
ExternalReference *ExternalReference `xml:"ExternalReference,omitempty"`
|
|
}
|
|
|
|
// ExternalReference contains external reference information
|
|
type ExternalReference struct {
|
|
XMLName xml.Name `xml:"ExternalReference"`
|
|
URI string `xml:"URI"`
|
|
DocumentHash string `xml:"DocumentHash,omitempty"`
|
|
FileName string `xml:"FileName,omitempty"`
|
|
}
|
|
|
|
// TendererQualificationRequest contains qualification request information
|
|
type TendererQualificationRequest struct {
|
|
XMLName xml.Name `xml:"TendererQualificationRequest"`
|
|
CompanyLegalFormCode string `xml:"CompanyLegalFormCode,omitempty"`
|
|
SpecificTendererRequirement []SpecificTendererRequirement `xml:"SpecificTendererRequirement,omitempty"`
|
|
}
|
|
|
|
// SpecificTendererRequirement contains specific requirement information
|
|
type SpecificTendererRequirement struct {
|
|
XMLName xml.Name `xml:"SpecificTendererRequirement"`
|
|
TendererRequirementTypeCode string `xml:"TendererRequirementTypeCode"`
|
|
Description string `xml:"Description,omitempty"`
|
|
}
|
|
|
|
// TenderingProcess contains tendering process information
|
|
type TenderingProcess struct {
|
|
XMLName xml.Name `xml:"TenderingProcess"`
|
|
Description string `xml:"Description,omitempty"`
|
|
ProcedureCode string `xml:"ProcedureCode"`
|
|
SubmissionMethodCode string `xml:"SubmissionMethodCode,omitempty"`
|
|
GovernmentAgreementConstraintIndicator string `xml:"GovernmentAgreementConstraintIndicator,omitempty"`
|
|
ProcessJustification *ProcessJustification `xml:"ProcessJustification,omitempty"`
|
|
TenderSubmissionDeadlinePeriod *TenderSubmissionDeadlinePeriod `xml:"TenderSubmissionDeadlinePeriod,omitempty"`
|
|
OpenTenderEvent *OpenTenderEvent `xml:"OpenTenderEvent,omitempty"`
|
|
AuctionTerms *AuctionTerms `xml:"AuctionTerms,omitempty"`
|
|
ContractingSystem []ContractingSystem `xml:"ContractingSystem,omitempty"`
|
|
NoticeDocumentReference []NoticeDocumentReference `xml:"NoticeDocumentReference,omitempty"`
|
|
UBLExtensions *UBLExtensions `xml:"UBLExtensions,omitempty"`
|
|
}
|
|
|
|
// TenderSubmissionDeadlinePeriod contains tender submission deadline information
|
|
type TenderSubmissionDeadlinePeriod struct {
|
|
XMLName xml.Name `xml:"TenderSubmissionDeadlinePeriod"`
|
|
EndDate string `xml:"EndDate"`
|
|
EndTime string `xml:"EndTime,omitempty"`
|
|
}
|
|
|
|
// OpenTenderEvent contains open tender event information
|
|
type OpenTenderEvent struct {
|
|
XMLName xml.Name `xml:"OpenTenderEvent"`
|
|
OccurrenceDate string `xml:"OccurrenceDate,omitempty"`
|
|
OccurrenceTime string `xml:"OccurrenceTime,omitempty"`
|
|
Description string `xml:"Description,omitempty"`
|
|
LanguageID string `xml:"languageID,attr,omitempty"`
|
|
OccurenceLocation *OccurenceLocation `xml:"OccurenceLocation,omitempty"`
|
|
}
|
|
|
|
// OccurenceLocation contains occurrence location information
|
|
type OccurenceLocation struct {
|
|
XMLName xml.Name `xml:"OccurenceLocation"`
|
|
Description string `xml:"Description,omitempty"`
|
|
LanguageID string `xml:"languageID,attr,omitempty"`
|
|
}
|
|
|
|
// AuctionTerms contains auction terms information
|
|
type AuctionTerms struct {
|
|
XMLName xml.Name `xml:"AuctionTerms"`
|
|
AuctionConstraintIndicator string `xml:"AuctionConstraintIndicator,omitempty"`
|
|
}
|
|
|
|
// ContractingSystem contains contracting system information
|
|
type ContractingSystem struct {
|
|
XMLName xml.Name `xml:"ContractingSystem"`
|
|
ContractingSystemTypeCode string `xml:"ContractingSystemTypeCode,omitempty"`
|
|
}
|
|
|
|
// ProcurementProject contains procurement project information
|
|
type ProcurementProject struct {
|
|
XMLName xml.Name `xml:"ProcurementProject"`
|
|
ID string `xml:"ID"`
|
|
Name string `xml:"Name"`
|
|
Description string `xml:"Description"`
|
|
ProcurementTypeCode string `xml:"ProcurementTypeCode"`
|
|
SMESuitableIndicator string `xml:"SMESuitableIndicator,omitempty"`
|
|
Note string `xml:"Note,omitempty"`
|
|
|
|
RequestedTenderTotal *RequestedTenderTotal `xml:"RequestedTenderTotal,omitempty"`
|
|
MainCommodityClassification *MainCommodityClassification `xml:"MainCommodityClassification,omitempty"`
|
|
AdditionalCommodityClassification []AdditionalCommodityClassification `xml:"AdditionalCommodityClassification,omitempty"`
|
|
ProcurementAdditionalType *ProcurementAdditionalType `xml:"ProcurementAdditionalType,omitempty"`
|
|
RealizedLocation *RealizedLocation `xml:"RealizedLocation,omitempty"`
|
|
PlannedPeriod *PlannedPeriod `xml:"PlannedPeriod,omitempty"`
|
|
ContractExtension *ContractExtension `xml:"ContractExtension,omitempty"`
|
|
}
|
|
|
|
// ProcurementAdditionalType contains additional procurement type information
|
|
type ProcurementAdditionalType struct {
|
|
XMLName xml.Name `xml:"ProcurementAdditionalType"`
|
|
ProcurementTypeCode string `xml:"ProcurementTypeCode,omitempty"`
|
|
}
|
|
|
|
// ContractExtension contains contract extension information
|
|
type ContractExtension struct {
|
|
XMLName xml.Name `xml:"ContractExtension"`
|
|
MaximumNumberNumeric string `xml:"MaximumNumberNumeric,omitempty"`
|
|
}
|
|
|
|
// ProcessJustification contains process justification
|
|
type ProcessJustification struct {
|
|
XMLName xml.Name `xml:"ProcessJustification"`
|
|
ProcessReasonCode string `xml:"ProcessReasonCode"`
|
|
}
|
|
|
|
// RequestedTenderTotal contains tender total information
|
|
type RequestedTenderTotal struct {
|
|
XMLName xml.Name `xml:"RequestedTenderTotal"`
|
|
EstimatedOverallContractAmount CurrencyText `xml:"EstimatedOverallContractAmount"`
|
|
TotalAmount CurrencyText `xml:"TotalAmount,omitempty"`
|
|
}
|
|
|
|
// AmountAndCurrency returns the best-effort amount text and currency from RequestedTenderTotal.
|
|
func (r *RequestedTenderTotal) AmountAndCurrency() (amount string, currency string) {
|
|
if r == nil {
|
|
return "", ""
|
|
}
|
|
if amount, currency = r.EstimatedOverallContractAmount.AmountAndCurrency(); amount != "" {
|
|
return amount, currency
|
|
}
|
|
return r.TotalAmount.AmountAndCurrency()
|
|
}
|
|
|
|
// CurrencyText represents text with currency attribute
|
|
type CurrencyText struct {
|
|
CurrencyID string `xml:"currencyID,attr,omitempty"`
|
|
Text string `xml:",chardata"`
|
|
Value string `xml:"Value,omitempty"`
|
|
}
|
|
|
|
// AmountAndCurrency returns the amount text and currency id from a CurrencyText node.
|
|
func (c CurrencyText) AmountAndCurrency() (amount string, currency string) {
|
|
amount = strings.TrimSpace(c.Text)
|
|
if amount == "" {
|
|
amount = strings.TrimSpace(c.Value)
|
|
}
|
|
return amount, strings.TrimSpace(c.CurrencyID)
|
|
}
|
|
|
|
// MainCommodityClassification contains main commodity classification
|
|
type MainCommodityClassification struct {
|
|
XMLName xml.Name `xml:"MainCommodityClassification"`
|
|
ItemClassificationCode string `xml:"ItemClassificationCode"`
|
|
ItemClassificationName string `xml:"ItemClassificationName,omitempty"`
|
|
ItemClassificationDesc string `xml:"ItemClassificationDescription,omitempty"`
|
|
Name string `xml:"Name,omitempty"`
|
|
}
|
|
|
|
// AdditionalCommodityClassification contains additional commodity classification
|
|
type AdditionalCommodityClassification struct {
|
|
XMLName xml.Name `xml:"AdditionalCommodityClassification"`
|
|
ItemClassificationCode string `xml:"ItemClassificationCode"`
|
|
}
|
|
|
|
// RealizedLocation contains location information
|
|
type RealizedLocation struct {
|
|
XMLName xml.Name `xml:"RealizedLocation"`
|
|
Description string `xml:"Description,omitempty"`
|
|
Address *Address `xml:"Address,omitempty"`
|
|
}
|
|
|
|
// Address contains address information (different from PostalAddress)
|
|
type Address struct {
|
|
XMLName xml.Name `xml:"Address"`
|
|
StreetName string `xml:"StreetName,omitempty"`
|
|
AdditionalStreetName string `xml:"AdditionalStreetName,omitempty"`
|
|
CityName string `xml:"CityName,omitempty"`
|
|
PostalZone string `xml:"PostalZone,omitempty"`
|
|
CountrySubentityCode string `xml:"CountrySubentityCode,omitempty"`
|
|
Region string `xml:"Region,omitempty"`
|
|
Country *Country `xml:"Country,omitempty"`
|
|
}
|
|
|
|
// PlannedPeriod contains planned period information
|
|
type PlannedPeriod struct {
|
|
XMLName xml.Name `xml:"PlannedPeriod"`
|
|
StartDate string `xml:"StartDate,omitempty"`
|
|
EndDate string `xml:"EndDate,omitempty"`
|
|
DurationMeasure *DurationMeasure `xml:"DurationMeasure,omitempty"`
|
|
}
|
|
|
|
// DurationMeasure contains duration with unit information
|
|
type DurationMeasure struct {
|
|
XMLName xml.Name `xml:"DurationMeasure"`
|
|
UnitCode string `xml:"unitCode,attr,omitempty"`
|
|
Text string `xml:",chardata"`
|
|
}
|
|
|
|
// ProcurementProjectLot contains lot information
|
|
type ProcurementProjectLot struct {
|
|
XMLName xml.Name `xml:"ProcurementProjectLot"`
|
|
ID string `xml:"ID"`
|
|
TenderingTerms *TenderingTerms `xml:"TenderingTerms,omitempty"`
|
|
TenderingProcess *TenderingProcess `xml:"TenderingProcess,omitempty"`
|
|
ProcurementProject *ProcurementProject `xml:"ProcurementProject,omitempty"`
|
|
}
|
|
|
|
// Validate validates the ContractNotice structure
|
|
func (cn ContractNotice) Validate() error {
|
|
if cn.IssueDate == "" {
|
|
return fmt.Errorf("issue date is required")
|
|
}
|
|
|
|
// Validate date format - TED XML uses ISO 8601 format with timezone
|
|
validFormats := []string{
|
|
"2006-01-02+07:00",
|
|
"2006-01-02-07:00",
|
|
"2006-01-02T15:04:05+07:00",
|
|
"2006-01-02T15:04:05-07:00",
|
|
"2006-01-02T15:04:05Z",
|
|
"2006-01-02Z",
|
|
"2006-01-02",
|
|
time.RFC3339,
|
|
time.RFC3339Nano,
|
|
}
|
|
|
|
var validDate bool
|
|
for _, format := range validFormats {
|
|
if _, err := time.Parse(format, cn.IssueDate); err == nil {
|
|
validDate = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if !validDate {
|
|
return fmt.Errorf("invalid issue date format: %s (expected ISO 8601 format)", cn.IssueDate)
|
|
}
|
|
|
|
// Validate language code if provided (should be 3-letter ISO code)
|
|
if cn.NoticeLanguageCode != "" && len(cn.NoticeLanguageCode) != 3 {
|
|
return fmt.Errorf("invalid notice language code: %s (expected 3-letter ISO code)", cn.NoticeLanguageCode)
|
|
}
|
|
|
|
// Validate tender submission deadline format if present
|
|
if cn.firstLot() != nil &&
|
|
cn.firstLot().TenderingProcess != nil &&
|
|
cn.firstLot().TenderingProcess.TenderSubmissionDeadlinePeriod != nil {
|
|
|
|
deadline := cn.firstLot().TenderingProcess.TenderSubmissionDeadlinePeriod.EndDate
|
|
if deadline != "" {
|
|
var validDeadline bool
|
|
for _, format := range validFormats {
|
|
if _, err := time.Parse(format, deadline); err == nil {
|
|
validDeadline = true
|
|
break
|
|
}
|
|
}
|
|
if !validDeadline {
|
|
return fmt.Errorf("invalid tender submission deadline format: %s (expected ISO 8601 format)", deadline)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Validate currency code if provided in budget
|
|
if cn.firstLot() != nil &&
|
|
cn.firstLot().ProcurementProject != nil &&
|
|
cn.firstLot().ProcurementProject.RequestedTenderTotal != nil {
|
|
|
|
currency := cn.firstLot().ProcurementProject.RequestedTenderTotal.EstimatedOverallContractAmount.CurrencyID
|
|
if currency != "" && len(currency) != 3 {
|
|
return fmt.Errorf("invalid currency code: %s (expected 3-letter ISO code)", currency)
|
|
}
|
|
}
|
|
|
|
// Validate duration unit code if provided
|
|
if cn.firstLot() != nil &&
|
|
cn.firstLot().ProcurementProject != nil &&
|
|
cn.firstLot().ProcurementProject.PlannedPeriod != nil &&
|
|
cn.firstLot().ProcurementProject.PlannedPeriod.DurationMeasure != nil {
|
|
|
|
unitCode := cn.firstLot().ProcurementProject.PlannedPeriod.DurationMeasure.UnitCode
|
|
if unitCode != "" {
|
|
validUnits := map[string]bool{
|
|
"MONTH": true,
|
|
"DAY": true,
|
|
"WEEK": true,
|
|
"YEAR": true,
|
|
}
|
|
if !validUnits[unitCode] {
|
|
return fmt.Errorf("invalid duration unit code: %s (expected MONTH, DAY, WEEK, or YEAR)", unitCode)
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetID returns the contract notice ID
|
|
func (cn ContractNotice) GetID() string {
|
|
return cn.ID
|
|
}
|
|
|
|
// GetNoticeType returns the notice type code (BT-02, cbc:NoticeTypeCode text).
|
|
func (cn ContractNotice) GetNoticeType() string {
|
|
return strings.TrimSpace(cn.NoticeTypeCode.Text)
|
|
}
|
|
|
|
// GetNoticeFormType returns the form type from listName on cbc:NoticeTypeCode (BT-03), e.g. "competition".
|
|
func (cn ContractNotice) GetNoticeFormType() string {
|
|
return strings.TrimSpace(cn.NoticeTypeCode.ListName)
|
|
}
|
|
|
|
// GetLanguage returns the notice language
|
|
func (cn ContractNotice) GetLanguage() string {
|
|
return cn.NoticeLanguageCode
|
|
}
|
|
|
|
// GetPublicationInfo returns publication information if available
|
|
func (cn ContractNotice) GetPublicationInfo() *Publication {
|
|
if ef := ublEformsExtension(cn.Extensions); ef != nil {
|
|
return ef.Publication
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetOrganizations returns organizations from extensions
|
|
func (cn ContractNotice) GetOrganizations() *Organizations {
|
|
if ef := ublEformsExtension(cn.Extensions); ef != nil {
|
|
return ef.Organizations
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetNoticeSubType returns the notice subtype code from extensions
|
|
func (cn ContractNotice) GetNoticeSubType() string {
|
|
if ef := ublEformsExtension(cn.Extensions); ef != nil && ef.NoticeSubType != nil {
|
|
return ef.NoticeSubType.SubTypeCode
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// GetNoticePublicationID returns the notice publication ID from extensions
|
|
func (cn ContractNotice) GetNoticePublicationID() string {
|
|
pub := cn.GetPublicationInfo()
|
|
if pub != nil {
|
|
return pub.NoticePublicationID
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// GetOJSID returns the OJS gazette ID from extensions
|
|
func (cn ContractNotice) GetOJSID() string {
|
|
pub := cn.GetPublicationInfo()
|
|
if pub != nil {
|
|
return pub.GazetteID
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// GetPublicationDate returns the publication date from extensions
|
|
func (cn ContractNotice) GetPublicationDate() string {
|
|
pub := cn.GetPublicationInfo()
|
|
if pub != nil {
|
|
return pub.PublicationDate
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// GetOrganizationByID returns an organization by its ID
|
|
func (cn ContractNotice) GetOrganizationByID(orgID string) *Organization {
|
|
orgs := cn.GetOrganizations()
|
|
if orgs == nil {
|
|
return nil
|
|
}
|
|
|
|
for _, org := range orgs.Organization {
|
|
if org.Company != nil &&
|
|
org.Company.PartyIdentification != nil &&
|
|
org.Company.PartyIdentification.ID == orgID {
|
|
return &org
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetBuyerID returns the buyer organization ID from contracting party
|
|
func (cn ContractNotice) GetBuyerID() string {
|
|
if cn.ContractingParty != nil &&
|
|
cn.ContractingParty.Party != nil &&
|
|
cn.ContractingParty.Party.PartyIdentification != nil {
|
|
return cn.ContractingParty.Party.PartyIdentification.ID
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// GetReviewOrganizationID returns the review organization ID from appeal terms
|
|
func (cn ContractNotice) GetReviewOrganizationID() string {
|
|
if cn.firstLot() != nil &&
|
|
cn.firstLot().TenderingTerms != nil &&
|
|
cn.firstLot().TenderingTerms.AppealTerms != nil &&
|
|
cn.firstLot().TenderingTerms.AppealTerms.AppealReceiverParty != nil &&
|
|
cn.firstLot().TenderingTerms.AppealTerms.AppealReceiverParty.PartyIdentification != nil {
|
|
return cn.firstLot().TenderingTerms.AppealTerms.AppealReceiverParty.PartyIdentification.ID
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// GetProcedureCode returns the type of procedure
|
|
func (cn ContractNotice) GetProcedureCode() string {
|
|
if cn.TenderingProcess != nil {
|
|
return cn.TenderingProcess.ProcedureCode
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// GetProcurementTitle returns the notice-level procurement project title
|
|
func (cn ContractNotice) GetProcurementTitle() string {
|
|
if cn.ProcurementProject != nil && strings.TrimSpace(cn.ProcurementProject.Name) != "" {
|
|
return cn.ProcurementProject.Name
|
|
}
|
|
if cn.firstLot() != nil && cn.firstLot().ProcurementProject != nil {
|
|
return cn.firstLot().ProcurementProject.Name
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// GetProcurementDescription returns the notice-level procurement project description
|
|
func (cn ContractNotice) GetProcurementDescription() string {
|
|
if cn.ProcurementProject != nil && strings.TrimSpace(cn.ProcurementProject.Description) != "" {
|
|
return cn.ProcurementProject.Description
|
|
}
|
|
if cn.firstLot() != nil && cn.firstLot().ProcurementProject != nil {
|
|
return cn.firstLot().ProcurementProject.Description
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// GetContractNature returns the main nature of the contract
|
|
func (cn ContractNotice) GetContractNature() string {
|
|
if cn.ProcurementProject != nil {
|
|
return cn.ProcurementProject.ProcurementTypeCode
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// GetMainClassification returns the main CPV classification code
|
|
func (cn ContractNotice) GetMainClassification() string {
|
|
if cn.ProcurementProject != nil &&
|
|
cn.ProcurementProject.MainCommodityClassification != nil {
|
|
return cn.ProcurementProject.MainCommodityClassification.ItemClassificationCode
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func mainCommodityClassificationLabel(m *MainCommodityClassification) string {
|
|
if m == nil {
|
|
return ""
|
|
}
|
|
for _, s := range []string{m.ItemClassificationName, m.ItemClassificationDesc, m.Name} {
|
|
if v := strings.TrimSpace(s); v != "" {
|
|
return v
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// GetMainClassificationDescription returns the CPV / item classification description when present in the notice XML.
|
|
func (cn ContractNotice) GetMainClassificationDescription() string {
|
|
if cn.ProcurementProject == nil {
|
|
return ""
|
|
}
|
|
return mainCommodityClassificationLabel(cn.ProcurementProject.MainCommodityClassification)
|
|
}
|
|
|
|
// GetPlaceOfPerformance returns the place of performance
|
|
func (cn ContractNotice) GetPlaceOfPerformance() string {
|
|
if cn.ProcurementProject != nil &&
|
|
cn.ProcurementProject.RealizedLocation != nil &&
|
|
cn.ProcurementProject.RealizedLocation.Address != nil {
|
|
return cn.ProcurementProject.RealizedLocation.Address.Region
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// GetLotID returns the procurement lot ID
|
|
func (cn ContractNotice) GetLotID() string {
|
|
if cn.firstLot() != nil {
|
|
return cn.firstLot().ID
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// GetSelectionCriteria returns selection criteria from extensions
|
|
func (cn ContractNotice) GetSelectionCriteria() []SelectionCriteria {
|
|
if ef := ublEformsExtension(cn.Extensions); ef != nil {
|
|
return ef.SelectionCriteria
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetOfficialLanguages returns official languages from tender terms extensions
|
|
func (cn ContractNotice) GetOfficialLanguages() []string {
|
|
if cn.firstLot() != nil &&
|
|
cn.firstLot().TenderingTerms != nil &&
|
|
len(cn.firstLot().TenderingTerms.CallForTendersDocumentReference) > 0 &&
|
|
cn.firstLot().TenderingTerms.CallForTendersDocumentReference[0].UBLExtensions != nil &&
|
|
cn.firstLot().TenderingTerms.CallForTendersDocumentReference[0].UBLExtensions.UBLExtension != nil &&
|
|
cn.firstLot().TenderingTerms.CallForTendersDocumentReference[0].UBLExtensions.UBLExtension.ExtensionContent != nil &&
|
|
cn.firstLot().TenderingTerms.CallForTendersDocumentReference[0].UBLExtensions.UBLExtension.ExtensionContent.EformsExtension != nil &&
|
|
cn.firstLot().TenderingTerms.CallForTendersDocumentReference[0].UBLExtensions.UBLExtension.ExtensionContent.EformsExtension.OfficialLanguages != nil {
|
|
|
|
languages := make([]string, len(cn.firstLot().TenderingTerms.CallForTendersDocumentReference[0].UBLExtensions.UBLExtension.ExtensionContent.EformsExtension.OfficialLanguages.Language))
|
|
for i, lang := range cn.firstLot().TenderingTerms.CallForTendersDocumentReference[0].UBLExtensions.UBLExtension.ExtensionContent.EformsExtension.OfficialLanguages.Language {
|
|
languages[i] = lang.ID
|
|
}
|
|
return languages
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetDocumentURI returns the document URI from call for tenders document reference
|
|
func (cn ContractNotice) GetDocumentURI() string {
|
|
if cn.firstLot() != nil &&
|
|
cn.firstLot().TenderingTerms != nil &&
|
|
len(cn.firstLot().TenderingTerms.CallForTendersDocumentReference) > 0 &&
|
|
cn.firstLot().TenderingTerms.CallForTendersDocumentReference[0].Attachment != nil &&
|
|
cn.firstLot().TenderingTerms.CallForTendersDocumentReference[0].Attachment.ExternalReference != nil {
|
|
return cn.firstLot().TenderingTerms.CallForTendersDocumentReference[0].Attachment.ExternalReference.URI
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// GetAllDocumentURIs returns all document URIs from call for tenders document references
|
|
func (cn ContractNotice) GetAllDocumentURIs() []string {
|
|
var uris []string
|
|
for i := range cn.ProcurementProjectLots {
|
|
lot := &cn.ProcurementProjectLots[i]
|
|
if lot.TenderingTerms == nil {
|
|
continue
|
|
}
|
|
for _, docRef := range lot.TenderingTerms.CallForTendersDocumentReference {
|
|
if docRef.Attachment != nil &&
|
|
docRef.Attachment.ExternalReference != nil &&
|
|
docRef.Attachment.ExternalReference.URI != "" {
|
|
uris = append(uris, docRef.Attachment.ExternalReference.URI)
|
|
}
|
|
}
|
|
}
|
|
return uris
|
|
}
|
|
|
|
// GetTenderDeadline returns the tender submission deadline
|
|
func (cn ContractNotice) GetTenderDeadline() string {
|
|
if cn.firstLot() != nil &&
|
|
cn.firstLot().TenderingProcess != nil &&
|
|
cn.firstLot().TenderingProcess.TenderSubmissionDeadlinePeriod != nil {
|
|
return cn.firstLot().TenderingProcess.TenderSubmissionDeadlinePeriod.EndDate
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// GetBudget returns the estimated contract amount and currency.
|
|
// Lot-level BT-27 values are summed when present; procedure-level BT-27 is used when lots omit RequestedTenderTotal.
|
|
func (cn ContractNotice) GetBudget() (amount string, currency string) {
|
|
if amount, currency = budgetFromProcurementLots(cn.ProcurementProjectLots); amount != "" {
|
|
return amount, currency
|
|
}
|
|
if cn.ProcurementProject != nil && cn.ProcurementProject.RequestedTenderTotal != nil {
|
|
return cn.ProcurementProject.RequestedTenderTotal.AmountAndCurrency()
|
|
}
|
|
return "", ""
|
|
}
|
|
|
|
func budgetFromProcurementLots(lots []ProcurementProjectLot) (amount string, currency string) {
|
|
amounts := make([]valuedAmount, 0, len(lots))
|
|
for i := range lots {
|
|
pp := lots[i].ProcurementProject
|
|
if pp == nil || pp.RequestedTenderTotal == nil {
|
|
continue
|
|
}
|
|
lotAmount, lotCurrency := pp.RequestedTenderTotal.AmountAndCurrency()
|
|
if lotAmount == "" {
|
|
continue
|
|
}
|
|
value, ok := ParseEuropeanAmount(lotAmount)
|
|
if !ok || value <= 0 {
|
|
continue
|
|
}
|
|
amounts = append(amounts, valuedAmount{value: value, currency: lotCurrency})
|
|
}
|
|
|
|
total, currency := aggregateSameCurrencyAmounts(amounts)
|
|
if total <= 0 {
|
|
return "", ""
|
|
}
|
|
return strconv.FormatFloat(total, 'f', -1, 64), currency
|
|
}
|
|
|
|
// GetDuration returns the contract duration and unit
|
|
func (cn ContractNotice) GetDuration() (duration string, unit string) {
|
|
if cn.firstLot() != nil &&
|
|
cn.firstLot().ProcurementProject != nil &&
|
|
cn.firstLot().ProcurementProject.PlannedPeriod != nil &&
|
|
cn.firstLot().ProcurementProject.PlannedPeriod.DurationMeasure != nil {
|
|
|
|
durationMeasure := cn.firstLot().ProcurementProject.PlannedPeriod.DurationMeasure
|
|
return durationMeasure.Text, durationMeasure.UnitCode
|
|
}
|
|
return "", ""
|
|
}
|
|
|
|
// ==================== ContractAwardNotice Methods ====================
|
|
|
|
// Validate validates the ContractAwardNotice structure
|
|
func (can ContractAwardNotice) Validate() error {
|
|
if can.ID == "" {
|
|
return fmt.Errorf("contract award notice ID is required")
|
|
}
|
|
|
|
if can.ContractFolderID == "" {
|
|
return fmt.Errorf("contract folder ID is required")
|
|
}
|
|
|
|
if can.IssueDate == "" {
|
|
return fmt.Errorf("issue date is required")
|
|
}
|
|
|
|
// Validate date format - TED XML uses ISO 8601 format with timezone
|
|
validFormats := []string{
|
|
"2006-01-02+07:00",
|
|
"2006-01-02-07:00",
|
|
"2006-01-02T15:04:05+07:00",
|
|
"2006-01-02T15:04:05-07:00",
|
|
"2006-01-02T15:04:05Z",
|
|
"2006-01-02Z",
|
|
"2006-01-02",
|
|
time.RFC3339,
|
|
time.RFC3339Nano,
|
|
}
|
|
|
|
var validDate bool
|
|
for _, format := range validFormats {
|
|
if _, err := time.Parse(format, can.IssueDate); err == nil {
|
|
validDate = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if !validDate {
|
|
return fmt.Errorf("invalid issue date format: %s (expected ISO 8601 format)", can.IssueDate)
|
|
}
|
|
|
|
// Validate language code if provided (should be 3-letter ISO code)
|
|
if can.NoticeLanguageCode != "" && len(can.NoticeLanguageCode) != 3 {
|
|
return fmt.Errorf("invalid notice language code: %s (expected 3-letter ISO code)", can.NoticeLanguageCode)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetID returns the contract award notice ID
|
|
func (can ContractAwardNotice) GetID() string {
|
|
return can.ID
|
|
}
|
|
|
|
// GetNoticeType returns the notice type code (BT-02).
|
|
func (can ContractAwardNotice) GetNoticeType() string {
|
|
return strings.TrimSpace(can.NoticeTypeCode.Text)
|
|
}
|
|
|
|
// GetNoticeFormType returns the form type from listName (BT-03).
|
|
func (can ContractAwardNotice) GetNoticeFormType() string {
|
|
return strings.TrimSpace(can.NoticeTypeCode.ListName)
|
|
}
|
|
|
|
// GetLanguage returns the notice language
|
|
func (can ContractAwardNotice) GetLanguage() string {
|
|
return can.NoticeLanguageCode
|
|
}
|
|
|
|
// GetPublicationInfo returns publication information if available
|
|
func (can ContractAwardNotice) GetPublicationInfo() *Publication {
|
|
if can.Extensions != nil &&
|
|
can.Extensions.UBLExtension != nil &&
|
|
can.Extensions.UBLExtension.ExtensionContent != nil &&
|
|
can.Extensions.UBLExtension.ExtensionContent.EformsExtension != nil {
|
|
return can.Extensions.UBLExtension.ExtensionContent.EformsExtension.Publication
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetOrganizations returns organizations from extensions
|
|
func (can ContractAwardNotice) GetOrganizations() *Organizations {
|
|
if can.Extensions != nil &&
|
|
can.Extensions.UBLExtension != nil &&
|
|
can.Extensions.UBLExtension.ExtensionContent != nil &&
|
|
can.Extensions.UBLExtension.ExtensionContent.EformsExtension != nil {
|
|
return can.Extensions.UBLExtension.ExtensionContent.EformsExtension.Organizations
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetNoticeResult returns notice result from extensions
|
|
func (can ContractAwardNotice) GetNoticeResult() *NoticeResult {
|
|
if can.Extensions != nil &&
|
|
can.Extensions.UBLExtension != nil &&
|
|
can.Extensions.UBLExtension.ExtensionContent != nil &&
|
|
can.Extensions.UBLExtension.ExtensionContent.EformsExtension != nil {
|
|
return can.Extensions.UBLExtension.ExtensionContent.EformsExtension.NoticeResult
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetNoticeSubType returns the notice subtype code from extensions
|
|
func (can ContractAwardNotice) GetNoticeSubType() string {
|
|
if can.Extensions != nil &&
|
|
can.Extensions.UBLExtension != nil &&
|
|
can.Extensions.UBLExtension.ExtensionContent != nil &&
|
|
can.Extensions.UBLExtension.ExtensionContent.EformsExtension != nil &&
|
|
can.Extensions.UBLExtension.ExtensionContent.EformsExtension.NoticeSubType != nil {
|
|
return can.Extensions.UBLExtension.ExtensionContent.EformsExtension.NoticeSubType.SubTypeCode
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// GetNoticePublicationID returns the notice publication ID from extensions
|
|
func (can ContractAwardNotice) GetNoticePublicationID() string {
|
|
pub := can.GetPublicationInfo()
|
|
if pub != nil {
|
|
return pub.NoticePublicationID
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// GetOJSID returns the OJS gazette ID from extensions
|
|
func (can ContractAwardNotice) GetOJSID() string {
|
|
pub := can.GetPublicationInfo()
|
|
if pub != nil {
|
|
return pub.GazetteID
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// GetPublicationDate returns the publication date from extensions
|
|
func (can ContractAwardNotice) GetPublicationDate() string {
|
|
pub := can.GetPublicationInfo()
|
|
if pub != nil {
|
|
return pub.PublicationDate
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// GetBuyerID returns the buyer organization ID from contracting party
|
|
func (can ContractAwardNotice) GetBuyerID() string {
|
|
if can.ContractingParty != nil &&
|
|
can.ContractingParty.Party != nil &&
|
|
can.ContractingParty.Party.PartyIdentification != nil {
|
|
return can.ContractingParty.Party.PartyIdentification.ID
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// GetProcedureCode returns the type of procedure
|
|
func (can ContractAwardNotice) GetProcedureCode() string {
|
|
if can.TenderingProcess != nil {
|
|
return can.TenderingProcess.ProcedureCode
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// GetProcurementTitle returns the procurement project title
|
|
func (can ContractAwardNotice) GetProcurementTitle() string {
|
|
if can.ProcurementProject != nil && strings.TrimSpace(can.ProcurementProject.Name) != "" {
|
|
return can.ProcurementProject.Name
|
|
}
|
|
if lot := can.firstAwardLot(); lot != nil && lot.ProcurementProject != nil {
|
|
return lot.ProcurementProject.Name
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// GetProcurementDescription returns the procurement project description
|
|
func (can ContractAwardNotice) GetProcurementDescription() string {
|
|
if can.ProcurementProject != nil && strings.TrimSpace(can.ProcurementProject.Description) != "" {
|
|
return can.ProcurementProject.Description
|
|
}
|
|
if lot := can.firstAwardLot(); lot != nil && lot.ProcurementProject != nil {
|
|
return lot.ProcurementProject.Description
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// GetContractNature returns the main nature of the contract
|
|
func (can ContractAwardNotice) GetContractNature() string {
|
|
if can.ProcurementProject != nil && strings.TrimSpace(can.ProcurementProject.ProcurementTypeCode) != "" {
|
|
return can.ProcurementProject.ProcurementTypeCode
|
|
}
|
|
if lot := can.firstAwardLot(); lot != nil && lot.ProcurementProject != nil {
|
|
return lot.ProcurementProject.ProcurementTypeCode
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// GetMainClassification returns the main CPV classification code
|
|
func (can ContractAwardNotice) GetMainClassification() string {
|
|
if can.ProcurementProject != nil &&
|
|
can.ProcurementProject.MainCommodityClassification != nil {
|
|
return can.ProcurementProject.MainCommodityClassification.ItemClassificationCode
|
|
}
|
|
if lot := can.firstAwardLot(); lot != nil &&
|
|
lot.ProcurementProject != nil &&
|
|
lot.ProcurementProject.MainCommodityClassification != nil {
|
|
return lot.ProcurementProject.MainCommodityClassification.ItemClassificationCode
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// GetMainClassificationDescription returns the CPV / item classification description when present in the notice XML.
|
|
func (can ContractAwardNotice) GetMainClassificationDescription() string {
|
|
if can.ProcurementProject != nil {
|
|
if s := mainCommodityClassificationLabel(can.ProcurementProject.MainCommodityClassification); s != "" {
|
|
return s
|
|
}
|
|
}
|
|
if lot := can.firstAwardLot(); lot != nil && lot.ProcurementProject != nil {
|
|
return mainCommodityClassificationLabel(lot.ProcurementProject.MainCommodityClassification)
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// GetTotalAwardValue returns the total award amount and currency
|
|
func (can ContractAwardNotice) GetTotalAwardValue() (amount string, currency string) {
|
|
noticeResult := can.GetNoticeResult()
|
|
if noticeResult != nil && noticeResult.TotalAmount != nil {
|
|
return noticeResult.TotalAmount.AmountAndCurrency()
|
|
}
|
|
return "", ""
|
|
}
|
|
|
|
// GetAwardDate returns the tender award date
|
|
func (can ContractAwardNotice) GetAwardDate() string {
|
|
if can.TenderResult != nil {
|
|
return can.TenderResult.AwardDate
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// GetOrganizationByID returns an organization by its ID
|
|
func (can ContractAwardNotice) GetOrganizationByID(orgID string) *Organization {
|
|
orgs := can.GetOrganizations()
|
|
if orgs == nil {
|
|
return nil
|
|
}
|
|
|
|
for _, org := range orgs.Organization {
|
|
if org.Company != nil &&
|
|
org.Company.PartyIdentification != nil &&
|
|
org.Company.PartyIdentification.ID == orgID {
|
|
return &org
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetWinningTenderPartyID resolves the tendering party reference for the primary winning bid from NoticeResult.
|
|
func (can ContractAwardNotice) GetWinningTenderPartyID() string {
|
|
nr := can.GetNoticeResult()
|
|
if nr == nil {
|
|
return ""
|
|
}
|
|
for _, lr := range nr.LotResult {
|
|
winner := pickWinningLotTender(lr.LotTender)
|
|
if winner != nil && winner.TenderingParty != nil && winner.TenderingParty.ID != "" {
|
|
return winner.TenderingParty.ID
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// GetWinningTenderOrganization resolves the winning tenderer organization from NoticeResult via Organizations.
|
|
func (can ContractAwardNotice) GetWinningTenderOrganization() *Organization {
|
|
id := can.GetWinningTenderPartyID()
|
|
if id == "" {
|
|
return nil
|
|
}
|
|
return can.GetOrganizationByID(id)
|
|
}
|