@@ -8,6 +8,7 @@ import { QuoteService } from 'src/app/features/quotes/services/quote.service';
88import { NotificationService } from 'src/app/services/notification.service' ;
99import { LocalStorageService } from 'src/app/services/local-storage.service' ;
1010import { ProviderService , Provider } from 'src/app/services/provider.service' ;
11+ import { AccountServiceService } from 'src/app/services/account-service.service' ;
1112import { Tender , TenderAttachment } from 'src/app/models/tender.model' ;
1213import { LoginInfo } from 'src/app/models/interfaces' ;
1314import { API_ROLES } from 'src/app/models/roles.constants' ;
@@ -436,6 +437,7 @@ export class CreateTenderModalComponent implements OnInit, OnChanges {
436437 private notificationService = inject ( NotificationService ) ;
437438 private localStorage = inject ( LocalStorageService ) ;
438439 private providerService = inject ( ProviderService ) ;
440+ private accountService = inject ( AccountServiceService ) ;
439441 private router = inject ( Router ) ;
440442
441443 // Properties for tender creation modal
@@ -520,8 +522,8 @@ export class CreateTenderModalComponent implements OnInit, OnChanges {
520522 }
521523 }
522524
523- // Load filter options
524- this . loadFilterOptions ( ) ;
525+ // Filter options (categories, countries, compliance levels) and the provider list
526+ // are loaded lazily in proceedToProviderSelection() when the user enters step 3.
525527 }
526528
527529 ngOnChanges ( changes : SimpleChanges ) {
@@ -648,6 +650,8 @@ export class CreateTenderModalComponent implements OnInit, OnChanges {
648650
649651 // Move to Step 2: Date fields
650652 this . tenderCreationStep = 2 ;
653+ // Notify the parent dashboard so the new tender appears in the list immediately
654+ this . tenderUpdated . emit ( ) ;
651655 } ,
652656 error : ( error ) => {
653657 console . error ( 'Error creating tender:' , error ) ;
@@ -825,6 +829,16 @@ export class CreateTenderModalComponent implements OnInit, OnChanges {
825829 this . tenderLoading = false ;
826830 this . notificationService . showSuccess ( 'Tender details saved successfully!' ) ;
827831 this . tenderCreationStep = 3 ;
832+
833+ // Reset any previously active filters silently (emitEvent:false avoids
834+ // triggering the valueChanges → emitFilters → loadTenderProviders chain)
835+ this . orgFilters = { categories : [ ] , countries : [ ] , complianceLevels : [ ] } ;
836+ this . countriesCtrl . setValue ( [ ] , { emitEvent : false } ) ;
837+ this . categoriesCtrl . setValue ( [ ] , { emitEvent : false } ) ;
838+ this . complianceLevelsCtrl . setValue ( [ ] , { emitEvent : false } ) ;
839+
840+ // Load filter criteria and the provider list in parallel
841+ this . loadFilterOptions ( ) ;
828842 this . loadTenderProviders ( ) ;
829843 } ,
830844 error : ( error : any ) => {
@@ -836,51 +850,29 @@ export class CreateTenderModalComponent implements OnInit, OnChanges {
836850 }
837851
838852 /**
839- * Load providers from the provider API
853+ * Load providers from the search API.
854+ * An empty result set is valid (no providers match the active filters).
855+ * Only falls back to the full party-organisation list when the search
856+ * endpoint returns an actual HTTP error.
840857 */
841858 loadTenderProviders ( ) {
842859 this . tenderLoading = true ;
843860 this . tenderError = null ;
844861
845- console . log ( 'Loading providers from API...' ) ;
846-
847862 this . providerService . getProvidersForTenderNew ( this . orgFilters ) . subscribe ( {
848863 next : ( providers ) => {
849- // If search endpoint returns empty or fails, fallback to basic endpoint
850- if ( ! providers || providers . length === 0 ) {
851- console . log ( 'Search returned no providers, trying fallback endpoint...' ) ;
852- this . providerService . getProvidersForTender ( ) . subscribe ( {
853- next : ( fallbackProviders ) => {
854- this . tenderProviders = fallbackProviders ;
855- console . log ( 'Fallback loaded providers:' , fallbackProviders . length ) ;
856- this . tenderLoading = false ;
857- this . updateAvailableProviders ( ) ;
858-
859- if ( this . tenderCreationStep === 3 ) {
860- this . loadInvitedProviders ( ) ;
861- }
862- } ,
863- error : ( fallbackErr ) => {
864- this . tenderError = 'Failed to load providers from both endpoints: ' + ( fallbackErr . message || 'Unknown error' ) ;
865- this . tenderLoading = false ;
866- console . error ( 'Fallback endpoint also failed:' , fallbackErr ) ;
867- }
868- } ) ;
869- } else {
870- this . tenderProviders = providers ;
871- console . log ( 'Search loaded providers:' , providers . length ) ;
872- this . tenderLoading = false ;
873- this . updateAvailableProviders ( ) ;
874-
875- // After providers are loaded, load invited providers (if in edit mode)
876- if ( this . tenderCreationStep === 3 ) {
877- this . loadInvitedProviders ( ) ;
878- }
864+ this . tenderProviders = providers ?? [ ] ;
865+ console . log ( 'Search loaded providers:' , this . tenderProviders . length ) ;
866+ this . tenderLoading = false ;
867+ this . updateAvailableProviders ( ) ;
868+
869+ if ( this . tenderCreationStep === 3 ) {
870+ this . loadInvitedProviders ( ) ;
879871 }
880872 } ,
881873 error : ( err ) => {
882- // Search endpoint failed completely, try fallback
883- console . warn ( 'Search endpoint failed, trying fallback... ' , err ) ;
874+ // HTTP error from the search endpoint — fall back to the full organisation list
875+ console . warn ( 'Search endpoint returned an error, falling back to full provider list: ' , err ) ;
884876 this . providerService . getProvidersForTender ( ) . subscribe ( {
885877 next : ( fallbackProviders ) => {
886878 this . tenderProviders = fallbackProviders ;
@@ -895,7 +887,7 @@ export class CreateTenderModalComponent implements OnInit, OnChanges {
895887 error : ( fallbackErr ) => {
896888 this . tenderError = 'Failed to load providers: ' + ( fallbackErr . message || 'Unknown error' ) ;
897889 this . tenderLoading = false ;
898- console . error ( 'Error loading tender providers :' , fallbackErr ) ;
890+ console . error ( 'Fallback endpoint also failed :' , fallbackErr ) ;
899891 }
900892 } ) ;
901893 }
@@ -987,7 +979,13 @@ export class CreateTenderModalComponent implements OnInit, OnChanges {
987979 }
988980
989981 /**
990- * Load already invited providers by fetching tendering quotes with the coordinator quote's externalId
982+ * Load already invited providers by fetching tendering quotes with the coordinator quote's externalId.
983+ *
984+ * Name resolution priority:
985+ * 1. Match the provider's org URN (tender.selectedProviders[0]) against the already-loaded
986+ * tenderProviders list — this covers the common case with no extra API calls.
987+ * 2. Fall back to AccountServiceService.getOrgInfo() for providers not in the cached list
988+ * (e.g. when reopening the modal without navigating to the provider-search step first).
991989 */
992990 loadInvitedProviders ( ) {
993991 if ( ! this . createdQuoteId || ! this . currentUserId ) {
@@ -998,34 +996,46 @@ export class CreateTenderModalComponent implements OnInit, OnChanges {
998996 console . log ( 'Loading invited providers for externalId:' , this . createdQuoteId ) ;
999997
1000998 this . tenderLoading = true ;
1001-
999+
10021000 this . quoteService . getTenderingQuotesByUser ( this . currentUserId , API_ROLES . BUYER ) . subscribe ( {
1003- next : ( tenders ) => {
1001+ next : async ( tenders ) => {
10041002 console . log ( 'Received tenders:' , tenders ) ;
1005-
1006- // Clear existing invited providers
1007- this . invitedProviders = [ ] ;
1008-
1009- // Filter tenders that match our createdQuoteId as externalId
1010- const matchingTenders = tenders . filter ( t => t . external_id === this . createdQuoteId ) ;
1011-
1012- // Convert to invited providers format
1013- matchingTenders . forEach ( tender => {
1014- // TODO: Get proper provider info once provider service is available
1015- const provider : Provider = {
1016- id : tender . provider || undefined ,
1017- tradingName : tender . provider || 'Unknown Provider'
1018- } ;
1019-
1020- if ( tender . id ) {
1021- this . invitedProviders . push ( {
1022- provider : provider ,
1023- quoteId : tender . id
1024- } ) ;
1025- console . log ( 'Added invited provider:' , provider . tradingName , 'with quote ID:' , tender . id ) ;
1026- }
1027- } ) ;
1028-
1003+
1004+ // Filter tenders that match our coordinator quote as their parent
1005+ const matchingTenders = tenders . filter ( t => t . external_id === this . createdQuoteId && ! ! t . id ) ;
1006+
1007+ // Resolve provider display names, then populate invitedProviders
1008+ const entries = await Promise . all (
1009+ matchingTenders . map ( async ( tender ) => {
1010+ // The org URN lives in selectedProviders[0] (mapped from relatedParty[Seller].id)
1011+ const providerOrgUrn = tender . selectedProviders ?. [ 0 ] ;
1012+
1013+ // 1. Try the already-loaded provider list first (no extra network call)
1014+ const knownProvider = providerOrgUrn
1015+ ? this . tenderProviders . find ( p => p . id === providerOrgUrn )
1016+ : undefined ;
1017+
1018+ if ( knownProvider ) {
1019+ return { provider : knownProvider , quoteId : tender . id ! } ;
1020+ }
1021+
1022+ // 2. Fall back to account service to get the trading name by org URN
1023+ let tradingName = providerOrgUrn || 'Unknown Provider' ;
1024+ if ( providerOrgUrn ) {
1025+ try {
1026+ const org = await this . accountService . getOrgInfo ( providerOrgUrn ) ;
1027+ tradingName = org ?. tradingName || org ?. name || providerOrgUrn ;
1028+ } catch {
1029+ // Network error — keep the URN as a recognisable fallback
1030+ }
1031+ }
1032+
1033+ const provider : Provider = { id : providerOrgUrn , tradingName } ;
1034+ return { provider, quoteId : tender . id ! } ;
1035+ } )
1036+ ) ;
1037+
1038+ this . invitedProviders = entries ;
10291039 console . log ( 'Total invited providers loaded:' , this . invitedProviders . length ) ;
10301040 this . tenderLoading = false ;
10311041 } ,
@@ -1155,8 +1165,33 @@ export class CreateTenderModalComponent implements OnInit, OnChanges {
11551165 this . tenderLoading = false ;
11561166 } ,
11571167 error : ( error ) => {
1158- console . error ( 'Error deleting quote:' , error ) ;
1159- this . notificationService . showError ( 'Failed to remove provider invitation: ' + ( error . message || 'Unknown error' ) ) ;
1168+ // TEMPORARY WORKAROUND — sandbox environment issue:
1169+ // The TMForum/BAE backend successfully deletes the quote but then attempts to
1170+ // notify a downstream microservice (charging/events) that is unreachable in sandbox.
1171+ // This causes the BAE to return 500 {error: "Service unreachable"} AFTER the deletion
1172+ // has already completed. As a result, the HTTP 500 reaches this error handler even
1173+ // though the underlying operation succeeded.
1174+ //
1175+ // We detect this specific case (HTTP 500 + "Service unreachable" in the response body)
1176+ // and treat it as a success so the UI stays consistent with the actual backend state.
1177+ //
1178+ // TODO: Remove this workaround once the sandbox downstream service is reachable
1179+ // and the BAE no longer returns 500 on successful quote deletion.
1180+ const isKnownFalsePositive =
1181+ error . status === 500 &&
1182+ error . error ?. error === 'Service unreachable' ;
1183+
1184+ if ( isKnownFalsePositive ) {
1185+ console . warn (
1186+ '[WORKAROUND] deleteQuote returned 500 "Service unreachable" for quoteId:' , quoteId ,
1187+ '— quote was deleted on the backend. Removing from UI anyway.'
1188+ ) ;
1189+ this . invitedProviders = this . invitedProviders . filter ( ip => ip . quoteId !== quoteId ) ;
1190+ this . notificationService . showSuccess ( 'Provider invitation removed successfully' ) ;
1191+ } else {
1192+ console . error ( 'Error deleting quote:' , error ) ;
1193+ this . notificationService . showError ( 'Failed to remove provider invitation: ' + ( error . message || 'Unknown error' ) ) ;
1194+ }
11601195 this . tenderLoading = false ;
11611196 }
11621197 } ) ;
@@ -1245,10 +1280,11 @@ export class CreateTenderModalComponent implements OnInit, OnChanges {
12451280 }
12461281
12471282 /**
1248- * Load filter options (countries, categories, compliance levels)
1283+ * Load filter options (countries, categories, compliance levels).
1284+ * Only fetches the option lists — does NOT reset selected filter values
1285+ * or trigger a provider reload.
12491286 */
12501287 private loadFilterOptions ( ) : void {
1251- this . clearFilters ( ) ;
12521288 this . providerService . getFilterOptions ( ) . subscribe ( {
12531289 next : ( { categories, countries, complianceLevels } ) => {
12541290 this . categoriesOptions = categories ?? [ ] ;
0 commit comments