Files
tm_back/ted/model.go
T
2026-05-13 01:07:43 +03:30

2271 lines
96 KiB
Go

package ted
import (
"encoding/xml"
"fmt"
"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 pin.firstLot() != nil &&
pin.firstLot().ProcurementProject != nil &&
pin.firstLot().ProcurementProject.RequestedTenderTotal != nil {
contractAmount := pin.firstLot().ProcurementProject.RequestedTenderTotal.EstimatedOverallContractAmount
return contractAmount.Text, contractAmount.CurrencyID
}
if pin.ProcurementProject != nil && pin.ProcurementProject.RequestedTenderTotal != nil {
a := pin.ProcurementProject.RequestedTenderTotal.EstimatedOverallContractAmount
return a.Text, a.CurrencyID
}
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"`
}
// CurrencyText represents text with currency attribute
type CurrencyText struct {
CurrencyID string `xml:"currencyID,attr,omitempty"`
Text string `xml:",chardata"`
}
// 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 procurement project title
func (cn ContractNotice) GetProcurementTitle() string {
if cn.firstLot() != nil &&
cn.firstLot().ProcurementProject != nil {
return cn.firstLot().ProcurementProject.Name
}
return ""
}
// GetProcurementDescription returns the procurement project description
func (cn ContractNotice) GetProcurementDescription() string {
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
func (cn ContractNotice) GetBudget() (amount string, currency string) {
if cn.firstLot() != nil &&
cn.firstLot().ProcurementProject != nil &&
cn.firstLot().ProcurementProject.RequestedTenderTotal != nil {
contractAmount := cn.firstLot().ProcurementProject.RequestedTenderTotal.EstimatedOverallContractAmount
return contractAmount.Text, contractAmount.CurrencyID
}
return "", ""
}
// 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.Text, noticeResult.TotalAmount.CurrencyID
}
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)
}