Issue with searching as a regular user

I have come across a strange issue that I would like some feedback on. I am currently working through various issues of migrating SugarCRM 6.5.25 → SuiteCRM 8.9.1.

I’ve been able to work through many items, mostly messed up layout defs, but this one has me scratching my head.

Scenario: a user wants to search for an opportunity record so they perform a search and select only the ‘Opportunities’ module.

When they attempt to search, they receive an error: “An unknown error has occurred while performing the search. Contact an administrator if the problem persists. More information available in the logs.”

When I go sleuthing through the logs I find this stacktrace:

Fri Jan 2 2026 at 23:53:14 [165031][d7de7ef6-7d11-1bad-2376-68f81ae726a4][FATAL] [ERROR] [SearchThrowableHandler] TypeError: Unsupported operand types: int + string in /var/www/suite8/public/legacy/include/database/DBManager.php:1596
Stack trace:
#0 /var/www/suite8/public/legacy/include/SearchForm/SearchForm2.php(1378): DBManager->quoteType()
#1 /var/www/suite8/public/legacy/lib/Search/BasicSearch/BasicSearchEngine.php(172): SearchForm->generateSearchWhere()
#2 /var/www/suite8/public/legacy/lib/Search/BasicSearch/BasicSearchEngine.php(85): SuiteCRM\Search\BasicSearch\BasicSearchEngine->searchModules()
#3 /var/www/suite8/public/legacy/lib/Search/SearchEngine.php(78): SuiteCRM\Search\BasicSearch\BasicSearchEngine->search()
#4 /var/www/suite8/public/legacy/lib/Search/SearchWrapper.php(89): SuiteCRM\Search\SearchEngine->searchAndDisplay()
#5 /var/www/suite8/public/legacy/modules/Home/Search.php(50): SuiteCRM\Search\SearchWrapper::searchAndDisplay()
#6 /var/www/suite8/public/legacy/modules/Home/UnifiedSearch.php(41): require_once('...')
#7 /var/www/suite8/public/legacy/include/MVC/View/SugarView.php(800): include_once('...')
#8 /var/www/suite8/public/legacy/include/MVC/View/views/view.classic.php(72): SugarView->includeClassicFile()
#9 /var/www/suite8/public/legacy/include/MVC/View/SugarView.php(210): ViewClassic->display()
#10 /var/www/suite8/public/legacy/include/MVC/Controller/SugarController.php(443): SugarView->process()
#11 /var/www/suite8/public/legacy/include/MVC/Controller/SugarController.php(374): SugarController->processView()
#12 /var/www/suite8/public/legacy/include/MVC/SugarApplication.php(101): SugarController->execute()
#13 /var/www/suite8/public/legacy/index.php(52): SugarApplication->execute()
#14 {main}

After digging in and adding some debug code, it appears that the opportunities.amount field (which is a currency type) has a blank value instead of a numeric value in include/SearchForm/SearchForm2.php line 1378

Any idea why this is happening? I could temporarily fix this by checking if the value is blank and assigning it a 0 value, but that would be modifying core code and I’m not about that.

Thanks in advance!

A little further info,

For testing I made a modification to include/database/DBManager.php to use 0 if the value was blank, and it ended up using this query:

