Pular para conteúdo

0056 — A taxa de adesão: da imobiliária, por imobiliária, e de volta como comissão

Status CONCLUÍDA
Branch spec/0056-taxa-de-adesao
Criada em 2026-10-11
Apps afetados api · web · e2e
Revisão do problema aprovada na rodada 2
Revisão da experiência aprovada na rodada 6
Revisão do detalhamento aprovada na rodada 4
Revisão da implementação aprovada (api, web e branch)

Status: RASCUNHO → APROVADA → EM IMPLEMENTAÇÃO → CONCLUÍDA

A spec tem três partes, escritas em ordem, e quatro portões — cada um revisado por um subagente de contexto limpo, em até 3 rodadas. A máquina de revisão é do plugin superpowers; a régua é a rubrica correspondente em .claude/rubrics/. Achados vêm classificados: qualquer Critical reprova, Important em aberto reprova salvo decisão registrada do usuário, e Minor vira pendência aceita.

  1. A Parte I diz qual é o problema e como se convive com ele hoje. Revisada na seção 6 (necessidade × jornada de hoje). É o portão mais barato de todos, e o único que pergunta contra que problema isto está sendo construído.
  2. A Parte II diz como se usa o que vai existir. Revisada na seção 8 (jornada × viewports).
  3. A Parte III diz como se constrói. Revisada na seção 16 (segurança × clareza técnica) — autorização, isolamento, e se sobrou decisão para quem vai codar.
  4. A implementação é medida contra as três, na seção 17 (aderência × qualidade × testes) — uma revisão por fase, e uma da branch inteira antes de fechar.

Cada parte só se escreve depois que a anterior passou. Descrever tela sobre problema mal enquadrado, ou detalhar endpoint sobre experiência vaga, é o jeito mais caro de descobrir que era outra coisa.


Parte I — O problema

Aqui não entra solução: nem tela, nem endpoint, nem botão. Só o que dói, para quem, e o que se faz hoje. Se você já sabe qual é a tela, guarde para a Parte II.

1. Dor

A taxa de adesão é da imobiliária, e o produto a trata como preço da garantidora. Pelo combinado do negócio — descrito pelo usuário na entrevista desta spec (2026-10-11) —, a taxa é cobrada uma vez só, na contratação; quem define o valor é a imobiliária; o inquilino paga à garantidora; e a garantidora devolve o valor à imobiliária, descontados os impostos, como comissão sobre a garantia — 100% do líquido, ou um percentual combinado com cada parceira. O produto não sabe de nada disso. O que existe hoje é uma regra única para a garantidora inteira, escrita como receita dela, e nada sobre a volta do dinheiro.

Essa prática não está escrita em lugar nenhum que o repositório guarda, e é por isso que o produto a ignora. O Termo de Condições Gerais da TXC (docs/contratos/) descreve a Taxa Setup como paga à TXC "em função dos custos para a ativação da fiança onerosa", "de acordo com a Modalidade de Garantia contratada" — receita da garantidora, sem parceira no meio. E a spec 0055, que leu a planilha real do acerto, registrou na sua seção 5 que ali não aparece comissão nenhuma e que, se ela existisse, seria "decisão nova". O usuário confirmou na entrevista que a comissão já é paga hoje, fora do Termo e fora daquela planilha (seção 3, passos 8 e 9), e que o Termo será revisto, por fora desta spec, para refletir a prática.

Três consequências, todas sentidas hoje:

  • Toda imobiliária que combinou uma taxa diferente da regra global sai com o valor errado, a menos que alguém lembre de corrigi-lo. A correção existe, mas mora num lugar só e depende de memória: o analista sobrescreve o valor no parecer de aprovação. Se ele esquece, ou não sabe o que foi combinado com aquela parceira, a garantia é emitida — e o boleto de entrada sai — com a taxa da regra global, não com a combinada.
  • A comissão não existe no produto. Quanto a garantidora deve a cada imobiliária, de que garantias, com qual desconto de imposto, e se já pagou: nada disso tem registro. É conta de planilha paga por PIX, desconectada das baixas que o produto já registra e do acerto mensal que a spec 0055 acabou de trazer para dentro.
  • A tela de Configurações explica outra coisa. O bloco se chama Pagamento inicial, descreve o valor como "cobrado na contratação da garantia, além da mensalidade recorrente", e justifica a escolha da base pelo "esforço e risco da garantidora". Quem abre a tela sai entendendo o contrário do negócio — que a taxa é receita dela. E as três bases são descritas sobre o aluguel — Aluguel mensal, "aluguel × prazo", "aluguel × multiplicador LMI" — onde o cálculo usa, nas três, o Pacote Locatício (aluguel + condomínio + IPTU), que é a base de tudo desde a spec 0006.

O custo de não fazer é dinheiro errado nas duas pontas: taxa cobrada do inquilino diferente da que a imobiliária combinou com ele — o que chega à parceira como reclamação do cliente dela —, e comissão calculada à mão, sem lastro no que o produto sabe que foi pago.

2. Quem sente

  • ANALYST e ADMIN_TENANT da garantidora, na aprovação de toda análise de crédito. É caminho obrigatório: toda garantia passa pelo parecer, e é ali o único ponto em que a taxa pode ser acertada. Cada aprovação de imobiliária fora da regra global exige que o analista saiba de cor o que foi combinado com ela.
  • A imobiliária (AGENCY_USER, AGENCY_MANAGER), ao preencher a análise. Ela é quem combina a taxa com o inquilino, mas não tem onde dizê-la. Enquanto preenche, o Resumo financeiro do próprio formulário lhe mostra, ao vivo, um Pagamento inicial calculado pela regra global — um número que pode não ser o que ela combinou, e pode não ser o que vai ser cobrado, se o analista o sobrescrever na aprovação. Depois, é quem recebe a comissão sem ter como conferi-la.
  • A garantidora, no fechamento. É quem calcula e paga a comissão, fora do sistema; e o ADMIN_TENANT é quem configura a regra, numa tela que descreve o negócio errado.

Alcance: a taxa é parte de toda garantia emitida (é a cobrança de entrada — spec 0054), e a comissão incide sobre toda taxa paga. Não há exceção no caminho; o que varia é quantas parceiras têm acordo diferente da regra global. A produção tem uma garantidora em operação desde outubro de 2026 (TXC Garantia), com imobiliária parceira cadastrada; não há volume acumulado para medir quantas aprovações exigem correção manual — o alcance se sustenta pelo desenho: a regra é uma só, e o acordo é por parceira.

3. A jornada de hoje

A taxa, da combinação à garantia emitida:

  1. A imobiliária combina a taxa com o inquilino [fora do sistema]. É ela quem decide, e cada parceira faz de um jeito (relato do usuário): umas cobram um valor fixo, outras um percentual, outras nada. A forma exata de cada acordo — sobre que base incide um percentual, por exemplo — não foi levantada parceira a parceira; o que se sabe é que ela varia, e que a configuração do passo 2 não varia.
  2. A garantidora configura uma regra só, em Configurações → Pagamento inicial: tipo (sem cobrança, fixo, percentual), valor e base. Vale para todas as parceiras.
  3. Alguém preenche a análise de crédito — a imobiliária ou a equipe da garantidora. Não há onde informar a taxa combinada. Na criação, e de novo a cada edição que toque preço, o produto calcula a taxa pela regra global e a guarda no resumo financeiro da análise. Enquanto se preenche, o Resumo financeiro do formulário já mostra essa mesma taxa da regra global, ao vivo, a quem preenche — inclusive à imobiliária.
  4. A combinação chega ao analista por fora [fora do sistema] — mensagem, conversa, ou memória do acordo daquela parceira.
  5. Na aprovação, o analista sobrescreve o valor no campo Taxa setup (R$) do parecer. O campo nasce preenchido com a sugestão da regra global vigente (fix 0009). Só vai para a garantia o que o analista tocar; se ele não mexer, nada é enviado, e a garantia calcula pela regra global vigente na emissão (show.tsx, handleApprove).
  6. A garantia é emitida com essa taxa, e a cobrança de entrada sai com ela.

A comissão, da taxa paga ao PIX:

  1. O inquilino paga a entrada por boleto da garantidora, e a baixa registra quanto do pagamento foi para a adesão (spec 0054).
  2. A garantidora calcula a comissão numa planilha [relato do usuário; fora do sistema] — não a planilha do acerto que a 0055 leu, onde ela não aparece: taxa paga, menos os impostos, vezes o percentual da parceira.
  3. Paga por PIX [fora do sistema], sem registro no produto. O acerto mensal da 0055 não a considera — ali ficou decidido que o setup não entra no acerto, e isso valia para a cobrança do setup, não para a volta dele.

Onde quebra:

  • No passo 2, a regra global não representa o negócio. O acordo é por parceira, e a configuração não tem onde guardá-lo. Tudo o que vem depois é correção desse desencontro.
  • No passo 5, o acerto depende de memória. É o único lugar onde a taxa certa pode entrar, e chega lá por fora do sistema (passo 4). Quem errou não tem como perceber: o campo aceita qualquer valor, e o valor da regra global parece correto.
  • No passo 3, quem sabe a taxa não tem onde dizê-la — e vê um número que pode estar errado. A imobiliária sabe exatamente o que combinou, preenche a análise e não pode informar o valor; e, no momento em que digita, o resumo do formulário lhe mostra a taxa da regra global como se fosse a dela.
  • Nos passos 8 e 9, a comissão não tem lastro. O produto sabe quanto de adesão foi pago e quando, mas a conta é refeita à mão, e o pagamento não fica em lugar nenhum.

O que já se tentou. O campo de taxa no parecer (o approved_initial_fee_value do Decision) é o contorno em uso para o acordo por parceira. O fix 0009 corrigiu a sugestão dele para os parâmetros vigentes, mas não pode sugerir o acordo de uma parceira que o produto não conhece. Para a comissão, o contorno é a planilha.

4. Resultado esperado

Passa a ter resposta, dentro do produto:

  • Qual é a taxa de adesão desta imobiliária? — a regra global, ou um acordo próprio dela, guardado onde o produto encontra.
  • Qual taxa este inquilino vai pagar? — conhecida ao preencher a análise, por quem a preenche, e não só na aprovação.
  • Quanto devo de comissão a esta imobiliária, de quais garantias, e já paguei? — saindo do que o produto já registra como adesão paga, com o desconto de imposto aplicado.

Como se sabe que resolveu — sinais verificáveis, e os três foram os escolhidos pelo usuário na entrevista:

  • A garantia sai com a taxa combinada com a imobiliária sem o analista digitar nada no parecer. O campo do parecer continua existindo, mas deixa de ser o lugar onde o acordo entra.
  • A planilha de comissão deixa de ser necessária: o que a garantidora deve a cada imobiliária de comissão, e o que já pagou, sai do produto mês a mês.
  • Quem abre Configurações entende o que é a taxa de adesão e para quem ela volta, sem precisar perguntar — e a base de cálculo é descrita como a que o produto usa.

5. Fora de escopo

Cogitado na entrevista e deixado de fora, por decisão do usuário:

  • Recálculo retroativo. Análises e garantias existentes mantêm a taxa que têm; a regra nova vale para o que nascer ou for editado depois. É coerente com a decisão de 2026-08-15 registrada no fix 0009: a taxa se fecha na aprovação, e não há recálculo em massa quando os parâmetros mudam.
  • Emissão de nota fiscal. O imposto entra só como desconto da comissão. Quem emite nota contra quem segue a questão reaberta na spec 0055.
  • Parcelamento da taxa e primeira mensalidade na entrada. As duas opções continuam existindo e funcionando como hoje; só o texto delas é revisto, para falar a língua do bloco novo.
  • Transferência automática da comissão. A garantidora paga por fora, como já paga o que deve; o produto registra.
  • Trocar a base de cálculo. O percentual continua incidindo sobre o Pacote Locatício, não sobre o aluguel isolado. A tela passa a dizer isso; o cálculo não muda.

6. Revisão do problema

Preenchido pelo laço do /spec new, rubrica .claude/rubrics/problema.md. Registrar todas as rodadas, inclusive as reprovadas.

Rodada Critical Important Minor Nota Veredicto
1 1 2 3 6 Reprovada — premissa de negócio (taxa da imobiliária, comissão) sem fonte e em choque com o Termo e com a seção 5 da 0055; o que a imobiliária vê hoje descrito com imprecisão; passo 1 espelhava o enum. Origem e Termo levados ao usuário
2 0 1 3 8 Aprovada — o Important (a imobiliária vê a taxa ao vivo no resumo do formulário, não só depois de salvar) e os três Minor foram corrigidos após a aprovação

Resultado: aprovada na rodada 2.

Pendências aceitas: nenhuma em aberto. Duas recomendações da rodada 2 seguem para a Parte III: o desconto de impostos da comissão (quais tributos e sobre que base; a entrevista fixou só "um percentual único em Configurações") precisa ser colhido do usuário ali; e os três tipos do passo 1 (fixo, percentual, nada) não devem ser tomados como o universo dos acordos, já que a forma exata não foi levantada parceira a parceira.


Parte II — A experiência

Não preencher antes da revisão da seção 6 estar aprovada.

7. Jornada pretendida

A taxa de adesão passa a ter uma regra padrão da garantidora e, por cima dela, uma regra própria por imobiliária, quando houver acordo diferente. A regra que vale para uma análise é a da imobiliária dela, se existir, e a padrão, se não; é chamada aqui de regra em vigor. A regra pode permitir editar o valor na análise de crédito. A comissão sai do que o produto já registra como adesão paga, e entra no acerto mensal da 0055.

Decisões do usuário que dão forma à jornada (entrevista de 2026-10-11, além das da seção 4):

  • quem configura a regra própria de uma imobiliária é a garantidora, no cadastro da imobiliária; a imobiliária a vê, mas não a edita;
  • quem pode editar o valor na análise é quem a preenche — a imobiliária ou a equipe da garantidora —, quando a regra em vigor permite, e só com tipo valor fixo ou percentual (sem taxa não há o que editar). O valor editado é livre, de zero para cima;
  • o parecer continua podendo corrigir a taxa, mas o campo nasce com o valor da análise — o editado, se houve edição; o da regra em vigor, se não;
  • a comissão é o valor de adesão pago, menos um percentual único de impostos da garantidora, vezes o percentual de comissão — 100% por padrão, ou outro, por imobiliária;
  • a comissão nasce quando o inquilino paga a adesão, na competência do pagamento, e abate o que a imobiliária deve no acerto daquele mês. Imobiliária que não recolhe mensalidade, e por isso não deve nada, passa a ter acerto também — só de comissão, com o saldo a favor dela, que a garantidora paga e registra.

Ponto de entrada.

  • A regra padrão: menu Parâmetros (título da tela: Configurações) → o bloco que hoje se chama Pagamento inicial, e passa a se chamar Taxa de adesão.
  • A regra própria: menu Imobiliárias → Nova imobiliária ou Editar → um bloco novo, Taxa de adesão e comissão, depois de Endereço. O detalhe da imobiliária mostra a regra em vigor dela, em leitura.
  • A imobiliária vê a própria regra: menu Minha Imobiliária → aba Dados, em leitura.
  • A edição na análise: menu Análise de Crédito → Nova análise (ou editar uma em preenchimento) → um bloco novo, Taxa de adesão, logo abaixo de Locação e renda, só quando a regra em vigor permite editar.
  • A comissão: onde o acerto já está (0055) — Cobranças → Repasses para a garantidora, Minha Imobiliária → Repasses para a imobiliária — e o detalhe do acerto, que ganha a comissão do mês.

