R-022 — Troca de e-mail unifica as contas quando o destino já existe
TLDR: Quando o checkout manda trocar o e-mail de uma conta para um endereço que já pertence a outra, as duas contas são unificadas na conta do e-mail novo — relações movidas, assinatura mais longa preservada e a conta antiga encerrada com o e-mail renomeado. Antes disso a troca falhava em silêncio e o usuário ficava com duas contas.
Given / When / Then
Dado uma conta com o e-mail antigo e nenhuma conta com o e-mail novo
Quando a task change_user_email processa a troca
Então a conta é apenas renomeada para o e-mail novo
Dado uma conta com o e-mail antigo e outra conta já usando o e-mail novo
Quando a task change_user_email processa a troca
Então todas as relações da conta antiga passam para a conta do e-mail novo, a conta antiga é encerrada com o e-mail renomeado para onion-merge-<hex><epoch>-<e-mail original> e a task retorna merged: True
Dado que a conta antiga e a conta do e-mail novo têm assinaturas diferentes
Quando as contas são unificadas
Então a conta que sobrevive fica com a maior expires_at das duas, com o subscription_status correspondente, e volta a ficar ativa
Dado que as duas contas têm progresso no mesmo áudio, aula, meditação ou live
Quando as contas são unificadas
Então sobra um único progresso: o concluído vence o não concluído e, entre dois não concluídos, vence a maior position
Dado que as duas contas têm o mesmo registro único não-progresso (reação, objetivo, envio de e-mail ou jornada) Quando as contas são unificadas Então o registro da conta que sobrevive é mantido e o da conta encerrada é descartado
Dado que a conta unificada termina com assinatura enabled
Quando ela tem UserTrial em aberto (converted_at nulo), próprio ou vindo da conta encerrada
Então esses trials são marcados como convertidos, sem sobrescrever conversão já registrada
Dado que o e-mail antigo e o e-mail novo são o mesmo endereço
Quando a task processa a troca
Então nada é alterado e a task retorna reason: "same_account"
Constraints
- A conta que sobrevive é sempre a do e-mail novo — é a que o usuário acessa
- A conta encerrada segue a forma do merge de customers do checkout: e-mail prefixado com
onion-merge-<hex><epoch>-, nome prefixado com[Desativado], conta inativa e soft-deletada. O endereço original continua legível no fim do e-mail e fica livre para uso - Telefone e documento da conta encerrada são preservados para rastreio;
manychat_subscriber_idé limpo para não ficar duplicado entre as duas contas - Campos de perfil (
name,phone,document,manychat_subscriber_id) da conta encerrada só preenchem campos vazios na conta que sobrevive PushDevicesmovidos têm o campoemailatualizado para o e-mail da conta que sobrevive- Conta já soft-deletada não é alvo de merge: o e-mail dela já foi anonimizado
- Todo o merge roda numa transação — se qualquer passo falhar, nenhuma conta é alterada
Linked test
tests/accounts/test_account_merge.py
tests/webhooks/test_email_change_merge_journey.py