diff --git a/internal/tender/entity.go b/internal/tender/entity.go index 62cbbdf..37b34aa 100644 --- a/internal/tender/entity.go +++ b/internal/tender/entity.go @@ -262,11 +262,22 @@ func (t *Tender) IsExpired() bool { return false } - // Check if deadline (in milliseconds) is in the past now := time.Now().Unix() return t.TenderDeadline < now } +// IsRecommendable reports whether a tender should appear in AI recommendation results. +func (t *Tender) IsRecommendable(now int64) bool { + switch t.Status { + case TenderStatusExpired, TenderStatusCancelled, TenderStatusAwarded, TenderStatusClosed: + return false + } + if t.TenderDeadline <= now { + return false + } + return true +} + // GetTenderURL returns the TED tender URL func (t *Tender) GetTenderURL() string { if t.TenderURL != "" { diff --git a/internal/tender/entity_test.go b/internal/tender/entity_test.go index 72ec624..6e8e571 100644 --- a/internal/tender/entity_test.go +++ b/internal/tender/entity_test.go @@ -72,3 +72,52 @@ func TestResolvedEstimatedValueAndCurrencyPrefersTopLevel(t *testing.T) { t.Fatalf("expected USD, got %q", currency) } } + +func TestTenderIsRecommendable(t *testing.T) { + now := int64(1_700_000_000) + + tests := []struct { + name string + tender Tender + want bool + }{ + { + name: "active with future deadline", + tender: Tender{Status: TenderStatusActive, TenderDeadline: now + 3600}, + want: true, + }, + { + name: "active with past deadline", + tender: Tender{Status: TenderStatusActive, TenderDeadline: now - 3600}, + want: false, + }, + { + name: "active with deadline at now", + tender: Tender{Status: TenderStatusActive, TenderDeadline: now}, + want: false, + }, + { + name: "active without deadline", + tender: Tender{Status: TenderStatusActive, TenderDeadline: 0}, + want: false, + }, + { + name: "expired status with future deadline", + tender: Tender{Status: TenderStatusExpired, TenderDeadline: now + 3600}, + want: false, + }, + { + name: "awarded with future deadline", + tender: Tender{Status: TenderStatusAwarded, TenderDeadline: now + 3600}, + want: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.tender.IsRecommendable(now); got != tt.want { + t.Fatalf("IsRecommendable() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/internal/tender/service.go b/internal/tender/service.go index d741622..e2764c0 100644 --- a/internal/tender/service.go +++ b/internal/tender/service.go @@ -955,10 +955,10 @@ func (s *tenderService) Recommend(ctx context.Context, form *SearchForm, paginat if !ok { continue } - if form.OnlyActiveDeadlines && tender.TenderDeadline <= now { + if form.OnlyActiveDeadlines && !tender.IsRecommendable(now) { continue } - if len(form.Status) > 0 && tender.Status != TenderStatusActive { + if len(form.Status) > 0 && !statusAllowed(tender.Status, form.Status) { continue } @@ -985,6 +985,15 @@ func (s *tenderService) Recommend(ctx context.Context, form *SearchForm, paginat }, nil } +func statusAllowed(status TenderStatus, allowed []string) bool { + for _, s := range allowed { + if string(status) == strings.TrimSpace(s) { + return true + } + } + return false +} + // Delete deletes a tender // GetTenderStatistics retrieves tender statistics