Jornada, caso feliz — a Solar cobra 1% do Pacote × multiplicador do LMI e deixa o corretor ajustar; a garantidora devolve 80% do líquido a ela; o padrão da garantidora é sem taxa.

  1. A garantidora revê a regra padrão. Em Parâmetros, o bloco Taxa de adesão diz o que a taxa é: cobrada uma vez do inquilino, definida pela imobiliária, recebida pela garantidora e devolvida à imobiliária, menos os impostos, como comissão. Escolhe Sem taxa, informa Impostos descontados da comissão — 16,33% neste exemplo, número ilustrativo — e deixa Comissão devolvida à imobiliária em 100%. Salva.
  2. A garantidora registra o acordo da Solar. Em Imobiliárias → Solar → Editar, no bloco Taxa de adesão e comissão, troca Seguir a regra da garantidora por Regra própria desta imobiliária: Percentual, 1%, base Pacote × multiplicador do LMI, liga Permitir editar o valor na análise. Em Comissão, troca Seguir o padrão (100%) por 80%. Uma linha embaixo mostra o efeito num exemplo: "Num pacote de R$ 2.500,00: taxa R$ 750,00 · comissão R$ 502,02" (impostos de 16,33% sobre 750 = 122,48; líquido 627,52; × 80% = 502,02 — o arredondamento é o da RN8). Salva.
  3. A Solar preenche uma análise. Em Nova análise, informa aluguel, condomínio, IPTU e prazo em Locação e renda. Logo abaixo aparece Taxa de adesão, já preenchida com R$ 750,00 e a origem: "Regra da Solar: 1% de Pacote × LMI (30)". O corretor combinou R$ 500,00 com o inquilino, digita 500 e o texto muda para "Valor alterado · pela regra seriam R$ 750,00", com Usar o valor da regra ao lado. O Resumo financeiro da própria tela passa a mostrar Taxa de adesão R$ 500,00. Envia.
  4. O analista aprova. No parecer, Taxa de adesão (R$) vem com R$ 500,00 e a explicação "Informado na análise por Fulana (Solar) · pela regra seriam R$ 750,00". Ele não toca no campo e aprova. A garantia é emitida com R$ 500,00.
  5. O inquilino paga a entrada. A baixa registra R$ 500,00 de adesão, em 10/11.
  6. A comissão aparece no acerto de novembro da Solar. Em Cobranças → Repasses, novembro, a linha da Solar mostra o saldo já com a comissão abatida. No detalhe, além de Previsão e Pagamentos, há Comissão: uma linha por garantia cuja adesão foi paga no mês — inquilino, garantia, adesão paga R$ 500,00, impostos (R$ 81,65), percentual (80%) e comissão (R$ 334,68). O painel Acerto passa a mostrar Mensalidades e multas, Comissão (negativa) e Saldo; Recebido e Falta passam a ser contra o saldo.
  7. A Solar transfere o saldo e a garantidora registra a transferência e fecha a competência, como hoje (0055). Fechado, a comissão daquele mês fica fechada com ele.

O caso sem recolhimento. A Moradas não recolhe mensalidade. No mês em que uma adesão de cliente dela é paga, aparece um acerto da Moradas em Repasses só com Comissão, e o saldo é a favor da imobiliária. A garantidora paga por PIX, por fora, e registra em Registrar pagamento de comissão — data e valor, que vem com o saldo. Saldo zerado, fecha a competência. O que a tela de acerto mostra nesse caso, e no caso misto em que a comissão passa das mensalidades, está em 7.1, Acerto — o saldo e os dois sentidos.

A garantidora preenchendo. É a mesma jornada do passo 3, com uma diferença: o bloco Taxa de adesão só aparece quando se sabe de qual imobiliária é a análise, porque a regra em vigor depende dela. Essa imobiliária é a do seletor, se escolhida, ou a do imóvel escolhido — é do imóvel que a análise herda a imobiliária (RN3), e a tela tem de mostrar o mesmo número que o servidor vai gravar (decisão do usuário, 2026-10-12, na revisão da branch). Sem imobiliária nenhuma — nem no seletor, nem no imóvel —, vale a regra padrão.

Custo de uso. O sistema já sabe e não pergunta: o Pacote Locatício e o prazo (de Locação e renda), o multiplicador do LMI (de Parâmetros), a regra em vigor da imobiliária, e o valor de adesão que foi pago (da baixa, spec 0054). O que se digita:

  • regra padrão: tipo, valor, base, uma chave, e dois percentuais (impostos e comissão), uma vez;
  • regra própria: um toque para sair do padrão, os mesmos campos, uma vez por imobiliária com acordo diferente;
  • na análise: nada, no caso comum — o valor vem calculado. Só a exceção digita;
  • no parecer: nada, se a análise já trouxe o valor certo;
  • comissão: nada, além de registrar o pagamento quando o saldo é a favor da imobiliária.

7.1 Desktop

Parâmetros → Taxa de adesão. Continua ocupando a linha inteira da grade, onde está o Pagamento inicial. De cima para baixo:

  • a descrição do bloco, em duas frases, no lugar da atual: "Cobrada uma única vez do inquilino, na contratação. Quem define o valor é a imobiliária; a garantidora recebe e devolve a ela, descontados os impostos, como comissão sobre a garantia. Esta é a regra de toda imobiliária sem regra própria no cadastro.";
  • Tipo de taxa — o seletor segmentado de hoje, renomeado: a legenda Tipo de cobrança inicial passa a Tipo de taxa, e as opções Sem cobrança inicial, Valor fixo (R$) e Percentual de uma base passam a Sem taxa, Valor fixo e Percentual;
  • conforme o tipo: Valor (R$); ou Percentual (%) e Base de cálculo, com as três bases descritas como o produto calcula: Pacote Locatício mensal, Pacote × meses da locação, Pacote × multiplicador do LMI. A dica de cada uma passa a dizer que o Pacote é aluguel + condomínio + IPTU;
  • Permitir editar o valor na análise de crédito — chave, visível só com Valor fixo ou Percentual;
  • Impostos descontados da comissão (%) e Comissão devolvida à imobiliária (%), lado a lado, nesta ordem — a do cálculo e a do passo 1 da jornada;
  • a linha de exemplo, que se recalcula enquanto se digita, num pacote de referência de R$ 2.500,00 e 12 meses. Com Sem taxa, diz "Sem taxa de adesão: não há comissão.". O exemplo do cadastro da imobiliária segue a mesma regra, com os impostos da garantidora;
  • as duas chaves de hoje — parcelamento e primeira mensalidade na entrada —, com o texto revisto para dizer taxa de adesão onde hoje diz pagamento inicial.

As dicas (?) de cada campo deixam de falar em "esforço e risco da garantidora".

Imobiliárias → Editar → Taxa de adesão e comissão. Um FormPanel, como Dados gerais, Telefones e Endereço. Duas partes:

  • Taxa — duas opções: Seguir a regra da garantidora, com o resumo dela na mesma linha ("Sem taxa", "R$ 300,00 fixo", "1% de Pacote × LMI"); ou Regra própria desta imobiliária, que abre os mesmos campos do bloco de Parâmetros: tipo, valor ou percentual e base, e a chave de edição na análise;
  • Comissão — Seguir o padrão da garantidora (100%) ou Percentual próprio, com um campo de percentual.

Embaixo, a mesma linha de exemplo, com os impostos da garantidora. No detalhe da imobiliária, uma seção em leitura mostra a regra em vigor e diz se é própria ou padrão.

A escolha Seguir / Própria, nas duas partes, é um grupo de dois rádios.

O administrador da plataforma não vê o bloco — nem no formulário, nem a seção em leitura do detalhe. No formulário, é o mesmo tratamento que já se dá ao campo de recolhimento (apps/web/src/pages/agencies/form.tsx:114, podeDefinirRecolhimento = !isAdminGlobal(...)). No detalhe é decisão nova, não precedente: ali o recolhimento aparece para todos (apps/web/src/pages/agencies/show.tsx:193-197). A razão é a mesma do formulário, levada um passo adiante: taxa e comissão são acordo comercial do cliente com a parceira — valor de dinheiro, não só uma cláusula sim/não —, e o SaaS admin administra a plataforma, não o negócio do cliente.

Minha Imobiliária → Dados. A mesma seção em leitura do detalhe: a taxa em vigor, se é própria ou padrão, a permissão de editar na análise e o percentual de comissão.

Análise de Crédito → formulário → Taxa de adesão. Um FormPanel logo abaixo de Locação e renda, presente só quando a regra em vigor permite editar. Dentro:

  • Taxa de adesão (R$) — campo monetário à direita, no padrão BRL do app, preenchido com o valor da regra;
  • a origem, uma linha embaixo: "Regra da Solar: 1% de Pacote × LMI (30)" ou "Regra da garantidora: R$ 300,00 fixo";
  • depois de editado, a linha vira "Valor alterado · pela regra seriam R$ 750,00", e aparece Usar o valor da regra (botão de texto).

Enquanto o campo não foi editado, ele acompanha o aluguel, o condomínio, o IPTU e o prazo: mudar Locação e renda recalcula o valor. Depois de editado, o valor digitado fica, e só a linha de origem se atualiza.

Sem a permissão de editar, o bloco não aparece; o Resumo financeiro da tela mostra a taxa da regra em vigor — a da imobiliária, não mais sempre a padrão. A linha do resumo passa de Pagamento inicial a Taxa de adesão.

A imobiliária já vê o resumo financeiro inteiro hoje, e continua vendo. Desde 2026-08-15 todo papel com tenant — inclusive AGENCY_USER — tem tenant_settings:read (apps/api/app/db/rbac_seed.py:56-66 e :403), justamente para o resumo da análise funcionar para ela; GET /settings exige só essa permissão (apps/api/app/api/v1/routers/tenant_settings.py:65). O comentário de FinancialSummaryPanel.tsx que diz que a imobiliária "não alcança GET /settings" é anterior a essa decisão e está desatualizado — a frase "Os parâmetros da garantidora não estão disponíveis para você…" só aparece quando a leitura falha. Esta spec não muda o que ela vê no resumo, só a linha da taxa, que passa a sair da regra em vigor.

Análise de Crédito → detalhe → parecer. O campo Taxa setup (R$) passa a se chamar Taxa de adesão (R$), nasce com o valor da análise, e a explicação diz de onde ele veio: "Informado na análise por Fulana (Solar) · pela regra seriam R$ 750,00", quando houve edição; "Pela regra da Solar, hoje: 1% de Pacote × LMI (30). Você pode alterar.", quando não houve — no lugar do "Sugerido pelos parâmetros da garantidora (X). Você pode alterar." de hoje. O diálogo de emissão da garantia e a tela da garantia trocam Taxa de setup por Taxa de adesão; o parecer já decidido troca Taxa setup aprovada por Taxa de adesão aprovada; e o resumo do diálogo de aprovação troca Taxa setup por Taxa de adesão.

Acerto (0055) → Comissão. O detalhe do acerto, que hoje tem três painéis lado a lado (Previsão, Pagamentos, Acerto), ganha Comissão como uma seção de largura inteira, entre os painéis e a seção Mensalidades: os painéis respondem quanto, a comissão é o que mudou o quanto, e as mensalidades são o detalhe longo que já estava ali. É uma tabela: inquilino, garantia, data do pagamento da adesão, adesão paga, impostos, percentual de comissão, comissão; e o total. De uma linha, abre-se a garantia.

Acerto — o saldo e os dois sentidos. Dois créditos, uma lista de movimentações, e a conta compensa os dois sentidos (decisão do usuário, 2026-10-11):

  • Mensalidades e multas — o que a imobiliária deve (o Devido de hoje); Comissão — o que a garantidora deve; Saldo = mensalidades e multas − comissão;
  • Movimentações = as transferências da imobiliária para a garantidora (Recebido) e os pagamentos de comissão da garantidora para a imobiliária (Pago à imobiliária) — uma lista só, a lista Transferências de hoje, renomeada. Cada linha mantém o formato de hoje (dd/mm · R$ X, e o botão de remover) e ganha, antes da data, o sentido em uma palavra, Recebido ou Pago. A observação continua fora da linha, como hoje, no detalhe e no histórico — o painel tem um terço da largura e não cabe mais nada; no celular, a linha é a mesma;
  • Falta = saldo − recebido + pago. Positiva: a imobiliária ainda deve. Negativa: a garantidora ainda deve. Zero: quitado.

Só há dois erros de registro, e cada um é de um sentido. A compensação faz com que uma transferência legítima nunca vire erro só porque uma comissão chegou depois:

  • Recebido a mais — o recebido passou das mensalidades e multas: a imobiliária mandou mais do que jamais deveu. É exatamente a regra da 0055 ("O recebido passou do devido"), medida contra a mesma coisa; a comissão não entra nela;
  • Pago a mais — o pago à imobiliária passou da comissão: a garantidora pagou mais comissão do que existe.

Fora desses dois, a falta diz quem deve a quem, e o botão é o do sentido que falta. A tabela se lê de cima para baixo, e vale a primeira linha que se aplica. Se os dois erros acontecem juntos, o painel mostra as duas linhas (Recebido a mais e Pago a mais) no lugar da falta, e o motivo é o do primeiro — removido um, aparece o outro:

Estado Painel Acerto Botão Motivo para não fechar
Recebido > mensalidades e multas os números, com Recebido a mais R$ X no lugar da falta — "O recebido passou das mensalidades e multas em R$ X. Remova a transferência registrada a mais para fechar." (o de hoje, com o nome novo)
Pago > comissão os números, com Pago a mais R$ X no lugar da falta — "O pago à imobiliária passou da comissão em R$ X. Remova o pagamento registrado a mais para fechar."
Falta > 0 (a imobiliária deve) Mensalidades e multas · Comissão · Saldo · Recebido · Pago à imobiliária (quando houver) · Falta receber Registrar transferência "Faltam R$ X a receber." (o de hoje)
Falta < 0 (a garantidora deve) Mensalidades e multas · Comissão · Saldo · Recebido (quando houver) · Pago à imobiliária (quando houver) · A pagar à imobiliária Registrar pagamento de comissão "Faltam R$ X a pagar à imobiliária."
Falta = 0 os números, e Quitado — nenhum: pode fechar, se o resto deixar

Os dois exemplos de trabalho, lado a lado:

  • Transferiu cedo, comissão chegou depois. Mensalidades e multas R$ 1.000,00; a Solar transfere R$ 1.000,00 no dia 5; no dia 25, uma adesão paga gera R$ 300,00 de comissão. Saldo R$ 700,00; falta = 700 − 1.000 = −R$ 300,00. Recebido não passou das mensalidades, então não é erro: A pagar à imobiliária R$ 300,00, e a garantidora registra o pagamento.
  • Comissão maior que as mensalidades. Mensalidades e multas R$ 200,00, comissão R$ 300,00; a Solar transferiu R$ 200,00. Saldo −R$ 100,00; falta = −100 − 200 = −R$ 300,00: A pagar à imobiliária R$ 300,00 — os R$ 200,00 que ela mandou mais os R$ 100,00 de comissão que sobram.

A mesma fórmula, e o mesmo tratamento dos dois lados do zero.

A comissão de cada adesão congela no pagamento (decisão do usuário, 2026-10-11). Ela é calculada com o percentual de impostos e o percentual de comissão vigentes no dia em que a adesão foi paga, e não muda mais — nem em mês aberto, nem em mês reaberto. Mudar um dos percentuais em Parâmetros ou no cadastro vale para os pagamentos seguintes; ao salvar, as duas telas dizem isso: "Vale para as adesões pagas a partir de hoje; as já pagas mantêm a comissão calculada." Por isso um pagamento de comissão nunca vira pago a mais por causa de uma regra que mudou: pago a mais só acontece por pagamento registrado a mais.

A recusa de declaração que já existe (DECLARATION_BELOW_RECEIVED, apps/web/src/pages/agency-settlements/show.tsx:197-202) continua valendo, medida contra o mesmo número do recebido a mais: uma correção que deixaria as mensalidades e multas abaixo do que já foi recebido é recusada, como hoje. A comissão não a dispara — saldo negativo é só "a garantidora deve". O texto troca devido por mensalidades e multas: "Com esta correção as mensalidades e multas ficariam abaixo do que já foi recebido.", e a variante de cada lado muda do mesmo jeito.

Enquanto há mensalidade sem declaração, o saldo é parcial. O Devido (parcial) de hoje (PaineisDoAcerto.tsx:86) passa para o Saldo (parcial) — é ele que ainda vai mudar. Com o saldo parcial, Registrar transferência continua como hoje (a imobiliária pode mandar dinheiro antes de declarar tudo), mas Registrar pagamento de comissão não aparece: pagar comissão sobre um saldo que ainda vai mudar é o caminho do pago a mais. No lugar do botão, o painel diz "Declare as mensalidades para pagar a comissão." Na linha Falta < 0 da tabela, então, o botão existe só com o saldo final.

