diff --git a/internal/tender/recommendation_filter.go b/internal/tender/recommendation_filter.go index 162f82c..6426c10 100644 --- a/internal/tender/recommendation_filter.go +++ b/internal/tender/recommendation_filter.go @@ -46,15 +46,15 @@ func (s *tenderService) buildRejectedTenderIDSet(ctx context.Context, companyID return excluded, nil } -func (s *tenderService) buildRejectedTenderIDSetForCompanies(ctx context.Context, companyIDs []string) (map[string]struct{}, error) { - excluded := make(map[string]struct{}) +func (s *tenderService) buildRejectedTenderIDSetsForCompanies(ctx context.Context, companyIDs []string) (map[string]map[string]struct{}, error) { + excludedByCompany := make(map[string]map[string]struct{}) if s.rejectedTenderLister == nil { - return excluded, nil + return excludedByCompany, nil } normalized := normalizeRecommendationCompanyScope(companyIDs) if len(normalized) == 0 { - return excluded, nil + return excludedByCompany, nil } results := make([][]string, len(normalized)) @@ -75,11 +75,13 @@ func (s *tenderService) buildRejectedTenderIDSetForCompanies(ctx context.Context return nil, err } - for _, ids := range results { - addTenderIDsToSet(excluded, ids) + for i, companyID := range normalized { + excluded := make(map[string]struct{}) + addTenderIDsToSet(excluded, results[i]) + excludedByCompany[companyID] = excluded } - return excluded, nil + return excludedByCompany, nil } func addTenderIDsToSet(set map[string]struct{}, ids []string) { @@ -117,3 +119,18 @@ func isRecommendedTenderExcluded(tender Tender, tenderRef string, excluded map[s return false } + +func isRecommendedTenderExcludedForAllCompanies(tender Tender, tenderRef string, excludedByCompany map[string]map[string]struct{}, companyIDs []string) bool { + normalized := normalizeRecommendationCompanyScope(companyIDs) + if len(normalized) == 0 { + return false + } + + for _, companyID := range normalized { + if !isRecommendedTenderExcluded(tender, tenderRef, excludedByCompany[companyID]) { + return false + } + } + + return true +} diff --git a/internal/tender/recommendation_filter_test.go b/internal/tender/recommendation_filter_test.go index 6ce3937..19e26df 100644 --- a/internal/tender/recommendation_filter_test.go +++ b/internal/tender/recommendation_filter_test.go @@ -71,7 +71,7 @@ func TestIsRecommendedTenderExcluded(t *testing.T) { } } -func TestBuildRejectedTenderIDSetForCompanies(t *testing.T) { +func TestBuildRejectedTenderIDSetsForCompanies(t *testing.T) { service := &tenderService{ rejectedTenderLister: stubRejectedTenderLister{ byCompany: map[string][]string{ @@ -81,17 +81,48 @@ func TestBuildRejectedTenderIDSetForCompanies(t *testing.T) { }, } - got, err := service.buildRejectedTenderIDSetForCompanies(context.Background(), []string{"company-a", "company-b", "company-a"}) + got, err := service.buildRejectedTenderIDSetsForCompanies(context.Background(), []string{"company-a", "company-b", "company-a"}) if err != nil { - t.Fatalf("buildRejectedTenderIDSetForCompanies() error = %v", err) + t.Fatalf("buildRejectedTenderIDSetsForCompanies() error = %v", err) } - for _, tenderID := range []string{"tender-1", "tender-2", "tender-3"} { - if _, ok := got[tenderID]; !ok { - t.Fatalf("expected excluded set to contain %q", tenderID) - } + if _, ok := got["company-a"]["tender-1"]; !ok { + t.Fatalf("expected company-a exclusions to contain tender-1") } - if len(got) != 3 { - t.Fatalf("buildRejectedTenderIDSetForCompanies() len = %d, want 3", len(got)) + if _, ok := got["company-a"]["tender-2"]; !ok { + t.Fatalf("expected company-a exclusions to contain tender-2") + } + if _, ok := got["company-b"]["tender-2"]; !ok { + t.Fatalf("expected company-b exclusions to contain tender-2") + } + if _, ok := got["company-b"]["tender-3"]; !ok { + t.Fatalf("expected company-b exclusions to contain tender-3") + } + if len(got) != 2 { + t.Fatalf("buildRejectedTenderIDSetsForCompanies() len = %d, want 2", len(got)) + } +} + +func TestIsRecommendedTenderExcludedForAllCompanies(t *testing.T) { + tender := Tender{ + Model: mongo.Model{}, + ContractFolderID: "055329e0-3d8f-4eeb-946a-85a451f2e36b", + NoticePublicationID: "00423458-2026", + } + tender.ID, _ = bson.ObjectIDFromHex("507f1f77bcf86cd799439011") + + procRef := FormatAIProcedureRef(tender.ContractFolderID, tender.NoticePublicationID) + excludedByCompany := map[string]map[string]struct{}{ + "company-a": {procRef: {}}, + "company-b": {}, + } + + if isRecommendedTenderExcludedForAllCompanies(tender, procRef, excludedByCompany, []string{"company-a", "company-b"}) { + t.Fatalf("expected tender to remain visible when not all companies rejected it") + } + + excludedByCompany["company-b"][procRef] = struct{}{} + if !isRecommendedTenderExcludedForAllCompanies(tender, procRef, excludedByCompany, []string{"company-a", "company-b"}) { + t.Fatalf("expected tender to be hidden when all companies rejected it") } } diff --git a/internal/tender/service.go b/internal/tender/service.go index 25b0494..d499dca 100644 --- a/internal/tender/service.go +++ b/internal/tender/service.go @@ -973,10 +973,13 @@ func (s *tenderService) Recommend(ctx context.Context, form *SearchForm, paginat return nil, err } - var excluded map[string]struct{} + var ( + excluded map[string]struct{} + excludedByCompany map[string]map[string]struct{} + ) if form.ExcludeRejectedTenders { if len(companyIDs) > 0 { - excluded, err = s.buildRejectedTenderIDSetForCompanies(ctx, companyIDs) + excludedByCompany, err = s.buildRejectedTenderIDSetsForCompanies(ctx, companyIDs) } else { excluded, err = s.buildRejectedTenderIDSet(ctx, companyID) } @@ -999,7 +1002,11 @@ func (s *tenderService) Recommend(ctx context.Context, form *SearchForm, paginat if !ok { continue } - if isRecommendedTenderExcluded(tender, tenderRef, excluded) { + if len(companyIDs) > 0 { + if isRecommendedTenderExcludedForAllCompanies(tender, tenderRef, excludedByCompany, companyIDs) { + continue + } + } else if isRecommendedTenderExcluded(tender, tenderRef, excluded) { continue } if form.OnlyActiveDeadlines && !tender.IsRecommendable(now) {