To provide a consolidated guide of patterns and best practices for creating and maintaining dashboards, SQL queries, BI parameterization, and UI/UX within the Sankhya ecosystem (JSP/HTML/Java).
This skill should be used when:
Aplicar padrões de JSP/JSTL e organização server-side para reduzir erros de compilação, falhas de renderização e regressões em dashboards/telas.
Diretrizes de implementação
isELIgnored="false" para habilitar ${...} em tempo de renderização.core_rt para JSTL core no ecossistema Sankhya.c:if, c:choose, c:forEach).localStorage (ordem de colunas e ordenação).c:set para evitar Erro 500 no servidor Java do Sankhya.<script>. Utilizar containers HTML ocultos para passar dados ao JavaScript, mantendo a saúde do editor de código (IDE Linting).Os nomes de tabelas e campos abaixo são representativos e podem variar conforme a implementação da instância.
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" isELIgnored="false" %>
<%@ taglib prefix="snk" uri="/WEB-INF/tld/sankhyaUtil.tld" %>
<%@ taglib uri="http://java.sun.com/jstl/core_rt" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
<snk:load />
Carregamento de assets em dashboard/gadget
contextPath + BASE_FOLDER.openLevel), manter caminho absoluto para evitar quebra de resolução.<script src="${pageContext.request.contextPath}/${BASE_FOLDER}/js/app.js"></script>
<link rel="stylesheet" href="${pageContext.request.contextPath}/${BASE_FOLDER}/css/style.css" />
Consumo seguro de snk:query
query.rows (não no objeto raiz).empty query.rows.<snk:query var="qDados">
SELECT CAB.NUNOTA, CAB.CODPARC
FROM TGFCAB CAB
</snk:query>
<c:choose>
<c:when test="${empty qDados.rows}">
<span>Sem resultados</span>
</c:when>
<c:otherwise>
<c:forEach var="linha" items="${qDados.rows}">
${linha.NUNOTA}
</c:forEach>
</c:otherwise>
</c:choose>
Sanitização de parâmetros antes da SQL
" e ") antes de injetar em query.<c:set var="raw_codusu" value="${empty param.P_CODUSU ? '0' : param.P_CODUSU}" />
<c:set var="codusu_limpo" value="${fn:replace(raw_codusu, '\"', '')}" />
<c:set var="codusu_limpo" value="${fn:replace(codusu_limpo, '"', '')}" />
<c:set var="codusu_seguro" value="${empty codusu_limpo ? '0' : codusu_limpo}" />
<snk:query var="qAcessos">
SELECT CODUSU, NOMEUSU
FROM TSIUSU
WHERE CODUSU = :codusu_seguro
</snk:query>
Estado de tela e lazy-load em dashboard único
var dadosGlobais = [];
var produtoAtual = null;
var abaCarregada = {};
function abrirDetalhe(dado) {
produtoAtual = dado;
abaCarregada = {};
trocarAba("estoque");
}
function trocarAba(aba) {
if (aba === "estoque" && !abaCarregada.estoque) carregarAbaEstoque(produtoAtual.CODPROD);
if (aba === "pedidos" && !abaCarregada.pedidos) carregarAbaPedidos(produtoAtual.CODPROD);
if (aba === "parceiros" && !abaCarregada.parceiros) carregarAbaParceiros(produtoAtual.CODPROD);
}
Exemplo de Blindagem e Separação de Camadas
<%-- 1. Blindagem no topo do arquivo --%>
<c:set var="v_salesagent" value="${empty param.SALESAGENT ? '0' : param.SALESAGENT}" />
<%-- 2. Container oculto para dados (Separação JSP vs JS) --%>
<div id="data-container" style="display:none;">
[
<c:forEach var="row" items="${qDados.rows}" varStatus="loop">
{ "id": ${row.ID}, "nome": "${fn:replace(row.NOME, '"', '\\"')}" }${!loop.last ? ',' : ''}
</c:forEach>
]
</div>
<script>
// 3. JS apenas lê os dados do container
const rawData = document.getElementById('data-container').textContent.trim();
const myData = rawData ? JSON.parse(rawData) : [];
</script>
Padronizar identidade visual em componentes BI para consistência entre gadgets HTML5, tabelas e indicadores.
Diretrizes de UI/UX
--color-*) para evitar valores espalhados.BKCOLOR, FGCOLOR) quando necessário.Os nomes de tabelas e campos abaixo são representativos e podem variar conforme a implementação da instância.
<style>
:root {
--color-bg: #F5F7FA;
--color-surface: #FFFFFF;
--color-text: #1F2937;
--color-success: #1A7F37;
--color-warning: #B26A00;
--color-danger: #B42318;
--color-accent: #0E5A8A;
}
.card {
background: var(--color-surface);
color: var(--color-text);
border-radius: 8px;
padding: 12px;
}
</style>
SELECT
V.CODMETA,
V.VALOR_ATUAL,
V.VALOR_META,
CASE WHEN V.VALOR_ATUAL >= V.VALOR_META THEN '#1A7F37' ELSE '#B42318' END AS BKCOLOR,
'#FFFFFF' AS FGCOLOR
FROM AD_DADOS_VENDA V
<style>
#tblDados thead th { position: sticky; top: 0; z-index: 4; }
#tblDados .col-fixa-1 { position: sticky; left: 0; z-index: 3; }
#tblDados .col-fixa-2 { position: sticky; left: var(--fix-col-1-width); z-index: 2; }
.row-aprovacao td { background: #ffe8cc; color: #7a3a00; }
.row-parcial td { background: #fff4c4; color: #5e4c00; }
</style>
Estruturar exploração de dados com foco em performance, legibilidade e mapeamento correto de entidades Sankhya.
Boas práticas de exploração (DBExplorer)
DBEXPMAXROW) para evitar carga excessiva.SELECT * em tabelas com campos volumosos (BLOB/CLOB).Mapas essenciais do ecossistema
TDDTAB, TDDCAM, TDDOPC, TDDINS, TDDLIG.TGFCAB, TGFITE, TGFTOP, TGFPAR, TGFPRO, TGFEST, TGFVAR.TSIUSU, TSIGRU, TSIACI, TSIIMP.Padrões de SQL recomendados
CODTIPOPER + data de alteração (DHTIPOPER/DHALTER).(... = :P_PARAM OR :P_PARAM IS NULL).Os nomes de tabelas e campos abaixo são representativos e podem variar conforme a implementação da instância.
SELECT
CAB.NUNOTA,
CAB.CODPARC,
CAB.DTNEG,
ITE.SEQUENCIA,
ITE.CODPROD,
(ITE.VLRTOT - ITE.VLRDESC) AS VLR_LIQUIDO
FROM TGFCAB CAB
JOIN TGFITE ITE
ON ITE.NUNOTA = CAB.NUNOTA
JOIN TGFTOP TOP
ON TOP.CODTIPOPER = CAB.CODTIPOPER
AND TOP.DHALTER = CAB.DHTIPOPER
WHERE (CAB.CODPARC = :P_CODPARC OR :P_CODPARC IS NULL)
AND (CAB.CODVEND = :P_CODVEND OR :P_CODVEND IS NULL)
SELECT
U.CODUSU,
U.NOMEUSU,
G.NOMEGRUPO,
A.CODREL,
I.NOME AS DESCRICAO_RECURSO,
A.CONS,
A.ALTERA
FROM TSIUSU U
JOIN TSIGRU G ON G.CODGRUPO = U.CODGRUPO
JOIN TSIACI A ON A.CODGRUPO = U.CODGRUPO
JOIN TSIIMP I ON I.CODREL = A.CODREL
WHERE U.CODUSU = :P_CODUSU
ORDER BY I.NOME
Aplicar fluxo de desenvolvimento de componentes HTML5 no BI para garantir renderização, reatividade e navegação entre níveis.
Estrutura e publicação
.zip com index.html como entrada principal.assets/ (CSS, JS, libs, imagens).Fluxo de dados e parâmetros
: para bind padrão.:# para substituição literal (avaliar com cautela e validação).:@ para literal textual em cenários como LIKE./*inCollection*/.Os nomes de tabelas e campos abaixo são representativos e podem variar conforme a implementação da instância.
SELECT
C.CODCID,
C.NOMECID,
C.UF
FROM AD_TABELA_EXEMPLO C
WHERE /*inCollection*/ C.CODCID IN :P_CODCID /*inCollection*/
Reatividade e ciclo de vida
DOMContentLoaded em conteúdo injetado.<script>
function renderizarComponente(dados) {
// Atualizar DOM, gráficos e KPIs com os dados recebidos
}
function iniciar() {
const dadosIniciais = window.snkBIData || [];
renderizarComponente(dadosIniciais);
}
setTimeout(iniciar, 300);
</script>
Drill-down e eventos
Navegação multi-nível (openLevel e contrato de contexto)
NIVEL_RESUMO, NIVEL_DETALHE, NIVEL_ITEM) para evitar acoplamento em string solta.openLevel em funções dedicadas por rota de navegação (ex.: abrir detalhe por vendedor, abrir itens por parceiro).ARG_* para chaves e P_* para filtros/período).openLevel e parâmetros obrigatórios antes de navegar.var cfg = window.DASH_CONFIG || {};
var NIVEL_DETALHE = cfg.NIVEL_DETALHE || "NIVEL_B";
var NIVEL_ITEM = cfg.NIVEL_ITEM || "NIVEL_C";
function abrirNivelDetalhe(codigoEntidade) {
if (!codigoEntidade || typeof openLevel !== "function") return;
openLevel(NIVEL_DETALHE, {
ARG_CODENT: parseInt(codigoEntidade, 10),
P_PERIODO_INI: cfg.P_PERIODO_INI || "",
P_PERIODO_FIN: cfg.P_PERIODO_FIN || "",
P_CODMETA: cfg.P_CODMETA || ""
});
}
function abrirNivelItem(codigoEntidadeFilha) {
if (!codigoEntidadeFilha || typeof openLevel !== "function") return;
openLevel(NIVEL_ITEM, {
ARG_CODENT_FILHA: parseInt(codigoEntidadeFilha, 10),
P_PERIODO_INI: cfg.P_PERIODO_INI || "",
P_PERIODO_FIN: cfg.P_PERIODO_FIN || "",
P_CODMETA: cfg.P_CODMETA || ""
});
}
Segurança e bloqueio de acesso por escopo
WHERE para reaproveitamento em KPIs, grids e gráficos.CODUSU_LOG ou função equivalente de usuário logado) para evitar spoof de parâmetro de usuário.Os nomes de tabelas e campos abaixo são representativos e podem variar conforme a implementação da instância.
SELECT
M.CODMETA,
M.CODENTIDADE,
SUM(M.VLRPREV) AS VLR_PREV,
SUM(M.VLRREAL) AS VLR_REAL
FROM AD_DADOS_META M
WHERE M.CODMETA = :P_CODMETA
AND M.DTREF BETWEEN TO_DATE(:P_PERIODO_INI, 'DD/MM/YYYY')
AND TO_DATE(:P_PERIODO_FIN, 'DD/MM/YYYY')
AND EXISTS (
SELECT 1
FROM AD_META_USUARIO_LIB L
WHERE L.CODMETA = M.CODMETA
AND L.CODUSU = STP_GET_CODUSULOGADO
)
GROUP BY M.CODMETA, M.CODENTIDADE
Grid hierárquica com expansão/colapso
filhosPorPai e estado nosExpandidos para renderização incremental da árvore.var filhosPorPai = {};
var nosExpandidos = {};
function alternarNo(codNo) {
var id = String(codNo);
nosExpandidos[id] = !nosExpandidos[id];
renderizarGrid();
}
function obterVisiveis(raiz) {
var lista = [];
function visitar(pai) {
(filhosPorPai[pai] || []).forEach(function (no) {
lista.push(no);
if (nosExpandidos[String(no.CODNO)]) visitar(String(no.CODNO));
});
}
visitar(String(raiz || ""));
return lista;
}
Resiliência de carregamento
vazio) sem derrubar o layout inteiro.Navegação intra-nível (single JSP)
function abrirTelaNativa(resourceIdBase64, pkObj) {
var pk = btoa(JSON.stringify(pkObj));
top.location.href = "/mge/system.jsp#app/" + resourceIdBase64 + "/" + pk + "&pk-refresh=" + Date.now();
}
function onKpiClick(lista) {
abrirModalLista("Itens selecionados", "Navegação por atalho", lista);
}
function onGraficoClick(grupo) {
filtrarTabelaPorGrupo(grupo);
}
Feedback operacional de interface
executeQuery.Variáveis internas de segurança
CODUSU_LOG, CODGRU_LOG, CODVEN_LOG).