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.
- 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.
- A Parte II diz como se usa o que vai existir. Revisada na seção 8 (jornada × viewports).
- 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.
- 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¶
ANALYSTeADMIN_TENANTda 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:
- 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.
- 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.
- 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.
- A combinação chega ao analista por fora [fora do sistema] — mensagem, conversa, ou memória do acordo daquela parceira.
- 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). - A garantia é emitida com essa taxa, e a cobrança de entrada sai com ela.
A comissão, da taxa paga ao PIX:
- O inquilino paga a entrada por boleto da garantidora, e a baixa registra quanto do pagamento foi para a adesão (spec 0054).
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- O inquilino paga a entrada. A baixa registra R$ 500,00 de adesão, em 10/11.
- 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.
- 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:
- "Faltam N mensalidades a declarar." (o de hoje);
- 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
calcularTaxaDeAdesaode 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ãofalse: permite editar o valor na análise;commission_percent—Numeric(5,2), não nulo, padrão100.00, faixa0–100: a parte do líquido que volta à imobiliária;commission_tax_percent—Numeric(5,2), não nulo, padrão0.00, faixa0–100: os impostos descontados da comissão. Nunca vazio (decisão do usuário, 2026-10-11):PATCH /settingscom o camponullresponde 422COMMISSION_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 onullé visto, e onde se recusa: no service, não no schema. Um validador de schema sairia pelovalidation_error_handlercomcode: "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 pormodel_dump(exclude_unset=True)— é o que faz cominterest_percent_month(tenant_settings_service.py:59-62) — e, comcommission_tax_percent,commission_percentouallow_manual_initial_feepresentes e nulos, levantaBusinessRuleError422:COMMISSION_TAX_REQUIREDpara o primeiro,FIELD_REQUIREDpara os outros dois, sempre comdetails: [{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ãofalse.false= segue a regra padrão;initial_fee_type,initial_fee_value,initial_fee_base— os mesmos tipos e enums deTenantSettings, anuláveis;allow_manual_initial_fee—bool, anulável;commission_percent—Numeric(5,2), anulável, faixa0–100.null= segue o padrão da garantidora. É independente deinitial_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 emapp/domain/:regra_de_adesao(settings, agencia | None) -> RegraDeAdesao, comorigem(TENANTouAGENCY),tipo,valor,base,permite_editarepercentual_de_comissao. - Sem imobiliária, ou com
initial_fee_custom = false: tipo, valor, base epermite_editarvêm da regra padrão, eorigem = TENANT; - com
initial_fee_custom = true: vêm da imobiliária, eorigem = AGENCY; permite_editaréfalsesempre que o tipo éNONE, qualquer que seja a chave;percentual_de_comissaovem 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) einformed_initial_fee_by(FKusers.id, anulável,ondelete="SET NULL").null= não informado: vale a regra.CreditAnalysisCreateeCreditAnalysisUpdateaceitaminformed_initial_fee; quem gravainformed_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: 422INITIAL_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_bysó 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/websó 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. nullexplícito é sempre aceito: limpa o valor einformed_initial_fee_by. Atenção aoupdate: ele monta o payload commodel_dump(exclude_none=True)(credit_analysis_service.py:465), que descarta onull. 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 —
DRAFTePENDING—, porque é oupdatede 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
PATCHque não trazinformed_initial_feenão o revalida nem o apaga. -
A imobiliária da análise não muda depois de criada:
CreditAnalysisUpdatenã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 doapps/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 emfinancial_initial_paymentoinformed_initial_fee, quando não nulo, ou o valor da regra, quando nulo.informed_initial_feeentra 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 onullnão chega ao payload (credit_analysis_service.py:465, RN5), oupdateacrescentainformed_initial_fee: Noneao payload quando o campo veio presente e nulo, antes de calcularmuda_precoe oantes/depoisdo 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 temfinancial_snapshot_atnulo 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 entreapproved_initial_fee_valueepricing.initial_fee,:242-246e: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), quandosetup_paid > 0e a garantia da cobrança tempartner_agency_id, o service grava na cobrança, na mesma transação, cinco colunas novas deBilling: commission_tax_percent— ocommission_tax_percentda garantidora naquele momento;commission_percent— opercentual_de_comissaoda 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 depaid_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_monthigual à competência, não removidas, de garantia daquela imobiliária. Aberto, entram todas essas. Fechado, entram as que têmcommission_settlement_idigual ao acerto (RN15) — a mesma troca de critério quecontas_do_acertojá 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 decommission_amountdas comissões do acerto (RN10);balance=devido − commission;recebido— soma das movimentaçõesFROM_AGENCY(RN13);paid_to_agency— soma das movimentaçõesTO_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 ecommission == 0epaid_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):READYexatamente quando não há motivo para não fechar (Parte II, 7.1):undeclared == 0, nãooverpaid, nãooverpaid_to_agency,falta == 0e nãonada_a_acertar.CLOSEDcomo 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 edetailspassam 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 chamafalta < 0de "o recebido passou do devido" (agency_settlement_service.py:687-697) sai: com comissão, falta negativa é a garantidora devendo.detailsganhaoverpaid,overpaid_to_agency,balanceecommission, além dos de hoje. -
RN13 — Movimentações com sentido.
AgencySettlementTransferganhadirection(enumTransferDirection:FROM_AGENCY,TO_AGENCY; não nulo;server_default = 'FROM_AGENCY', o que faz das transferências de hoje o que elas são).TransferRequestganhadirection, opcional, padrãoFROM_AGENCY— o cliente de hoje continua funcionando sem mudar nada. FROM_AGENCY:TRANSFER_DATE_IN_FUTUREcomo hoje; eTRANSFER_EXCEEDS_REMAININGpassa a medir contradevido − recebido, não mais contrafalta(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é afaltade hoje, e nada muda.TO_AGENCY— as recusas, nesta ordem:- data futura → 422
TRANSFER_DATE_IN_FUTURE(o de hoje); - recebido a mais em aberto (
recebido > devido) → 422RECEIVED_EXCEEDS_DUE_OPEN("Remova a transferência registrada a mais antes de pagar a comissão."). Sem isso,−faltaincluiria 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; undeclared > 0→ 422COMMISSION_PAYMENT_BEFORE_DECLARATIONS("Declare as mensalidades antes de pagar a comissão.");falta ≥ 0→ 422NOTHING_TO_PAY_AGENCY("Não há valor a pagar à imobiliária neste mês.");amount > −falta→ 422COMMISSION_PAYMENT_EXCEEDS_REMAINING("O valor passa R$ X do que falta pagar à imobiliária.").
- data futura → 422
- 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 mesmoDELETE …/transfers/{id}de hoje, para os dois sentidos. -
Auditoria:
FROM_AGENCYcontinuaTRANSFER_REGISTERED/TRANSFER_REMOVED;TO_AGENCYregistraCOMMISSION_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, emdeclare) continua comparandodevidocomrecebido— o mesmo número da RN11overpaid. A comissão não a dispara. -
RN15 — Fechar e reabrir.
closegrava, além doagency_settlement_iddos membros (:762),commission_settlement_idnas cobranças de comissão do acerto (RN10, aberto) — coluna nova deBilling, FKagency_settlements.id, anulável,ondelete="SET NULL".reopenanula sócommission_settlement_idnessas 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 travaFOR UPDATEdo 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 comFOR 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_agencyememberscontinua existindo (0 no acerto só de comissão); - o detalhe ganha
commissions: uma linha por cobrança de comissão (RN10), combilling_id,guarantee_id,guarantee_number,renter_name,paid_on,setup_paid,commission_tax_percent,commission_tax_amount,commission_percentecommission_amount, ordenadas por inquilino, até 500 comcommissions_truncated— o mesmo teto declarado da RN20 da 0055; e as movimentações ganhamdirection.
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_GLOBALnão grava nenhum dos seis campos da RN2: presente no corpo dePOST/PATCH /partner-agencies, 403PLATFORM_ADMIN_CANNOT_SET_INITIAL_FEE, no desenho degarantir_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ãoexclude_none: umnullmandado por ele também é recusado — senão ele anularia a comissão própria de uma imobiliária pelo caminho donullda RN2; - a criação é
POST /api/v1/onboarding/partner-agencies(routers/onboarding.py,OnboardingService.create_partner_agency,onboarding_service.py:381), não umPOSTem/partner-agencies. Ela recebe os seis campos com as mesmas regras (RN2). Não precisa de recusa própria nem de variante de resposta: oADMIN_GLOBALjá para em_get_tenantcom 422TENANT_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 noPATCH, nas leituras e norestore— é por eles que o CA23 se testa; - e não os recebe: a resposta da imobiliária para o
ADMIN_GLOBALomite os seis, numa variante comoPartnerAgencyAgencyResponse(apps/api/app/schemas/partner_agency.py:141) faz comcollects_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 tempartner_agencies:update. -
RN18 —
GET /api/v1/settings/initial-fee-ruledevolve 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 oruleda 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 opcionalpartner_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 (
PartnerAgencyRepositorycom o filtro de tenant); fora dele ou removido, 404PARTNER_AGENCY_NOT_FOUND; ausente, devolve a regra padrão (origem = TENANT). -
ADMIN_GLOBAL: 403PLATFORM_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.
CreditAnalysisResponseganhainitial_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 ocommission_percentem 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) esnapshot_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_nameno log de mudanças só a quem temdecisions: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-analysesusa o mesmo modelo (apps/api/app/api/v1/routers/credit_analyses.py:37) mas monta as linhas fora do_build_response; aliinitial_feevem 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
TenantSettingsda garantidora dona da análise (credit_analysis.tenant_id), nunca as de quem chama — o mesmo cuidado que_apply_financial_snapshotjá registra (credit_analysis_service.py:937-940). -
Para o
ADMIN_GLOBAL,rulevem nulo. Ele lê análise de qualquer garantidora (tenant_scopenulo), erulecomsource = AGENCYé a regra própria inteira — o dado que a RN17 esconde dele.rule_valuee 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:210esconde a seção Pagamento na entrada inteira quando a taxa e a primeira parcela são zero, e:213esconde 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ó deinformed_value, que todo perfil recebe — não darule, que vem nula para oADMIN_GLOBAL. -
RN21 — Comissão de baixa feita pelo fechamento: nenhuma. O fechamento baixa membros de canal
AGENCY_PIX, que são mensalidade, e gravasetup_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:
BillingUpdatesó aceitadue_dateenotes, 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/webe não mudam contrato. Também é doapps/webo 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 ecommission_settlement_id(FK, com índice), mais um índice em(tenant_id, commission_reference_month); o enumtransfer_directioneagency_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 viramFROM_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çõesTO_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 deGET /settings/initial-fee-rule): noPartnerAgencyRepository, filtrado pelotenant_iddo 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_idsó é 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:readno escopo dele), agora com os campos da regra; outra imobiliária, 404, como hoje. - A análise: o
informed_initial_feesó 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_GLOBALrecebe: 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.rulevem nulo para ele (RN19); o valor informado erule_valueficam — são a taxa que o resumo financeiro já mostra. Na garantia e nosimulate, 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_idde 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 umRegraDeAdesaoResponsenovo emschemas/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_paymentpassa 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 eCommitRoutejá no router);routers/onboarding.py,routers/partner_agencies.py,routers/credit_analyses.py,routers/guarantees.py,routers/billing.pyerouters/agency_settlements.pysó 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,NONEsem 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 contradevido, pago a mais, acerto só de comissãoREADYcom falta zero,nada_a_acertarcom 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=falseanula, 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,nullexplícito limpa noupdateapesar doexclude_none, sobrevive à mudança de regra,_byvem 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 doTO_AGENCY,FROM_AGENCYsemdirectionigual a hoje, fechar grava e reabrir anulacommission_settlement_id, listas incluem o acerto só de comissão;- negativos de acesso:
AGENCY_USERda B → 404 na regra da A (RN18);ADMIN_GLOBAL→ 403 na regra em vigor;ANALYST→ 403 emTO_AGENCY;AGENCY_MANAGER→ 403 emTO_AGENCY;ADMIN_TENANTdo 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 comcollects_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.tspassa a receber a regra em vigor, nãoTenantSettings. - 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 novocomponents/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.tsxao 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-agencyebillings/components/RepassesDaCompetencia(colunas e cartão). data-testidnovos: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 nofinally— regra e percentuais são do tenant compartilhado do e2e (a lição da spec 0025 noCLAUDE.mdda 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 /settingscomcommission_tax_percent: nullresponde 422COMMISSION_TAX_REQUIRED; um tenant recém-migrado lêcommission_tax_percent = 0.00,commission_percent = 100.00eallow_manual_initial_fee = false(RN1, RN23). - [x] CA2. Imobiliária com
initial_fee_custom = true,PERCENTAGEsem base → 422INITIAL_FEE_RULE_INCOMPLETE; voltando afalse, os quatro campos da taxa ficam nulos mesmo que o corpo os traga (RN2). - [x] CA3.
regra_de_adesaodevolve a regra da imobiliária comcustom = true, a padrão comfalse,permite_editar = falsecom tipoNONEe 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 = 500grava o valor,informed_initial_fee_by= o usuário da requisição, efinancial_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, enullé aceito (RN5). - [x] CA6. Gravado o valor, a garantidora desliga a edição na regra; um
PATCHda análise sem o campo mantém o valor; umPATCHcom"informed_initial_fee": nullanula o valor e o autor; umPATCHcom valor não nulo responde 422INITIAL_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_feecom o nome de quem informou, a imobiliária dele (nulo para quem é da garantidora),rule_valueesnapshot_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 = 500numa garantia da imobiliária com comissão 80% e impostos 16,33% gravacommission_tax_amount = 81.65ecommission_amount = 334.68; com 750,122.48e502.02(RN8). - [x] CA10. Depois da baixa, mudar os impostos para 10% não muda
commission_amountdaquela cobrança (RN8). - [x] CA11. Baixa com
paid_onnum mês cujo acerto da imobiliária está fechado gravacommission_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çãoOPEN;TO_AGENCYde 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, eTO_AGENCY→ 422RECEIVED_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_AGENCYcom mensalidade não declarada → 422COMMISSION_PAYMENT_BEFORE_DECLARATIONS; com falta ≥ 0 → 422NOTHING_TO_PAY_AGENCY; acima de −falta → 422COMMISSION_PAYMENT_EXCEEDS_REMAINING(RN13). - [x] CA18.
POST …/transferssemdirectionse comporta exatamente como hoje, e a transferência gravada saiFROM_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 gravacommission_settlement_id, reabrir anula (RN12, RN15, RN16). - [x] CA20. Declarar uma correção que deixaria o
devidoabaixo 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_PIXnão cria comissão (RN21). - [x] CA21a. Com a comissão de 300 já gerada no mês e devido 1.000, o
ADMIN_TENANTregistra a transferência da imobiliária,FROM_AGENCYde 1.000, — aceito; falta −300.FROM_AGENCYde 1.001 → 422TRANSFER_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_ide mantémstatus,setup_paide 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_bycontinua 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 /guaranteescomoADMIN_GLOBALtraz os quatro campos nulos nas garantias cominitial_fee_source = AGENCY;simulatede análise com regra própriaFIXEDresponde 200 cominitial_fee_basenulo (RN7). - [x] CA21g.
PATCHda imobiliária com regra própria completa enviando sóinitial_fee_valueé aceito e mantémallow_manual_initial_fee(RN2). - [x] CA21h.
AGENCY_USERlêGET /settingscomcommission_percentnulo; oADMIN_TENANTlê o valor (RN1). - [x] CA21i. Emissão de garantia de imobiliária com regra própria grava
initial_fee_source = AGENCYe o tipo, o valor e a base dela; oADMIN_GLOBALlê essa garantia com os quatro nulos (RN7). - [x] CA21c. Baixa com
paid_onem outubro, com outubro e novembro fechados para a imobiliária:commission_reference_month= dezembro (RN9). - [x] CA21d.
closecom falta −300 respondeSETTLEMENT_NOT_READYcom "faltam R$ 300,00 a pagar à imobiliária", não "o recebido passou do devido" (RN12).
Autorização e isolamento
- [x] CA22.
AGENCY_USERda imobiliária B pedeGET /settings/initial-fee-rule? partner_agency_id=<A>→ 404; sem o parâmetro, recebe a regra da B (RN18). - [x] CA23.
ADMIN_GLOBAL→ 403PLATFORM_ADMIN_NO_TENANTna regra em vigor; 403PLATFORM_ADMIN_CANNOT_SET_INITIAL_FEEao mandarcommission_percentnoPATCHda imobiliária; a resposta do cadastro para ele não traz os seis campos (RN17, RN18). - [x] CA24.
ADMIN_TENANTdo tenant 2 pede a regra em vigor de uma imobiliária do tenant 1 → 404 (RN18). - [x] CA25.
ANALYSTeAGENCY_MANAGER→ 403 emPOST …/transferscomTO_AGENCY(10.1). - [x] CA26.
OPERATOReANALYST→ 403 emPATCH /partner-agencies/{id}comcommission_percent(10.1);VIEWER→ 403 emPATCH /credit-analyses/{id}cominformed_initial_fee. - [x] CA27. O
AGENCY_USERlê a própria imobiliária com os seis campos da regra (RN17). - [x] CA27a.
ADMIN_GLOBALlê uma análise de imobiliária com regra própria:initial_fee.rulevem nulo;ADMIN_GLOBALmanda"commission_percent": nullnoPATCHda imobiliária → 403PLATFORM_ADMIN_CANNOT_SET_INITIAL_FEE(RN17, RN19). - [x] CA27b.
ADMIN_TENANTmanda"commission_percent": nullnuma imobiliária com 80% → grava nulo, e a regra em vigor passa a devolver o percentual do padrão (RN2, RN3). - [x] CA27c.
PATCHda análise com"informed_initial_fee": nullrefazfinancial_initial_paymentpela 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.pye as contas novas deacerto_com_imobiliaria.py, com os testes (CA3, CA9, CA14–CA16) - [x]
apps/api— modelos e a migrationr2f4n6b8j0m3(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.tssobre a regra em vigor; Parâmetros; imobiliária e Minha Imobiliária; formulário, detalhe e parecer da análise; garantia; acerto e listas — comdata-testid, desktop e 375px no mesmo passo, e os*.test.tsx - [x]
apps/e2e— os dois specs novos, comfinallydesfazendo a regra - [x]
make test(vitest do web, pytest pelo container) emake lintsem 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
downgradeapaga as movimentaçõesTO_AGENCYantes 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ópriaFIXEDouNONE(e o valor naNONE), para a regra em vigor não devolver base que não explica número nenhum. TRANSFER_EXCEEDS_REMAININGe a recusa de declaração medem contra mensalidades e multas, como a spec manda; um teste da 0055 que comparava odetailsdo fechamento campo a campo foi atualizado para conferir os dois campos de antes dentro dodetailsampliado.- 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:
- 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.
- 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.