O selo de situação (Aberto, Pronto para fechar, Fechado) é o mesmo nas listas e no detalhe, e acompanha os motivos: é Pronto para fechar exatamente quando não há motivo para não fechar. Uma consequência, que muda a regra de hoje: o acerto só de comissão, sem mensalidade nenhuma, fica Pronto para fechar quando a falta zera — hoje um acerto sem mensalidade em aberto é "nada a acertar"; com comissão, há o que acertar.

Acerto sem mensalidade nenhuma no canal da imobiliária (o caso da Moradas): os painéis Previsão e Pagamentos e a seção Mensalidades não aparecem — não há o que mostrar neles, e painel vazio sugere dado faltando. Ficam o painel Acerto, sem a linha Mensalidades e multas — no desktop, na primeira coluna da mesma grade de três, com as outras duas vazias, para não esticar um painel de quatro linhas pela tela —, e a Comissão, em largura inteira, embaixo.

Comissão zero não abre acerto. Uma adesão paga de R$ 0,00, ou com percentual de comissão 0%, gera comissão zero, que não entra na seção nem cria acerto só de comissão — não há o que acertar. Num acerto que já existe por causa das mensalidades, ela também não aparece.

A ordem dos motivos para não fechar. Os de hoje continuam na ordem de hoje, e os novos entram depois das declarações, porque o saldo só é final quando tudo está declarado — convidar a pagar comissão sobre saldo parcial leva ao pago a mais:

  1. "Faltam N mensalidades a declarar." (o de hoje);
  2. o motivo da tabela acima, conforme o estado.

O "Não há mensalidade a acertar neste mês." passa a "Não há nada a acertar neste mês.".

O diálogo de registrar. Registrar pagamento de comissão é o diálogo de transferência de hoje com três trocas: o título; Recebido em vira Pago em; e o valor vem com o A pagar à imobiliária. Valor e Observação (opcional) ficam como estão.

O que a imobiliária lê. Em Minha Imobiliária → Repasses — a lista, o cartão do celular e o detalhe —, os mesmos números, escritos do lado dela. Uma tabela só, e ela vale nos três lugares:

Na voz da garantidora Na voz da imobiliária
Recebido Pago à garantidora
Pago à imobiliária Recebido da garantidora
Falta receber · "R$ X a receber" Falta pagar · "R$ X a pagar"
A pagar à imobiliária · "R$ X a pagar" A receber da garantidora · "R$ X a receber"
Recebido a mais Pago a mais
Pago a mais (pela garantidora) Recebido a mais
"Faltam R$ X a receber." "Faltam R$ X a pagar à garantidora."
"Faltam R$ X a pagar à imobiliária." "A garantidora ainda vai pagar R$ X."
"O recebido passou das mensalidades e multas…" "O pago passou das mensalidades e multas em R$ X. A garantidora precisa remover a transferência registrada a mais."
"O pago à imobiliária passou da comissão…" "A garantidora registrou R$ X a mais de comissão e precisa removê-lo."
Quitado Quitado
Palavra de sentido na lista de movimentações: Recebido (transferência da imobiliária) · Pago (pagamento de comissão) Pago (a transferência que ela fez) · Recebido (o pagamento de comissão que ela recebeu)
"Declare as mensalidades para pagar a comissão." "Declare as mensalidades para a comissão ser paga."

Saldo, Mensalidades e multas e Comissão não mudam de nome: são o mesmo número para os dois lados. O saldo negativo, na voz dela, lê-se "R$ X a seu favor". Os botões de registrar continuam só para a garantidora, como hoje.

O que já existe e segue igual. O diálogo Fechar competência acrescenta uma linha à descrição de hoje quando há comissão: "A comissão de R$ 334,68, de 1 garantia, fica fechada nesta competência." Num acerto só de comissão, só essa linha aparece. Reabrir reabre o mês com a mesma comissão — os valores de cada adesão estão congelados desde o pagamento —, e o texto de hoje ("Declarações, transferências e nota fiscal ficam") passa a dizer movimentações no lugar de transferências. O Histórico nomeia o pagamento como Pagamento de comissão registrado e Pagamento de comissão removido, e quando a linha é à imobiliária o botão, o tooltip e o diálogo de confirmação dizem Remover pagamento e "Remover o pagamento de R$ X de dd/mm?" no lugar de transferência. A nota fiscal fica exatamente como na 0055 — uma por acerto, opcional, sem dizer quem emitiu contra quem —, inclusive no acerto só de comissão (decisão do usuário, 2026-10-11); a questão fiscal segue aberta, fora desta spec.

As listas de Repasses (Cobranças → Repasses e Minha Imobiliária → Repasses). As colunas de hoje passam a ser: Imobiliária (na da garantidora) ou Competência (na da imobiliária), Situação, Mensalidades (mostra — quando não há nenhuma), Comissão (nova; — quando não há), Saldo (no lugar de Devido, podendo ser negativo), Recebido, Pago à imobiliária (nova; — quando zero), Falta — escrita "R$ X a receber" ou "R$ X a pagar" conforme o sinal, e, nos três estados que não são falta, "Recebido a mais R$ X", "Pago a mais R$ X" ou "Quitado", os mesmos do painel (a tabela de estados) —, e Nota fiscal. Na lista da imobiliária, os rótulos são os da voz dela (tabela acima). A Situação segue o selo descrito acima. O estado vazio da lista da garantidora passa a "Nenhuma imobiliária tem mensalidade a repassar nem comissão nesta competência.", e a segunda frase passa a "Mensalidades aparecem para quem recolhe no cadastro; comissão, para toda imobiliária com adesão paga no mês." — o botão Ver imobiliárias fica. O estado vazio da lista da imobiliária — hoje "Ainda não há repasses. Eles aparecem aqui a partir da primeira competência com mensalidades que vocês recolhem." — passa a "Ainda não há repasses. Eles aparecem aqui a partir da primeira competência com mensalidades que vocês recolhem ou com comissão de taxa de adesão." Cai, com isso, a premissa da 0055 de que a imobiliária que não recolhe vê só o estado vazio (RepassesDaImobiliaria.tsx:32-34): com comissão, ela passa a ter repasses.

7.2 Mobile (375px)

Parâmetros → Taxa de adesão. A tela já empilha o seletor de tipo na vertical no celular (spec 0016). Os campos novos seguem o mesmo: uma coluna, Impostos e Comissão um embaixo do outro, a linha de exemplo quebrando em duas linhas. Nada some. A tela é de configuração, usada no escritório; o requisito é que caiba sem rolagem horizontal, não que seja rápida no polegar.

Imobiliárias → Editar. O formulário já é de uma coluna no celular; o bloco novo também. As duas opções (Seguir / Própria) viram duas linhas de rádio com área de toque de 44px; o resumo da regra padrão desce para baixo do rótulo, em vez de ficar na mesma linha.

Minha Imobiliária → Dados. A seção segue o padrão da aba: SectionTitle e Field dentro do mesmo Paper, numa grade que já é de uma coluna no celular. Nada muda além da largura.

Análise de Crédito → formulário → Taxa de adesão. É o bloco que alguém vai usar no celular de verdade: o formulário da análise já é preenchido por corretor fora do escritório. Uma coluna: o campo de valor em largura inteira, a origem embaixo, e Usar o valor da regra como botão de texto de 44px de altura, embaixo da origem — não na mesma linha, que não cabe em 375px. O teclado é numérico com decimal (inputMode="decimal"), como no campo de taxa do parecer (DecisionPanel.tsx:387). O bloco é curto (um campo): o teclado aberto cobre o que vem abaixo — Observações e o Resumo financeiro —, mas não o próprio campo nem a origem.

Parecer e garantia. Só troca rótulo e texto de explicação; o layout é o de hoje.

Acerto → Comissão. No celular o acerto já empilha, com só o Acerto aberto (0055). A Comissão entra como mais uma MobileDetailSection colapsável, fechada por padrão, com o título "Comissão · R$ 334,68", depois de Pagamentos. No acerto sem mensalidade nenhuma, Previsão e Pagamentos somem também no celular (como em 7.1), e a Comissão vem logo depois do painel Acerto. Aberta, cada linha da tabela vira um RecordCard: inquilino no título, adesão paga e comissão em destaque, data, impostos e percentual como linhas secundárias. O painel Acerto, aberto, mostra as mesmas linhas do desktop, na ordem da tabela de 7.1 — Mensalidades e multas, Comissão, Saldo, Recebido, Pago à imobiliária (quando houver), Falta receber ou A pagar à imobiliária — e, embaixo, a lista de Movimentações com o botão de registrar, como hoje a de transferências. Nada do painel se esconde no celular: é ele que responde, de relance, quanto falta e para quem. Na Minha Imobiliária, os rótulos são os da voz dela, pela mesma tabela.

Listas de Repasses. O RecordCard de cada linha já tem um valor em destaque, e hoje ele difere entre as listas: a da garantidora destaca Falta (RepassesDaCompetencia.tsx:234-235), a da imobiliária destaca Devido (RepassesDaImobiliaria.tsx:107-108). Fica assim, com os nomes novos: na da garantidora, a Falta, escrita como na coluna do desktop — "R$ X a receber", "R$ X a pagar", "Recebido a mais R$ X", "Pago a mais R$ X" ou "Quitado"; na da imobiliária, também a Falta, na voz dela — "R$ X a pagar", "R$ X a receber", "Pago a mais R$ X", "Recebido a mais R$ X" ou "Quitado". Hoje esse cartão destaca o Devido; com dinheiro nos dois sentidos, o saldo deixa de responder a pergunta do cartão — quanto falta, e para quem —, porque um saldo a favor dela continua negativo depois de pago. Os dois cartões passam a destacar a mesma coisa. No mês fechado, o destaque é "Fechado", nos dois, como o da garantidora já é hoje (RepassesDaCompetencia.tsx:234). A comissão entra como linha secundária do cartão, "Comissão R$ 334,68", só quando houver. A meta "N mensalidades · N declaradas" vira "Só comissão" quando não há mensalidade, e a meta de valores do cartão da garantidora, que hoje traz Recebido, ganha Pago R$ X ao lado quando houver pagamento à imobiliária.

Detalhe da imobiliária. A seção em leitura segue a grade do detalhe, que já é de uma coluna no celular.

Registrar pagamento de comissão. O diálogo é o de transferência, que já abre em tela cheia no celular (TransferenciaDialog.tsx:46); as trocas são as de 7.1 (título e Pago em), e o layout é o de hoje.

PWA. Nada novo para instalar ou guardar offline: as telas são as mesmas, com campos a mais. O que muda é o bundle, e o PWA instalado precisa recarregar para vê-lo, como em toda mudança de tela.

7.3 Estados

  • Regra padrão nunca salva. A garantidora que nunca abriu Parâmetros tem os valores de fábrica de hoje, e o bloco os mostra. A comissão começa em 100% e os impostos em 0% — nunca vazio (decisão do usuário, 2026-10-11: "por padrão vamos deixar 0 nunca vazio, assim não tem confusão"). Até a garantidora informar o percentual real, a comissão sai sobre o valor bruto da adesão; é o que o bloco mostra na linha de exemplo, e é o que ela vê no acerto. O campo não aceita ficar vazio: apagado, a tela recusa no próprio campo com "Informe um percentual; use 0 se não há imposto a descontar.". Como o percentual sempre existe, não há estado de "comissão aguardando os impostos" em lugar nenhum — o acerto sempre tem a comissão calculada. A decisão anterior desta entrevista, de não fechar o mês sem os impostos, perdeu o objeto e saiu.
  • Imobiliária sem regra própria. O bloco do cadastro vem em Seguir a regra da garantidora; nada a fazer.
  • Regra própria com Sem taxa. Vale: a imobiliária não cobra adesão, mesmo que a padrão cobre. O bloco da análise não aparece, e não há comissão.
  • A garantidora preenchendo sem ter escolhido a imobiliária. Enquanto não há imobiliária no seletor nem imóvel escolhido, o bloco Taxa de adesão não aparece e o Resumo financeiro mostra a taxa da regra padrão. Escolhido o imóvel, vale a regra da imobiliária dele, mesmo com o seletor vazio — o bloco aparece se ela permitir, e o resumo passa a mostrar a taxa dela.
  • A regra em vigor não carrega (falha de rede). Sem a regra, a tela não sabe se ela permite editar, e por isso o bloco não aparece: mostrar um campo que talvez não devesse existir é pior do que esconder um que devia. A linha Taxa de adesão do Resumo financeiro mostra "Não foi possível carregar a regra da taxa." com Tentar de novo ao lado; carregada, o bloco aparece, se a regra permitir. A análise pode ser salva e enviada assim: sem valor informado, a taxa sai da regra, na aprovação, como sempre saiu. Vazio não é zero, pelo mesmo motivo do calcularTaxaDeAdesao de hoje: zero é taxa legítima. Quando o que falha é a leitura das configurações inteiras, o painel inteiro já não calcula nada; a frase de hoje, "Os parâmetros da garantidora não estão disponíveis para você…", que parte da premissa desatualizada de 7.1, passa a "Não foi possível carregar os parâmetros da garantidora." com Tentar de novo — e não convive com a da regra: é uma ou outra.
  • Campo editado e apagado. Vazio volta ao comportamento da regra, como Usar o valor da regra.
  • A garantidora troca de imobiliária depois de editar o valor. O valor editado é descartado e o campo volta à regra da nova imobiliária — o acordo era com a outra. Se a nova regra não permite editar (ou é Sem taxa), o bloco some. Em qualquer caso, uma linha avisa no lugar da origem, uma vez: "A taxa voltou para a regra da Moradas."
  • Devolvida em pendência, e a regra deixou de permitir editar (ou passou a Sem taxa) enquanto a análise estava com a garantidora. O valor que foi informado continua valendo — foi informado quando a regra permitia —, e o bloco aparece em leitura, com o valor e a linha "Informado quando a regra permitia editar; a regra da Solar não permite mais." e o botão Usar o valor da regra, que descarta o valor informado. Sem valor informado, o bloco não aparece, como em 7.1.
  • A regra muda entre a análise e a aprovação. Valor editado na análise fica como está. Valor não editado acompanha a regra vigente, como hoje (fix 0009), e o parecer mostra o valor recalculado.
  • Análise já enviada. O formulário não abre fora de rascunho ou pendência — mostra "Somente análises em edição ou pendentes podem ser alteradas." —, e isso não muda. Depois do envio, quem preencheu vê a taxa no detalhe da análise, no Resumo financeiro que a tela já mostra. A linha Taxa de adesão traz:
  • valor editado: o valor editado, e a origem "Informado na análise por Fulana (Solar) · pela regra seriam R$ 750,00" — entre parênteses vai o nome da imobiliária, ou garantidora quando quem editou foi a equipe dela: "por Beltrano (garantidora)" — inclusive quando o editado é R$ 0,00: a linha aparece com zero, porque zero informado é decisão, não ausência (hoje a linha some com zero);
  • valor não editado: o valor calculado no último salvamento da análise — o resumo guardado, como hoje —, e a origem "Pela regra da Solar, calculada em 12/11: 1% de Pacote × LMI (30)". A data está ali porque o parecer recalcula pela regra vigente (fix 0009), e se a regra mudou depois os dois números diferem; a data diz qual é o velho. É a divergência que o fix 0009 já aceitou, agora com o motivo à vista.

É o mesmo texto para a imobiliária e para a garantidora. Devolvida em pendência, a análise volta a abrir no formulário — no passo de documentos, como hoje (CreditAnalysisForm.tsx:441); o bloco Taxa de adesão está no primeiro passo, um Voltar antes, editável, com o valor que tinha. - Sem permissão de editar a imobiliária. Quem só lê o cadastro vê a seção em leitura do detalhe; o bloco do formulário não aparece para quem não abre o formulário. - Adesão paga num mês já fechado. A comissão entra na competência do pagamento, que é um mês aberto — o mês fechado não muda. Uma adesão estornada depois de fechado o mês da comissão é caso da Parte III. - Acerto vazio. Competência sem mensalidade no canal da imobiliária e sem adesão paga: não há acerto, como hoje (0055). - Erro ao salvar em Parâmetros ou no cadastro: a recusa de um campo aparece no campo, como já faz Parâmetros desde o fix 0015; o resto, na faixa do topo.

