Todo dia 5, o gerente comercial da distribuidora abria uma planilha com quinze abas para fechar a comissão do mês. Cada aba puxava o CRM por exportação manual, aplicava um percentual que só ele lembrava de onde tinha saído, e cuspia um valor por vendedor. No dia em que um vendedor conferiu a própria conta e achou uma diferença de R$ 400, ninguém soube dizer se o errado era a planilha ou o CRM. A regra de comissão da empresa inteira morava numa fórmula de célula que uma pessoa entendia e ninguém versionava.
Para calcular comissão no Salesforce sem planilha, você guarda cada regra como um registro de Custom Metadata (categoria, percentual, teto de desconto, fator de penalidade) e um serviço Apex aplica a regra sobre cada linha da Oportunidade no fechamento. A regra vira dado versionado na org, não uma fórmula escondida numa aba que só uma pessoa entende.
Por que a planilha de comissão sempre quebra
A planilha não quebra por falta de capricho. Ela quebra por ser um segundo sistema, paralelo ao CRM, que precisa ser reconciliado à mão todo mês. O dado da venda nasce no Salesforce, viaja para o Excel numa exportação, encontra uma regra que vive só ali, e volta como um número que ninguém consegue rastrear até a origem. Cada passo dessa ponte é uma chance de erro: uma linha filtrada por engano, um percentual colado na célula errada, uma venda que entrou depois do fechamento e ficou de fora.
Pior que o erro é a opacidade. Quando o vendedor pergunta "por que a minha comissão desse mês foi essa?", a resposta honesta é "porque a planilha calculou assim", e isso não é resposta. A regra deveria ser auditável: qualquer pessoa deveria conseguir apontar para a venda, apontar para a regra que se aplicou àquela venda, e ver a conta. Numa planilha com fórmula aninhada em quinze abas, essa auditoria é impossível, e a confiança no número, que é o único ativo de um plano de comissão, evapora.
A pergunta que decide se você precisa de engine
Antes de construir qualquer coisa, seja honesto sobre o tamanho do seu problema, porque metade das empresas que pedem uma "engine de comissão" não precisa de uma. A pergunta é: a sua comissão é um percentual único, igual para todo mundo e para todo produto? Se a resposta é sim, se todo vendedor ganha, por exemplo, 4 por cento sobre o valor da Oportunidade fechada, você não precisa de engine, de Custom Metadata nem de Apex. Um formula field na Oportunidade que multiplica o valor por 0,04 resolve, sem código, sem deploy e sem teste. Construir uma máquina de regras para uma regra só é peso morto.
A engine só começa a se pagar quando a comissão tem estrutura: percentual diferente por categoria de produto, faixas que aceleram acima de uma meta, um redutor quando o vendedor deu desconto demais, papéis que ganham percentuais distintos sobre a mesma venda. É quando essas regras existem e, principalmente, quando elas mudam ao longo do tempo, que faz sentido tirá-las de dentro do código e transformá-las em configuração. A partir daqui, este post assume que você está nesse segundo caso.
A virada: cada regra de comissão é um registro de Custom Metadata
Em vez de escrever o percentual dentro de um if no Apex, cada regra de comissão vira um registro
de Custom Metadata. Um tipo Commission_Rule__mdt com um registro por categoria
de produto descreve tudo que a regra precisa saber: a categoria a que ela se aplica, o percentual base, o
teto de desconto a partir do qual a comissão é penalizada, e o fator que reduz a taxa quando esse teto é
ultrapassado. O código não conhece nenhum número; ele lê a configuração e aplica.
O ganho é o mesmo que move o dashboard de KPIs configurável via Custom Metadata: mudar a regra deixa de ser um projeto de desenvolvimento e vira a edição de um registro. Quando o comercial decide que a categoria de válvulas passa de 5 para 6 por cento no próximo trimestre, alguém edita um campo, e a mudança já viaja entre sandbox e produção no próximo deploy junto com o resto do metadado. Ninguém abre a IDE, ninguém escreve teste novo, ninguém arrisca quebrar o cálculo das outras categorias mexendo no arquivo.
A conta feita na frente
O motivo de a comissão precisar rodar por linha, e não por um percentual único sobre o cabeçalho, fica claro quando você faz a conta. Veja uma Oportunidade da Vetra, a distribuidora fictícia que mantenho como demonstração do portfólio, com três linhas de categorias diferentes. A regra do plano: filtros comissionam a 3 por cento, válvulas a 5 por cento, conexões a 1,5 por cento, e qualquer linha com desconto acima de 10 por cento tem a taxa cortada pela metade, para não premiar quem fecha à base de abatimento.
| Produto | Líquido | Categoria | Taxa base | Desconto | Regra aplicada | Comissão |
|---|---|---|---|---|---|---|
| Filtro industrial A | R$ 11.875 | Filtros | 3% | 5% | taxa cheia | R$ 356,25 |
| Válvula B | R$ 63.360 | Válvulas | 5% | 12% | redutor: 2,5% | R$ 1.584,00 |
| Conexão C | R$ 1.000 | Conexões | 1,5% | 0% | taxa cheia | R$ 15,00 |
| Total | R$ 76.235 | R$ 1.955,25 |
A comissão correta dessa Oportunidade é R$ 1.955,25: R$ 356,25 do filtro (3 por cento de R$ 11.875), R$ 1.584,00 da válvula (a taxa base de 5 por cento cai para 2,5 por cento porque o desconto de 12 por cento passou do teto), e R$ 15,00 da conexão. Se a empresa fizesse o atalho de aplicar um percentual único de 3 por cento sobre o líquido total de R$ 76.235, chegaria a R$ 2.287,05, uma diferença de R$ 331,80 nessa venda sozinha. O atalho paga a mais porque ignora duas coisas que só a conta por linha enxerga: que a maior linha é de uma categoria com regra própria, e que o desconto alto nela deveria derrubar a comissão, não mantê-la.
Comissionar sobre o líquido, e não sobre o bruto, é a outra decisão que a tabela deixa explícita. O líquido
de cada linha é o TotalPrice da OpportunityLineItem, que já vem com o desconto
aplicado. Pagar sobre o bruto seria pagar o vendedor por receita que a empresa não recebeu. As faixas de
desconto que chegam a esse campo da linha eu destrinchei no post sobre
desconto por volume no Salesforce sem sair da Oportunidade,
e a comissão é o passo seguinte na mesma corrente: o desconto define o líquido, e o líquido define o quanto
se paga.
O código que aplica a regra
Com as regras em Custom Metadata, o Apex fica curto e não guarda nenhum número de negócio. Ele carrega as regras num mapa por categoria, varre as linhas da Oportunidade, e para cada uma aplica a taxa da categoria, cortando pela metade quando o desconto passou do teto. O núcleo, bulkificado para tratar várias Oportunidades de uma vez:
Map<String, Commission_Rule__mdt> regraPorCategoria = new Map<String, Commission_Rule__mdt>();
for (Commission_Rule__mdt r : Commission_Rule__mdt.getAll().values()) {
regraPorCategoria.put(r.Categoria__c, r);
}
Map<Id, Decimal> comissaoPorOpp = new Map<Id, Decimal>();
for (OpportunityLineItem oli : [
SELECT OpportunityId, Product2.Family, Discount, TotalPrice
FROM OpportunityLineItem
WHERE OpportunityId IN :oppIds
WITH USER_MODE]) {
Commission_Rule__mdt regra = regraPorCategoria.get(oli.Product2.Family);
if (regra == null) continue; // categoria sem regra nao comissiona
Decimal taxa = regra.Percentual__c;
Decimal desconto = oli.Discount == null ? 0 : oli.Discount;
if (desconto > regra.Teto_Desconto__c) { // desconto acima do teto derruba a taxa
taxa = taxa * regra.Fator_Penalidade__c; // ex.: 0.5
}
Decimal liquido = oli.TotalPrice == null ? 0 : oli.TotalPrice;
Decimal comissao = liquido * taxa / 100;
comissaoPorOpp.put(oli.OpportunityId,
(comissaoPorOpp.get(oli.OpportunityId) == null ? 0 : comissaoPorOpp.get(oli.OpportunityId)) + comissao);
}
List<Opportunity> paraAtualizar = new List<Opportunity>();
for (Id oppId : comissaoPorOpp.keySet()) {
paraAtualizar.add(new Opportunity(Id = oppId, Commission_Amount__c = comissaoPorOpp.get(oppId).setScale(2)));
}
update as user paraAtualizar;
Três decisões salvam esse código. O Commission_Rule__mdt.getAll() lê todas as regras da memória
de metadados sem gastar uma query SOQL, então a engine roda de graça em relação ao limite de consultas por
mais categorias que você tenha. O WITH USER_MODE e o update as user fazem o cálculo
respeitar CRUD e FLS do usuário que disparou o salvamento, em vez de escrever à revelia da permissão dele, o
cuidado que detalhei no post sobre
segurança em Apex com CRUD, FLS e sharing. E o acúmulo em
Map por Oportunidade mantém o trigger bulkificado: um fechamento em massa que toca mil linhas de
duzentas Oportunidades roda em uma query, não em mil.
A escolha de fazer isso em Apex e não em Flow segue a fronteira de sempre. Enquanto o cálculo cabe na própria linha, um Flow before-save resolveria barato; quando ele precisa somar as linhas irmãs e escrever o valor agregado no pai, cruzando com uma configuração externa, é território de trigger, como argumentei no post sobre quando usar Flow ou Apex no Salesforce.
Por que Custom Metadata, e não Custom Setting nem objeto custom
A regra de comissão poderia morar em três lugares, e a escolha não é gosto. Um objeto custom seria o lugar errado: objeto custom é para dado transacional que os usuários criam e editam o tempo todo, como o lançamento de comissão de cada venda, não para a regra que rege esses lançamentos. Guardar a regra num objeto custom obrigaria a query dentro do loop de cálculo e misturaria configuração com dado operacional.
Entre Custom Setting e Custom Metadata, a diferença que decide é o deploy. Custom Setting não viaja no pacote de metadados: você configura os valores à mão em cada ambiente, e a sandbox nunca reflete produção sem alguém redigitar. Custom Metadata é metadado de verdade: os registros de regra viajam entre sandbox e produção no mesmo deploy que o resto do projeto, versionados no repositório junto com o código. Para uma regra de negócio que precisa ser idêntica entre ambientes e auditável no histórico do Git, Custom Metadata é a única resposta que não deixa a produção divergir da sandbox em silêncio. É o mesmo raciocínio de eleger um dono único para cada dado que defendi no post sobre fonte de verdade única no Salesforce: a regra tem um lugar canônico, e todo ambiente lê dali.
O caso da Vetra: o dia que a regra mudou sem deploy de código
Na Vetra montei exatamente essa engine, e o teste real dela veio quando "o comercial" decidiu, no meio do
trimestre, que a categoria de conexões estava desestimulada demais a 1,5 por cento e deveria subir para 2 por
cento. Na versão de planilha, isso seria uma caçada por todas as fórmulas que referenciavam conexões. Na
engine, foi editar o campo Percentual__c do registro de Custom Metadata da categoria, subir o
metadado, e pronto: o fechamento seguinte já pagou a taxa nova, sem uma linha de Apex tocada, sem teste
refeito, sem risco de encostar no cálculo das outras categorias.
O que essa mudança de dois minutos provou não foi a esperteza do código, foi a economia de manutenção. O custo de uma engine config-driven se cobra justamente aqui, no dia em que a regra muda, que é o dia que mais acontece num plano de comissão. Cada ajuste de taxa que teria virado um chamado de desenvolvimento vira a edição de um registro que o próprio time de operações consegue revisar antes de subir. A engine não é mais inteligente que a planilha; ela é auditável e barata de manter, e num plano de comissão essas duas propriedades valem mais que qualquer sofisticação de fórmula.
Quando não montar a engine (e parar antes de complicar)
Vale repetir o freio do começo, porque a tentação de sofisticar é grande. Se a sua comissão é plana, um formula field basta, e insistir na engine é gastar semana de projeto para manter uma regra que caberia numa multiplicação. Mesmo quando a engine se justifica, resista a modelar dentro dela toda exceção imaginável: metas trimestrais com aceleradores progressivos, split de comissão entre vendedor e SDR, clawback quando o cliente cancela. Cada uma dessas é real, mas cada uma dobra a complexidade do cálculo e do teste.
Comece pela regra que paga 80 por cento dos casos, a taxa por categoria com o redutor de desconto, e só adicione a próxima camada quando ela doer de verdade no fechamento. Construa a máquina no tamanho do problema: as regras que existem hoje viram registros, o código que as aplica fica curto e cego aos números, e a planilha de quinze abas, que ninguém confiava e todo mundo temia, deixa de existir. Um plano de comissão não precisa ser esperto. Precisa ser um número que o vendedor consegue conferir e a empresa consegue explicar.
Sua comissão ainda mora numa planilha que ninguém confia?
Eu tiro a regra da planilha, modelo cada faixa como Custom Metadata, monto a engine em Apex bulkificado com o teste que sobe sem quebrar, e deixo o número auditável dentro do CRM. Comece com um diagnóstico gratuito de 45 minutos.
Falar no WhatsApp Ver serviçosPerguntas frequentes
Como calcular comissão no Salesforce sem planilha?
Você guarda cada regra de comissão como um registro de Custom Metadata (categoria do produto, percentual, teto de desconto, fator de penalidade) e um serviço Apex aplica a regra sobre cada linha da Oportunidade no fechamento. A planilha morre porque a regra vira dado versionado dentro da org, não uma fórmula escondida numa aba que só uma pessoa entende.
Custom Metadata ou objeto custom para guardar regra de comissão?
Use Custom Metadata quando a regra é configuração que muda raramente e precisa viajar entre sandbox e produção no deploy, que é o caso do percentual por categoria. Use objeto custom quando cada registro é um dado transacional que os usuários criam e editam o tempo todo, como o lançamento de comissão de cada venda. Regra é metadado; lançamento é dado.
Comissão sobre o valor bruto ou sobre o líquido?
Comissione sobre o líquido de cada linha, o TotalPrice da OpportunityLineItem, que já desconta o abatimento aplicado. Comissionar sobre o bruto paga o vendedor por receita que a empresa não recebeu, e premia quem dá desconto. Ligar a comissão ao líquido alinha o incentivo do vendedor à margem real do negócio.
Quando não vale a pena montar uma engine de comissão?
Se a sua comissão é um percentual único e fixo sobre o valor da Oportunidade, você não precisa de engine nenhuma: um formula field resolve sem código, sem deploy e sem teste. A engine com Custom Metadata só se paga quando existem regras diferentes por produto, faixa ou papel, e essas regras mudam ao longo do tempo sem que você queira mexer em Apex a cada mudança.
