From addon-studio
Cria, revisa e refatora regras de negócio Sankhya com `@BusinessRule` (interface `Regra` + `ContextoRegra`) para barramento de eventos, liberação de limite e regras transacionais. Use ao criar, alterar, revisar, auditar ou padronizar regras de negócio, ao implementar `executar`/`onPreSave`/`onPostSave`, ao trabalhar com arquivos `*Regra.java`, ou ao tocar em código com `@BusinessRule`.
How this skill is triggered — by the user, by Claude, or both
Slash command
/addon-studio:business-ruleThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
`@BusinessRule` implementa logica automatica disparada por eventos do ciclo de vida comercial — principalmente **confirmacao e faturamento** de documentos (Pedidos, Notas de Venda). Disponivel a partir do Addon Studio 2.0.
@BusinessRule) — Addon Studio 2.0@BusinessRule implementa logica automatica disparada por eventos do ciclo de vida comercial — principalmente confirmacao e faturamento de documentos (Pedidos, Notas de Venda). Disponivel a partir do Addon Studio 2.0.
Referencias complementares:
addon-studio— Stack + restricoes Java 8dependency-injection— Injecao de dependencia (Guice)
@BusinessRule vs @Callback vs @Listener| Hook | Escopo | Quando usar |
|---|---|---|
@BusinessRule | Notas de Saida e Mov. Interna (Vendas, Remessas, etc.) | Logica que interage com barramento de regras (ContextoRegra): liberacoes de limite, validacoes complexas na confirmacao/faturamento. |
@Callback | Todos documentos comerciais, incluindo Notas de Entrada | Eventos de negocio onde @BusinessRule nao atua (ex: notas de compra) ou quando barramento nao e necessario. |
@Listener | Operacoes CRUD (insert/update/delete) em qualquer entidade | Validacoes e modificacoes de campo disparadas ao salvar/excluir. Preferir para CRUD simples. |
Regra rapida:
@BusinessRule.@Callback.@Listener.@BusinessRuleimport br.com.sankhya.modelcore.comercial.Regra;
import br.com.sankhya.modelcore.comercial.ContextoRegra;
import br.com.sankhya.studio.annotations.hooks.BusinessRule;
import com.google.inject.Inject;
@BusinessRule(description = "Valida desconto na confirmacao")
public class ValidacaoDescontoRegra implements Regra {
private final DescontoService descontoService;
@Inject
public ValidacaoDescontoRegra(DescontoService descontoService) {
this.descontoService = descontoService;
}
@Override
public void beforeUpdate(ContextoRegra ctx) throws Exception {
DynamicVO notaVO = (DynamicVO) ctx.getPrePersistEntityState().getNewVO();
// logica de negocio delegada ao Service
descontoService.validar(notaVO, ctx.getBarramentoRegra());
}
// Demais metodos da interface podem ficar vazios se nao forem necessarios
}
@BusinessRule| Atributo | Obrigatorio | Descricao |
|---|---|---|
description | Sim | Descricao legivel da regra — aparece nos logs e configuracoes. |
Regra — Metodos disponíveisImplemente apenas os metodos que forem necessarios. Os demais podem ficar com corpo vazio.
| Metodo | Quando dispara | Foco da @BusinessRule |
|---|---|---|
beforeInsert(ctx) | Antes de inserir o documento | Raramente usado — prefira @Listener |
afterInsert(ctx) | Apos inserir o documento | Raramente usado — prefira @Listener |
beforeUpdate(ctx) | Antes de atualizar (inclui confirmacao/faturamento) | Caso de uso principal |
afterUpdate(ctx) | Apos atualizar (inclui confirmacao/faturamento) | Integracoes assincronas pos-confirmacao |
beforeDelete(ctx) | Antes de excluir o documento | Raramente usado — prefira @Listener |
afterDelete(ctx) | Apos excluir o documento | Raramente usado — prefira @Listener |
ContextoRegra — API// Estado atual (com modificacoes do evento)
DynamicVO notaVO = (DynamicVO) ctx.getPrePersistEntityState().getNewVO();
// Estado anterior (disponivel em beforeUpdate e beforeDelete)
DynamicVO oldNotaVO = (DynamicVO) ctx.getPrePersistEntityState().getOldVO();
// Leitura de campos do DynamicVO
BigDecimal nuNota = notaVO.asBigDecimal("NUNOTA");
String confirmada = notaVO.asString("CONFIRMADA");
BigDecimal percDesc = notaVO.asBigDecimal("PERCDESC");
// Escrita de campo (modificacao em memoria, persiste com a transacao)
notaVO.setProperty("OBSERVACAO", "Conferido automaticamente.");
// Aviso nao bloqueante para o usuario
ctx.getBarramentoRegra().addMensagem("Desconto acima do limite — aviso gerado.");
// Solicitacao de liberacao de limite (evento deve estar cadastrado no sistema)
LiberacaoSolicitada lib = new LiberacaoSolicitada(
notaVO.asBigDecimal("NUNOTA"), // numero da nota
"TGFCAB", // tabela
2000, // ID do evento de liberacao
BigDecimal.ONE // ID do usuario solicitante
);
lib.setPendente(true);
ctx.getBarramentoRegra().addLiberacaoSolicitada(lib);
// Lance excecao — mensagem exibida ao usuario e transacao revertida
throw new Exception("Limite de credito excedido. Operacao bloqueada.");
O evento beforeUpdate dispara em varios momentos. Para agir somente na confirmacao:
@Override
public void beforeUpdate(ContextoRegra ctx) throws Exception {
DynamicVO notaVO = (DynamicVO) ctx.getPrePersistEntityState().getNewVO();
DynamicVO oldNotaVO = (DynamicVO) ctx.getPrePersistEntityState().getOldVO();
// Abordagem 1: comparar campo CONFIRMADA entre old e new
boolean isConfirmando = "S".equals(notaVO.asString("CONFIRMADA"))
&& (oldNotaVO == null || !"S".equals(oldNotaVO.asString("CONFIRMADA")));
if (!isConfirmando) return;
// ... logica especifica da confirmacao
}
import br.com.sankhya.modelcore.comercial.Regra;
import br.com.sankhya.modelcore.comercial.ContextoRegra;
import br.com.sankhya.modelcore.util.LiberacaoSolicitada;
import br.com.sankhya.studio.annotations.hooks.BusinessRule;
import com.google.inject.Inject;
import java.math.BigDecimal;
@BusinessRule(description = "Solicita liberacao para vendas com desconto alto")
public class LiberacaoDescontoRegra implements Regra {
@Override
public void beforeUpdate(ContextoRegra ctx) throws Exception {
DynamicVO notaVO = (DynamicVO) ctx.getPrePersistEntityState().getNewVO();
Boolean isConfirmando = (Boolean) JapeSession.getProperty("CabecalhoNota.confirmando.nota");
if (!Boolean.TRUE.equals(isConfirmando)) return;
BigDecimal percDesc = notaVO.asBigDecimal("PERCDESC");
if (percDesc == null || percDesc.compareTo(new BigDecimal("10")) <= 0) return;
LiberacaoSolicitada lib = new LiberacaoSolicitada(
notaVO.asBigDecimal("NUNOTA"),
"TGFCAB",
2000, // ID do evento de liberacao cadastrado no sistema
BigDecimal.ONE
);
lib.setPendente(true);
ctx.getBarramentoRegra().addLiberacaoSolicitada(lib);
ctx.getBarramentoRegra().addMensagem("Solicitacao de liberacao enviada para desconto acima de 10%.");
}
}
@BusinessRule(description = "Adiciona observacao de conferencia na confirmacao")
public class AdicionaObservacaoConfirmacaoRegra implements Regra {
@Override
public void beforeUpdate(ContextoRegra ctx) throws Exception {
DynamicVO notaVO = (DynamicVO) ctx.getPrePersistEntityState().getNewVO();
DynamicVO oldNotaVO = (DynamicVO) ctx.getPrePersistEntityState().getOldVO();
boolean isConfirmando = "S".equals(notaVO.asString("CONFIRMADA"))
&& (oldNotaVO == null || !"S".equals(oldNotaVO.asString("CONFIRMADA")));
if (!isConfirmando) return;
String obsAtual = notaVO.asString("OBSERVACAO");
String novaObs = "Nota conferida e confirmada pelo sistema.";
notaVO.setProperty("OBSERVACAO", obsAtual == null ? novaObs : obsAtual + "\n" + novaObs);
}
}
@BusinessRule(description = "Envia nota para sistema externo apos confirmacao")
public class IntegracaoExternaRegra implements Regra {
private final IntegracaoService integracaoService;
@Inject
public IntegracaoExternaRegra(IntegracaoService integracaoService) {
this.integracaoService = integracaoService;
}
@Override
public void afterUpdate(ContextoRegra ctx) throws Exception {
DynamicVO notaVO = (DynamicVO) ctx.getPrePersistEntityState().getNewVO();
DynamicVO oldNotaVO = (DynamicVO) ctx.getPrePersistEntityState().getOldVO();
boolean isConfirmando = "S".equals(notaVO.asString("CONFIRMADA"))
&& (oldNotaVO == null || !"S".equals(oldNotaVO.asString("CONFIRMADA")));
if (!isConfirmando) return;
BigDecimal nuNota = notaVO.asBigDecimal("NUNOTA");
// Assincrono — nao bloqueia thread da confirmacao
CompletableFuture.runAsync(() -> integracaoService.enviar(nuNota));
}
}
CompletableFuture, ExecutorService ou JMS. Nunca sincrono.@BusinessRule enxuta — delegue para @Component.addMensagem() para informar acoes automaticas executadas.Exception com mensagem clara para impedir a operacao.| Anti-Pattern | Correcao |
|---|---|
| Usar para CRUD simples (salvar/excluir) | Usar @Listener |
| Chamada sincrona a API/Web Service | Usar CompletableFuture ou JMS |
| Logica de negocio no metodo da interface | Mover para Service (@Component) |
Usar afterInsert para validacao | Validar em beforeInsert — apos salvar e tarde demais |
new em dependencias gerenciadas | Injetar via construtor com @Inject |
| Usar para Notas de Entrada (compras) | Usar @Callback |
@BusinessRule@Callback ou @Listener.Regra (nomear <Feature>Regra).@BusinessRule(description = "...").@Inject (Guice).oldVO/newVO ou JapeSession.@Component).addMensagem() ou excecao com mensagem clara.dependency-injection).action-button — botão dispara fluxo que pode invocar regracontroller — controller pode invocar regra via barramentodependency-injection — @Component da Regra precisa estar registrado no módulo Guiceentity — entidade alvo do eventorepository — acesso a dados dentro da regradependency-injection — wiring Guice da regratest — JUnit + Mockito da RegraProvides behavioral guidelines to reduce common LLM coding mistakes, focusing on simplicity, surgical changes, assumption surfacing, and verifiable success criteria.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.
npx claudepluginhub snk-devcenter/addon-studio --plugin addon-studio