8. Revisão da experiência

Preenchido pelo laço do /spec new, rubrica .claude/rubrics/experiencia.md. Registrar todas as rodadas, inclusive as reprovadas.

Rodada Critical Important Minor Nota Veredicto
1 0 5 7 7 Reprovada — o acerto só de comissão sem textos e visibilidades; análise enviada contradizendo o formulário; fechamento sem impostos sem decisão (levado ao usuário); bloco no cadastro sem dizer o que o SaaS admin vê; um achado sobre o resumo da imobiliária vinha de um comentário desatualizado e o texto foi tornado inequívoco
2 0 6 8 7 Reprovada — casos novos do acerto cruzando estado existente (transferência nos dois sentidos, ordem dos motivos, colunas e cartões das listas, nota fiscal — levada ao usuário), detalhe da análise mostrando snapshot contra parecer recalculado, regra que não carrega descrita em dois ramos contraditórios
3 0 4 5 7 Reprovada — bordas da tabela de estados criada na rodada 2: voz da imobiliária só no painel, vazio da lista dela, saldo parcial com botão de pagar comissão, selo de situação contradizendo o bloqueio. Corrigido isolando: uma tabela de voz e uma regra para o selo
4 1 4 4 6 Reprovada — o Critical: o erro recebido a mais medido contra o saldo fazia apagar transferência legítima quando a comissão chegava depois. Levado ao usuário (compensar os dois sentidos); os erros passam a ser medidos cada um contra o seu crédito. Também: listas sem os estados a mais, casos de borda do formulário, textos de impostos em branco espalhados, recusa de declaração
5 0 4 2 7 Reprovada — estado aguardando impostos sem linha na tabela (caía em Quitado); mudança de percentual sobre comissão já registrada (levado ao usuário: congela no pagamento); palavra de sentido das movimentações sem voz da imobiliária; cartão da imobiliária destacando saldo depois de pago
6 0 2 5 8 Aprovada — depois da aprovação: a pergunta sobre impostos voltarem a vazio levou o usuário a decidir que eles nascem em 0% e nunca ficam vazios, o que eliminou o estado aguardando os impostos inteiro; a precedência da tabela de estados ficou explícita; comissão zero, texto do parecer e largura do painel sozinho foram escritos

Resultado: aprovada na rodada 6.

Pendências aceitas: nenhum achado ficou aberto — os dois Important e os cinco Minor da rodada 6 foram tratados depois da aprovação. O tratamento não passou por nova rodada, e um deles foi uma remoção grande: a decisão do usuário de impostos em 0% e nunca vazios tirou da Parte II a linha da tabela de estados, a tabela Impostos em branco, um motivo de não fechar e um estado de 7.3. Remover estado não cria interação nova entre os que ficaram, e por isso não se pagou uma rodada; o portão do detalhamento lê a Parte II como insumo e é onde uma sobra apareceria.


Parte III — Detalhamento

Não preencher antes das revisões das seções 6 e 8 estarem aprovadas.

9. Regras de negócio

Nomes. "Regra padrão" é a da garantidora (TenantSettings); "regra própria" é a da imobiliária (PartnerAgency); "regra em vigor" é a que vale para uma análise (RN3). O valor de adesão continua se chamando initial_fee no código — a renomeação é de tela (RN22), não de coluna: o código já chama a mesma coisa de initial_fee, setup_amount e setup_paid, e renomear coluna em produção com cliente é custo sem ganho.

A regra

  • RN1 — A regra padrão ganha três campos em TenantSettings (apps/api/app/models/tenant_settings.py):
  • allow_manual_initial_fee — bool, não nulo, padrão false: permite editar o valor na análise;
  • commission_percent — Numeric(5,2), não nulo, padrão 100.00, faixa 0–100: a parte do líquido que volta à imobiliária;
  • commission_tax_percent — Numeric(5,2), não nulo, padrão 0.00, faixa 0–100: os impostos descontados da comissão. Nunca vazio (decisão do usuário, 2026-10-11): PATCH /settings com o campo null responde 422 COMMISSION_TAX_REQUIRED, com a mensagem "Informe um percentual; use 0 se não há imposto a descontar.". Omitir o campo não muda nada, como em todo PATCH desta tela. Como o null é visto, e onde se recusa: no service, não no schema. Um validador de schema sairia pelo validation_error_handler com code: "VALIDATION_ERROR" (apps/api/app/core/exceptions.py:157-165), e o código prometido nunca apareceria. O service já lê o que veio de fato por model_dump(exclude_unset=True) — é o que faz com interest_percent_month (tenant_settings_service.py:59-62) — e, com commission_tax_percent, commission_percent ou allow_manual_initial_fee presentes e nulos, levanta BusinessRuleError 422: COMMISSION_TAX_REQUIRED para o primeiro, FIELD_REQUIRED para os outros dois, sempre com details: [{field, message}] — o formato que Parâmetros já leva para o campo desde o fix 0015.

Para a imobiliária, commission_percent vem nulo em GET /settings (decisão do usuário, 2026-10-11) — o padrão das outras parceiras é informação de negociação, e ela não precisa dele: o próprio percentual em vigor ela recebe pela RN18 e pelo cadastro (RN17). É o desenho da RN25 da 0052, que mascarou preço para ela na mesma resposta. Os outros dois campos saem normalmente. Por isso commission_percent em TenantSettingsResponse é Decimal | None, embora a coluna seja não nula. Os commission_tax_percent já aparece em cada linha de comissão do acerto, e allow_manual_initial_fee só diz se a regra padrão deixa editar. Na Minha Imobiliária (Parte II, 7.1), a seção em leitura mostra o percentual em vigor dela — "Comissão: 80%" ou, se segue o padrão, "Comissão: 100% (padrão da garantidora)", com o número vindo da RN18, que é o dela.

Fora da faixa, 422 com o campo nomeado (o mesmo ValidationError por campo que Parâmetros já trata desde o fix 0015). Os campos de hoje (initial_fee_type, initial_fee_value, initial_fee_base, as duas chaves de parcelamento e de primeira mensalidade) ficam como estão.

  • RN2 — A regra própria mora na imobiliária, em seis campos novos de PartnerAgency (apps/api/app/models/partner_agency.py):
  • initial_fee_custom — bool, não nulo, padrão false. false = segue a regra padrão;
  • initial_fee_type, initial_fee_value, initial_fee_base — os mesmos tipos e enums de TenantSettings, anuláveis;
  • allow_manual_initial_fee — bool, anulável;
  • commission_percent — Numeric(5,2), anulável, faixa 0–100. null = segue o padrão da garantidora. É independente de initial_fee_custom: dá para seguir a taxa padrão e ter comissão própria (Parte II, 7.1, as duas partes do bloco).

Validação no service, ao gravar — sobre o estado resultante, isto é, o que está gravado com o corpo aplicado por cima (um PATCH só de initial_fee_value numa imobiliária já com regra própria completa passa, sem reenviar o tipo): - initial_fee_custom = true exige initial_fee_type; com FIXED, exige initial_fee_value ≥ 0; com PERCENTAGE, exige initial_fee_value em (0, 100] e initial_fee_base. Faltando qualquer um no estado resultante, 422 com o campo nomeado (INITIAL_FEE_RULE_INCOMPLETE); - allow_manual_initial_fee segue a mesma leitura: ausente no corpo, mantém o gravado; se o gravado é nulo e o resultado é custom = true, grava false; - initial_fee_custom = false anula os quatro campos da taxa, venham eles ou não — voltar ao padrão não deixa regra própria guardada que reapareceria sem ninguém ver; - commission_percent: null volta a comissão ao padrão. PartnerAgencyService.update monta o payload com exclude_none (partner_agency_service.py:120), que descartaria o null; os seis campos da regra são lidos à parte, por model_fields_set — presente e nulo grava nulo, ausente não mexe. O mesmo vale para a criação.

  • RN3 — A regra em vigor é uma função pura, num módulo novo apps/api/app/domain/taxa_de_adesao.py, no desenho das funções puras que já moram em app/domain/: regra_de_adesao(settings, agencia | None) -> RegraDeAdesao, com origem (TENANT ou AGENCY), tipo, valor, base, permite_editar e percentual_de_comissao.
  • Sem imobiliária, ou com initial_fee_custom = false: tipo, valor, base e permite_editar vêm da regra padrão, e origem = TENANT;
  • com initial_fee_custom = true: vêm da imobiliária, e origem = AGENCY;
  • permite_editar é false sempre que o tipo é NONE, qualquer que seja a chave;
  • percentual_de_comissao vem da imobiliária quando não nulo, e do padrão quando nulo.

A imobiliária de uma análise é credit_analysis.partner_agency_id, que vem do imóvel (credit_analysis_service.py:306). A imobiliária INACTIVE ou removida logicamente continua tendo a regra que tem: a regra é da parceria, e análises e garantias dela seguem existindo — o que muda com o status é se ela opera, não quanto se cobra. É a regra em vigor que alimenta o resumo (RN6), a emissão (RN7), a sugestão do parecer (RN7) e o endpoint da regra (RN18). O cálculo do valor continua sendo calculate_upfront_payment (pricing.py), que passa a receber o tipo, o valor e a base da regra em vigor, não mais os de TenantSettings direto.

O valor informado na análise

  • RN4 — A análise guarda o valor informado em duas colunas novas de CreditAnalysis: informed_initial_fee (Numeric(12,2), anulável) e informed_initial_fee_by (FK users.id, anulável, ondelete="SET NULL"). null = não informado: vale a regra. CreditAnalysisCreate e CreditAnalysisUpdate aceitam informed_initial_fee; quem grava informed_initial_fee_by é o service, com o usuário da requisição — nunca o corpo.

  • RN5 — Quando se pode informar.

  • Valor ≥ 0, duas casas. Negativo: 422 com o campo nomeado.
  • Valor não nulo só é aceito se a regra em vigor da análise, no momento da gravação, tem permite_editar = true. Se não tem: 422 INITIAL_FEE_EDIT_NOT_ALLOWED, "A regra de taxa de adesão desta imobiliária não permite editar o valor.". Na criação, a regra é a da imobiliária do imóvel informado.
  • informed_initial_fee_by só muda quando o valor muda. Reenviar o mesmo valor é no-op: não revalida contra a regra, não troca o autor, não entra no log de mudanças. Assim o analista que salva uma pendência não vira autor do valor que a imobiliária informou, e a imobiliária que salva uma análise cuja regra deixou de permitir edição não toma 422 por um valor que não mexeu.
  • O apps/web só envia o campo quando o usuário o tocou — digitou, apagou ou usou Usar o valor da regra — e nunca com o bloco em leitura (RN22). Hoje o formulário manda o corpo inteiro a cada salvamento (analysisBody(), CreditAnalysisForm.tsx:825-833); o campo fica fora dele até ser tocado. A regra do service acima é a defesa para o cliente que reenviar mesmo assim.
  • null explícito é sempre aceito: limpa o valor e informed_initial_fee_by. Atenção ao update: ele monta o payload com model_dump(exclude_none=True) (credit_analysis_service.py:465), que descarta o null. O campo é lido à parte, por "informed_initial_fee" in data.model_fields_set — presente e nulo limpa; ausente não mexe.
  • Os estados em que se grava são os do formulário — DRAFT e PENDING —, porque é o update de hoje que já recusa os outros.
  • O valor já gravado sobrevive a uma mudança da regra (Parte II, 7.3, devolvida em pendência): a validação acima só roda quando o campo vem no corpo. Um PATCH que não traz informed_initial_fee não o revalida nem o apaga.
  • A imobiliária da análise não muda depois de criada: CreditAnalysisUpdate não tem imóvel nem imobiliária (apps/api/app/schemas/credit_analysis.py:72-96). A troca de imobiliária da Parte II, 7.3, acontece só no formulário, antes de salvar a primeira vez, e é regra do apps/web: trocar o seletor descarta o valor digitado e recalcula pela regra da nova (RN22). Na API, a validação da criação já usa a regra da imobiliária do imóvel enviado.

  • RN6 — O resumo financeiro. _apply_financial_snapshot (credit_analysis_service.py:931) passa a usar a regra em vigor (RN3), e grava em financial_initial_payment o informed_initial_fee, quando não nulo, ou o valor da regra, quando nulo. informed_initial_fee entra em _CAMPOS_QUE_MUDAM_O_PRECO, para que informar o valor refaça o resumo. Limpar também refaz, e por caminho próprio: como o null não chega ao payload (credit_analysis_service.py:465, RN5), o update acrescenta informed_initial_fee: None ao payload quando o campo veio presente e nulo, antes de calcular muda_preco e o antes/depois do log de mudanças (:470-490) — assim a limpeza refaz o resumo e fica registrada como alteração, igual a informar. Uma coluna nova, financial_snapshot_at (DateTime(timezone=True), anulável), guarda quando o resumo foi calculado — é a data que o detalhe mostra na origem do valor não editado (Parte II, 7.3, análise já enviada). Análise de antes do deploy tem financial_snapshot_at nulo até o próximo recálculo; a origem sai então sem a data — "Pela regra da Solar: 1% de Pacote × LMI (30)".

Parecer e emissão

  • RN7 — Ordem de precedência da taxa na garantia, na emissão (guarantee_service.py, nos dois pontos que hoje escolhem entre approved_initial_fee_value e pricing.initial_fee, :242-246 e :498-541):
  • decision.approved_initial_fee_value, se não nulo — o analista tocou o campo;
  • credit_analysis.informed_initial_fee, se não nulo — informado na análise;
  • o valor da regra em vigor da análise, vigente na emissão — como hoje (fix 0009).

O pricing_snapshot da garantia (guarantee_service.py:252-254) e a resposta de POST /guarantees/simulate (:557-561) passam a gravar e devolver initial_fee_type, initial_fee_value e initial_fee_base da regra em vigor, mais initial_fee_source (TENANT/AGENCY) — hoje copiam os de TenantSettings, que deixariam de explicar o número quando a regra é da imobiliária. - initial_fee_source mora numa coluna nova de guarantees (apps/api/app/models/guarantee.py), String(10), anulável — a emissão monta a garantia com Guarantee(..., **pricing_snapshot) (guarantee_service.py:278), e cada chave do snapshot é coluna. Garantias de antes do deploy ficam com nulo (RN23). GuaranteeResponse e GuaranteeSimulateResponse (schemas/guarantee.py) ganham o campo; em GuaranteeSimulateResponse, initial_fee_type, initial_fee_value e initial_fee_base passam a anuláveis (schemas/guarantee.py:379-381) — pelo item seguinte, e porque a regra própria FIXED ou NONE tem base nula (RN2). - Base nula no snapshot. O snapshot de hoje faz settings.initial_fee_base.value (guarantee_service.py:254); com a regra em vigor, base nula grava initial_fee_base nulo (… .value if base else None), e o mesmo para valor nulo. A coluna já é anulável (apps/api/app/models/guarantee.py:202). - Para o ADMIN_GLOBAL, quando initial_fee_source = AGENCY, os quatro vêm nulos — em toda resposta de garantia: a anulação mora em GuaranteeService._to_response (guarantee_service.py:939-995), o ponto único por onde passam o detalhe, a lista (GET /guarantees, cross-tenant para ele) e as respostas de escrita; e, no simulate, na montagem da resposta. É a mesma regra própria que a RN17 e a RN19 escondem dele, e ele lê garantia de qualquer tenant. Com TENANT, nada muda: a regra padrão ele já lê hoje na garantia. - simulate sem credit_analysis_id usa a regra padrão (source = TENANT), inclusive para usuário de imobiliária com regra própria — sem análise, não há de qual imobiliária ler a regra, e a simulação avulsa sempre foi da garantidora. - O prazo usado em rule_value (RN19) é o guarantee_months da análise, como o resumo financeiro; a emissão usa a vigência das datas. Com base TOTAL_GUARANTEE os dois podem diferir — é a divergência de insumo de meses que o fix 0009 já registrou e aceitou, e o número que vale é o da emissão.

