Sempre mostrar terapeuta na busca, independente de availabilities_count
TLDR: Terapeutas Pro sem
Availabilitycadastrada deixam de ser excluídos da busca; a exigência de disponibilidade some tanto da query quanto da validação que apaga o registro deSearchTherapist.
Contexto
A terapeuta Rita Valente Fialho Canudo (SearchTherapist id 10421, plan_id 1 = Pro, availabilities_count: 0) não aparece em GET /api/v1/therapists?name=canudo&page=1 apesar de passar em todos os demais critérios (pro_bono_allowed: true, price: 150.0, nome bate com canudo).
A causa raiz é o filtro base de UserProfiles::SearchTherapistsQuery#eligible_therapists, que exige, para terapeutas Pro, ao menos uma disponibilidade cadastrada (availabilities_count >= 1). Existe ainda uma regra equivalente e complementar em SearchTherapists::UpdateByProfile#valid?, que destrói o registro de um Pro assim que ele fica sem Availability — fechando a busca por um segundo caminho.
A nova regra de negócio é sempre mostrar o terapeuta (Pro ou Regular) na busca, independente de ter disponibilidade cadastrada. Fora essa regra, todos os demais filtros (plano ativo, onboarding aprovado, perfil publicado, pro bono, preço para cliente, etc.) permanecem.
O contador availabilities_count continua sendo alimentado porque ainda é exibido no dashboard Avo (app/views/avo/cards/_search_therapist_count.html.erb:14-15).
Quando e onde UpdateByProfile é chamado
SearchTherapists::UpdateByProfile.call tem um único caller: SearchTherapists::RefreshByProfile#call (app/use_cases/search_therapists/refresh_by_profile.rb:10). Portanto, todo impacto da mudança em UpdateByProfile#valid? passa pelos gatilhos de RefreshByProfile:
Gatilhos síncronos de RefreshByProfile.call
- Job
SearchTherapists::RefreshByProfileJob—app/jobs/search_therapists/refresh_by_profile_job.rb:4. Ponto final de todo fluxo assíncrono. - Cron a cada minuto —
TherapistAvailableTimes::UpdateLastProfilesChangedJob→ itera perfis mexidos recentemente e chamaRefreshByProfile.call(profile.user_id)emapp/use_cases/therapist_available_times/update_last_profiles_changed.rb:17. - Cron a cada hora (minuto 0) —
TherapistAvailableTimes::RefreshJob→ percorre TODOS os perfis comsearch_therapistemapp/jobs/therapist_available_times/refresh_job.rb:5. SearchTherapists::Refresh—app/use_cases/search_therapists/refresh.rb:5. IteraUserProfile.published. Sem caller no repo; uso manual via console para reindexação em massa.
Callbacks que enfileiram RefreshByProfileJob via EnqueueRefreshByProfile (com dedup e delay de 2 s)
| Model | Hook | Arquivo:linha | Condição |
|---|---|---|---|
UserProfile |
after_commit |
user_profile.rb:241 |
qualquer mudança |
UserProfileCertificate |
after_commit |
user_profile_certificate.rb:29 |
qualquer mudança |
Availability |
after_commit |
availability.rb:159-161 |
qualquer mudança |
UserAddress |
after_commit |
user_address.rb:64 |
qualquer mudança |
FinancialSetting |
after_commit |
financial_setting.rb:58 |
qualquer mudança |
Subscription |
before_update |
subscription.rb:47-51 |
(pro? OR terapeuta?) AND active? |
Meeting |
after_commit |
meeting.rb:187 |
qualquer mudança |
MeetingParticipant |
after_commit |
meeting_participant.rb:82 |
professional? |
Rate |
after_commit |
rate.rb:38 |
qualquer mudança |
Callback que enfileira direto (sem passar por EnqueueRefreshByProfile)
User#before_updateemapp/models/user.rb:187-193, quandoname_changed?, agendaSearchTherapists::RefreshByProfileJobcom delay de 2 s.
Efeito concreto da mudança em valid?
Removida a checagem user.pro? && !user.availabilities.exists?:
- Nenhum Pro é destruído da tabela por perder Availability. Qualquer edit nos 10 models acima passa a recriar registros de Pros hoje ausentes.
- Via cron horário, em até 1 h a tabela converge para o novo universo sem ação manual.
- Para reindexar imediatamente após o deploy: SearchTherapists::Refresh.call no console.
- No caso da Rita, o registro já existe; a remoção do filtro da query basta para ela aparecer. A mudança no valid? é uma salvaguarda contra remoções futuras.
Arquivos a alterar
1. app/queries/user_profiles/search_therapists_query.rb
Simplificar initialize e remover o método eligible_therapists. Resultado:
ruby
def initialize
@collection = SearchTherapist.distinct
end
- Remover linhas 14-16 (body atual do
initializeque calculapro_plan_id) e 18-21 (métodoeligible_therapists). - A constante
DEFAULT_ORDER_DIRECTIONpermanece — continua em uso nodefault_order.
Efeito: a query passa a partir do universo completo de SearchTherapist. Os demais escopos (by_user, by_name, by_price, by_date, default_order, etc.) continuam intactos e agem em cima disso.
2. app/use_cases/search_therapists/update_by_profile.rb
Remover a linha 42:
ruby
return false if user.pro? && !user.availabilities.exists?
As demais checagens do valid? (onboarding, published, is_therapist, subscription presente, slug whitelisted, não expirada, ativa) permanecem — apenas a obrigatoriedade de availability para Pro sai.
A atribuição de availabilities_count na linha 22 permanece — o campo continua sendo útil para o Avo e para debugging.
3. spec/queries/user_profiles/search_therapists_query_spec.rb
- Contexto “when PRO therapist has no availabilities” (linhas 55-68): inverter expectativa — passa a esperar
to include(search_therapist). Atualizar descrição docontexte doitpara refletir o novo comportamento (“include therapist in the collection”). - Contexto “when both PRO and regular therapists exist” (linhas 70-91): alterar linha 87 de
expect(collection).not_to include(pro_without_availability)paraexpect(collection).to include(pro_without_availability). Atualizar a descrição doitpara “include every therapist regardless of availabilities”. - Contextos “regular sem availabilities”, “regular com availabilities” e “PRO com availabilities” permanecem inalterados.
4. spec/use_cases/search_therapists/update_by_profile_spec.rb
- Contexto “when user is PRO without availabilities” (linhas 22-37): inverter expectativa para
be truee ajustar descrição doitpara “create a SearchTherapist record”. - Contexto “when PRO user loses availabilities” (linhas 74-92): com a regra removida, o registro NÃO é mais destruído ao apagar a última availability. Atualizar para: descrição “when PRO user loses availabilities / keep the SearchTherapist record”, expectativa final
expect(SearchTherapist.exists?(profile_id: profile.id)).to be true.
O que NÃO mexer
- Coluna
search_therapists.availabilities_count(permanece no schema). - Atualização do contador em
UpdateByProfile(linha 22) — alimenta o dashboard Avo. - Callbacks de
EnqueueRefreshByProfilenos 11 modelos (Availability,Subscription, etc.) — continuam essenciais para manter o restante dos campos (price,score,plan_id,name, etc.) sincronizados. - Filtro
by_user(pro bono para terapeutas,plan_id = PRO+price > 0para clientes). - Demais checagens em
UpdateByProfile#valid?. - Specs de request em
spec/requests/api/v1/search_therapists_index_as_*— o shared contextwhen_setup_searchable_therapist.rbjá cria availability para todos os perfis, então continuam verdes sem alteração.
Verificação
-
Testes unitários afetados (rodar antes para ver falhas, depois para confirmar fix):
bash make test test=spec/queries/user_profiles/search_therapists_query_spec.rb make test test=spec/use_cases/search_therapists/update_by_profile_spec.rb -
Testes de request (não devem quebrar, só sanity check):
bash make test test=spec/requests/api/v1/search_therapists_index_as_anonymous_spec.rb make test test=spec/requests/api/v1/search_therapists_index_as_client_spec.rb make test test=spec/requests/api/v1/search_therapists_index_as_therapist_spec.rb -
Verificação manual no caso da Rita (
make console):ruby # Antes da mudança: deve retornar 0 # Depois da mudança: deve retornar 1 UserProfiles::SearchTherapistsQuery.new .by_name("canudo") .instance_variable_get(:@collection) .pluck(:id, :name) -
Smoke test end-to-end — autenticar como cliente e bater na rota, confirmar que a Rita aparece:
GET /api/v1/therapists?name=canudo&page=1 -
Regressão pro bono — autenticar como terapeuta (com e sem a flag
cost_reduction_phase_one) e confirmar que os filtrospro_bono_allowede quota continuam sendo aplicados corretamente sobre o universo expandido.
Observações finais
- Mudança de comportamento observável: terapeutas Pro recém-aprovados já aparecem na busca antes de cadastrar qualquer horário. Isso pode impactar métricas de “terapeutas ativos exibidos”; vale comunicar ao produto antes de subir.
- O
score: 0da Rita a posiciona no final do ranking (ordenaçãoORDER BY score DESC). Se o produto quiser que ela apareça com destaque, é outra discussão (SearchTherapists::CalculateScore).