R-002 — Renovação encadeia a partir de qualquer vigência em andamento não estornada
TLDR: Ao calcular
valid_since/valid_untilde uma novaMembership,WebhookMembershipServiceencadeia a partir de qualquer filiação do usuário com vigência em andamento (valid_until >= hoje) — independente dopayment_status(paid,pending,overdue) e independente de aprovação de admin — exceto filiaçõesrefunded, que nunca contam como vigência ativa.
Given / When / Then
Dado um usuário com uma Membership de valid_until no futuro (vigência em andamento) e payment_status diferente de refunded, aprovada ou não por admin
Quando uma nova Membership é criada (webhook do gateway confirma uma nova compra com status: "paid")
Então a nova filiação nasce com valid_since igual ao valid_until da filiação vigente anterior e valid_until um ano depois — encadeando o período em vez de sobrepor
Tabela de decisão
payment_status da filiação vigente anterior |
Conta como vigência em andamento? |
|---|---|
paid |
Sim |
pending |
Sim |
overdue |
Sim |
refunded |
Não |
nenhuma filiação com valid_until >= hoje |
Não — valid_since começa em Date.current |
Restrições
refundednunca conta como vigência em andamento, mesmo comvalid_untilno futuro.- Filiação vencida (
valid_untilno passado) nunca conta, independente dopayment_status. - Aprovação de admin (
admin_approved) não entra no filtro. - Sem nenhuma filiação com vigência em andamento, a nova filiação começa em
Date.currente termina no fim do mês corrente um ano depois. - Não existe scope novo em
Membershippara isso — o filtro é inline na query, padrão adotado desde o primeiro fix deste método.
Código
app/services/webhook_membership_service.rb — set_valid_since_and_until
ruby
membership_eligible_for_renewal = @user.memberships
.where.not(payment_status: :refunded)
.valid_gte_today
.order_by_newest_valid_until
.try(:first)
Teste vinculado
test/services/webhook_membership_service_test.rb:
- “sets valid_since based on last paid membership even when not admin approved”
- “sets valid_since based on last paid membership when it is admin approved”
- “sets valid_since based on existing membership when it is pending payment”
- “sets valid_since based on existing membership when it is overdue payment”
- “sets valid_since from today when existing membership is refunded”
Histórico
- 2026-07-17: removida
.admin_approveddo filtro — filiação paga mas ainda não aprovada por admin era ignorada. - 2026-07-30: trocado
.paidpor.where.not(payment_status: :refunded)— filiaçãopending/overdueainda vigente era ignorada, sobrepondo períodos quando o checkout de campanha liberava compra com pendência financeira. Ver spec.