A tela do parecer nasce com o valor que esta ordem daria sem o passo 1 (Parte II, 7.1); e, como hoje, só envia approved_initial_fee_value se o analista tocar o campo (show.tsx:622). O parecer não ganha regra nova no servidor: quem aprova sem tocar deixa a decisão para os passos 2 e 3.

A comissão

  • RN8 — A comissão congela na baixa da adesão (decisão do usuário, 2026-10-11). Em BillingService.aplicar_baixa (billing_service.py:306), quando setup_paid > 0 e a garantia da cobrança tem partner_agency_id, o service grava na cobrança, na mesma transação, cinco colunas novas de Billing:
  • commission_tax_percent — o commission_tax_percent da garantidora naquele momento;
  • commission_percent — o percentual_de_comissao da regra em vigor da imobiliária naquele momento (RN3);
  • commission_tax_amount = round_half_up(setup_paid × commission_tax_percent / 100);
  • commission_amount = round_half_up((setup_paid − commission_tax_amount) × commission_percent / 100);
  • commission_reference_month — a competência da comissão (RN9).

Arredondamento a centavo, ROUND_HALF_UP, o mesmo _round de pricing.py. Os números da Parte II saem daqui: R$ 500,00 a 16,33% → impostos R$ 81,65 → líquido R$ 418,35 → a 80%, R$ 334,68; e R$ 750,00 → impostos R$ 122,48 (122,475 arredonda para cima) → líquido R$ 627,52 → a 80%, R$ 502,02.

Depois de gravados, os cinco não mudam mais — nem quando os percentuais mudam (Parte II, 7.1, a comissão congela no pagamento), nem na reabertura. A cobrança sem imobiliária, ou com setup_paid zero, fica com os cinco nulos. Comissão calculada zero (adesão zero, ou percentual 0%) grava os percentuais e os valores, com commission_amount = 0, e commission_reference_month nulo: a RN9 não roda, nenhum registro de acerto é travado ou criado, e a comissão não entra em acerto (RN10). Não há o que acertar, e criar o registro abriria um acerto vazio que nunca fecha.

  • RN9 — A competência da comissão é a primeira competência, a partir do mês de paid_on, cujo acerto daquela imobiliária não está fechado no momento da baixa. No caso comum é o próprio mês de paid_on; com ele fechado, o seguinte, e assim por diante — o que cobre também o mês corrente fechado antes de acabar. Um mês fechado nunca muda por uma baixa — é a mesma proteção da RN14 da 0055, vista do lado de quem recebe.

A baixa se serializa com o fechamento pelo registro do acerto. Para cada competência candidata, a baixa obtém o registro com get_or_create_locked — o mesmo SELECT … FOR UPDATE (criando o registro se preciso) que close usa em _registro_para_escrever (agency_settlement_service.py:441-443). Assim um close concorrente ou terminou antes (e a baixa vê CLOSED e passa ao mês seguinte), ou espera a baixa terminar (e encontra a comissão nova entre as do mês). Criar o registro de um mês sem ele é inofensivo: a lista já mostra o mês, porque há comissão (RN16).

  • RN10 — Quem é comissão de um acerto. A comissão de uma imobiliária numa competência são as cobranças com commission_amount > 0, commission_reference_month igual à competência, não removidas, de garantia daquela imobiliária. Aberto, entram todas essas. Fechado, entram as que têm commission_settlement_id igual ao acerto (RN15) — a mesma troca de critério que contas_do_acerto já faz para os membros (acerto_com_imobiliaria.py:56-96).

Remover a cobrança (spec 0049) a tira da comissão; restaurá-la a devolve. Se, enquanto ela estava removida, o mês da comissão fechou, a restauração refaz a RN9 a partir do commission_reference_month gravado — com a mesma trava — e grava a primeira competência não fechada. Sem isso, a comissão voltaria apontando para um mês fechado que não a conta (fechado só conta quem tem commission_settlement_id), e ninguém a pagaria. Os valores congelados (RN8) não mudam: muda só a competência. Uma cobrança cuja comissão está num acerto fechado não se altera, remove nem restaura: a guarda _garantir_fora_de_acerto_fechado (billing_service.py:199) passa a olhar também commission_settlement_id, com o mesmo código BILLING_IN_CLOSED_SETTLEMENT. A mensagem de hoje fala em mensalidade (billing_service.py:215-217); quando o vínculo é commission_settlement_id, ela diz "A comissão desta cobrança está num acerto com a imobiliária já fechado. Reabra o acerto do mês para alterá-la."

As contas do acerto

  • RN11 — As contas, em contas_do_acerto (app/domain/acerto_com_imobiliaria.py), que ganha a lista de comissões e o pago à imobiliária como entrada:
  • devido — mantém o nome e o significado de hoje (mensalidades e multas), para não quebrar o contrato; na tela, vira Mensalidades e multas (RN22);
  • commission — soma de commission_amount das comissões do acerto (RN10);
  • balance = devido − commission;
  • recebido — soma das movimentações FROM_AGENCY (RN13);
  • paid_to_agency — soma das movimentações TO_AGENCY;
  • falta = balance − recebido + paid_to_agency;
  • overpaid (recebido a mais) = recebido > devido — mudou: hoje é falta < 0 (acerto_com_imobiliaria.py:51-53). Sem comissão as duas definições coincidem; com ela, só a nova não transforma transferência legítima em erro (Parte II, 7.1, decisão do usuário de 2026-10-11);
  • overpaid_to_agency (pago a mais) = paid_to_agency > commission;
  • parcial — como hoje, undeclared > 0;
  • nada_a_acertar = nenhum membro em aberto e commission == 0 e paid_to_agency == 0 — hoje é só a primeira metade. O pagamento à imobiliária entra para que o mês só de comissão cuja cobrança foi removida depois de paga aponte o pago a mais, em vez de responder "não há nada a acertar" (decisão do usuário, 2026-10-12, na revisão da branch).

  • RN12 — A situação (situacao_do_acerto): READY exatamente quando não há motivo para não fechar (Parte II, 7.1): undeclared == 0, não overpaid, não overpaid_to_agency, falta == 0 e não nada_a_acertar. CLOSED como hoje. O resto, OPEN. O fechamento (close) recusa nos mesmos casos:

  • NOTHING_TO_SETTLE (o de hoje), com a mensagem "Não há nada a acertar neste mês.";
  • SETTLEMENT_NOT_READY (o de hoje), cujo texto e details passam a seguir o primeiro motivo da ordem da Parte II, 7.1: mensalidades a declarar; recebido a mais (recebido > devido); pago a mais (paid_to_agency > commission); falta positiva ("faltam R$ X a receber"); falta negativa ("faltam R$ X a pagar à imobiliária"). O ramo de hoje que chama falta < 0 de "o recebido passou do devido" (agency_settlement_service.py:687-697) sai: com comissão, falta negativa é a garantidora devendo. details ganha overpaid, overpaid_to_agency, balance e commission, além dos de hoje.

  • RN13 — Movimentações com sentido. AgencySettlementTransfer ganha direction (enum TransferDirection: FROM_AGENCY, TO_AGENCY; não nulo; server_default = 'FROM_AGENCY', o que faz das transferências de hoje o que elas são). TransferRequest ganha direction, opcional, padrão FROM_AGENCY — o cliente de hoje continua funcionando sem mudar nada.

  • FROM_AGENCY: TRANSFER_DATE_IN_FUTURE como hoje; e TRANSFER_EXCEEDS_REMAINING passa a medir contra devido − recebido, não mais contra falta (agency_settlement_service.py:600). É a mesma régua do recebido a mais (RN11): a imobiliária pode mandar até as mensalidades e multas, com ou sem comissão no mês — se ela mandar tudo e a comissão existir, a garantidora devolve a comissão (Parte II, 7.1, exemplo 1, em qualquer ordem: transferência antes ou depois da comissão). Sem comissão, devido − recebido é a falta de hoje, e nada muda.
  • TO_AGENCY — as recusas, nesta ordem:
    1. data futura → 422 TRANSFER_DATE_IN_FUTURE (o de hoje);
    2. recebido a mais em aberto (recebido > devido) → 422 RECEIVED_EXCEEDS_DUE_OPEN ("Remova a transferência registrada a mais antes de pagar a comissão."). Sem isso, −falta incluiria o excesso recebido, e a garantidora registraria como comissão a devolução de um erro. Passada esta recusa, −falta ≤ commission − paid_to_agency, e o teto do item 5 nunca passa da comissão;
    3. undeclared > 0 → 422 COMMISSION_PAYMENT_BEFORE_DECLARATIONS ("Declare as mensalidades antes de pagar a comissão.");
    4. falta ≥ 0 → 422 NOTHING_TO_PAY_AGENCY ("Não há valor a pagar à imobiliária neste mês.");
    5. amount > −falta → 422 COMMISSION_PAYMENT_EXCEEDS_REMAINING ("O valor passa R$ X do que falta pagar à imobiliária.").
  • A data do pagamento à imobiliária é received_on — o campo mantém o nome, que vem da 0055 e é a data do movimento nos dois sentidos; no contrato, a descrição do campo passa a dizer isso. A tela o chama de Pago em (Parte II, 7.1).
  • Os dois exigem acerto aberto (SETTLEMENT_CLOSED, como hoje). A remoção é o mesmo DELETE …/transfers/{id} de hoje, para os dois sentidos.
  • Auditoria: FROM_AGENCY continua TRANSFER_REGISTERED/TRANSFER_REMOVED; TO_AGENCY registra COMMISSION_PAYMENT_REGISTERED/COMMISSION_PAYMENT_REMOVED, que o histórico nomeia como na Parte II.

  • RN14 — A recusa de declaração DECLARATION_BELOW_RECEIVED (agency_settlement_service.py, em declare) continua comparando devido com recebido — o mesmo número da RN11 overpaid. A comissão não a dispara.

  • RN15 — Fechar e reabrir. close grava, além do agency_settlement_id dos membros (:762), commission_settlement_id nas cobranças de comissão do acerto (RN10, aberto) — coluna nova de Billing, FK agency_settlements.id, anulável, ondelete="SET NULL". reopen anula só commission_settlement_id nessas cobranças. Não é o que faz com os membros — para eles a reabertura desfaz a baixa inteira (:806-817) —, porque a baixa da entrada não é do acerto: status, setup_paid, os pagamentos e os cinco campos da RN8 ficam como estão. A trava FOR UPDATE do fechamento (RN12 da 0055) passa a incluir essas cobranças. Contra uma remoção concorrente, vale a guarda da RN10 (que relê a cobrança com FOR UPDATE); contra uma baixa concorrente — que cria uma comissão nova —, vale a trava do registro da RN9.

  • RN16 — Listas e detalhe. Os critérios de linha passam a incluir a comissão:

  • list_by_month (RN20 da 0055): uma linha por imobiliária com membro na competência, ou com comissão na competência (RN10), ou com registro;
  • list_by_agency (RN21 da 0055): uma linha por competência com membro, ou com comissão, ou com registro;
  • as linhas das duas ganham commission, balance, paid_to_agency, overpaid_to_agency e members continua existindo (0 no acerto só de comissão);
  • o detalhe ganha commissions: uma linha por cobrança de comissão (RN10), com billing_id, guarantee_id, guarantee_number, renter_name, paid_on, setup_paid, commission_tax_percent, commission_tax_amount, commission_percent e commission_amount, ordenadas por inquilino, até 500 com commissions_truncated — o mesmo teto declarado da RN20 da 0055; e as movimentações ganham direction.

O predicado de existência muda nos dois pontos que hoje respondem 404: _carregar (:270-273, leituras do detalhe e das cobranças) e _registro_para_escrever (:434-439, toda escrita, inclusive close e TO_AGENCY). Hoje é "registro ou membro"; passa a "registro ou membro ou comissão (RN10)". No caminho normal a baixa já criou o registro (RN9), e o predicado novo não muda nada; ele existe para que a existência do acerto dependa do que ele contém, e não de um efeito colateral da baixa — o mesmo critério das listas. O registro continua nascendo na primeira escrita, como hoje, ou na baixa que gera comissão.

