Enhance Tender Entity and TED Parser with Status Management and Upsert Logic
- Added new fields to the Tender entity for handling cancellation, award, and suspension details, improving the entity's capability to manage tender statuses. - Implemented a DetectNoticeStatus method in the TEDParser to identify the status of TED notices from XML data, enhancing the parser's functionality. - Updated the ParseXML method to include notice status detection, ensuring accurate status representation in parsed documents. - Introduced upsert logic in the TED scraper to handle tender records more effectively, allowing for updates or creation based on existing ContractIDs. - Enhanced logging and error handling throughout the scraping process to improve traceability and robustness. - Added a comprehensive usage example document to illustrate the new features and usage patterns for the TED XML parser and scraper.
This commit is contained in:
+408
-84
@@ -3,6 +3,7 @@ package ted
|
||||
import (
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -27,9 +28,24 @@ const (
|
||||
DocumentTypeSubcontractNotice DocumentType = "SubcontractNotice"
|
||||
)
|
||||
|
||||
// NoticeStatus represents the status of a TED notice
|
||||
type NoticeStatus string
|
||||
|
||||
// Notice status constants for all possible TED notice statuses
|
||||
const (
|
||||
NoticeStatusDraft NoticeStatus = "Draft"
|
||||
NoticeStatusPublished NoticeStatus = "Published"
|
||||
NoticeStatusCancelled NoticeStatus = "Cancelled"
|
||||
NoticeStatusAwarded NoticeStatus = "Awarded"
|
||||
NoticeStatusClosed NoticeStatus = "Closed"
|
||||
NoticeStatusModified NoticeStatus = "Modified"
|
||||
NoticeStatusSuspended NoticeStatus = "Suspended"
|
||||
)
|
||||
|
||||
// ParsedDocument wraps any TED document with type information
|
||||
type ParsedDocument struct {
|
||||
Type DocumentType
|
||||
Type DocumentType `json:"type"`
|
||||
Status NoticeStatus `json:"status"`
|
||||
ContractNotice *ContractNotice `json:"contract_notice,omitempty"`
|
||||
ContractAwardNotice *ContractAwardNotice `json:"contract_award_notice,omitempty"`
|
||||
PriorInformationNotice *PriorInformationNotice `json:"prior_information_notice,omitempty"`
|
||||
@@ -78,6 +94,7 @@ type TEDDocument interface {
|
||||
GetID() string
|
||||
GetNoticeType() string
|
||||
GetLanguage() string
|
||||
GetNoticeStatus() NoticeStatus
|
||||
Validate() error
|
||||
}
|
||||
|
||||
@@ -123,6 +140,11 @@ func (pin PriorInformationNotice) GetLanguage() string {
|
||||
return pin.NoticeLanguageCode
|
||||
}
|
||||
|
||||
func (pin PriorInformationNotice) GetNoticeStatus() NoticeStatus {
|
||||
// Prior information notices are typically published
|
||||
return NoticeStatusPublished
|
||||
}
|
||||
|
||||
func (pin PriorInformationNotice) Validate() error {
|
||||
if pin.ID == "" {
|
||||
return fmt.Errorf("prior information notice ID is required")
|
||||
@@ -163,9 +185,10 @@ type DesignContest struct {
|
||||
}
|
||||
|
||||
// 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) GetID() string { return dc.ID }
|
||||
func (dc DesignContest) GetNoticeType() string { return dc.NoticeTypeCode }
|
||||
func (dc DesignContest) GetLanguage() string { return dc.NoticeLanguageCode }
|
||||
func (dc DesignContest) GetNoticeStatus() NoticeStatus { return NoticeStatusPublished }
|
||||
func (dc DesignContest) Validate() error {
|
||||
if dc.ID == "" {
|
||||
return fmt.Errorf("design contest ID is required")
|
||||
@@ -205,9 +228,10 @@ type QualificationSystemNotice struct {
|
||||
}
|
||||
|
||||
// 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) GetID() string { return qsn.ID }
|
||||
func (qsn QualificationSystemNotice) GetNoticeType() string { return qsn.NoticeTypeCode }
|
||||
func (qsn QualificationSystemNotice) GetLanguage() string { return qsn.NoticeLanguageCode }
|
||||
func (qsn QualificationSystemNotice) GetNoticeStatus() NoticeStatus { return NoticeStatusPublished }
|
||||
func (qsn QualificationSystemNotice) Validate() error {
|
||||
if qsn.ID == "" {
|
||||
return fmt.Errorf("qualification system notice ID is required")
|
||||
@@ -249,9 +273,10 @@ type ConcessionNotice struct {
|
||||
}
|
||||
|
||||
// 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) GetID() string { return cn.ID }
|
||||
func (cn ConcessionNotice) GetNoticeType() string { return cn.NoticeTypeCode }
|
||||
func (cn ConcessionNotice) GetLanguage() string { return cn.NoticeLanguageCode }
|
||||
func (cn ConcessionNotice) GetNoticeStatus() NoticeStatus { return NoticeStatusPublished }
|
||||
func (cn ConcessionNotice) Validate() error {
|
||||
if cn.ID == "" {
|
||||
return fmt.Errorf("concession notice ID is required")
|
||||
@@ -291,9 +316,10 @@ type PlanningNotice struct {
|
||||
}
|
||||
|
||||
// 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) GetID() string { return pn.ID }
|
||||
func (pn PlanningNotice) GetNoticeType() string { return pn.NoticeTypeCode }
|
||||
func (pn PlanningNotice) GetLanguage() string { return pn.NoticeLanguageCode }
|
||||
func (pn PlanningNotice) GetNoticeStatus() NoticeStatus { return NoticeStatusPublished }
|
||||
func (pn PlanningNotice) Validate() error {
|
||||
if pn.ID == "" {
|
||||
return fmt.Errorf("planning notice ID is required")
|
||||
@@ -333,9 +359,10 @@ type CompetitionNotice struct {
|
||||
}
|
||||
|
||||
// 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) GetID() string { return cn.ID }
|
||||
func (cn CompetitionNotice) GetNoticeType() string { return cn.NoticeTypeCode }
|
||||
func (cn CompetitionNotice) GetLanguage() string { return cn.NoticeLanguageCode }
|
||||
func (cn CompetitionNotice) GetNoticeStatus() NoticeStatus { return NoticeStatusPublished }
|
||||
func (cn CompetitionNotice) Validate() error {
|
||||
if cn.ID == "" {
|
||||
return fmt.Errorf("competition notice ID is required")
|
||||
@@ -376,9 +403,10 @@ type ResultNotice struct {
|
||||
}
|
||||
|
||||
// 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) GetID() string { return rn.ID }
|
||||
func (rn ResultNotice) GetNoticeType() string { return rn.NoticeTypeCode }
|
||||
func (rn ResultNotice) GetLanguage() string { return rn.NoticeLanguageCode }
|
||||
func (rn ResultNotice) GetNoticeStatus() NoticeStatus { return NoticeStatusAwarded }
|
||||
func (rn ResultNotice) Validate() error {
|
||||
if rn.ID == "" {
|
||||
return fmt.Errorf("result notice ID is required")
|
||||
@@ -420,9 +448,10 @@ type ChangeNotice struct {
|
||||
}
|
||||
|
||||
// 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) GetID() string { return cn.ID }
|
||||
func (cn ChangeNotice) GetNoticeType() string { return cn.NoticeTypeCode }
|
||||
func (cn ChangeNotice) GetLanguage() string { return cn.NoticeLanguageCode }
|
||||
func (cn ChangeNotice) GetNoticeStatus() NoticeStatus { return NoticeStatusModified }
|
||||
func (cn ChangeNotice) Validate() error {
|
||||
if cn.ID == "" {
|
||||
return fmt.Errorf("change notice ID is required")
|
||||
@@ -463,9 +492,10 @@ type SubcontractNotice struct {
|
||||
}
|
||||
|
||||
// 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) GetID() string { return sn.ID }
|
||||
func (sn SubcontractNotice) GetNoticeType() string { return sn.NoticeTypeCode }
|
||||
func (sn SubcontractNotice) GetLanguage() string { return sn.NoticeLanguageCode }
|
||||
func (sn SubcontractNotice) GetNoticeStatus() NoticeStatus { return NoticeStatusPublished }
|
||||
func (sn SubcontractNotice) Validate() error {
|
||||
if sn.ID == "" {
|
||||
return fmt.Errorf("subcontract notice ID is required")
|
||||
@@ -665,67 +695,123 @@ type SubcontractingTerm struct {
|
||||
SubcontractingDescription string `xml:"SubcontractingDescription,omitempty"`
|
||||
}
|
||||
|
||||
// NoticeStatusInformation contains status-specific fields
|
||||
type NoticeStatusInformation struct {
|
||||
Status NoticeStatus `xml:"NoticeStatus,omitempty"`
|
||||
StatusCode string `xml:"StatusCode,omitempty"`
|
||||
CancellationReason *CancellationReason `xml:"CancellationReason,omitempty"`
|
||||
AwardInformation *AwardInformation `xml:"AwardInformation,omitempty"`
|
||||
Modifications []NoticeModification `xml:"Modifications>Modification,omitempty"`
|
||||
SuspensionReason *SuspensionReason `xml:"SuspensionReason,omitempty"`
|
||||
}
|
||||
|
||||
// CancellationReason contains cancellation information
|
||||
type CancellationReason struct {
|
||||
XMLName xml.Name `xml:"CancellationReason"`
|
||||
ReasonCode string `xml:"ReasonCode"`
|
||||
Description string `xml:"Description,omitempty"`
|
||||
LanguageID string `xml:"languageID,attr,omitempty"`
|
||||
}
|
||||
|
||||
// AwardInformation contains award-specific information
|
||||
type AwardInformation struct {
|
||||
XMLName xml.Name `xml:"AwardInformation"`
|
||||
AwardDate string `xml:"AwardDate"`
|
||||
AwardedValue CurrencyText `xml:"AwardedValue,omitempty"`
|
||||
WinningTenderer *WinningTenderer `xml:"WinningTenderer,omitempty"`
|
||||
ContractNumber string `xml:"ContractNumber,omitempty"`
|
||||
}
|
||||
|
||||
// WinningTenderer contains information about the winning tenderer
|
||||
type WinningTenderer struct {
|
||||
XMLName xml.Name `xml:"WinningTenderer"`
|
||||
NaturalPersonIndicator string `xml:"NaturalPersonIndicator,omitempty"`
|
||||
Company *Company `xml:"Company,omitempty"`
|
||||
}
|
||||
|
||||
// NoticeModification contains modification information
|
||||
type NoticeModification struct {
|
||||
XMLName xml.Name `xml:"Modification"`
|
||||
ModificationDate string `xml:"ModificationDate"`
|
||||
ModificationReason string `xml:"ModificationReason"`
|
||||
Description string `xml:"Description,omitempty"`
|
||||
LanguageID string `xml:"languageID,attr,omitempty"`
|
||||
}
|
||||
|
||||
// SuspensionReason contains suspension information
|
||||
type SuspensionReason struct {
|
||||
XMLName xml.Name `xml:"SuspensionReason"`
|
||||
ReasonCode string `xml:"ReasonCode"`
|
||||
Description string `xml:"Description,omitempty"`
|
||||
LanguageID string `xml:"languageID,attr,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 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"`
|
||||
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 string `xml:"NoticeTypeCode"`
|
||||
NoticeLanguageCode string `xml:"NoticeLanguageCode"`
|
||||
StatusInformation *NoticeStatusInformation `xml:"StatusInformation,omitempty"`
|
||||
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"`
|
||||
}
|
||||
|
||||
// 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 string `xml:"NoticeTypeCode"`
|
||||
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"`
|
||||
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 string `xml:"NoticeTypeCode"`
|
||||
NoticeLanguageCode string `xml:"NoticeLanguageCode"`
|
||||
StatusInformation *NoticeStatusInformation `xml:"StatusInformation,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"`
|
||||
}
|
||||
|
||||
// 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"`
|
||||
XMLName xml.Name `xml:"NoticeResult"`
|
||||
TotalAmount *CurrencyText `xml:"TotalAmount,omitempty"`
|
||||
LotResult []LotResult `xml:"LotResult,omitempty"`
|
||||
LotTender []LotTender `xml:"LotTender,omitempty"`
|
||||
SettledContract []SettledContract `xml:"SettledContract,omitempty"`
|
||||
TenderingParty []TenderingParty `xml:"TenderingParty,omitempty"`
|
||||
}
|
||||
|
||||
// LotResult contains lot result information
|
||||
@@ -761,7 +847,14 @@ type LegalMonetaryTotal struct {
|
||||
|
||||
// TenderingParty contains tendering party information
|
||||
type TenderingParty struct {
|
||||
XMLName xml.Name `xml:"TenderingParty"`
|
||||
XMLName xml.Name `xml:"TenderingParty"`
|
||||
ID string `xml:"ID"`
|
||||
Tenderer []Tenderer `xml:"Tenderer,omitempty"`
|
||||
}
|
||||
|
||||
// Tenderer contains tenderer information
|
||||
type Tenderer struct {
|
||||
XMLName xml.Name `xml:"Tenderer"`
|
||||
ID string `xml:"ID"`
|
||||
}
|
||||
|
||||
@@ -786,13 +879,16 @@ type ReceivedSubmissionsStatistics struct {
|
||||
|
||||
// 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"`
|
||||
XMLName xml.Name `xml:"SettledContract"`
|
||||
ID string `xml:"ID"`
|
||||
IssueDate string `xml:"IssueDate,omitempty"`
|
||||
AwardDate string `xml:"AwardDate,omitempty"`
|
||||
Title string `xml:"Title,omitempty"`
|
||||
ContractFrameworkIndicator string `xml:"ContractFrameworkIndicator,omitempty"`
|
||||
SignatoryParty *SignatoryParty `xml:"SignatoryParty,omitempty"`
|
||||
ContractReference *ContractReference `xml:"ContractReference,omitempty"`
|
||||
LotTender *LotTender `xml:"LotTender,omitempty"`
|
||||
NoticeDocumentReference *NoticeDocumentReference `xml:"NoticeDocumentReference,omitempty"`
|
||||
}
|
||||
|
||||
// SignatoryParty contains signatory party information
|
||||
@@ -1439,6 +1535,62 @@ func (cn ContractNotice) GetLanguage() string {
|
||||
return cn.NoticeLanguageCode
|
||||
}
|
||||
|
||||
// GetNoticeStatus returns the notice status
|
||||
func (cn ContractNotice) GetNoticeStatus() NoticeStatus {
|
||||
if cn.StatusInformation != nil {
|
||||
if cn.StatusInformation.Status != "" {
|
||||
return cn.StatusInformation.Status
|
||||
}
|
||||
// Check for specific status indicators
|
||||
if cn.StatusInformation.CancellationReason != nil {
|
||||
return NoticeStatusCancelled
|
||||
}
|
||||
if cn.StatusInformation.AwardInformation != nil {
|
||||
return NoticeStatusAwarded
|
||||
}
|
||||
if cn.StatusInformation.SuspensionReason != nil {
|
||||
return NoticeStatusSuspended
|
||||
}
|
||||
if len(cn.StatusInformation.Modifications) > 0 {
|
||||
return NoticeStatusModified
|
||||
}
|
||||
}
|
||||
// Default to Published if no status information is available
|
||||
return NoticeStatusPublished
|
||||
}
|
||||
|
||||
// GetCancellationReason returns the cancellation reason if notice is cancelled
|
||||
func (cn ContractNotice) GetCancellationReason() *CancellationReason {
|
||||
if cn.StatusInformation != nil {
|
||||
return cn.StatusInformation.CancellationReason
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetAwardInformation returns the award information if notice is awarded
|
||||
func (cn ContractNotice) GetAwardInformation() *AwardInformation {
|
||||
if cn.StatusInformation != nil {
|
||||
return cn.StatusInformation.AwardInformation
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetModifications returns the list of modifications if notice is modified
|
||||
func (cn ContractNotice) GetModifications() []NoticeModification {
|
||||
if cn.StatusInformation != nil {
|
||||
return cn.StatusInformation.Modifications
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetSuspensionReason returns the suspension reason if notice is suspended
|
||||
func (cn ContractNotice) GetSuspensionReason() *SuspensionReason {
|
||||
if cn.StatusInformation != nil {
|
||||
return cn.StatusInformation.SuspensionReason
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetPublicationInfo returns publication information if available
|
||||
func (cn ContractNotice) GetPublicationInfo() *Publication {
|
||||
if cn.Extensions != nil &&
|
||||
@@ -1759,6 +1911,47 @@ func (can ContractAwardNotice) GetLanguage() string {
|
||||
return can.NoticeLanguageCode
|
||||
}
|
||||
|
||||
// GetNoticeStatus returns the notice status
|
||||
func (can ContractAwardNotice) GetNoticeStatus() NoticeStatus {
|
||||
if can.StatusInformation != nil && can.StatusInformation.Status != "" {
|
||||
return can.StatusInformation.Status
|
||||
}
|
||||
// Award notices are typically awarded status
|
||||
return NoticeStatusAwarded
|
||||
}
|
||||
|
||||
// GetCancellationReason returns the cancellation reason if notice is cancelled
|
||||
func (can ContractAwardNotice) GetCancellationReason() *CancellationReason {
|
||||
if can.StatusInformation != nil {
|
||||
return can.StatusInformation.CancellationReason
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetAwardInformation returns the award information if notice is awarded
|
||||
func (can ContractAwardNotice) GetAwardInformation() *AwardInformation {
|
||||
if can.StatusInformation != nil {
|
||||
return can.StatusInformation.AwardInformation
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetModifications returns the list of modifications if notice is modified
|
||||
func (can ContractAwardNotice) GetModifications() []NoticeModification {
|
||||
if can.StatusInformation != nil {
|
||||
return can.StatusInformation.Modifications
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetSuspensionReason returns the suspension reason if notice is suspended
|
||||
func (can ContractAwardNotice) GetSuspensionReason() *SuspensionReason {
|
||||
if can.StatusInformation != nil {
|
||||
return can.StatusInformation.SuspensionReason
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetPublicationInfo returns publication information if available
|
||||
func (can ContractAwardNotice) GetPublicationInfo() *Publication {
|
||||
if can.Extensions != nil &&
|
||||
@@ -1915,3 +2108,134 @@ func (can ContractAwardNotice) GetOrganizationByID(orgID string) *Organization {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetAwardedContractors returns detailed information about awarded contractors
|
||||
func (can ContractAwardNotice) GetAwardedContractors() []AwardedContractorInfo {
|
||||
var contractors []AwardedContractorInfo
|
||||
|
||||
noticeResult := can.GetNoticeResult()
|
||||
if noticeResult == nil {
|
||||
return contractors
|
||||
}
|
||||
|
||||
// Create a map of tender ID to tender info for quick lookup
|
||||
tenderMap := make(map[string]*LotTender)
|
||||
for _, lotTender := range noticeResult.LotTender {
|
||||
tenderMap[lotTender.ID] = &lotTender
|
||||
}
|
||||
|
||||
// Create a map of tendering party ID to organization ID
|
||||
partyToOrgMap := make(map[string]string)
|
||||
for _, tenderingParty := range noticeResult.TenderingParty {
|
||||
for _, tenderer := range tenderingParty.Tenderer {
|
||||
partyToOrgMap[tenderingParty.ID] = tenderer.ID
|
||||
}
|
||||
}
|
||||
|
||||
// Process settled contracts to extract award information
|
||||
for _, contract := range noticeResult.SettledContract {
|
||||
if contract.LotTender != nil {
|
||||
// Get tender information
|
||||
tender := tenderMap[contract.LotTender.ID]
|
||||
if tender == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// Get organization information
|
||||
var org *Organization
|
||||
if tender.TenderingParty != nil {
|
||||
orgID := partyToOrgMap[tender.TenderingParty.ID]
|
||||
if orgID != "" {
|
||||
org = can.GetOrganizationByID(orgID)
|
||||
}
|
||||
}
|
||||
|
||||
// Create awarded contractor info
|
||||
contractor := AwardedContractorInfo{
|
||||
ContractID: contract.ID,
|
||||
TenderID: contract.LotTender.ID,
|
||||
AwardDate: contract.AwardDate,
|
||||
IssueDate: contract.IssueDate,
|
||||
}
|
||||
|
||||
// Set contract reference if available
|
||||
if contract.ContractReference != nil {
|
||||
contractor.ContractReference = contract.ContractReference.ID
|
||||
}
|
||||
|
||||
// Set amount and currency from tender
|
||||
if tender.LegalMonetaryTotal != nil {
|
||||
contractor.Amount = tender.LegalMonetaryTotal.PayableAmount.Text
|
||||
contractor.Currency = tender.LegalMonetaryTotal.PayableAmount.CurrencyID
|
||||
}
|
||||
|
||||
// Set organization information
|
||||
if org != nil && org.Company != nil {
|
||||
if org.Company.PartyName != nil {
|
||||
contractor.Name = org.Company.PartyName.Name
|
||||
}
|
||||
if org.Company.PartyLegalEntity != nil {
|
||||
contractor.CompanyID = org.Company.PartyLegalEntity.CompanyID
|
||||
}
|
||||
if org.Company.PartyIdentification != nil {
|
||||
contractor.OrganizationID = org.Company.PartyIdentification.ID
|
||||
}
|
||||
|
||||
// Set address information
|
||||
if org.Company.PostalAddress != nil {
|
||||
addressParts := []string{}
|
||||
if org.Company.PostalAddress.StreetName != "" {
|
||||
addressParts = append(addressParts, org.Company.PostalAddress.StreetName)
|
||||
}
|
||||
if org.Company.PostalAddress.CityName != "" {
|
||||
addressParts = append(addressParts, org.Company.PostalAddress.CityName)
|
||||
}
|
||||
if org.Company.PostalAddress.PostalZone != "" {
|
||||
addressParts = append(addressParts, org.Company.PostalAddress.PostalZone)
|
||||
}
|
||||
contractor.Address = strings.Join(addressParts, ", ")
|
||||
|
||||
if org.Company.PostalAddress.Country != nil {
|
||||
contractor.Country = org.Company.PostalAddress.Country.IdentificationCode
|
||||
}
|
||||
}
|
||||
|
||||
// Set contact information
|
||||
if org.Company.Contact != nil {
|
||||
contractor.ContactName = org.Company.Contact.Name
|
||||
contractor.ContactEmail = org.Company.Contact.ElectronicMail
|
||||
contractor.ContactPhone = org.Company.Contact.Telephone
|
||||
}
|
||||
}
|
||||
|
||||
// Set lot information
|
||||
if tender.TenderLot != nil {
|
||||
contractor.LotID = tender.TenderLot.ID
|
||||
}
|
||||
|
||||
contractors = append(contractors, contractor)
|
||||
}
|
||||
}
|
||||
|
||||
return contractors
|
||||
}
|
||||
|
||||
// AwardedContractorInfo contains detailed information about an awarded contractor
|
||||
type AwardedContractorInfo struct {
|
||||
ContractID string `json:"contract_id"`
|
||||
TenderID string `json:"tender_id"`
|
||||
LotID string `json:"lot_id,omitempty"`
|
||||
Name string `json:"name"`
|
||||
CompanyID string `json:"company_id,omitempty"`
|
||||
OrganizationID string `json:"organization_id,omitempty"`
|
||||
Address string `json:"address,omitempty"`
|
||||
Country string `json:"country,omitempty"`
|
||||
Amount string `json:"amount"`
|
||||
Currency string `json:"currency,omitempty"`
|
||||
AwardDate string `json:"award_date,omitempty"`
|
||||
IssueDate string `json:"issue_date,omitempty"`
|
||||
ContractReference string `json:"contract_reference,omitempty"`
|
||||
ContactName string `json:"contact_name,omitempty"`
|
||||
ContactEmail string `json:"contact_email,omitempty"`
|
||||
ContactPhone string `json:"contact_phone,omitempty"`
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user