Evento de parcela atrasada para o accounts
TLDR: quando o webhook do Asaas leva uma parcela de compra padrão para
overdue, ocheckout-apigrava umOutboxEvent(accounts.installment_overdue) com o pagamento inteiro — cliente, todas as parcelas do carnê e a indicação de qual venceu — e despacha para oPOST /api/v1/eventsdoaccounts. Oaccountsdeixa de receber oPAYMENT_OVERDUEdireto do gateway, e o atraso deixa de depender de a parcela — ou mesmo a compra — já existir lá: o payload é o doPURCHASE_CREATEDmaispurchased_at,statuspor parcela e a parcela vencida apontada, e é ele que alimenta opurchases.Syncdo lado de lá.
Branch: a partir de
main— oPURCHASE_CREATEDfoi mergeado em7033256(PR #268) e é o molde inteiro desta spec.
Contexto
Hoje o INSTALLMENT_OVERDUE do accounts nasce da tradução PAYMENT_OVERDUE →
INSTALLMENT_OVERDUE no ASAAS_EVENT_MAP do synapse: o webhook do Asaas bate direto lá, e
o payload que chega é o do gateway — só a cobrança vencida. A regra R-001 do accounts
documenta a consequência como restrição deliberada (“nunca cria, só resolve pelo
provider_ref”), porque o payload do Asaas não garante installmentNumber, value nem
dueDate.
O preço é a ordem de chegada: se a Installment ainda não existir no accounts quando o
atraso chegar, o use case levanta ValueError, soma tentativa e termina FAILED — o atraso
simplesmente some.
Quem tem o pagamento completo é o checkout-api: cliente, produto e o carnê inteiro com
gateway_id de cada cobrança, buscável ao vivo por Asaas::Installments::FetchFromGateway.
Ele já entrega PURCHASE_CREATED para o accounts por esse caminho (outbox →
POST /api/v1/events com X-Origin: checkout), em main desde a PR #268. Mandando o
atraso da mesma origem, o evento chega autocontido e a restrição da R-001 cai.
O lado do accounts já está escrito e em revisão: branch feat/checkout-installment-overdue,
PR ibft-corp/accounts#27, com spec própria
(.project/docs/specs/20260922090311_checkout_as_financial_event_source.md). Lá o
InstallmentOverdue virou orquestrador — sincroniza a Purchase, delega a conciliação do
carnê a installments.Reconcile e marca OVERDUE a parcela apontada — e a via de entrada do
Asaas sai inteira (AsaasEventMapper, ASAAS_EVENT_MAP, ASAAS_WEBHOOK_TOKEN).
Esta spec cobre só o lado do checkout-api: quem produz o evento.
O contrato mudou em 514c716: a compra é criada, não só resolvida
A primeira versão do accounts resolvia a Purchase por external_id e levantava
ValueError se não achasse. Os commits 514c716 (feat: sync the purchase from the overdue
event) e f4af3c8 (docs: purchase sync resolves create or update) trocaram isso por
purchases.Sync: não existe, cria; existe, atualiza. O motivo está na spec deles — uma
compra anterior ao PURCHASE_CREATED nunca vai existir no accounts, e o atraso dela
falharia “hoje, amanhã e sempre; não é corrida de entrega, é buraco permanente”.
A consequência direta para esta spec é o payload: como existe ramo de criação, ele precisa
carregar tudo que purchases.Create consome — product_slug, payment_method, provider,
customer (com e-mail, senão Failure("customer_incomplete")) e o purchased_at novo. Ou
seja, o payload do INSTALLMENT_OVERDUE é o do PURCHASE_CREATED mais três coisas:
purchased_at, status em cada parcela, e o overdue_installment.
O Sync é chamado com installments: [] de propósito: quem monta o carnê neste fluxo é o
Reconcile, o único que aplica o status por parcela.
Nota sobre os nomes citados na task
A task descreve os arquivos com os nomes que a branch do PURCHASE_CREATED tinha no começo.
Todos foram renomeados antes do merge; esta spec usa os nomes que estão em main:
| Nome na task | Nome em main |
|---|---|
app/services/payments/purchases/build_accounts_event_payload.rb |
app/serializers/accounts/purchase_serializer.rb |
app/services/payments/purchases/dispatch_accounts_event.rb |
app/services/accounts/dispatch_purchase_created.rb (Accounts::DispatchPurchaseCreated) |
Asaas::Installments::LiveFetcher |
Asaas::Installments::FetchFromGateway |
Por consequência, o orquestrador novo é Accounts::DispatchInstallmentOverdue em
app/services/accounts/, e não Payments::DispatchAccountsInstallmentOverdueEvent — o
namespace Accounts:: é onde o molde acabou morando.
Objetivos
- Disparar
INSTALLMENT_OVERDUEpara oaccountsquando o webhook do Asaas levar uma parcela de compra padrão paraoverdue, com o pagamento inteiro dentro. - Mandar o payload completo o bastante para o
purchases.Syncdoaccountscriar a compra quando ela não existe lá — é o que fecha o buraco das compras anteriores aoPURCHASE_CREATED. - Reaproveitar o
Accounts::PurchaseSerializerinteiro — o payload do atraso é um superconjunto do payload da compra criada. - Reaproveitar a infra genérica de outbox (
OutboxEvent,Outbox::DispatchEventsJob,Outbox::DispatchRegistry) e o dispatcher HTTP já escrito para oaccounts. - Idempotência por parcela (
provider_refda cobrança vencida), não por pagamento — um mesmo carnê pode atrasar várias vezes, em parcelas diferentes. - Nunca derrubar o processamento do webhook: qualquer falha na montagem ou gravação do
evento é logada e engolida, como já faz
Accounts::DispatchPurchaseCreated.
Fora de escopo
- Qualquer mudança no
accounts. O contrato consumido já está implementado e em PR aberta (#27). Esta spec só produz o evento no formato que lá já é esperado. - Disparo para
kinddiferente destandard.Purchasesó existe noaccountspara compra padrão (oPURCHASE_CREATEDtem o mesmo recorte) — um atraso de reparcelamento cairia empurchase not founde virariaIncomingEventFAILED. Reparcelamento já tem o caminhonew_checkout.repayment_sync. - Backfill dos atrasos históricos. Parcelas que já estão
overduehoje não geram evento retroativo; só a próxima transição observada pelo webhook dispara. (O que deixou de ser problema é a compra antiga: com opurchases.Syncdo514c716, um pagamento que nunca tevePURCHASE_CREATEDdespachado passa a ter aPurchasecriada pelo próprio evento de atraso.) - Job de varredura para parcelas que vencem sem webhook. O Asaas é quem avisa; se ele não avisar, não há evento. Mesmo risco aceito na #268.
- Evento de parcela paga / carnê quitado.
PURCHASE_APPROVEDePURCHASE_REFUNDEDsão outros eventos, ainda sem use case registrado noaccounts.
Decisões
O gatilho é o status gravado na parcela, não o nome do evento do webhook.
context.installment_params[:status] == :overdue reflete o estado que o webhook acabou de
escrever, via a normalização que Asaas::Utils::NormalizePaymentStatus já faz: qualquer
evento permitido que deixe a cobrança em overdue dispara — hoje PAYMENT_OVERDUE e um
PAYMENT_UPDATED sobre cobrança já vencida. Amarrar no nome do evento significaria manter
uma segunda lista em paralelo com PAYMENT_OVERDUE_STATUSES.
O recorte por nome de evento não some, no entanto: ele continua no
context.should_update_payment, que é ASAAS_APOLO_PAYMENT_ALLOWED_EVENT_TYPES.include?
sobre o nome do webhook. Na prática isso deixa PAYMENT_DUNNING_REQUESTED de fora — ele
está em ASAAS_ALL_PAYMENT_EVENTS, mas não na lista permitida, então o && corta na
primeira condição e o status nem é avaliado. É inofensivo: o Asaas manda PAYMENT_OVERDUE
antes de qualquer negativação da mesma cobrança, então o evento já foi despachado, e a
idempotência por provider_ref cortaria a repetição. Incluir negativação exigiria mexer no
ASAAS_APOLO_PAYMENT_ALLOWED_EVENT_TYPES, que é compartilhado com o resto do fluxo de
atualização de pagamento — fora do escopo desta spec.
Parcela com status intraduzível é filtrada da lista, não mapeada. O
FetchFromGateway normaliza para o vocabulário do checkout-api
(paid, pending, overdue, refunded, deleted_or_canceled_by_new_payment, draft,
creating_on_gateway, unknown) e o accounts só conhece quatro
(PENDING/PAID/OVERDUE/REFUNDED) — o Reconcile levanta erro em qualquer outro.
Filtrar é seguro porque o Reconcile nunca apaga parcela local ausente do payload, e
evita a colisão real de (purchase, number) quando uma cobrança cancelada e a cobrança nova
que a substituiu dividem o mesmo installmentNumber. Mapear tudo para PENDING faria uma
cobrança cancelada renascer e passar a contar no recálculo da Account.
A parcela apontada é resolvida dentro da lista já filtrada, não pelo webhook. O
overdue_installment sai da entrada cujo gateway_id bate com context.gateway_id — não
dos installment_params. Isso garante o invariante que o accounts exige (“a parcela
apontada precisa estar na lista installments”) por construção: se a cobrança vencida não
estiver na lista buscada ao vivo, o evento não é gravado.
Completude do carnê não é exigida, diferente do PURCHASE_CREATED. Lá o
installments_complete? existe porque purchases.Create cria o carnê de uma vez e um carnê
parcial ficaria errado para sempre. Aqui o Reconcile é incremental e não destrutivo, e o
filtro de status por si só já faz a contagem divergir de payment.installment_count sempre
que houver cobrança cancelada no plano. Exigir completude silenciaria o atraso justamente nos
carnês mais bagunçados. O que impede evento vazio é a checagem da parcela apontada.
Idempotência por provider_ref da cobrança vencida. O PURCHASE_CREATED deduplica por
external_id porque é um evento por compra; aqui são N por compra. A checagem é
payload -> 'payload' -> 'overdue_installment' ->> 'provider_ref'. É defesa contra ruído
(dois webhooks quase simultâneos para a mesma cobrança), não contra dano: o accounts já é
idempotente para parcela já OVERDUE (Success() sem data, sem avisar o assinante duas
vezes).
Os dois dispatchers do accounts passam a herdar de uma base.
Outbox::Dispatchers::AccountsPurchaseCreate e o novo AccountsInstallmentOverdue postam
para o mesmo endpoint, com os mesmos headers e o mesmo timeout — a única diferença seria o
nome da classe. A base Outbox::Dispatchers::AccountsBase carrega o POST; as duas subclasses
existem só para dar um alvo distinto por event_name no DispatchRegistry.
Desvio da task: o
[como]pedia um dispatcher novo e independente. A extração é uma deduplicação de 100% do arquivo e mexe num arquivo que já está emmain— vetável, mas a recomendação é fazer junto, enquanto só existem dois.
O payload é montado por herança, não por extração.
Accounts::InstallmentOverdueSerializer < Accounts::PurchaseSerializer: herda
external_id, product_slug, customer, payment_method, provider e installments, e
acrescenta purchased_at e overdue_installment.
Supersede a decisão anterior desta spec. Quando o
accountsainda resolvia aPurchasesem criá-la, o payload do atraso não levavaproduct_slug/payment_method/provider, a sobreposição com oPURCHASE_CREATEDera de dois campos, e a resposta certa eram dois POROs compartilhados (Accounts::CustomerPayloadeAccounts::InstallmentsPayload). Com opurchases.Syncdo514c716a sobreposição virou total — extrair dois objetos para depois recompor o mesmo hash inteiro seria cerimônia. A herança diz a verdade sobre a relação entre os dois contratos: um é o outro, mais três coisas.
O PURCHASE_CREATED passa a mandar status na parcela. Efeito colateral de o
#installments compartilhado passar a traduzir o status. É inofensivo: purchases.Create
monta a lista lendo só number, amount, due_on e provider_ref, e ignora chaves extras.
O filtro de status também vale para o PURCHASE_CREATED. Mesmo método #installments,
mesmo filtro. Na prática não muda nada lá: o evento só é despachado quando
gateway_installments.size == payment.installment_count (que continua sendo medido sobre a
lista crua do gateway), e um carnê recém-criado não tem cobrança cancelada. Mas se um dia
tiver, o PURCHASE_CREATED passa a emitir menos parcelas do que installment_count — o
accounts cria só as que vieram, e o próximo atraso concilia o resto.
Contrato do envelope
Gravado em OutboxEvent#payload e postado cru pelo dispatcher:
json
{
"event_type": "INSTALLMENT_OVERDUE",
"external_id": "payment_2d4be59d215115",
"occurred_at": "2026-09-22T09:33:18-03:00",
"payload": {
"external_id": "payment_2d4be59d215115",
"product_slug": "curso-extensao-2026",
"payment_method": "BOLETO",
"provider": "asaas",
"purchased_at": "2026-07-15T10:00:00-03:00",
"customer": {
"name": "João da Silva",
"email": "joao@example.com",
"document": "12345678900",
"document_type": "CPF",
"phone": "11999999999",
"country": "BR"
},
"installments": [
{"number": 1, "amount": 600.0, "due_on": "2026-08-10", "status": "PAID", "provider_ref": "pay_5847293846"},
{"number": 2, "amount": 600.0, "due_on": "2026-09-10", "status": "OVERDUE", "provider_ref": "pay_5847293847"}
],
"overdue_installment": {"number": 2, "provider_ref": "pay_5847293847"}
}
}
Tudo que está acima da linha do customer é idêntico ao PURCHASE_CREATED e sai do
PurchaseSerializer sem uma linha nova. O que é específico deste evento:
purchased_at=payment.created_at.iso8601. OPURCHASE_CREATEDnão manda o campo — a compra acabou de acontecer e otimezone.now()doaccountsé a resposta certa. Aqui manda, porque a compra que este evento pode estar registrando é de meses atrás, e gravá-la como comprada agora corromperia a ordenação e qualquer relatório por data.installments[].status, no vocabulário doaccounts(PENDING/PAID/OVERDUE/REFUNDED, maiúsculas). Quem traduz é ocheckout-api, do mesmo jeito que já traduzCREDIT_CARD → CARTAO. Cobrança com status fora dos quatro não entra na lista.overdue_installment, que aponta uma parcela que precisa estar na listainstallments— senão oaccountslevantaValueErrore o evento viraFAILED.
E o que muda de significado em relação ao PURCHASE_CREATED:
occurred_atéTime.current, o momento em que o atraso foi observado — nãopayment.created_at, que é o que oPURCHASE_CREATEDusa (lá os dois coincidem).customeré aplicado, e não só carregado para auditoria: quando a compra ainda não existe,purchases.Createchamacustomers.Synccom esse bloco. UmcustomersememaildevolveFailure("customer_incomplete")e o evento terminaFAILED— hoje oCustomerdocheckout-apisempre tem e-mail, mas é uma dependência real do contrato.product_slugprecisa resolver. Se não resolver nenhumProductnoaccounts, ele cria um com dados mínimos (nameeexternal_id= o slug) — não falha, mas suja o catálogo. OPurchaseSerializerlêobject.checkout.product.slug, que aR-003garante existir.amounté número JSON (600.0), não string formatada; o campo de vencimento édue_on, nãodue_date;provider_refé ogateway_idda cobrança individual, não ogateway_installment_iddo plano.
Mudanças
1. Accounts::PurchaseSerializer — status traduzido e filtrado
Modificar: app/serializers/accounts/purchase_serializer.rb
#installments passa a traduzir o status normalizado do FetchFromGateway para o
vocabulário do accounts, e a descartar a cobrança cujo status não tem tradução:
ruby
INSTALLMENT_STATUSES = {
pending: "PENDING",
paid: "PAID",
overdue: "OVERDUE",
refunded: "REFUNDED"
}.freeze
Cada entrada vira {number:, amount: total.to_f, due_on: due_date.to_s, status:, provider_ref: gateway_id}.
Os outros quatro status possíveis (deleted_or_canceled_by_new_payment, draft,
creating_on_gateway, unknown) não entram na lista.
Nada mais muda: external_id, product_slug, customer, payment_method e provider
ficam como estão, e são justamente o que o evento de atraso herda.
1b. Accounts::InstallmentOverdueSerializer — o payload do atraso
Criar: app/serializers/accounts/installment_overdue_serializer.rb
```ruby module Accounts class InstallmentOverdueSerializer < PurchaseSerializer attributes :purchased_at, :overdue_installment
def purchased_at
object.created_at.iso8601
end
def overdue_installment
overdue = installments.find { |i| i[:provider_ref] == @instance_options.fetch(:overdue_provider_ref) }
{number: overdue[:number], provider_ref: overdue[:provider_ref]}
end end end ```
A parcela apontada sai da lista já montada e já filtrada — é o que garante, por construção, o
invariante que o accounts exige. O orquestrador é quem checa que ela existe antes de
serializar (ver §2), então aqui overdue nunca é nil.
2. Accounts::DispatchInstallmentOverdue — orquestrador
Criar: app/services/accounts/dispatch_installment_overdue.rb
Espelha Accounts::DispatchPurchaseCreated, o irmão no mesmo namespace. Assinatura
call(payment:, gateway_id:).
ruby
EVENT_NAME = "accounts.installment_overdue"
EVENT_TYPE = "INSTALLMENT_OVERDUE"
Fluxo:
return if already_dispatched?—OutboxEvent.where(event_name: EVENT_NAME).exists?(["payload -> 'payload' -> 'overdue_installment' ->> 'provider_ref' = ?", @gateway_id]).gateway_installments = Asaas::Installments::FetchFromGateway.call(gateway_service:, payment:), comgateway_service = Asaas::Client.new(payment.checkout.organization_id)— igual ao molde.return unless overdue_in_plan?— a cobrança vencida precisa estar na lista já traduzida e filtrada. Não está (foi cancelada, ou o plano ao vivo não a devolveu) → sai sem gravar, em silêncio: o próximo webhook do mesmo pagamento tenta de novo.OutboxEvent.create!(event_name: EVENT_NAME, payload: envelope)+Outbox::DispatchEventsJob.perform_later(event).rescue => e→Rails.logger.errorenil, como no molde (o webhook nunca cai por causa do evento).
O envelope monta
Accounts::InstallmentOverdueSerializer.new(@payment, installments: gateway_installments, overdue_provider_ref: @gateway_id).as_json,
com occurred_at: Time.current.iso8601.
Diferente do molde, não há installments_complete? — ver a decisão sobre completude.
3. Novo step no PaymentsFlow
Criar: app/use_cases/incoming_webhooks/payment_gateways/asaas/payments/dispatch_installment_overdue_event.rb
Ucasy::Base, espelhando DispatchPurchaseCreatedEvent:
```ruby required_attributes(:payment, :installment_params, :should_update_payment, :gateway_id)
def call return unless should_dispatch?
Accounts::DispatchInstallmentOverdue.call( payment: context.payment, gateway_id: context.gateway_id ) end
private
def should_dispatch? context.should_update_payment && context.installment_params[:status] == :overdue && context.payment.kind == “standard” end ```
Modificar: app/use_cases/incoming_webhooks/payment_gateways/asaas/payments/payments_flow.rb
— adiciona o step no fim, depois de DispatchPurchaseCreatedEvent. Os dois nunca disparam no
mesmo webhook: o de compra criada exige ASAAS_INITIAL_EVENTS, o de atraso exige status
overdue, e PAYMENT_OVERDUE não está em ASAAS_INITIAL_EVENTS.
4. Dispatcher e registro
Criar: app/services/outbox/dispatchers/accounts_base.rb — o POST para
"#{ENV["ACCOUNTS_API_URL"]}/api/v1/events" com X-Origin: checkout,
Authorization: Bearer #{ENV["ACCOUNTS_API_TOKEN"]} e timeout de 3s; levanta erro claro se as
envs não estiverem configuradas. É o corpo atual de AccountsPurchaseCreate, movido.
Criar: app/services/outbox/dispatchers/accounts_installment_overdue.rb —
class AccountsInstallmentOverdue < AccountsBase.
Modificar: app/services/outbox/dispatchers/accounts_purchase_create.rb — passa a herdar
de AccountsBase.
Modificar: app/services/outbox/dispatch_registry.rb — adiciona
"accounts.installment_overdue" => Outbox::Dispatchers::AccountsInstallmentOverdue.
.env.example não muda: ACCOUNTS_API_URL e ACCOUNTS_API_TOKEN já entraram na #268.
Como verificar
| O que precisa ser verdade | Teste |
|---|---|
#installments traduz os quatro status conhecidos e ordena por number |
spec/serializers/accounts/purchase_serializer_spec.rb (existe, atualizado) |
Parcela com status intraduzível (ex.: deleted_or_canceled_by_new_payment) não entra na lista |
idem |
O PURCHASE_CREATED segue montando o mesmo payload, agora com status por parcela |
idem |
O payload do atraso herda product_slug, payment_method, provider e customer do PURCHASE_CREATED |
spec/serializers/accounts/installment_overdue_serializer_spec.rb |
purchased_at é o created_at do pagamento, não o momento do atraso |
idem |
overdue_installment aponta a cobrança do webhook e está dentro de installments |
idem |
O envelope INSTALLMENT_OVERDUE bate com o contrato do accounts, com occurred_at no momento do atraso |
spec/services/accounts/dispatch_installment_overdue_spec.rb |
| Não grava evento quando a cobrança vencida não está na lista buscada ao vivo | idem |
Grava evento mesmo quando o carnê ao vivo tem menos parcelas que installment_count |
idem |
Não grava evento duas vezes para o mesmo provider_ref |
idem |
| Grava evento novo para uma segunda parcela do mesmo pagamento | idem |
| Falha na busca ao vivo é logada e não levanta | idem |
O step dispara quando o webhook leva a parcela para overdue |
spec/use_cases/incoming_webhooks/payment_gateways/asaas/payments/dispatch_installment_overdue_event_spec.rb |
O step não dispara para kind diferente de standard |
idem |
O step não dispara quando should_update_payment é falso ou o status não é overdue |
idem |
O dispatcher posta o envelope com X-Origin: checkout e Bearer token |
spec/services/outbox/dispatchers/accounts_installment_overdue_spec.rb |
O registro resolve accounts.installment_overdue |
spec/services/outbox/dispatch_registry_spec.rb (existe, atualizado) |
make run.test path="spec/serializers/accounts spec/services/accounts spec/services/outbox spec/use_cases/incoming_webhooks/payment_gateways/asaas/payments"
Ponta a ponta, contra o accounts da branch feat/checkout-installment-overdue (PR #27),
nos dois ramos do purchases.Sync:
- Compra que o
accountsjá conhece — postar o webhookPAYMENT_OVERDUEnuma compra padrão que já tevePURCHASE_CREATEDprocessado. - Compra que o
accountsnão conhece — postar o mesmo webhook numa compra anterior aoPURCHASE_CREATED, e conferir que aPurchase, oProducte oCustomernascem ali, compurchased_atna data original da compra (não emnow()). É o caso que o514c716existe para resolver e o que esta spec passa a alimentar.
Nos dois: (a) OutboxEvent sent no admin do checkout-api, (b) IncomingEvent
PROCESSED no admin do accounts, (c) a Installment apontada em OVERDUE e a Account
do pagador recalculada.
Ordem de deploy
O accounts (PR #27) antes do checkout-api ligar o disparo. Entre os dois deploys o
Asaas passa a receber 404 no webhook apontado para o accounts e o evento novo ainda não
está sendo disparado — janela curta e sem registro, porque com o mapper removido o evento
nem vira IncomingEvent. Se a janela precisar ser zero, a PR 3 do accounts (remoção da via
do Asaas) sai depois do disparo daqui estar em produção.
Passo de operação, fora do código: desligar, no painel do Asaas, o webhook apontado para o
accounts.
Documentação
- Criar
.project/docs/reference/payments/accounts_installment_overdue_event.md— contrato do envelope, espelhandoaccounts_purchase_created_event.md, deixando explícito que ele é o payload doPURCHASE_CREATEDmaispurchased_at,statuspor parcela eoverdue_installment, que as parcelas vêm de busca ao vivo no Asaas (nunca doInstallmentlocal) e que status intraduzível é filtrado. - Criar
.project/docs/rules/payments/accounts_installment_overdue_dispatch.md(R-005) — quando o atraso é despachado (standard+ statusoverdue), idempotência porprovider_ref, filtro de status, o silêncio deliberado quando a cobrança vencida não está no plano, e por que aqui não se exige carnê completo. - Atualizar
.project/docs/rules/payments/outbox_event_dispatch.md(R-004) — citaraccounts.installment_overduecomo terceiro consumidor da tabela genérica. - Atualizar
.project/docs/reference/payments/accounts_purchase_created_event.md— a parcela doPURCHASE_CREATEDpassa a levarstatus, e cobrança com status intraduzível deixa de entrar na lista. - Atualizar
.project/docs/RULES.mde.project/docs/README.md— linha doR-005e o índice dos dois documentos novos.