SELECT  opportunities.id , opportunities.modified_user_id , opportunities.created_by , opportunities.assigned_user_id , opportunities.campaign_id , opportunities.currency_id , opportunities.name , opportunities.date_entered , opportunities.date_modified  , jt0.user_name modified_by_name , jt0.created_by modified_by_name_owner  , 'Users' modified_by_name_mod , jt1.user_name created_by_name , jt1.created_by created_by_name_owner  , 'Users' created_by_name_mod, opportunities.description , opportunities.deleted  , jt2.user_name assigned_user_name , jt2.created_by assigned_user_name_owner  , 'Users' assigned_user_name_mod, opportunities.opportunity_type  , accounts.name account_name, jtl3.account_id account_id , jt4.name campaign_name , jt4.assigned_user_id campaign_name_owner  , 'Campaigns' campaign_name_mod, opportunities.lead_source , opportunities.amount , opportunities.amount_usdollar , opportunities.date_closed , opportunities.next_step , opportunities.sales_stage , opportunities.probability , opportunities_cstm.sms_consent_c , opportunities_cstm.utm_medium_c , opportunities_cstm.date_signed_c , opportunities_cstm.externalclickid_c , opportunities_cstm.payment_link_c , opportunities_cstm.agreement_sent_date_c , opportunities_cstm.set_up_ttl_c , opportunities_cstm.client_type_c , opportunities_cstm.projected_live_service_date_c , opportunities_cstm.utm_campaign_c , opportunities_cstm.utm_term_c , opportunities_cstm.utmurl_c , opportunities_cstm.utm_source_c , opportunities_cstm.industry_c , opportunities_cstm.payment_total_c , opportunities_cstm.agreement_sent_c , opportunities_cstm.utm_content_c , opportunities_cstm.additional_services_c , opportunities_cstm.answernet_doing_c , opportunities_cstm.businesstype_c , opportunities_cstm.company_do_c , opportunities_cstm.contract_expiration_date_c , opportunities_cstm.customer_since_c , opportunities_cstm.decision_maker_c , opportunities_cstm.follow_up_comments_c , opportunities_cstm.hours_c , opportunities_cstm.indirect_partner_c , opportunities_cstm.industry_dom_c , opportunities_cstm.jjwg_maps_address_c , opportunities_cstm.jjwg_maps_geocode_status_c , opportunities_cstm.jjwg_maps_lat_c , opportunities_cstm.jjwg_maps_lng_c , opportunities_cstm.last_user_c , opportunities_cstm.location_c , opportunities_cstm.lost_reason_c , opportunities_cstm.product_type_c , opportunities_cstm.sab_account_number_c , opportunities_cstm.save_a_client_c , opportunities_cstm.site_lst_c , opportunities_cstm.status_c , opportunities_cstm.system_c , opportunities_cstm.watch_list_c , opportunities_cstm.website_source_c , opportunities_cstm.w_competition_c , opportunities_cstm.w_follow_up_c  FROM opportunities  LEFT JOIN opportunities_cstm ON opportunities.id = opportunities_cstm.id_c   LEFT JOIN  users jt0 ON opportunities.modified_user_id=jt0.id AND jt0.deleted=0

 AND jt0.deleted=0  LEFT JOIN  users jt1 ON opportunities.created_by=jt1.id AND jt1.deleted=0

 AND jt1.deleted=0  LEFT JOIN  users jt2 ON opportunities.assigned_user_id=jt2.id AND jt2.deleted=0

 AND jt2.deleted=0  LEFT JOIN  accounts_opportunities jtl3 ON opportunities.id=jtl3.opportunity_id AND jtl3.deleted=0

 LEFT JOIN  accounts accounts ON accounts.id=jtl3.account_id AND accounts.deleted=0
 AND accounts.deleted=0  LEFT JOIN  campaigns jt4 ON opportunities.campaign_id=jt4.id AND jt4.deleted=0

 AND jt4.deleted=0 where (((LTRIM(RTRIM(CONCAT(IFNULL(opportunities.name,'')))) LIKE 'Test Opportunity%' OR LTRIM(RTRIM(CONCAT(IFNULL(opportunities.name,'')))) LIKE 'Test Opportunity%' ) OR ( LTRIM(RTRIM(CONCAT(IFNULL(accounts.name,'')))) LIKE 'Test Opportunity%' OR LTRIM(RTRIM(CONCAT(IFNULL(accounts.name,'')))) LIKE 'Test Opportunity%' ) OR ( opportunities.amount = 0))) AND opportunities.deleted=0 ORDER BY opportunities.date_entered DESC LIMIT 0,11

Notice that it is using the opportunities.amount field as part of the search. This seems really odd to me, as that wouldn’t really be a good field to be searching against.

OK I was able to solve this by removing the ‘amount’ field from custom/modules/Opportunities/metadata/SearchFields.php

Hopefully this will help someone in the future!