O endpoint da regra, a tela e a migration

  • RN17 — A imobiliária e o SaaS admin.
  • O ADMIN_GLOBAL não grava nenhum dos seis campos da RN2: presente no corpo de POST/PATCH /partner-agencies, 403 PLATFORM_ADMIN_CANNOT_SET_INITIAL_FEE, no desenho de garantir_que_o_saas_admin_nao_define_o_recolhimento (partner_agency_service.py:37-60) — recusa o campo, não o PATCH. "Presente" aqui é model_fields_set, não exclude_none: um null mandado por ele também é recusado — senão ele anularia a comissão própria de uma imobiliária pelo caminho do null da RN2;
  • a criação é POST /api/v1/onboarding/partner-agencies (routers/onboarding.py, OnboardingService.create_partner_agency, onboarding_service.py:381), não um POST em /partner-agencies. Ela recebe os seis campos com as mesmas regras (RN2). Não precisa de recusa própria nem de variante de resposta: o ADMIN_GLOBAL já para em _get_tenant com 422 TENANT_REQUIRED (onboarding_service.py:640-645), e quem cria é sempre a garantidora, que recebe a resposta inteira. As recusas e a variante desta regra valem no PATCH, nas leituras e no restore — é por eles que o CA23 se testa;
  • e não os recebe: a resposta da imobiliária para o ADMIN_GLOBAL omite os seis, numa variante como PartnerAgencyAgencyResponse (apps/api/app/schemas/partner_agency.py:141) faz com collects_monthly. É decisão nova (Parte II, 7.1), não precedente: o detalhe hoje mostra o recolhimento para ele;
  • a própria imobiliária recebe os seis em leitura (decisão do usuário, 2026-10-11) — a variante dela continua omitindo só collects_monthly. Gravar ela já não grava: não tem partner_agencies:update.

  • RN18 — GET /api/v1/settings/initial-fee-rule devolve a regra em vigor (RN3), para o formulário da análise calcular e decidir se mostra o bloco antes de existir análise. Numa análise que já existe (edição, pendência), o formulário usa o rule da RN19, que vem da imobiliária da análise mesmo removida — e não chama este endpoint, que responde 404 para imobiliária removida porque análise nova não nasce nela. Parâmetro opcional partner_agency_id.

  • Usuário de imobiliária: o parâmetro é ignorado se igual ao dele e recusado com 404 se diferente; ausente, vale o dele. A regra que ele recebe é sempre a da própria imobiliária — vínculo derivado (RN10/RN12 da 0034).
  • Usuário da garantidora: o parâmetro é resolvido no tenant (PartnerAgencyRepository com o filtro de tenant); fora dele ou removido, 404 PARTNER_AGENCY_NOT_FOUND; ausente, devolve a regra padrão (origem = TENANT).
  • ADMIN_GLOBAL: 403 PLATFORM_ADMIN_NO_TENANT — não há garantidora de quem ler a regra; é o mesmo motivo pelo qual ele não opera a análise.

  • RN19 — O que a análise devolve. CreditAnalysisResponse ganha initial_fee: informed_value, informed_by_name, informed_by_agency_name (nulo quando quem informou é da garantidora), rule (a regra em vigor, RN3, lida na hora, com o commission_percent em vigor da imobiliária — que, para a imobiliária, é o dela), rule_value (o valor da regra em vigor sobre o pacote e o prazo da análise, lido na hora) e snapshot_at (RN6). É o que a tela precisa para as origens da Parte II — "Informado na análise por Fulana (Solar) · pela regra seriam R$ 750,00", "Pela regra da Solar, calculada em 12/11". Visível para quem já lê a análise (credit_analyses:read), no escopo que já vale para ela.

  • O nome de quem informou vai para todo perfil que lê a análise, inclusive a imobiliária — e é exceção consciente à regra da 0036 que mostra changed_by_name no log de mudanças só a quem tem decisions:read (credit_analysis_service.py:830). A origem do valor é parte do que a Parte II promete à imobiliária ("por Beltrano (garantidora)"), e o detalhe já lhe mostra nomes da garantidora (created_by_user, approved_by_user, :1012-1013). O log de mudanças segue a regra da 0036.
  • Só no detalhe. GET /credit-analyses usa o mesmo modelo (apps/api/app/api/v1/routers/credit_analyses.py:37) mas monta as linhas fora do _build_response; ali initial_fee vem nulo, sem cálculo por linha — a lista não mostra taxa, e calcular a regra por linha seria custo sem tela.
  • A regra é lida com as TenantSettings da garantidora dona da análise (credit_analysis.tenant_id), nunca as de quem chama — o mesmo cuidado que _apply_financial_snapshot já registra (credit_analysis_service.py:937-940).
  • Para o ADMIN_GLOBAL, rule vem nulo. Ele lê análise de qualquer garantidora (tenant_scope nulo), e rule com source = AGENCY é a regra própria inteira — o dado que a RN17 esconde dele. rule_value e o valor informado ficam: são a taxa que o resumo financeiro já mostra.

  • RN20 — A linha Taxa de adesão aparece com zero quando há valor informado zero (Parte II, 7.3). Hoje FinancialSummaryPanel.tsx:210 esconde a seção Pagamento na entrada inteira quando a taxa e a primeira parcela são zero, e :213 esconde a linha com zero. Passa a ser: com valor informado (inclusive zero), a seção e a linha aparecem; sem valor informado, o comportamento de hoje. A condição depende só de informed_value, que todo perfil recebe — não da rule, que vem nula para o ADMIN_GLOBAL.

  • RN21 — Comissão de baixa feita pelo fechamento: nenhuma. O fechamento baixa membros de canal AGENCY_PIX, que são mensalidade, e grava setup_paid = 0 (agency_settlement_service.py:740); a RN8 não dispara.

  • RN21a — Não há estorno de baixa. O caso que a Parte II, 7.3, deixou para cá — adesão estornada depois de fechado o mês da comissão — não existe no código: BillingUpdate só aceita due_date e notes, e a única operação que desfaz uma baixa paga é a remoção (spec 0049), coberta pela RN10 (recusada com o mês da comissão fechado).

  • RN22 — Textos. As renomeações e os textos novos da Parte II — Taxa de adesão no lugar de Pagamento inicial, Taxa setup, Taxa de setup e Taxa setup aprovada; os rótulos do seletor; Mensalidades e multas, Saldo, Movimentações; a tabela de voz da imobiliária; os motivos, o diálogo de fechar, o de remover e os estados vazios — são do apps/web e não mudam contrato. Também é do apps/web o comportamento do bloco no formulário que não depende do servidor: acompanhar o pacote até ser editado, Usar o valor da regra, e descartar o valor digitado quando a garantidora troca a imobiliária antes da primeira gravação (Parte II, 7.3). Os nomes de campo da API ficam.

  • RN23 — Migration r2f4n6b8j0m3_taxa_de_adesao_e_comissao, down_revision = "o3v5y7a9c1e3" (a cabeça de hoje):

  • upgrade: tenant_settings + allow_manual_initial_fee (server_default false), commission_percent (server_default 100.00), commission_tax_percent (server_default 0.00); partner_agencies + initial_fee_custom (server_default false) e os cinco anuláveis; credit_analyses + informed_initial_fee, informed_initial_fee_by (FK), financial_snapshot_at; guarantees + initial_fee_source (anulável); billings + os cinco da RN8 e commission_settlement_id (FK, com índice), mais um índice em (tenant_id, commission_reference_month); o enum transfer_direction e agency_settlement_transfers.direction (server_default 'FROM_AGENCY').
  • Só acrescenta. Toda coluna nova é anulável ou tem server_default, e nenhum dado existente é reescrito: as garantias e cobranças de hoje ficam sem comissão (RN8 vale para baixas feitas depois do deploy), as transferências de hoje viram FROM_AGENCY, e toda imobiliária segue a regra padrão. É o que a produção com cliente pede (TXC em operação desde 2026-10) — nada para conferir antes de migrar além de a migration rodar.
  • Comissão de adesão paga antes do deploy não é criada. É a mesma linha da seção 5 (sem recálculo retroativo): adesões já baixadas seguem sem comissão no produto, e a planilha cobre o que ficou para trás.
  • downgrade: apaga primeiro as movimentações TO_AGENCY — o código anterior não conhece o sentido e leria um pagamento de comissão como transferência recebida da imobiliária, falseando o acerto —, depois remove índices, FKs, colunas e o enum, na ordem inversa. Perde a regra própria das imobiliárias, os valores informados e as comissões congeladas — é o caminho de volta de um deploy ruim, e o deploy da 0028 faz backup antes de migrar.

10. Autorização e isolamento

Nenhuma permissão nova. Toda escrita e leitura desta spec cabe em permissões que já existem e já governam a mesma superfície: a regra padrão é PATCH /settings, a regra própria é o cadastro da imobiliária, o valor informado é a análise, a comissão é a baixa da cobrança e o acerto. Uma permissão nova para "editar taxa" separaria o que hoje não se separa — quem edita o cadastro edita a cláusula de recolhimento, que é da mesma natureza.

10.1 Matriz de papéis

Papel Pode Não pode
ADMIN_GLOBAL ler e editar o resto do cadastro da imobiliária, como hoje gravar ou ler a regra própria e a comissão da imobiliária (RN17: 403 ao gravar, campos omitidos ao ler); ler a regra em vigor (RN18: 403); operar acerto (já recusado hoje, _garantir_que_nao_e_o_saas_admin); PATCH /settings (é require_system_role("ADMIN_TENANT"))
ADMIN_TENANT editar a regra padrão; criar/editar a regra própria e a comissão de qualquer imobiliária do próprio tenant; informar o valor na análise; aprovar com taxa; registrar pagamento de comissão; fechar e reabrir acerto ler ou editar regra de imobiliária de outro tenant (404); registrar pagamento de comissão com saldo parcial, acima do que falta, ou sem nada a pagar (422, RN13)
ANALYST ler a regra padrão e a em vigor; informar o valor na análise que edita (credit_analyses:update); aprovar com taxa editar a regra padrão (403, sem o papel); editar a regra própria (403, sem partner_agencies:update); registrar movimentação (403, sem agency_settlements:manage)
OPERATOR ler as regras; informar o valor ao criar e editar análise editar qualquer regra (403); registrar movimentação (403)
VIEWER ler as regras, a origem do valor na análise e a comissão no acerto informar valor (403, sem credit_analyses:update); editar regra (403); registrar movimentação (403)
AGENCY_USER ler a regra em vigor da própria imobiliária e a própria regra em leitura; informar o valor na análise da própria imobiliária; ver a comissão no acerto da própria imobiliária ler a regra de outra imobiliária (404, RN18); ver a análise ou o acerto de outra imobiliária (404, como hoje); editar regra (403); registrar movimentação (403); informar valor quando a regra não permite (422, RN5)
AGENCY_MANAGER o mesmo do AGENCY_USER, mais declarar (como hoje) o mesmo negativo do AGENCY_USER; registrar pagamento de comissão (403, sem agency_settlements:manage)

10.2 Permissões

Endpoint Permissão Muda nesta spec
GET /api/v1/settings tenant_settings:read resposta ganha os campos da RN1; commission_percent nulo para a imobiliária
PATCH /api/v1/settings require_system_role("ADMIN_TENANT") corpo ganha os campos da RN1
GET /api/v1/settings/initial-fee-rule tenant_settings:read novo (RN18)
POST /api/v1/onboarding/partner-agencies partner_agencies:create corpo ganha os campos da RN2 (o SaaS admin já é barrado por TENANT_REQUIRED, RN17)
PATCH /api/v1/partner-agencies/{id} partner_agencies:update idem
GET /api/v1/partner-agencies, GET …/{id} partner_agencies:list / :read resposta ganha os campos da RN2, omitidos para o SaaS admin
POST /api/v1/credit-analyses credit_analyses:create corpo ganha informed_initial_fee (RN4, RN5)
PATCH /api/v1/credit-analyses/{id} credit_analyses:update idem
GET /api/v1/credit-analyses/{id} credit_analyses:read resposta ganha initial_fee (RN19)
POST /api/v1/guarantees (emissão) guarantees:create precedência da RN7; pricing_snapshot com a regra em vigor
POST /api/v1/guarantees/simulate guarantees:read (+ guarantees:create com análise, como hoje) idem, na resposta
POST /api/v1/guarantees/{id}/billings/{billing_id}/pay billings:update grava a comissão (RN8, RN9) — sem mudar o contrato
DELETE /api/v1/billings/{id}, POST /api/v1/billings/{id}/restore billings:delete / billings:restore guarda da RN10 com commission_settlement_id; a restauração refaz a RN9
PATCH /api/v1/guarantees/{id}/billings/{billing_id} billings:update guarda da RN10
POST /api/v1/partner-agencies/{id}/restore partner_agencies:restore resposta pelo seletor de variante (RN17)
GET …/{agency}/{month}/billings, PUT …/billings/{billing_id}/declaration, POST …/declare-remaining, POST …/invoice agency_settlements:read / :declare predicado de existência da RN16
GET /api/v1/agency-settlements, …/by-agency/{id}, …/{agency}/{month} agency_settlements:read respostas ganham os campos da RN16
POST /api/v1/agency-settlements/{agency}/{month}/transfers agency_settlements:manage corpo ganha direction (RN13)
DELETE …/transfers/{id}, POST …/close, POST …/reopen agency_settlements:manage regras da RN12, RN13 e RN15

O seed RBAC (rbac_seed.py) não muda. O seed de deploy roda igual, como em todo /ship.

10.3 Isolamento por garantidora (tenant)

  • FKs do corpo e onde se resolvem:
  • partner_agency_id (query de GET /settings/initial-fee-rule): no PartnerAgencyRepository, filtrado pelo tenant_id do solicitante, antes de ler a regra; fora → 404 (RN18);
  • nenhuma FK nova nos corpos de escrita: informed_initial_fee_by é derivado do usuário (RN4); a imobiliária da análise continua vindo do imóvel, resolvido como hoje; commission_settlement_id só é gravado pelo fechamento, a partir do acerto já resolvido no escopo (_resolver_imobiliaria, agency_settlement_service.py:118).
  • Leituras novas — a regra em vigor, as comissões do acerto, a origem do valor na análise — passam pelos repositórios que já filtram por tenant_id (a cobrança, a garantia, a imobiliária, a análise). Nenhuma consulta nova sem o filtro; a das comissões por competência usa o índice (tenant_id, commission_reference_month) (RN23).
  • Outro tenant → 404, nunca 403: imobiliária, análise e acerto de outra garantidora respondem 404 como hoje.

10.4 Isolamento por imobiliária

  • A regra em vigor: o usuário de imobiliária só lê a da própria (RN18), e a de outra responde 404. O vínculo é current_user.partner_agency_id, nunca o parâmetro.
  • O cadastro: ele lê a própria imobiliária como hoje (partner_agencies:read no escopo dele), agora com os campos da regra; outra imobiliária, 404, como hoje.
  • A análise: o informed_initial_fee só se grava na análise que ele já alcança (agency_scope); a de outra imobiliária responde 404 antes de chegar à RN5.
  • O acerto e a comissão: as comissões listadas são as das garantias da imobiliária do acerto (RN10), e o acerto de outra imobiliária responde 404 como hoje (_resolver_imobiliaria). A linha de comissão traz o nome do inquilino da garantia — dado que a imobiliária já vê, porque a garantia é da carteira dela.
  • Usuário da garantidora continua vendo tudo do tenant, inclusive a carteira própria (análises sem imobiliária, que seguem a regra padrão e nunca têm comissão).

