This commit is contained in:
2026-05-08 15:30:25 -03:00
parent 2bbc71eee2
commit 20ae0d1f2f
14 changed files with 4600 additions and 3503 deletions
@@ -410,17 +410,29 @@ function badgeClassByStatus(status) {
var s = String(status || "").toLowerCase();
if (!s) return "badge bg-secondary";
if (s.indexOf("erro") >= 0 || s.indexOf("reprov") >= 0 || s.indexOf("cancel") >= 0) return "badge bg-danger";
if (s.indexOf("sucesso") >= 0 || s.indexOf("aprov") >= 0 || s.indexOf("gerad") >= 0 || s.indexOf("liberad") >= 0) return "badge bg-success";
if (s.indexOf("aguard") >= 0 || s.indexOf("pend") >= 0) return "badge bg-warning";
if (s.indexOf("sucesso") >= 0 || s.indexOf("aprov") >= 0 || s.indexOf("gerad") >= 0 || s.indexOf("liberad") >= 0) return "badge bg-success";
if (s.indexOf("bloque") >= 0 || s.indexOf("rejeit") >= 0) return "badge bg-danger";
return "badge bg-info";
}
function setBadge(selector, valor) {
var texto = String(valor || "").trim() || "-";
var classe = badgeClassByStatus(texto);
var textoLower = texto.toLowerCase();
// Mantem os badges de resumo da SC sem destaque verde para sucesso/geracao.
if (
(selector === "#statusSCProtheus_label" && textoLower.indexOf("sc cadastrada com sucesso") >= 0) ||
(selector === "#statusSC_label" && textoLower.indexOf("pedido gerado") >= 0)
) {
classe = "badge bg-info";
}
$(selector)
.text(texto)
.removeClass("bg-secondary bg-success bg-danger bg-warning bg-info")
.addClass(badgeClassByStatus(texto));
.addClass(classe);
}
function normalizarStatusCadastro(statusCadastro, numeroSC) {
@@ -487,11 +499,35 @@ function possuiFornecedorCotacao(row) {
return !!(fornece && loja);
}
function textoUtilFornecedor(valor) {
var txt = String(valor || "").trim();
if (!txt) return "";
if (txt === "-" || txt === "--") return "";
if (txt.toUpperCase() === "NULL") return "";
return txt;
}
function obterNomeFornecedorCompleto(row) {
var candidatos = [
textoUtilFornecedor(row.C8_FORNOME),
textoUtilFornecedor(row.A2_NOME),
textoUtilFornecedor(row.A2_NREDUZ)
].filter(function (v) { return !!v; });
if (!candidatos.length) return "";
candidatos.sort(function (a, b) {
if (b.length !== a.length) return b.length - a.length;
return a.localeCompare(b);
});
return candidatos[0];
}
function classificarLinhaCotacao(row) {
var status = String(row.STATUS || "").trim().toUpperCase();
if (linhaCotacaoVencedora(row)) {
return { tipo: "PEDIDO_GERADO", label: "VENCEDOR", badge: "badge bg-success", tipoFornecedor: "Fornecedor homologado" };
return { tipo: "PEDIDO_GERADO", label: "GANHOU", badge: "badge bg-success", tipoFornecedor: "Fornecedor homologado" };
}
if (possuiFornecedorCotacao(row)) {
@@ -499,7 +535,7 @@ function classificarLinhaCotacao(row) {
}
if (status.indexOf("PEDIDO_GERADO") >= 0) {
return { tipo: "PERDEDOR", label: "PERDEDOR", badge: "badge bg-warning", tipoFornecedor: "-" };
return { tipo: "PERDEDOR", label: "", badge: "badge bg-warning", tipoFornecedor: "-" };
}
if (!possuiFornecedorCotacao(row)) {
@@ -515,6 +551,41 @@ function formatarValorCotacao(valor) {
return floatToBRL(parseNumeroCotacao(txt));
}
function formatarDocumentoVisual(valor) {
var txt = String(valor || "").trim();
if (!txt || documentoEhPlaceholder(txt)) return "-";
return txt;
}
function classeCardCotacao(tipo) {
if (tipo === "PEDIDO_GERADO") return "sc-cotacao-card sc-cotacao-card--winner";
if (tipo === "FORNECEDOR_HOMOLOGADO") return "sc-cotacao-card sc-cotacao-card--homologado";
if (tipo === "NOVO_FORNECEDOR") return "sc-cotacao-card sc-cotacao-card--novo";
if (tipo === "PERDEDOR") return "sc-cotacao-card sc-cotacao-card--perdedor";
return "sc-cotacao-card";
}
function renderizarResumoCotacao(totalCotacoes, totalComPedido, menorTotal) {
var alvo = $("#cotacaoResultadoResumo");
if (!alvo.length) return;
var menorTxt = (menorTotal !== null) ? floatToBRL(menorTotal) : "-";
alvo.html([
'<div class="sc-cotacao-kpi">',
' <span class="sc-cotacao-kpi-label">Quantidade</span>',
' <span class="sc-cotacao-kpi-value">' + escapeHTML(String(totalCotacoes || 0)) + "</span>",
"</div>",
'<div class="sc-cotacao-kpi">',
' <span class="sc-cotacao-kpi-label">Pedidos Gerados</span>',
' <span class="sc-cotacao-kpi-value">' + escapeHTML(String(totalComPedido || 0)) + "</span>",
"</div>",
'<div class="sc-cotacao-kpi">',
' <span class="sc-cotacao-kpi-label">Menor Valor</span>',
' <span class="sc-cotacao-kpi-value">' + escapeHTML(menorTxt) + "</span>",
"</div>"
].join(""));
}
function comporDataHora(data, hora) {
var d = String(data || "").trim();
var h = String(hora || "").trim();
@@ -614,13 +685,14 @@ function renderizarResultadoCotacao(resumo) {
if (!resumo || !resumo.detalhes || !resumo.detalhes.length) {
setBadge("#cotacaoResultado_label", "Sem cotacao");
lista.html('<li class="sc-cotacao-item"><span class="sc-cotacao-item-info">Aguardando vinculo da cotacao na SC.</span></li>');
renderizarResumoCotacao(0, 0, null);
lista.html('<div class="sc-cotacao-empty">Aguardando vinculo da cotacao na SC.</div>');
return;
}
var statusResumo = "Sem fornecedor definido";
if ((resumo.qtdPedidoGerado || 0) > 0) {
statusResumo = "Pedido gerado em " + resumo.qtdPedidoGerado + " item(ns)";
statusResumo = "Cotacao concluida";
} else if ((resumo.qtdFornecedorHomologado || 0) > 0) {
statusResumo = "Fornecedor homologado em " + resumo.qtdFornecedorHomologado + " item(ns)";
} else if ((resumo.qtdNovoFornecedor || 0) > 0) {
@@ -629,24 +701,69 @@ function renderizarResultadoCotacao(resumo) {
setBadge("#cotacaoResultado_label", statusResumo);
var html = resumo.detalhes.map(function (linha) {
var detalhesOrdenados = resumo.detalhes.slice().sort(function (a, b) {
var pesoA = (a.tipo === "PEDIDO_GERADO") ? 0 : 1;
var pesoB = (b.tipo === "PEDIDO_GERADO") ? 0 : 1;
if (pesoA !== pesoB) return pesoA - pesoB;
var totalA = parseNumeroCotacao(a.total);
var totalB = parseNumeroCotacao(b.total);
if (totalA === totalB) return 0;
return totalA - totalB;
});
var menorTotal = null;
var totalComPedido = 0;
for (var i = 0; i < detalhesOrdenados.length; i++) {
var t = parseNumeroCotacao(detalhesOrdenados[i].total);
if (t > 0 && (menorTotal === null || t < menorTotal)) menorTotal = t;
if (!documentoEhPlaceholder(detalhesOrdenados[i].pedido)) totalComPedido++;
}
renderizarResumoCotacao(detalhesOrdenados.length, totalComPedido, menorTotal);
var html = detalhesOrdenados.map(function (linha) {
var numeroTxt = escapeHTML(linha.numero || "-");
var itemTxt = escapeHTML(linha.item || "-");
var produtoTxt = escapeHTML(linha.produto || "-");
var fornecedorNomeTxt = escapeHTML(linha.fornecedorNome || linha.fornecedor || "Fornecedor nao informado");
var fornecedorTxt = escapeHTML(linha.fornecedor || "-");
var tipoFornecedorTxt = escapeHTML(linha.tipoFornecedor || "-");
var precoTxt = escapeHTML(formatarValorCotacao(linha.preco));
var totalTxt = escapeHTML(formatarValorCotacao(linha.total));
var pedidoTxt = escapeHTML(linha.pedido || "-");
var pedidoTxt = escapeHTML(formatarDocumentoVisual(linha.pedido));
var statusApiTxt = escapeHTML(linha.statusApi || "-");
var statusTxt = escapeHTML(linha.label || "EM COTACAO");
var statusRaw = String(linha.label || "").trim();
var statusTxt = escapeHTML(statusRaw);
var badgeStatus = linha.badge || "badge bg-secondary";
var classeCard = classeCardCotacao(linha.tipo);
var qtdTxt = escapeHTML(String(linha.quantidade || "-"));
var entregaTxt = escapeHTML(normalizarDataProtheus(linha.dataPrevista || ""));
var condicaoTxt = escapeHTML(linha.condicao || "-");
return [
'<li class="sc-cotacao-item">',
' <span class="sc-cotacao-item-info"><strong>Cotacao ' + numeroTxt + '</strong> | Item ' + itemTxt + " | Produto: " + produtoTxt + " | Fornecedor: " + fornecedorTxt + " | Tipo: " + tipoFornecedorTxt + " | Preco: " + precoTxt + " | Total: " + totalTxt + " | Pedido: " + pedidoTxt + " | Status: " + statusApiTxt + "</span>",
' <span class="' + badgeStatus + '">' + statusTxt + "</span>",
"</li>"
'<article class="' + classeCard + '">',
' <div class="sc-cotacao-top">',
" <div>",
' <div class="sc-cotacao-supplier">' + fornecedorNomeTxt + "</div>",
' <div class="sc-cotacao-code">Fornecedor: ' + fornecedorTxt + " | Tipo: " + tipoFornecedorTxt + "</div>",
" </div>",
statusRaw ? (' <span class="' + badgeStatus + '">' + statusTxt + "</span>") : "",
" </div>",
' <div class="sc-cotacao-meta">',
' <div class="sc-cotacao-meta-box"><span class="sc-cotacao-meta-label">Preco unitario</span><span class="sc-cotacao-meta-value">' + precoTxt + "</span></div>",
' <div class="sc-cotacao-meta-box"><span class="sc-cotacao-meta-label">Total</span><span class="sc-cotacao-meta-value">' + totalTxt + "</span></div>",
" </div>",
' <div class="sc-cotacao-footer">',
' <span><strong>Cotacao:</strong> ' + numeroTxt + "</span>",
' <span><strong>Item:</strong> ' + itemTxt + "</span>",
' <span><strong>Qtd:</strong> ' + qtdTxt + "</span>",
' <span><strong>Produto:</strong> ' + produtoTxt + "</span>",
' <span><strong>Cond.:</strong> ' + condicaoTxt + "</span>",
' <span><strong>Data entrega:</strong> ' + (entregaTxt || "-") + "</span>",
' <span><strong>Pedido:</strong> ' + pedidoTxt + "</span>",
' <span><strong>Status API:</strong> ' + statusApiTxt + "</span>",
" </div>",
"</article>"
].join("");
}).join("");
@@ -658,13 +775,166 @@ function renderizarErroCotacao(mensagem) {
if (!lista.length) return;
setBadge("#cotacaoResultado_label", "Erro consulta cotacao");
renderizarResumoCotacao(0, 0, null);
lista.html(
'<li class="sc-cotacao-item"><span class="sc-cotacao-item-info">' +
'<div class="sc-cotacao-empty">' +
escapeHTML(mensagem || "Nao foi possivel carregar a cotacao no momento.") +
"</span></li>"
"</div>"
);
}
function mapearStatusAlcada(codigo) {
var status = String(codigo || "").trim();
if (status === "01") return { label: "Pendente em niveis anteriores", badge: "badge bg-warning", classe: "pendente" };
if (status === "02") return { label: "Pendente", badge: "badge bg-warning", classe: "pendente" };
if (status === "03") return { label: "Aprovado", badge: "badge bg-success", classe: "aprovado" };
if (status === "04") return { label: "Bloqueado", badge: "badge bg-danger", classe: "bloqueado" };
if (status === "05") return { label: "Aprovado/rejeitado pelo nivel", badge: "badge bg-info", classe: "pendente" };
if (status === "06") return { label: "Rejeitado", badge: "badge bg-danger", classe: "bloqueado" };
if (status === "07") return { label: "Documento rejeitado ou bloqueado por outro usuario", badge: "badge bg-danger", classe: "bloqueado" };
return { label: status ? ("Status " + status) : "Sem status", badge: "badge bg-secondary", classe: "" };
}
function primeiroTextoPreenchido() {
for (var i = 0; i < arguments.length; i++) {
var txt = String(arguments[i] || "").trim();
if (txt) return txt;
}
return "";
}
function montarResumoRastreioPedido(pedidos, numeroPedido) {
if (!pedidos || !pedidos.length) return null;
var pedidoFiltro = normalizarCodigoComparacao(numeroPedido);
var pedidoBase = null;
for (var i = 0; i < pedidos.length; i++) {
var num = normalizarCodigoComparacao(String((pedidos[i] || {}).C7_NUM || ""));
if (!pedidoFiltro || num === pedidoFiltro) {
pedidoBase = pedidos[i];
break;
}
}
if (!pedidoBase) pedidoBase = pedidos[0];
var assinaturas = [];
var vistos = {};
for (var j = 0; j < pedidos.length; j++) {
var ped = pedidos[j] || {};
var alcadas = ped.ALCADAS || [];
for (var k = 0; k < alcadas.length; k++) {
var a = alcadas[k] || {};
var chave = [
String(a.CR_TIPO || "").trim(),
String(a.CR_USER || "").trim(),
String(a.CR_USERLIB || "").trim(),
String(a.CR_STATUS || "").trim(),
String(a.CR_PRAZO || "").trim(),
String(a.CR_DATALIB || "").trim()
].join("|");
if (vistos[chave]) continue;
vistos[chave] = true;
var statusInfo = mapearStatusAlcada(a.CR_STATUS);
assinaturas.push({
statusCodigo: String(a.CR_STATUS || "").trim(),
statusLabel: statusInfo.label,
badge: statusInfo.badge,
classe: statusInfo.classe,
tipo: String(a.CR_TIPO || "").trim(),
prazo: normalizarDataProtheus(a.CR_PRAZO),
dataLiberacao: normalizarDataProtheus(a.CR_DATALIB),
usuario: primeiroTextoPreenchido(a.CR_USER_NOME, a.AK_NOME, a.CR_USER),
usuarioLiberacao: primeiroTextoPreenchido(a.CR_USERLIB_NOME, a.AK_NOME_USERLIB, a.CR_USERLIB)
});
}
}
var resumo = {
numero: String(pedidoBase.C7_NUM || "").trim(),
statusPedido: String(pedidoBase.STATUS || "").trim(),
fornecedor: primeiroTextoPreenchido(pedidoBase.A2_NOME, pedidoBase.A2_NREDUZ, pedidoBase.C7_FORNECE),
emissao: normalizarDataProtheus(pedidoBase.C7_EMISSAO),
entrega: normalizarDataProtheus(pedidoBase.C7_DATPRF),
assinaturas: assinaturas
};
return resumo;
}
function renderizarErroRastreioPedido(mensagem) {
setBadge("#pedidoRastreio_label", "Erro no rastreio");
$("#pedidoRastreioResumo").text("Nao foi possivel consultar o rastreio do pedido.");
$("#pedidoRastreioAssinaturas").html(
'<div class="sc-cotacao-empty">' + escapeHTML(mensagem || "Erro ao consultar rastreio.") + "</div>"
);
}
function renderizarRastreioPedido(resumo) {
var badge = $("#pedidoRastreio_label");
var resumoEl = $("#pedidoRastreioResumo");
var lista = $("#pedidoRastreioAssinaturas");
if (!badge.length || !resumoEl.length || !lista.length) return;
if (!resumo || !resumo.numero) {
setBadge("#pedidoRastreio_label", "Sem pedido");
resumoEl.text("Sem pedido vinculado.");
lista.html('<div class="sc-cotacao-empty">Sem assinaturas para exibir.</div>');
return;
}
var possuiBloqueio = resumo.assinaturas.some(function (a) { return a.classe === "bloqueado"; });
var possuiPendente = resumo.assinaturas.some(function (a) { return a.classe === "pendente"; });
var todosAprovados = resumo.assinaturas.length > 0 && resumo.assinaturas.every(function (a) { return a.statusCodigo === "03"; });
var statusTopo = "Pedido pendente";
if (possuiBloqueio) statusTopo = "Pedido bloqueado";
else if (todosAprovados) statusTopo = "Pedido aprovado";
else if (!possuiPendente && resumo.statusPedido) statusTopo = resumo.statusPedido;
setBadge("#pedidoRastreio_label", statusTopo);
var resumoTxt = "Pedido " + (resumo.numero || "-");
if (resumo.fornecedor) resumoTxt += " | Fornecedor: " + resumo.fornecedor;
if (resumo.emissao) resumoTxt += " | Emissao: " + resumo.emissao;
if (resumo.entrega) resumoTxt += " | Entrega: " + resumo.entrega;
resumoEl.text(resumoTxt);
if (!resumo.assinaturas.length) {
lista.html('<div class="sc-cotacao-empty">Pedido encontrado, mas sem alcadas retornadas pela API.</div>');
return;
}
var html = resumo.assinaturas.map(function (a, idx) {
var classeCard = "sc-pedido-ass-card" + (a.classe ? (" " + a.classe) : "");
var nivel = "Nivel " + (idx + 1);
var usuario = escapeHTML(a.usuario || "-");
var usuarioLib = escapeHTML(a.usuarioLiberacao || "-");
var prazo = escapeHTML(a.prazo || "-");
var dataLib = escapeHTML(a.dataLiberacao || "-");
var tipo = escapeHTML(a.tipo || "-");
var statusLabel = escapeHTML(a.statusLabel || "-");
var badgeStatus = a.badge || "badge bg-secondary";
return [
'<article class="' + classeCard + '">',
' <div class="sc-pedido-ass-header">',
' <div class="sc-pedido-ass-title">' + nivel + " - " + usuario + "</div>",
' <span class="' + badgeStatus + '">' + statusLabel + "</span>",
" </div>",
' <div class="sc-pedido-ass-meta">',
' <span><strong>Tipo:</strong> ' + tipo + "</span>",
' <span><strong>Prazo:</strong> ' + prazo + "</span>",
' <span><strong>Data liberacao:</strong> ' + dataLib + "</span>",
' <span><strong>Liberado por:</strong> ' + usuarioLib + "</span>",
" </div>",
"</article>"
].join("");
}).join("");
lista.html(html);
}
function montarResumoCotacaoPorLinhas(rows, cotacao) {
if (!rows || !rows.length) return null;
@@ -699,10 +969,14 @@ function montarResumoCotacaoPorLinhas(rows, cotacao) {
detalhes.push({
numero: String(row.C8_NUM || "").trim(),
item: item,
produto: String(row.C8_PRODUTO || "").trim(),
produto: String(row.B1_DESC || row.C8_DESCRI || row.C8_PRODUTO || "").trim(),
quantidade: String(row.C8_QUANT || "").trim(),
condicao: String(row.C8_COND || "").trim(),
dataPrevista: String(row.C8_DATPRF || "").trim(),
fornecedor: possuiFornecedorCotacao(row)
? (String(row.C8_FORNECE || "").trim() + "/" + String(row.C8_LOJA || "").trim())
: "",
fornecedorNome: obterNomeFornecedorCompleto(row),
preco: row.C8_PRECO,
total: row.C8_TOTAL,
pedido: String(row.C8_NUMPED || "").trim(),
@@ -881,7 +1155,7 @@ function montarStatusAndamento(scRow, cotacao, pedido) {
return "";
}
function preencherResumoSC(resumoCotacao, erroCotacao) {
function preencherResumoSC(resumoCotacao, erroCotacao, resumoPedido, erroPedido) {
var numero = valorCampo("numeroSCProtheus");
var statusCadastro = valorCampo("statusSCProtheus");
var solicitante = valorCampo("solicitanteSCProtheus");
@@ -934,6 +1208,14 @@ function preencherResumoSC(resumoCotacao, erroCotacao) {
} else {
renderizarResultadoCotacao(null);
}
if (erroPedido) {
renderizarErroRastreioPedido(erroPedido);
} else if (resumoPedido && resumoPedido.numero) {
renderizarRastreioPedido(resumoPedido);
} else {
renderizarRastreioPedido(null);
}
}
function consultarAndamentoSC() {
@@ -974,7 +1256,19 @@ function consultarAndamentoSC() {
}
}
preencherResumoSC(resumoCotacao, erroCotacao);
var resumoPedido = null;
var erroPedido = String(row.PEDIDO_ERRO || "").trim();
var jsonPedido = String(row.PEDIDO_JSON || "").trim();
if (jsonPedido) {
try {
var pedidos = JSON.parse(jsonPedido);
resumoPedido = montarResumoRastreioPedido(pedidos, pedido);
} catch (ePed) {
if (!erroPedido) erroPedido = "Falha ao ler rastreio do pedido retornado pelo ds_consultaSC";
}
}
preencherResumoSC(resumoCotacao, erroCotacao, resumoPedido, erroPedido);
} catch (e) {
console.warn("Nao foi possivel consultar andamento da SC no ds_consultaSC:", e);
}
@@ -1001,47 +1295,11 @@ function parseNumeroCotacao(valor) {
return parseFloat(texto) || 0;
}
function recalcularTotalCotacao() {
var soma = 0;
$("input[name^='selecionado___']").each(function() {
var idCampo = String($(this).attr("id") || "");
if (idCampo.indexOf("___") < 0) return;
var idx = idCampo.split("___")[1];
// pega qtd e preço da linha
var qtd = parseNumeroCotacao($("#qtdc___" + idx).val() || "0");
var preco = parseNumeroCotacao($("#preco___" + idx).val() || "0");
// calcula total da linha
var total = qtd * preco;
// atualiza campo total da linha
$("#total___" + idx).val(total.toFixed(2));
// se marcado, acumula na soma
if ($(this).is(":checked")) {
soma += total;
}
});
// Atualiza o hidden e dispara change pro Fluig gravar
$("#valorTotalCotacao").val(soma.toFixed(2)).trigger("change");
$("#valorTotalCotacaoLabel").text(floatToBRL(soma));
}
// dispara sempre que marcar/desmarcar
$(document).on("change", "input[name^='selecionado___']", function() {
recalcularTotalCotacao();
});
// recalcula ao abrir a atividade
$(document).ready(function() {
recalcularTotalCotacao();
});
// garante que o total seja salvo antes de enviar a atividade
function beforeSendValidate(numState, nextState) {
console.log("Executando beforeSendValidate, recalculando total...");
recalcularTotalCotacao();
return true; // precisa retornar true senão bloqueia o envio
var campoTotal = $("#valorTotalCotacao");
if (campoTotal.length && !String(campoTotal.val() || "").trim()) {
campoTotal.val("0").trigger("change");
}
return true;
}