10.5 Plataforma × cliente (SaaS admin)

  • O que o ADMIN_GLOBAL recebe: nos endpoints de imobiliária, as listas e o detalhe continuam cross-tenant como hoje (cadastro é dado que ele já lê), sem os campos da regra e da comissão (RN17). Na regra em vigor, 403 (RN18). No acerto, 403, como hoje. Na análise, initial_fee.rule vem nulo para ele (RN19); o valor informado e rule_value ficam — são a taxa que o resumo financeiro já mostra. Na garantia e no simulate, os quatro campos da regra vêm nulos quando a origem é a imobiliária (RN7). As cobranças não ganham no schema (BillingResponse, BillingListItem) os campos da RN8: a comissão só sai pelo acerto, que ele não alcança.
  • A tela do SaaS admin (/app/admin/*) não muda. O formulário de imobiliária, aberto por ele no app da garantidora, não mostra o bloco (Parte II, 7.1).
  • Nenhuma tela da garantidora aceita tenant_id de fora.

11. Impacto por app

apps/api

  • Modelos: guarantee.py (initial_fee_source, RN7), tenant_settings.py (RN1), partner_agency.py (RN2), credit_analysis.py (RN4, RN6), billing.py (RN8, RN15), agency_settlement.py (RN13: TransferDirection, direction).
  • Migration: alembic/versions/r2f4n6b8j0m3_taxa_de_adesao_e_comissao.py (RN23).
  • Domínio: novo app/domain/taxa_de_adesao.py (RN3 e o cálculo da comissão da RN8, funções puras); app/domain/acerto_com_imobiliaria.py (RN11, RN12).
  • Schemas: schemas/tenant_settings.py (TenantSettingsUpdate, TenantSettingsResponse), schemas/partner_agency.py (create, update, response e a variante sem a regra para o SaaS admin), schemas/credit_analysis.py (create, update, CreditAnalysisResponse.initial_fee), schemas/guarantee.py (initial_fee_source; o tipo anulável no simulate), schemas/agency_settlement.py (TransferRequest, TransferResponse, as linhas e o detalhe), e um RegraDeAdesaoResponse novo em schemas/tenant_settings.py.
  • Services: onboarding_service.py (create_partner_agency: os campos e a validação da RN2 — sem recusa nem variante, RN17); pricing.py (calculate_upfront_payment passa a receber tipo, valor e base da regra em vigor, RN3); tenant_settings_service.py (RN1, RN18); partner_agency_service.py (RN2, RN17); credit_analysis_service.py (RN4–RN6, RN19); guarantee_service.py (RN7); billing_service.py (RN8–RN10); agency_settlement_service.py (RN11–RN16).
  • Routers: routers/tenant_settings.py (endpoint novo da RN18, com Swagger completo e CommitRoute já no router); routers/onboarding.py, routers/partner_agencies.py, routers/credit_analyses.py, routers/guarantees.py, routers/billing.py e routers/agency_settlements.py só documentam no Swagger os campos e erros novos.
  • Testes (TDD, em apps/api/tests/):
  • test_taxa_de_adesao_regra.py — RN3 pura: padrão, própria, NONE sem edição, comissão nula seguindo o padrão;
  • test_comissao_calculo.py — RN8 pura, com os dois exemplos da Parte II e o arredondamento de 122,475;
  • test_acerto_contas.py (existente, ampliado) — RN11 e RN12: os dois exemplos de trabalho da Parte II, recebido a mais medido contra devido, pago a mais, acerto só de comissão READY com falta zero, nada_a_acertar com comissão;
  • test_tenant_settings_taxa.py — RN1: commission_tax_percent: null → 422; faixas;
  • test_partner_agency_taxa.py — RN2 e RN17: validação, custom=false anula, SaaS admin 403 ao gravar e campos omitidos ao ler, a imobiliária lê os seus;
  • test_credit_analysis_taxa_informada.py — RN4–RN6: aceito com permissão, 422 sem, null explícito limpa no update apesar do exclude_none, sobrevive à mudança de regra, _by vem do usuário mesmo se o corpo tentar mandá-lo (o schema não tem o campo);
  • test_guarantee_precedencia_taxa.py — RN7, os três passos;
  • test_billing_comissao.py — RN8–RN10: congela na baixa, não muda com percentual novo, competência do mês fechado vai para o corrente, remoção tira, guarda do fechado;
  • test_agency_settlement_comissao.py — RN13–RN16: os quatro erros do TO_AGENCY, FROM_AGENCY sem direction igual a hoje, fechar grava e reabrir anula commission_settlement_id, listas incluem o acerto só de comissão;
  • negativos de acesso: AGENCY_USER da B → 404 na regra da A (RN18); ADMIN_GLOBAL → 403 na regra em vigor; ANALYST → 403 em TO_AGENCY; AGENCY_MANAGER → 403 em TO_AGENCY; ADMIN_TENANT do tenant 2 → 404 na imobiliária do tenant 1.

apps/web

Pelo inventário de telas (apps/web/CLAUDE.md):

  • Parâmetros (pages/settings/index.tsx): o bloco Taxa de adesão (Parte II, 7.1); campos novos, linha de exemplo, textos e dicas.
  • Imobiliárias (pages/agencies/form.tsx, show.tsx, api.ts): o bloco Taxa de adesão e comissão e a seção em leitura; escondidos do SaaS admin, e o formulário omite os seis campos do corpo quando quem salva é ele — como já faz com collects_monthly (apps/web/src/pages/agencies/form.tsx:58-59) —, senão a RN17, que recusa até null, daria 403 em toda edição dele.
  • Minha Imobiliária (pages/my-agency/index.tsx, RepassesDaImobiliaria.tsx): a seção em leitura na aba Dados; a lista de repasses com as colunas e o cartão na voz dela.
  • Análise de Crédito (pages/credit-analyses/CreditAnalysisForm.tsx, show.tsx, api.ts, components/DecisionPanel.tsx, components/FinancialSummaryPanel.tsx, components/taxaDeAdesao.ts, components/GuaranteeCreateDialog.tsx): o bloco Taxa de adesão no formulário (chamando a RN18), a origem no detalhe e no parecer, a precedência da RN7 na sugestão, a linha com zero (RN20). taxaDeAdesao.ts passa a receber a regra em vigor, não TenantSettings.
  • Garantias (pages/guarantees/components/GuaranteeSections.tsx): o rótulo.
  • Cobranças (pages/billings/components/RepassesDaCompetencia.tsx): colunas, cartão e estado vazio.
  • Acerto — fora do menu, /repasses/:partnerAgencyId/:referenceMonth (pages/agency-settlements/show.tsx, api.ts, components/PaineisDoAcerto.tsx, components/TransferenciaDialog.tsx, um componente novo components/ComissaoDoAcerto.tsx): a seção Comissão, a tabela de estados, as movimentações com sentido, o diálogo de pagar comissão, os motivos e a voz da imobiliária.
  • Testes unitários (*.test.tsx ao lado, spec 0047): settings/index.test.tsx, agencies/form.test.tsx, credit-analyses/components/taxaDeAdesao.test.ts (novo), credit-analyses/CreditAnalysisForm.test.tsx (bloco aparece/some pela regra, acompanha o pacote até ser editado, Usar o valor da regra), agency-settlements/show.test.tsx (cada linha da tabela de estados, nas duas vozes), my-agency e billings/components/RepassesDaCompetencia (colunas e cartão).
  • data-testid novos: settings-commission-percent-input, settings-commission-tax-input, settings-allow-manual-fee-switch, settings-initial-fee-example; agency-fee-rule-follow-radio, agency-fee-rule-custom-radio, agency-fee-type-toggle, agency-fee-value-input, agency-fee-base-select, agency-allow-manual-fee-switch, agency-commission-follow-radio, agency-commission-custom-radio, agency-commission-percent-input, agency-fee-rule-readonly; my-agency-fee-rule-readonly; credit-analysis-initial-fee-panel, credit-analysis-initial-fee-input, credit-analysis-initial-fee-origin, credit-analysis-initial-fee-use-rule, credit-analysis-initial-fee-retry; financial-summary-initial-fee-origin; decision-approve-setup-input (o de hoje, mantido); settlement-commission-section, settlement-commission-row-{billingId}, settlement-balance, settlement-paid-to-agency, settlement-commission-pay, settlement-movement-{id}, settlement-commission-pay-dialog.
  • Menu e rota: nada novo no menu; a rota do acerto já exige agency_settlements:read (App.tsx), e quem digita a URL de outra imobiliária recebe "Acerto não encontrado" como hoje.
  • Responsividade e PWA: como na Parte II, 7.2. Sem asset novo; o PWA recarrega o bundle como em toda mudança de tela.

apps/e2e

  • Specs novos:
  • taxa-de-adesao-regra.spec.ts — garantidora configura a regra padrão e uma regra própria com edição; a imobiliária cria análise, o bloco aparece com o valor da regra, ela edita, o resumo mostra o editado; o analista aprova sem tocar e a garantia sai com o editado; em 375px, o bloco sem rolagem horizontal;
  • comissao-no-acerto.spec.ts — baixa da entrada de uma garantia da imobiliária; o acerto do mês mostra a comissão; a garantidora registra o pagamento com saldo negativo e fecha; a imobiliária lê na voz dela; em 375px, o cartão e o painel.
  • Specs existentes a rodar (telas tocadas): agencies.spec.ts, repasses-por-imobiliaria.spec.ts, credit-analyses.spec.ts, credit-analyses-mobile.spec.ts, emissao-de-garantia-na-analise.spec.ts, settings-mobile-snackbar.spec.ts, isolamento-imobiliaria.spec.ts.
  • Seeds: os specs novos criam a regra própria na imobiliária que o seed já tem (tests/fixtures/seeds/agencies) e desfazem no finally — regra e percentuais são do tenant compartilhado do e2e (a lição da spec 0025 no CLAUDE.md da raiz).

12. Contrato de API

GET /api/v1/settings/initial-fee-rule?partner_agency_id={uuid} — tenant_settings:read.

{
  "source": "AGENCY",
  "partner_agency_id": "8b1c…",
  "partner_agency_name": "Solar",
  "initial_fee_type": "PERCENTAGE",
  "initial_fee_value": "1.00",
  "initial_fee_base": "LMI",
  "allow_manual_initial_fee": true,
  "commission_percent": "80.00",
  "commission_tax_percent": "16.33",
  "lmi_months_multiplier": 30
}

Erros: 404 PARTNER_AGENCY_NOT_FOUND (outro tenant, removida, ou de outra imobiliária para usuário de imobiliária); 403 PLATFORM_ADMIN_NO_TENANT; 404 Configurações se o tenant não tem TenantSettings, como GET /settings já faz.

PATCH /api/v1/settings — ADMIN_TENANT. Campos novos, opcionais:

{ "allow_manual_initial_fee": true, "commission_percent": "100.00", "commission_tax_percent": "16.33" }

Erros novos: 422 COMMISSION_TAX_REQUIRED (commission_tax_percent: null); 422 de faixa nos dois percentuais, com o campo nomeado.

POST /api/v1/onboarding/partner-agencies e PATCH /api/v1/partner-agencies/{id} — partner_agencies:create / :update. Campos novos, opcionais (null presente grava nulo, RN2):

{
  "initial_fee_custom": true,
  "commission_percent": "80.00",
  "initial_fee_type": "PERCENTAGE",
  "allow_manual_initial_fee": true,
  "initial_fee_base": "LMI",
  "initial_fee_value": "1.00"
}

Resposta (nos dois, e nas leituras): os mesmos seis, para a garantidora e para a própria imobiliária; omitidos para o ADMIN_GLOBAL. Erros novos: 422 INITIAL_FEE_RULE_INCOMPLETE (campo nomeado); 403 PLATFORM_ADMIN_CANNOT_SET_INITIAL_FEE.

POST / PATCH /api/v1/credit-analyses[/{id}] — credit_analyses:create / :update. Campo novo, opcional e anulável: "informed_initial_fee": "500.00". Erros novos: 422 INITIAL_FEE_EDIT_NOT_ALLOWED; 422 de valor negativo, com o campo nomeado.

GET /api/v1/credit-analyses/{id} — credit_analyses:read. Campo novo:

"initial_fee": {
  "informed_value": "500.00",
  "informed_by_name": "Fulana",
  "informed_by_agency_name": "Solar",
  "rule": { "source": "AGENCY", "initial_fee_type": "PERCENTAGE", "initial_fee_value": "1.00",
            "initial_fee_base": "LMI", "allow_manual_initial_fee": true,
            "commission_percent": "80.00" },
  "rule_value": "750.00",
  "snapshot_at": "2026-11-12T14:03:00-03:00"
}

POST /api/v1/agency-settlements/{partner_agency_id}/{reference_month}/transfers — agency_settlements:manage. Campo novo, opcional: "direction": "TO_AGENCY" (padrão FROM_AGENCY); received_on é a data do movimento nos dois sentidos (RN13). TRANSFER_EXCEEDS_REMAINING passa a medir FROM_AGENCY contra devido − recebido. Erros novos: 422 RECEIVED_EXCEEDS_DUE_OPEN, COMMISSION_PAYMENT_BEFORE_DECLARATIONS, NOTHING_TO_PAY_AGENCY, COMMISSION_PAYMENT_EXCEEDS_REMAINING (RN13, nesta ordem).

Leituras do acerto — agency_settlements:read. Linhas (list_by_month, list_by_agency) e detalhe ganham:

{ "devido": "1000.00", "commission": "300.00", "balance": "700.00",
  "recebido": "1000.00", "paid_to_agency": "0.00", "falta": "-300.00",
  "overpaid": false, "overpaid_to_agency": false }

O detalhe ganha commissions (RN16) e commissions_truncated; cada transferência ganha direction. overpaid muda de definição (RN11) sem mudar de nome.

POST /api/v1/guarantees e POST /api/v1/guarantees/simulate — permissões de hoje. Sem campo novo no corpo; pricing_snapshot e a resposta do simulate ganham initial_fee_source e passam a trazer tipo, valor e base da regra em vigor (RN7).

POST …/close — SETTLEMENT_NOT_READY com o texto do primeiro motivo e details ampliado; NOTHING_TO_SETTLE com "Não há nada a acertar neste mês." (RN12).

DELETE /api/v1/billings/{id}, POST /api/v1/billings/{id}/restore e PATCH /api/v1/guarantees/{id}/billings/{billing_id} — 409 BILLING_IN_CLOSED_SETTLEMENT também quando a comissão da cobrança está num acerto fechado, com a mensagem da RN10 para esse caso.

Migration: r2f4n6b8j0m3_taxa_de_adesao_e_comissao — RN23, com o downgrade descrito lá.

13. Critérios de aceite

A regra

  • [x] CA1. PATCH /settings com commission_tax_percent: null responde 422 COMMISSION_TAX_REQUIRED; um tenant recém-migrado lê commission_tax_percent = 0.00, commission_percent = 100.00 e allow_manual_initial_fee = false (RN1, RN23).
  • [x] CA2. Imobiliária com initial_fee_custom = true, PERCENTAGE sem base → 422 INITIAL_FEE_RULE_INCOMPLETE; voltando a false, os quatro campos da taxa ficam nulos mesmo que o corpo os traga (RN2).
  • [x] CA3. regra_de_adesao devolve a regra da imobiliária com custom = true, a padrão com false, permite_editar = false com tipo NONE e chave ligada, e o percentual de comissão do padrão quando o da imobiliária é nulo (RN3).

O valor na análise

  • [x] CA4. Com a regra em vigor permitindo edição, criar a análise com informed_initial_fee = 500 grava o valor, informed_initial_fee_by = o usuário da requisição, e financial_initial_payment = 500 (RN4, RN5, RN6).
  • [x] CA5. Com a regra em vigor sem edição, o mesmo corpo responde 422 INITIAL_FEE_EDIT_NOT_ALLOWED, e null é aceito (RN5).
  • [x] CA6. Gravado o valor, a garantidora desliga a edição na regra; um PATCH da análise sem o campo mantém o valor; um PATCH com "informed_initial_fee": null anula o valor e o autor; um PATCH com valor não nulo responde 422 INITIAL_FEE_EDIT_NOT_ALLOWED (RN5).
  • [x] CA7. Emissão: com approved_initial_fee_value, ele vale; sem ele e com valor informado, o informado vale; sem os dois, a regra em vigor na emissão (RN7).
  • [x] CA8. O detalhe da análise traz initial_fee com o nome de quem informou, a imobiliária dele (nulo para quem é da garantidora), rule_value e snapshot_at (RN19); a linha Taxa de adesão aparece com R$ 0,00 informado (RN20).

A comissão

  • [x] CA9. Baixa de entrada com setup_paid = 500 numa garantia da imobiliária com comissão 80% e impostos 16,33% grava commission_tax_amount = 81.65 e commission_amount = 334.68; com 750, 122.48 e 502.02 (RN8).
  • [x] CA10. Depois da baixa, mudar os impostos para 10% não muda commission_amount daquela cobrança (RN8).
  • [x] CA11. Baixa com paid_on num mês cujo acerto da imobiliária está fechado grava commission_reference_month = o mês seguinte a ele, se aberto (RN9).
  • [x] CA12. Garantia sem imobiliária, ou setup_paid = 0: os cinco campos ficam nulos; comissão calculada zero não cria linha nem acerto (RN8, RN10).
  • [x] CA13. Remover a cobrança de uma comissão de acerto aberto a tira das contas; com o acerto fechado, remover responde BILLING_IN_CLOSED_SETTLEMENT (RN10).

O acerto

  • [x] CA14. Exemplo 1 da Parte II: devido 1.000, recebido 1.000, comissão 300 → saldo 700, falta −300, overpaid = false, situação OPEN; TO_AGENCY de 300 → falta 0, READY (RN11, RN12, RN13).
  • [x] CA15. Exemplo 2: devido 200, comissão 300, recebido 200 → falta −300 (RN11).
  • [x] CA16. Recebido 1.100 com devido 1.000 → overpaid = true, fechar recusa, e TO_AGENCY → 422 RECEIVED_EXCEEDS_DUE_OPEN; comissão de 300 paga e depois a cobrança dela removida → overpaid_to_agency = true, fechar recusa (RN11, RN12, RN13).
  • [x] CA17. TO_AGENCY com mensalidade não declarada → 422 COMMISSION_PAYMENT_BEFORE_DECLARATIONS; com falta ≥ 0 → 422 NOTHING_TO_PAY_AGENCY; acima de −falta → 422 COMMISSION_PAYMENT_EXCEEDS_REMAINING (RN13).
  • [x] CA18. POST …/transfers sem direction se comporta exatamente como hoje, e a transferência gravada sai FROM_AGENCY (RN13, RN23).
  • [x] CA19. Imobiliária que não recolhe, com uma comissão no mês: aparece na lista do mês e na lista dela, com members = 0; quitada a falta, READY; fechar grava commission_settlement_id, reabrir anula (RN12, RN15, RN16).
  • [x] CA20. Declarar uma correção que deixaria o devido abaixo do recebido continua recusada; uma comissão que deixa o saldo negativo não dispara a recusa (RN14).
  • [x] CA21. O fechamento de um acerto com membros AGENCY_PIX não cria comissão (RN21).
  • [x] CA21a. Com a comissão de 300 já gerada no mês e devido 1.000, o ADMIN_TENANT registra a transferência da imobiliária, FROM_AGENCY de 1.000, — aceito; falta −300. FROM_AGENCY de 1.001 → 422 TRANSFER_EXCEEDS_REMAINING (RN13).
  • [x] CA21b. Imobiliária que não recolhe: a baixa da entrada cria o registro do mês (RN9); o detalhe responde 200 com a comissão e sem membros; o TO_AGENCY é aceito; uma baixa de adesão R$ 0,00 não cria registro nem linha na lista (RN8, RN16).
  • [x] CA21e. Cobrança com comissão em outubro removida com o acerto aberto; outubro fecha; a restauração grava commission_reference_month = novembro (RN10).
  • [x] CA21f. Reabrir um acerto com comissão anula commission_settlement_id e mantém status, setup_paid e os cinco campos da RN8 da cobrança da entrada (RN15).
  • [x] CA21j. Imobiliária informa R$ 500; o analista salva a pendência reenviando "informed_initial_fee": "500.00" → informed_initial_fee_by continua a da imobiliária e nada entra no log; com a regra que deixou de permitir edição, reenviar o mesmo valor não dá 422 (RN5).
  • [x] CA21k. GET /guarantees como ADMIN_GLOBAL traz os quatro campos nulos nas garantias com initial_fee_source = AGENCY; simulate de análise com regra própria FIXED responde 200 com initial_fee_base nulo (RN7).
  • [x] CA21g. PATCH da imobiliária com regra própria completa enviando só initial_fee_value é aceito e mantém allow_manual_initial_fee (RN2).
  • [x] CA21h. AGENCY_USER lê GET /settings com commission_percent nulo; o ADMIN_TENANT lê o valor (RN1).
  • [x] CA21i. Emissão de garantia de imobiliária com regra própria grava initial_fee_source = AGENCY e o tipo, o valor e a base dela; o ADMIN_GLOBAL lê essa garantia com os quatro nulos (RN7).
  • [x] CA21c. Baixa com paid_on em outubro, com outubro e novembro fechados para a imobiliária: commission_reference_month = dezembro (RN9).
  • [x] CA21d. close com falta −300 responde SETTLEMENT_NOT_READY com "faltam R$ 300,00 a pagar à imobiliária", não "o recebido passou do devido" (RN12).

Autorização e isolamento

  • [x] CA22. AGENCY_USER da imobiliária B pede GET /settings/initial-fee-rule? partner_agency_id=<A> → 404; sem o parâmetro, recebe a regra da B (RN18).
  • [x] CA23. ADMIN_GLOBAL → 403 PLATFORM_ADMIN_NO_TENANT na regra em vigor; 403 PLATFORM_ADMIN_CANNOT_SET_INITIAL_FEE ao mandar commission_percent no PATCH da imobiliária; a resposta do cadastro para ele não traz os seis campos (RN17, RN18).
  • [x] CA24. ADMIN_TENANT do tenant 2 pede a regra em vigor de uma imobiliária do tenant 1 → 404 (RN18).
  • [x] CA25. ANALYST e AGENCY_MANAGER → 403 em POST …/transfers com TO_AGENCY (10.1).
  • [x] CA26. OPERATOR e ANALYST → 403 em PATCH /partner-agencies/{id} com commission_percent (10.1); VIEWER → 403 em PATCH /credit-analyses/{id} com informed_initial_fee.
  • [x] CA27. O AGENCY_USER lê a própria imobiliária com os seis campos da regra (RN17).
  • [x] CA27a. ADMIN_GLOBAL lê uma análise de imobiliária com regra própria: initial_fee.rule vem nulo; ADMIN_GLOBAL manda "commission_percent": null no PATCH da imobiliária → 403 PLATFORM_ADMIN_CANNOT_SET_INITIAL_FEE (RN17, RN19).
  • [x] CA27b. ADMIN_TENANT manda "commission_percent": null numa imobiliária com 80% → grava nulo, e a regra em vigor passa a devolver o percentual do padrão (RN2, RN3).
  • [x] CA27c. PATCH da análise com "informed_initial_fee": null refaz financial_initial_payment pela regra e registra a alteração no log de mudanças (RN6).

A tela (Parte II)

  • [x] CA28. Parâmetros: o bloco se chama Taxa de adesão, com a descrição, os rótulos e a linha de exemplo da Parte II, 7.1; o campo de impostos apagado mostra a recusa no campo (RN1, RN22).
  • [x] CA29. Formulário da análise: com a regra permitindo, o bloco aparece abaixo de Locação e renda com o valor da regra e a origem; acompanha o pacote até ser editado; editado, mostra Valor alterado e Usar o valor da regra; sem a regra permitindo, não aparece; falha da regra mostra Tentar de novo no resumo (Parte II, 7.1 e 7.3).
  • [x] CA30. Acerto: cada linha da tabela de estados da Parte II, 7.1, mostra o painel, o botão e o motivo descritos, na voz da garantidora e na da imobiliária (show.test.tsx).
  • [x] CA31. Em 375px, Parâmetros, o formulário da imobiliária, o bloco da análise e o acerto com comissão não têm rolagem horizontal (scrollWidth − clientWidth = 0).

14. Plano de implementação

Ordem obrigatória no monorepo: api → web → e2e.

  • [x] apps/api — domínio puro primeiro: taxa_de_adesao.py e as contas novas de acerto_com_imobiliaria.py, com os testes (CA3, CA9, CA14–CA16)
  • [x] apps/api — modelos e a migration r2f4n6b8j0m3 (upgrade e downgrade exercitados)
  • [x] apps/api — schemas → services → routers, na ordem: settings (RN1, RN18), imobiliária (RN2, RN17), análise (RN4–RN6, RN19), garantia (RN7), cobrança (RN8–RN10), acerto (RN11–RN16)
  • [x] apps/api — docstrings + Swagger (summary, description, response_description) no endpoint novo e nos campos novos
  • [x] apps/api — testes de acesso negado (CA22–CA27)
  • [x] apps/web — taxaDeAdesao.ts sobre a regra em vigor; Parâmetros; imobiliária e Minha Imobiliária; formulário, detalhe e parecer da análise; garantia; acerto e listas — com data-testid, desktop e 375px no mesmo passo, e os *.test.tsx
  • [x] apps/e2e — os dois specs novos, com finally desfazendo a regra
  • [x] make test (vitest do web, pytest pelo container) e make lint sem apontamento novo nos arquivos tocados
  • [x] Validado no dev-remote em desktop e 375px

15. Decisões em aberto

Nenhuma. As decisões que a entrevista e as revisões levantaram foram tomadas pelo usuário em 2026-10-11 e estão citadas onde valem: a origem e o Termo (seção 1), a comissão no acerto compensando os dois sentidos (RN11), a nota fiscal como na 0055 (Parte II, 7.1), a comissão congelada no pagamento (RN8), os impostos em 0% e nunca vazios (RN1), e a imobiliária vendo a própria regra (RN17).

16. Revisão do detalhamento

Preenchido pelo laço do /spec new, rubrica .claude/rubrics/detalhamento.md. Registrar todas as rodadas, inclusive as reprovadas.

Rodada Critical Important Minor Nota Veredicto
1 0 7 5 6 Reprovada — o código contradizendo o "como hoje": teto da transferência medido contra a falta nova, 404 do acerto só de comissão, null descartado por exclude_none (imobiliária, settings, análise), corrida baixa × fechamento e mês corrente fechado, criação de imobiliária em outro endpoint, emissão/simulate/remoção fora da 10.2, regra própria exposta ao SaaS admin pela análise
2 0 7 4 7 Reprovada — coluna inexistente para initial_fee_source; regra própria exposta ao SaaS admin pela garantia; comissão zero criando acerto vazio; teto do TO_AGENCY incluindo excesso recebido; comissão órfã ao restaurar depois do fechamento; semântica do PATCH da imobiliária; comissão padrão visível à imobiliária (levado ao usuário: nulo para ela)
3 0 3 5 7 Reprovada — correções da rodada 2 fecham entre si (sem ciclo de trava, teto do TO_AGENCY se sustenta); sobraram: regra própria exposta ao SaaS admin pela lista de garantias, campos não anuláveis no simulate e base nula no snapshot, reenvio do valor informado pelo formulário trocando o autor ou dando 422
4 0 1 7 8 Aprovada — o Important (validador de schema não produziria o código prometido) e os sete Minor foram corrigidos depois da aprovação, sem nova rodada: a recusa do null foi para o service, e o resto é citação, exemplo de contrato e frase de escopo

Resultado: aprovada na rodada 4. Nenhum achado ficou aberto: o Important e os sete Minor da rodada 4 foram tratados depois da aprovação, sem nova rodada.

Riscos aceitos: nenhum.

17. Revisão da implementação

Preenchido pelo /spec implement, rubrica .claude/rubrics/implementacao.md. As revisões de fase (api, web, e2e) vão nas primeiras linhas; a da branch inteira, na última.

Rodada Escopo Critical Important Minor Nota Veredicto
1 apps/api 1 0 4 7 Reprovada — os mascaramentos do SaaS admin na garantia e no rule da análise sem teste. Corrigido: negativos escritos, com red-green (revertida a regra, vermelho; restaurada, verde); Minors tratados (Swagger de garantia e cobrança, RN23 com o DELETE do downgrade, testes de CA8 e do pago a mais pelo endpoint) ou anotados para a revisão da branch (resolução da RN18 e do reopen fora do repositório; teste da baixa de R$ 0,00)
2 apps/web 0 0 8 8,5 Aprovada — Minors: corrigidos a escrita divergente da base mensal ("do Pacote" nas duas telas) e o tolerate403/encodeURIComponent nas leituras da regra do cadastro; ficam para a revisão da branch o aviso de comissão congelada no cadastro como legenda fixa (o salvar navega para a lista), formatadores e tipo da regra duplicados, CampoBRL paralelo ao CurrencyTextField, multiplicador do LMI vindo de GET /settings numa análise existente, o resumo com a taxa da imobiliária anterior durante a troca, o fechar do aviso de Parâmetros sem data-testid próprio, e o botão por linha no laço da tabela de estados
3 branch 0 1 6 8 Aprovada — o Important era [SPEC INCOMPLETA]: o formulário calculava pela regra padrão com o seletor vazio e um imóvel de imobiliária escolhido, enquanto o servidor grava pela imobiliária do imóvel. Levado ao usuário, que decidiu pela imobiliária do imóvel (7.3 reescrita) e, no outro [SPEC INCOMPLETA], por apontar o pago a mais no mês só de comissão (RN11). Os dois implementados com red-green; dos Minors e da triagem, entraram antes do merge o data-testid do fechar do aviso, o botão por linha no teste da tabela de estados, o teste da baixa de R$ 0,00, a RN18 e o reopen pelo repositório, os testes da RN14 (comissão não dispara a recusa), da precedência no simulate e do fechamento sem comissão, e a asserção do e2e restrita à linha da taxa

Resultado: aprovada na rodada 3 da revisão de branch.

Defeitos aceitos (triados pela revisão da branch como aceitáveis; nenhum muda dado): - o aviso de comissão congelada no cadastro da imobiliária é legenda fixa, porque o salvar navega para a lista e o aviso "ao salvar" não teria onde aparecer; - formatadores de percentual e o tipo da regra repetidos em três arquivos do apps/web, e o CampoBRL paralelo ao CurrencyTextField — dívida de reuso, sem divergência de comportamento (a única divergência, "de/do Pacote", foi corrigida); - numa análise existente, o multiplicador do LMI do bloco vem de GET /settings; se essa leitura falhar, o cálculo da tela zera em vez de usar initial_fee.rule_value — o campo não é enviado sem ser tocado (RN5), e o resumo ao lado já mostra a falha; - durante a troca de imobiliária, o resumo calcula por um instante com a regra anterior; - os session.get(PartnerAgency, …) de billing_service, credit_analysis_service e guarantee_service resolvem por um id que vem de registro do mesmo tenant; - os specs e2e novos montam o cenário pela API e não cobrem a imobiliária digitando o valor pela tela nem o fechamento na voz dela — as duas vozes e o bloco estão nos testes unitários do apps/web.

18. Registro

O que divergiu do planejado, e por quê:

  • Duas decisões do usuário na revisão da branch (2026-10-12) mudaram a spec depois de aprovada: a regra do formulário sai da imobiliária do imóvel quando o seletor está vazio (7.3), e o pagamento à imobiliária já registrado conta como "algo a acertar" (RN11).
  • O downgrade apaga as movimentações TO_AGENCY antes de tirar a coluna — decisão da implementação, levada para a RN23: o código anterior leria um pagamento de comissão como transferência recebida.
  • Base nula fora de PERCENTAGE. A RN2 passou a anular a base da regra própria FIXED ou NONE (e o valor na NONE), para a regra em vigor não devolver base que não explica número nenhum.
  • TRANSFER_EXCEEDS_REMAINING e a recusa de declaração medem contra mensalidades e multas, como a spec manda; um teste da 0055 que comparava o details do fechamento campo a campo foi atualizado para conferir os dois campos de antes dentro do details ampliado.
  • Um teste e2e da 0003 (CA8 do fix 0009) conferia o número do snapshot no texto do parecer; a spec moveu essa explicação para a linha da taxa no resumo, e a asserção foi levada para lá, sem afrouxar.
  • Decisões de tela sem regra na spec, tomadas na implementação e conferidas na revisão da fase web: comissão própria vazia é recusada no campo; ao sair de Seguir a regra, a regra própria nasce como cópia da padrão; Registrar transferência aparece com falta positiva ou saldo parcial; o detalhe da imobiliária monta a regra em vigor a partir do cadastro e de GET /settings, porque a RN18 responde 404 para imobiliária removida.
  • Ambiente. O container do dev-remote estava com a imagem anterior ao último merge da main; os testes da API rodaram depois de copiar para ele a árvore desta branch, e o rebuild consolidou.

Afirmações conferidas

Fatos sobre o repositório que uma rodada de portão já conferiu abrindo o arquivo. O recortar.py leva esta seção junto em qualquer parte, e o revisor da rodada seguinte pode tomá-los como dados em vez de reconferir tudo do zero.

Afirmação Onde na spec Conferida em
O cálculo da taxa usa o Pacote Locatício nas três bases (pricing.py:59-73) 1, 5 problema, rodada 2
Configurações diz Aluguel mensal (apps/web/src/pages/settings/index.tsx:692) e "esforço e risco" na dica (:69); bloco Pagamento inicial em :601, linha inteira em :599 1, 7.1 problema r2; experiência r1
O Termo, Cláusula 5ª, Par. 5º: "Em função dos custos para a ativação da fiança onerosa…", "de acordo com a Modalidade de Garantia contratada" 1 problema, rodada 2
Seção 5 da 0055: comissão "se existir, é decisão nova"; setup fora do acerto (:205) 1, 3 problema, rodada 2
approved_initial_fee_value só vai quando o analista toca o campo (show.tsx:622, preenchido em :669-672); sem ele a garantia usa as settings vigentes na emissão (guarantee_service.py:229-246) 3 problema, rodada 2
O Resumo financeiro do formulário calcula ao vivo a partir de GET /settings (CreditAnalysisForm.tsx:349, :1843) 2, 3 problema, rodada 2
AGENCY_USER tem tenant_settings:read desde 2026-08-15 (rbac_seed.py:56-66, :403) 2, 7.1 problema, rodada 2
Fix 0009 decidiu não recalcular em massa, 2026-08-15 (linha 135) 5 problema, rodada 2
Seletor de tipo vertical no celular (apps/web/src/pages/settings/index.tsx:618) 7.2 experiência, rodada 1
Formulário de imobiliária com Dados gerais, Telefones, Endereço (apps/web/src/pages/agencies/form.tsx:255/353/407); podeDefinirRecolhimento = !isAdminGlobal (:114) 7.1 experiência, rodada 1
Acerto: três painéis lado a lado; no celular só Acerto aberto, Previsão e Pagamentos em MobileDetailSection fechadas (PaineisDoAcerto.tsx:140-193) 7.1, 7.2 experiência, rodada 1
Rótulos Taxa setup (R$) (DecisionPanel.tsx:374), Taxa setup aprovada (:336), Taxa de setup (GuaranteeCreateDialog.tsx:395, GuaranteeSections.tsx:120), Taxa setup (apps/web/src/pages/credit-analyses/show.tsx:1645) 7.1 experiência, rodada 1
Formulário não abre fora de DRAFT/PENDING (CreditAnalysisForm.tsx:1080-1090) 7.3 experiência, rodada 1
TransferenciaDialog em tela cheia no celular (:46) 7.2 experiência, rodada 1

Duas regras, e a segunda é a que faz a primeira valer:

  1. Só entra o que um revisor conferiu — não o que você conferiu ao escrever. O ponto é aproveitar trabalho já pago por uma rodada, não abrir atalho para afirmar sem prova.
  2. Mexeu na frase da spec, a linha sai da tabela. A afirmação volta a ser conferível. Sem isso a tabela vira o oposto do portão: uma afirmação carimbada como verdadeira porque um dia foi.

Feature sem afirmação sobre o repositório não precisa da seção — apague-a.