Novo git da alteração

This commit is contained in:
2026-03-01 17:27:48 -03:00
parent 8b7a079235
commit 9ec8d6e70b
366 changed files with 50299 additions and 27325 deletions
Binary file not shown.
+350 -201
View File
@@ -3,234 +3,383 @@
<head>
<meta charset="utf-8">
<title>Criar Solicitação de Compras</title>
<title>Criar Solicitações de Compras</title>
<!-- Fluig SG + deps -->
<!-- Fluig Style Guide -->
<link rel="stylesheet" href="/style-guide/css/fluig-style-guide.min.css">
<link rel="stylesheet" href="/style-guide/css/fluig-style-guide-filter.min.css"> <!-- ADICIONE -->
<link rel="stylesheet" href="/style-guide/css/fluig-style-guide-filter.min.css">
<link rel="stylesheet" href="comprasV2.css">
<link rel="stylesheet" href="style.css">
<!-- jQuery + Fluig -->
<script src="/portal/resources/js/jquery/jquery.js"></script>
<script src="/portal/resources/js/jquery/jquery-ui.min.js"></script>
<script src="/resources/js/mustache/mustache-min.js"></script>
<script src="/portal/resources/js/wcmapi.js"></script>
<script src="/style-guide/js/fluig-style-guide.min.js"></script>
<script src="/style-guide/js/fluig-style-guide-filter.min.js"></script> <!-- ADICIONE -->
<script src="/style-guide/js/fluig-style-guide-filter.min.js"></script>
<script type="text/javascript" src="/webdesk/vcXMLRPC.js"></script>
<script type="text/javascript" src="anexos.js" charset="utf-8"></script>
<!-- seu JS/CSS -->
<link rel="stylesheet" href="form.css">
<script src="compras.js"></script>
<style>
/* Painel Ginseng - Cabeçalho customizado */
.panel.panel-info.painel-ginseng>.panel-heading {
background-color: #04506b !important;
/* azul institucional */
color: #fff !important;
/* texto branco */
font-size: 16px !important;
font-weight: bold !important;
border-color: #04506b !important;
display: flex;
align-items: center;
gap: 8px;
/* espaço entre ícone e texto */
}
/* Painel Ginseng - Corpo */
.panel.panel-info.painel-ginseng {
border-color: #04506b !important;
}
/* Ícone dentro do painel */
.panel.panel-info.painel-ginseng .flaticon {
color: #fff !important;
/* ícone branco no header */
font-size: 18px;
}
.m-t-10 {
margin-top: 10px;
}
.m-t-15 {
margin-top: 15px;
}
.text-right {
text-align: right;
}
.cursor-pointer {
cursor: pointer;
}
</style>
</head>
<body>
<div class="fluig-style-guide">
<form name="form" role="form">
<form class="form-horizontal" role="form" name="form">
<div class="fs-display-block">
<!-- PAINEL: Dados da solicitação -->
<div class="panel panel-info">
<div class="panel-heading"><strong>Dados da Solicitação</strong></div>
<div class="panel panel-info painel-ginseng">
<div class="panel-heading" style="font-size:16px;">
<i class="flaticon flaticon-tests-central icon-md" aria-hidden="true"></i>
<strong>Dados da Solicitação</strong>
</div>
<div class="panel-body">
<div class="container activity-all">
<div class="activity activity-0 activity-1">
<!-- Linha 1: Empresa e Filial -->
<div class="row m-t-10">
<div class="col-sm-4">
<label>Empresa <span class="text-danger">*</span></label>
<input type="zoom" class="form-control requiredInput" name="estabelecimento" id="estabelecimento"
data-zoom="{
'displayKey':'LOJA',
'datasetId':'dsSysCompany',
'placeholder': 'Pesquisar por nome do estabelecimento.',
'fields':[{'field':'DESCRICAO','label':'nome da loja','standard':'true','search':'true'},
{'field':'BPCS','label':'estabelecimento','search':'true'},
{'field':'CODIGO','label':'PROTHEUS','search':'true'}]
}" />
<p class="text-danger text-error">Preenchimento obrigatório.</p>
</div>
<div class="row">
<div class="col-sm-6">
<label>Empresa</label>
<span class="required text-danger"><strong> * </strong></span>
<input type="zoom" class="form-control requiredInput" name="estabelecimento" id="estabelecimento"
data-zoom="{
'displayKey':'LOJA',
'datasetId':'dsSysCompany',
'placeholder': 'Pesquisar por nome do estabelecimento.',
'fields':[
{
'field':'BPCS',
'label':'Filial',
'standard':'true',
'search':'true'
},
{
'field':'DESCRICAO',
'label':'estabelecimento',
'search':'true'
}
]
}" />
<p class="text-danger text-error">
Preenchimento obrigatório.
</p>
</div>
<div class="col-sm-4">
<label>Cnpj</label>
<input type="hidden" name="filialCodigo" id="filialCodigo">
<input type="text" name="filialDesc" id="filialDesc" class="form-control"
placeholder="Selecione uma filial no campo Empresa" readonly>
</div>
<div class="col-sm-2">
<label>Filial Entrega</label>
<input type="text" name="filialDesc" id="filialDesc" class="form-control zoom"
placeholder="Selecione..." readonly>
</div>
<div class="col-sm-2">
<label>Estado</label>
<input type="text" name="filialest" id="filialest" class="form-control" readonly>
</div>
<!-- Linha 2: Tipo de Solicitação -->
<div class="col-sm-4">
<label>Tipo de Solicitação <span class="text-danger">*</span></label>
<select name="tipoSolicitacao" id="tipoSolicitacao" class="form-control" required>
<option value="">Selecione</option>
<option>Material</option>
<option>Serviço</option>
</select>
</div>
</div>
<div class="row m-t-15">
<div class="col-sm-2">
<label>Tipo de Solicitação <span class="text-danger">*</span></label>
<select name="tipoSolicitacao" id="tipoSolicitacao" class="form-control" required>
<option value="">Selecione</option>
<option>Material</option>
<option>Serviço</option>
</select>
</div>
</div>
<div class="col-sm-6">
<label>Produto/Serviço <span class="text-danger">*</span></label>
<div class="input-group">
<input type="hidden" name="produtoCod" id="produtoCod">
<input type="text" name="produtoDesc" id="produtoDesc" class="form-control zoom" data-zoom="produto"
placeholder="Buscar..." readonly required>
<span class="input-group-btn">
<button class="btn btn-default" type="button" id="btnBuscaProd">
<span class="fluigicon fluigicon-search"></span>
</button>
<button class="btn btn-danger" type="button" id="btnLimpaProd">
<span class="fluigicon fluigicon-trash"></span>
</button>
</span>
<div class="row m-t-10">
<div class="col-sm-4">
<label>Produto/Serviço <span class="text-danger">*</span></label>
<div class="input-group">
<input type="hidden" name="produtoCod" id="produtoCod">
<input type="text" name="DescoProduto" id="DescoProduto" class="form-control"
placeholder="Buscar..." readonly required>
<span class="input-group-btn">
<button class="btn btn-info" type="button" id="btnBuscaProd">
<span class="fluigicon fluigicon-search"></span>
</button>
<button class="btn btn-danger" type="button" id="btnLimpaProd">
<span class="fluigicon fluigicon-trash"></span>
</button>
</span>
</div>
</div>
<div class="col-sm-2">
<label>Último Preço</label>
<input type="text" name="ultimoPreco" id="ultimoPreco" class="form-control text-right" value="0,00"
readonly>
</div>
<div class="col-sm-2">
<label>Preço de Referência</label>
<input type="text" name="precoRef" id="precoRef" class="form-control text-right" value="0,00">
</div>
<div class="col-sm-2">
<label>Quantidade <span class="text-danger">*</span></label>
<div class="input-group">
<span class="input-group-btn">
<button type="button" class="btn btn-danger" id="qtdMenos"></button>
</span>
<input type="number" min="0" step="1" name="quantidade" id="quantidade"
class="form-control text-center" value="0" required>
<span class="input-group-btn">
<button type="button" class="btn btn-success" id="qtdMais">+</button>
</span>
</div>
</div>
<div class="col-sm-2">
<label>Data da Necessidade <span class="text-danger">*</span></label>
<div class="input-group">
<input type="text" name="dataNec" id="dataNec" class="form-control" placeholder="dd/mm/aaaa"
required>
<span class="input-group-btn">
<button type="button" class="btn btn-primary">
<span class="fluigicon fluigicon-calendar" aria-hidden="true"></span>
</button>
</span>
</div>
</div>
</div>
<div class="row m-t-10">
<div class="col-sm-4">
<label>Centro de Custo <span class="text-danger">*</span></label>
<input type="zoom" class="form-control" name="centroCusto" id="centroCusto" data-zoom="{
'displayKey':'centroCusto',
'datasetId':'ds_centroCusto',
'placeholder': 'Pesquisar número ou nome do centro de custo.',
'fields':[
{
'field':'codigoCentroCusto',
'label':'Código',
'standard':'true',
'search':'true'
},
{
'field':'centroCusto',
'label':'Centro de custo',
'search':'true'
},
{
'field': 'gestorCentroCusto',
'label': 'Gestor',
'visible': 'false'
}
]
}" required />
</div>
<div class="col-sm-4">
<label>Gestor do centro de Custo</label>
<input type="text" name="gestorCentroCusto" id="gestorCentroCusto" class="form-control"
placeholder="Selecione o centro de custo no campo anterior" readonly>
</div>
<div class="col-sm-2">
<label>Emergencial?</label>
<select name="emergencial" id="emergencial" class="form-control">
<option>Não</option>
<option>Sim</option>
</select>
</div>
<div class="col-sm-2">
<label>Total</label>
<input type="text" name="total" id="total" class="form-control text-right" value="R$ 0,00" readonly>
</div>
</div>
<div class="row m-t-10">
<div class="col-sm-2">
<label class="control-label">Adicionar Anexo</label>
<div class="componentAnexo">
<div class="input-group">
<input type="hidden" class="descAnexo" name="fdComprovante" value="Anexo" />
<input type="text" id="fnComprovante" name="fnComprovante"
class="form-control inputAnexo input-sm" placeholder="Selecione um arquivo" readonly />
</div>
<div class="icones">
<button type="button" class="btnUpFile btn btn-success btn-sm" data-acao="upload"
onclick="anexo(event)" title="Selecionar">
<i class="fluigicon fluigicon-file-upload icon-sm"></i>
</button>
<button style="display: none;" type="button" class="btnViewerFile btn btn-info btn-sm"
data-acao="viewer" onclick="anexo(event)" title="Visualizar" disabled>
<i class="fluigicon fluigicon-eye-open icon-sm"></i>
</button>
<button style="display: none;" type="button" class="btnDownloadFile btn btn-info btn-sm"
data-acao="download" onclick="anexo(event)" title="Download" disabled>
<i class="fluigicon fluigicon-download icon-sm"></i>
</button>
</div>
</div>
</div>
</div>
<div class="row m-t-10">
<div class="col-sm-10">
<label>Observação</label>
<textarea name="obs" id="obs" class="form-control" rows="3"></textarea>
</div>
</div>
<div class="row m-t-10">
<div class="form-group text-center">
<div class="col-sm-12">
<button type="button" id="btnAddItem" class="btn btn-primary" style="display: none;">
<span class="fluigicon fluigicon-add"></span> Adicionar Item
</button>
</div>
</div>
</div>
<!-- PAINEL: Itens -->
<div class="panel panel-default">
<div class="panel-heading"><strong>Itens</strong></div>
<div class="panel-body">
<table id="tbItens" tablename="tbItens" class="table table-bordered" noaddbutton="true"
nodeletebutton="true">
<thead>
<tr>
<th>#</th>
<th>Código</th>
<th>Descrição</th>
<th>Último Preço</th>
<th>Pc Ref</th>
<th>Qtd</th>
<th>Total</th>
<th>Data Necessidade</th>
<th>Excluir</th>
</tr>
</thead>
<tbody>
<!-- LINHA MODELO -->
<tr class="tableRow" detail="true">
<td><span class="tableIndex"></span></td>
<td>
<input type="text" name="Codproduto" class="form-control" readonly>
</td>
<td>
<input type="text" name="produtoDesc" class="form-control" readonly>
</td>
<td>
<input type="text" name="ultimoPrecoItem" class="form-control text-right" value="0,00"
readonly>
</td>
<td>
<input type="text" name="Refpreco" class="form-control text-right" value="0,00" readonly>
</td>
<td>
<input type="number" name="qtd" class="form-control text-right" min="0" step="1" readonly>
</td>
<td>
<input type="text" name="totalValor" class="form-control text-right" readonly>
</td>
<td>
<input type="text" name="dataNecItem" class="form-control" placeholder="dd/mm/aaaa">
</td>
<td class="text-center">
<button type="button" class="btn btn-link" onclick="fnWdkRemoveChild(this)">
<span class="fluigicon fluigicon-trash"></span>
</button>
</td>
</tr>
</tbody>
</table>
<div class="row m-t-10">
<div class="col-sm-3">
<label>Total de Itens</label>
<input type="text" name="totalItens" id="totalItens" class="form-control text-right" readonly
value="0">
</div>
<div class="col-sm-3">
<label>Total Geral</label>
<input type="text" name="totalGeral" id="totalGeral" class="form-control text-right" readonly
value="R$ 0,00">
</div>
</div>
</div>
</div>
</div>
<div class="col-sm-2">
<label>Último Preço</label>
<input type="text" name="ultimoPreco" id="ultimoPreco" class="form-control text-right" value="0,00"
readonly>
</div>
<div class="col-sm-2">
<label>Quantidade <span class="text-danger">*</span></label>
<div class="input-group">
<span class="input-group-btn">
<button type="button" class="btn btn-default" id="qtdMenos"></button>
</span>
<input type="number" min="0" step="1" name="quantidade" id="quantidade"
class="form-control text-center" value="0" required>
<span class="input-group-btn">
<button type="button" class="btn btn-default" id="qtdMais">+</button>
</span>
<div class="activity activity-4">
<div>
<br />
<h2>
<i class="flaticon flaticon-check-circle" aria-hidden="true"></i>
&nbsp;Validação do centro de custo &nbsp;
</h2>
<h6>
Informações complementares sobre a validação do centro de custo
da solicitação.
</h6>
<br />
</div>
<div class="row">
<div class="col-md-10 col-xs-6">
<div class="form-group">
<label for="userValidacaoGestor">Gestor aprovador do centro de custo</label>
<input type="text" name="userValidacaoGestor" id="userValidacaoGestor" class="form-control"
readonly />
</div>
</div>
<div class="col-md-2 col-xs-6">
<div class="form-group">
<label for="dataValidacaoGestor">Data</label>
<input type="text" name="dataValidacaoGestor" id="dataValidacaoGestor" class="form-control"
readonly />
</div>
</div>
</div>
<div class="row justificativa-activity-4">
<div class="col-md-12">
<div class="form-group">
<label for="justificativaDecisaoGestor">Justificativa</label>
<textarea class="form-control" name="justificativaDecisaoGestor" id="justificativaDecisaoGestor"
placeholder="Descreva a justificativa para a validação do centro de custo."></textarea>
</div>
</div>
</div>
</div>
<div class="col-sm-2">
<label>Data da Necessidade <span class="text-danger">*</span></label>
<input type="text" name="dataNec" id="dataNec" class="form-control" placeholder="dd/mm/aaaa" required>
</div>
</div>
<div class="row m-t-10">
<div class="col-sm-6">
<label>Centro de Custo <span class="text-danger">*</span></label>
<input type="zoom" name="ccDesc" id="ccDesc" class="form-control" data-zoom="{
'displayKey':'centroCusto',
'datasetId':'ds_centroCusto',
'placeholder': 'Pesquisar número ou nome do centro de custo.',
'fields':[
{
'field':'codigoCentroCusto',
'label':'Código',
'standard':'true',
'search':'true'
},
{
'field':'centroCusto',
'label':'Centro de custo',
'search':'true'
},
{
'field': 'gestorCentroCusto',
'label': 'Gestor',
'visible': 'false'
},
{
'field': 'emailGestor',
'label': 'Email do gestor',
'visible': 'false'
},
{
'field': 'idGestor',
'label': 'Id do gestor',
'visible': 'false'
}
]
}" />
</div>
<div class="col-sm-3">
<label>Compra Emergencial?</label>
<select name="emergencial" id="emergencial" class="form-control">
<option>Não</option>
<option>Sim</option>
</select>
</div>
<div class="col-sm-3">
<label>Total</label>
<input type="text" name="total" id="total" class="form-control text-right" value="R$ 0,00" readonly>
</div>
</div> <!-- aqui sim fecha corretamente a row -->
<div class="row m-t-20">
<div class="col-sm-12">
<button type="button" class="btn btn-success" id="btnAddAnexo">
<span class="fluigicon fluigicon-upload"></span> Adicionar Anexo
</button>
</div>
<div class="col-sm-12">
<label>Observação do Item</label>
<textarea name="obs" id="obs" class="form-control" rows="2"></textarea>
</div>
</div>
<div class="text-center m-t-15">
<button type="button" id="btnAddItem" class="btn btn-primary">
<span class="fluigicon fluigicon-add"></span> Adicionar Item
</button>
</div>
</div>
</div>
<!-- PAINEL: Itens -->
<div class="panel panel-default">
<div class="panel-heading"><strong>Itens</strong></div>
<div class="panel-body">
<table id="tbItens" tablename="tbItens" class="table table-bordered">
<thead>
<tr>
<th>Produto</th>
<th>Centro de Custo</th>
<th>Qtd</th>
<th>Preço Ref.</th>
<th>Total</th>
<th>Ação</th>
</tr>
</thead>
<tbody>
<!-- LINHA MODELO (detail=true) -->
<tr class="tableRow" detail="true">
<td>
<input type="hidden" name="CodProd" class="form-control">
<input type="text" name="DescProd" class="form-control" readonly>
</td>
<td>
<input type="hidden" name="Codcc" class="form-control">
<input type="text" name="Desccc" class="form-control" readonly>
</td>
<td><input type="number" name="qtd" class="form-control text-right" min="0" step="1" value="0"></td>
<td><input type="text" name="Refpreco" class="form-control text-right" value="0,00"></td>
<td><input type="text" name="totalProd" class="form-control text-right" value="R$ 0,00" readonly></td>
<td class="text-center">
<button type="button" class="btn btn-link" onclick="fnWdkRemoveChild(this)">
<span class="fluigicon fluigicon-trash"></span>
</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</form>
</div>
</div>
</form>
</div>
<!-- Seu JS no final -->
<script src="compras.js"></script>
</body>
</html>
+468 -176
View File
@@ -1,203 +1,495 @@
// ======== DATASETS (troque aqui se o id for diferente) ========
const DS_EMPRESAS = 'dsSyscompany';
const DS_FILIAIS = 'dsFiliais'; // se o seu for outro, troque aqui
const DS_CC = 'ds_centroCusto';
const DS_PRODUTOS = 'ProdutoTI'; // se o id for "ProdutosTI", troque aqui
// ======== MAPEAMENTO DE CAMPOS (ajuste se seus datasets usam nomes diferentes) ========
// Empresas / Filiais / Centro de Custo
const EMP_FIELDS = { code: 'codigo', desc: 'descricao' };
const FIL_FIELDS = { code: 'codigo', desc: 'descricao' };
const CC_FIELDS = { code: 'codigo', desc: 'descricao' };
// Produtos (inclui alternativas típicas do Protheus)
const PROD_FIELDS = {
code: ['codigo','B1_COD','CODIGO'],
desc: ['descricao','B1_DESC','DESCRICAO'],
um: ['um','B1_UM','UM'],
lastPrice: ['ultimoPreco','ULTIMO_PRECO','ULT_PRECO','ULTIMOPRECO']
};
// ======== INIT ========
$(document).ready(function () {
FLUIGC.calendar('#dataNec');
try {
FLUIGC.calendar('#dataNec');
} catch (e) {
console.warn("Erro ao iniciar calendário:", e);
}
// qty/preço
$('#precoRef').on('blur', recalcTotal);
$('#quantidade').on('input', recalcTotal);
$('#qtdMais').on('click', () => stepQtd(1));
$('#qtdMenos').on('click', () => stepQtd(-1));
toggleBotaoAddItem(); // inicia escondido
// produto
$('#btnBuscaProd').on('click', openZoomProduto);
$('#btnLimpaProd').on('click', () => {
$('#produtoCod,#produtoDesc').val('');
$('#ultimoPreco').val('0,00');
/* ========= Eventos ========= */
$('#precoRef, #quantidade').on('input', recalcTotal);
$('#qtdMais').on('click', function () {
let v = parseInt($('#quantidade').val() || '0', 10);
$('#quantidade').val(v + 1);
recalcTotal();
});
// zooms
bindZoom($('#empresaDesc'), DS_EMPRESAS, EMP_FIELDS, 'empresaCodigo','empresaDesc');
bindZoom($('#filialDesc'), DS_FILIAIS, FIL_FIELDS, 'filialCodigo','filialDesc');
bindZoom($('#ccDesc'), DS_CC, CC_FIELDS, 'ccCod','ccDesc');
$('#qtdMenos').on('click', function () {
let v = parseInt($('#quantidade').val() || '0', 10);
v = Math.max(0, v - 1);
$('#quantidade').val(v);
recalcTotal();
});
// itens
$('#btnAddItem').on('click', addItem);
$('#btnBuscaProd').on('click', consultarProdutos);
$('#btnLimpaProd').on('click', limparCamposPrincipais);
$('#btnAddItem').on('click', adicionarItem);
});
// ======== HELPERS ========
function stepQtd(n){
const $q = $('#quantidade');
let v = parseInt($q.val()||'0',10)+n;
if (v < 0) v = 0;
$q.val(v);
recalcTotal();
}
$(document).ready(function () {
if ($("#formMode").val() == "VIEW") {
showAndBlock(["all"]);
} else {
//show the right fields
var activity = $("#activity").val();
var requestDate = getCurrentDate();
function parseMoneyBR(s){
if(!s) return 0;
s = (''+s).replace(/[^\d,,-]/g,'').replace(/\./g,'').replace(',', '.');
const v = Number(s);
return isNaN(v) ? 0 : v;
}
function formatMoneyBR(v){
return v.toLocaleString('pt-BR',{style:'currency',currency:'BRL'});
}
function recalcTotal(){
const qtd = parseFloat($('#quantidade').val()||'0');
const preco = parseMoneyBR($('#precoRef').val());
const tot = (qtd * preco) || 0;
$('#total').val(formatMoneyBR(tot));
}
$(".activity").hide();
// Compat: usa filtermodal OU filterModal, dependendo da versão do SG
function openFilterModalCompat(opts){
var fn = (FLUIGC && (FLUIGC.filtermodal || FLUIGC.filterModal));
if (!fn) {
FLUIGC.toast({
title: 'Erro: ',
message: 'Plugin de filtro (filtermodal) não está carregado. Verifique os includes do Style Guide.',
type: 'danger'
});
return { onSelected: function(){} };
}
return fn(opts);
}
$(".activity-" + activity).show();
// pega a primeira chave existente no item
function pick(obj, keys){
if (!Array.isArray(keys)) return obj[keys];
for (const k of keys) if (obj[k] !== undefined && obj[k] !== null) return obj[k];
return '';
}
function firstKey(keys){ return Array.isArray(keys) ? keys[0] : keys; }
if (activity == 0 || activity == 1) {
//primeira atividade
//$("#dataAbertura").val(requestDate[0] + " - " + requestDate[1]);
$("#dataAbertura").val(requestDate[0]);
// ======== ZOOMS GENÉRICOS (empresa, filial, centro de custo) ========
function bindZoom($input, datasetId, fieldsMap, hiddenIdFld, displayFld){
const codeK = fieldsMap.code, descK = fieldsMap.desc;
} else if (activity == 4) {
showAndBlock([0]);
$("#userValidacaoGestor").val($("#currentUserName").val());
$("#dataValidacaoGestor").val(
requestDate[0] + " - " + requestDate[1]
);
updt_line();
} else if (activity == 6) {
showAndBlock([0, 4]);
$("#userValidacaoCompras").val($("#currentUserName").val());
$("#dataValidacaoCompras").val(
requestDate[0] + " - " + requestDate[1]
);
$input.on('click', function(){
openFilterModalCompat({
title: 'Selecionar',
size: 'large',
filterFields: codeK + ', ' + descK,
source: {
url: '/api/public/ecm/dataset/search',
contentType: 'application/json',
root: 'content',
limit: 50,
pattern: '',
param: { datasetId: datasetId }
},
columns: [
{ title: 'Código', data: codeK },
{ title: 'Descrição', data: descK }
],
onSelected: function(data){
$('#'+hiddenIdFld).val(data[codeK]);
$('#'+displayFld).val(data[codeK] + ' - ' + data[descK]);
if ($("#justificativaDecisaoGestor").val() == "") {
$(".justificativa-activity-4").hide();
}
});
});
}
updt_line();
// ======== ZOOM DE PRODUTO ========
function openZoomProduto(){
openFilterModalCompat({
title: 'Buscar Produto/Serviço',
size: 'large',
filterFields: firstKey(PROD_FIELDS.code) + ', ' + firstKey(PROD_FIELDS.desc),
source: {
url: '/api/public/ecm/dataset/search',
contentType: 'application/json',
root: 'content',
limit: 50,
param:{ datasetId: DS_PRODUTOS }
},
columns: [
{ title: 'Código', data: firstKey(PROD_FIELDS.code) },
{ title: 'Descrição', data: firstKey(PROD_FIELDS.desc) },
{ title: 'UM', data: firstKey(PROD_FIELDS.um) },
{ title: 'Último Preço', data: firstKey(PROD_FIELDS.lastPrice) }
],
onSelected: function(item){
const cod = pick(item, PROD_FIELDS.code);
const desc = pick(item, PROD_FIELDS.desc);
const ult = pick(item, PROD_FIELDS.lastPrice);
} else if (activity == 57) {
$("#userCotacaoGestor").val($("#currentUserName").val());
$("#dataAprovCompras").val(
requestDate[0] + " - " + requestDate[1]
);
showAndBlock([0, 4, 6]);
$('#produtoCod').val(cod);
$('#produtoDesc').val(cod + ' - ' + desc);
$('#ultimoPreco').val(ult || '0,00');
recalcTotal();
if ($("#justificativaDecisaoGestor").val() == "") {
$(".justificativa-activity-4").hide();
}
if ($("#justificativaDecisaoCompras").val() == "") {
$(".justificativa-activity-6").hide();
}
updt_line();
} else if (activity == 31) {
$("#userRealizacaoCompras").val($("#currentUserName").val());
$("#dataRealizacaoCompras").val(
requestDate[0] + " - " + requestDate[1]
);
showAndBlock([0, 4, 6, 57]);
if ($("#justificativaDecisaoGestor").val() == "") {
$(".justificativa-activity-4").hide();
}
if ($("#justificativaDecisaoCompras").val() == "") {
$(".justificativa-activity-6").hide();
}
updt_line();
} else if (activity == 18) {
if ($("justificativaValidaProblema") != "") {
showAndBlock([0, 4, 6, 24, 57, 31]);
} else {
showAndBlock([0, 4, 6, 31]);
}
$("#userValidacaoItens").val($("#currentUserName").val());
$("#dataValidacaoItens").val(requestDate[0] + " - " + requestDate[1]);
if ($("#justificativaDecisaoGestor").val() == "") {
$(".justificativa-activity-4").hide();
}
if ($("#justificativaDecisaoCompras").val() == "") {
$(".justificativa-activity-6").hide();
}
if ($("#consideracoesCompras").val() == "") {
$(".justificativa-activity-31").hide();
}
$("input[name=validacaoItens]").on("change", function () {
$(".justificativaDecisaoItens").hide();
if (
$("input[name=validacaoItens]:checked").val() == "incorreto" ||
$("input[name=validacaoItens]:checked").val() == "naoEntregue"
) {
$(".justificativaDecisaoItens").show();
}
});
$("input[name=validacaoItens]").trigger("change");
updt_line();
} else if (activity == 24) {
showAndBlock([0, 4, 6, 31, 18]);
if ($("#justificativaDecisaoGestor").val() == "") {
$(".justificativa-activity-4").hide();
}
if ($("#justificativaDecisaoCompras").val() == "") {
$(".justificativa-activity-6").hide();
}
if ($("#consideracoesCompras").val() == "") {
$(".justificativa-activity-31").hide();
}
updt_line();
}
});
}
});
function getCurrentDate() {
var newDate = new Date();
var h = newDate.getHours();
if (h < 10) {
h = "0" + h;
}
var m = newDate.getMinutes();
if (m < 10) {
m = "0" + m;
}
var hour = h + ":" + m; // + ":" + s;
var day = newDate.getDate();
if (day < 10) {
day = "0" + day;
}
var month = newDate.getMonth() + 1;
if (month < 10) {
month = "0" + month;
}
newDate = day + "/" + month + "/" + newDate.getFullYear();
var currentDate = [newDate, hour];
return currentDate;
}
// ======== ITENS (tabela filha) ========
function addItem(){
if(!$('#produtoCod').val()){
FLUIGC.toast({title:'Atenção: ', message:'Selecione um produto.', type:'warning'}); return;
}
if(!$('#ccCod').val()){
FLUIGC.toast({title:'Atenção: ', message:'Selecione o centro de custo.', type:'warning'}); return;
}
const qtd = parseFloat($('#quantidade').val()||'0');
if(qtd <= 0){
FLUIGC.toast({title:'Atenção: ', message:'Quantidade deve ser > 0.', type:'warning'}); return;
}
const row = wdkAddChild('tbItens');
$('#produtoCod___'+row).val($('#produtoCod').val());
$('#produtoDesc___'+row).val($('#produtoDesc').val());
$('#ccCod___'+row).val($('#ccCod').val());
$('#ccDesc___'+row).val($('#ccDesc').val());
$('#qtd___'+row).val(qtd);
$('#precoRef___'+row).val($('#precoRef').val());
$('#total___'+row).val($('#total').val());
}
// ======== VALIDAÇÃO (se usar o hook validateForm) ========
function validateForm(form){
var msgs = [];
function req(id, label){ if(!form.getValue(id)) msgs.push('Preencha "'+label+'".'); }
req('tipoSolicitacao','Tipo de Solicitação');
req('produtoCod','Produto/Serviço');
req('quantidade','Quantidade');
req('dataNec','Data da Necessidade');
req('ccCod','Centro de Custo');
if(msgs.length){ throw msgs.join('\n'); }
function toggleBotaoAddItem() {
const temProduto = $('#produtoCod').val() || $('#DescoProduto').val();
if (temProduto && temProduto.trim() !== "") {
$('#btnAddItem').show();
} else {
$('#btnAddItem').hide();
}
}
function setSelectedZoomItem(selectedItem) {
var name_item = selectedItem.inputId;
var ehItem = name_item.indexOf("___") >= 0;
var indice = "";
if (ehItem){
var valores = name_item.split("___");
name_item = valores[0];
indice = valores[1];
if (selectedItem.inputId === "estabelecimento") {
// joga o código da filial (cnpj, etc.)
$("#filialDesc").val(selectedItem.CNPJ); // supondo que o dataset dsSysCompany devolva o campo CNPJ
$("#filialest").val(selectedItem.ESTADO); // supondo que o dataset dsSysCompany devolva o campo ESTADO
}
if (selectedItem.inputId === "centroCusto") {
// joga o nome do gestor
$("#gestorCentroCusto").val(selectedItem.gestorCentroCusto); // supondo que o dataset dsCentroCusto devolva o campo GestorCentroCusto
}
}
function removedZoomItem(removedItem) {
if (removedItem.inputId === "estabelecimento") {
// Quando remover a empresa, limpa os campos relacionados
$("#filialDesc").val('');
$("#filialest").val('');
}
}
/* ========= Config ========= */
const DATASET_PRODUTOS = "dsProd"; // dataset de produtos
let todosProdutos = [];
let paginaAtual = 1;
const itensPorPagina = 10;
/* ========= Utils ========= */
function escapeHTML(txt) {
return $('<div>').text(txt == null ? '' : String(txt)).html();
}
function brMoneyToFloat(v) {
if (!v) return 0;
return parseFloat(String(v).replace(/\./g, '').replace(',', '.')) || 0;
}
function floatToBRL(n) {
try {
return new Intl.NumberFormat('pt-BR', { style: 'currency', currency: 'BRL' }).format(n || 0);
} catch (_) {
return 'R$ ' + (n || 0).toFixed(2).replace('.', ',');
}
}
/* ========= Modal Consulta Produtos ========= */
function consultarProdutos() {
FLUIGC.modal({
title: 'Consulta de Produtos',
content:
'<div id="buscaProduto" style="margin-bottom:10px;">' +
'<input type="text" id="filtroDescricao" class="form-control" placeholder="Buscar por descrição..." onkeyup="filtrarProdutos()">' +
'</div>' +
'<div id="listaProdutos"></div>' +
'<div id="paginacaoProdutos" style="margin-top:10px; text-align:center;"></div>',
id: 'modalConsultaProdutos',
size: 'large',
actions: [{ 'label': 'Fechar', 'autoClose': true }]
}, function (err) {
if (!err) carregaListaProdutos();
});
}
function carregaListaProdutos(filtro) {
// Só chama o dataset se ainda não carregou nada
if (todosProdutos.length === 0) {
let dataset = DatasetFactory.getDataset(DATASET_PRODUTOS, null, null, null);
todosProdutos = dataset && dataset.values ? dataset.values : [];
console.log("Produtos carregados do dataset:", todosProdutos.length);
}
paginaAtual = 1;
renderizaProdutos(filtro);
}
function filtrarProdutos() {
paginaAtual = 1;
renderizaProdutos();
}
function renderizaProdutos(filtro) {
filtro = (filtro || $('#filtroDescricao').val() || '').toLowerCase();
// Agora filtra no cache (sem chamar dataset de novo)
const produtosFiltrados = todosProdutos.filter(p =>
(p.descricao || '').toLowerCase().includes(filtro) ||
(p.codigo || '').toLowerCase().includes(filtro) ||
(p.medida || '').toLowerCase().includes(filtro)
);
const inicio = (paginaAtual - 1) * itensPorPagina;
const fim = inicio + itensPorPagina;
const produtosPagina = produtosFiltrados.slice(inicio, fim);
let html = `
<table class="table table-striped">
<thead>
<tr>
<th>Codigo</th>
<th>Descricao</th>
<th>UM</th>
<th>Ultimo Preco</th>
<th>Acao</th>
</tr>
</thead>
<tbody>
`;
if (produtosPagina.length === 0) {
html += `
<tr>
<td colspan="5" class="text-center text-muted">
Nenhum produto encontrado
</td>
</tr>`;
} else {
produtosPagina.forEach(produto => {
const codigo = escapeHTML(produto['codigo']);
const descricao = escapeHTML(produto['descricao']);
const um = escapeHTML(produto['medida'] || '');
const preco = produto['ultimoPreco'] || '0,00';
html += `
<tr>
<td>${codigo}</td>
<td>${descricao}</td>
<td>${um}</td>
<td>${preco}</td>
<td>
<button class="btn btn-sm btn-success selecionar-produto"
data-codigo="${codigo}"
data-descricao="${descricao}"
data-preco="${preco}">
Selecionar
</button>
</td>
</tr>`;
});
}
html += `</tbody></table>`;
$('#listaProdutos').html(html);
renderizaPaginacao(produtosFiltrados.length);
}
function renderizaPaginacao(totalItens) {
const totalPaginas = Math.ceil(totalItens / itensPorPagina);
let html = '';
if (totalPaginas > 1) {
if (paginaAtual > 1) {
html += '<button class="btn btn-sm btn-default" onclick="mudarPagina(' + (paginaAtual - 1) + ')">Anterior</button> ';
}
html += ' Pagina ' + paginaAtual + ' de ' + totalPaginas + ' ';
if (paginaAtual < totalPaginas) {
html += '<button class="btn btn-sm btn-default" onclick="mudarPagina(' + (paginaAtual + 1) + ')">Proxima</button>';
}
}
$('#paginacaoProdutos').html(html);
}
function mudarPagina(n) {
paginaAtual = n;
renderizaProdutos();
}
/* ========= Seleção ========= */
function selecionarProduto(codigo, descricao, preco) {
$('#produtoCod').val(codigo);
$('#DescoProduto').val(codigo + ' - ' + descricao);
$('#ultimoPreco').val(preco);
FLUIGC.toast({ title: 'Produto', message: 'Selecionado: ' + descricao, type: 'success' });
$('#modalConsultaProdutos').modal('hide');
toggleBotaoAddItem(); // 👈 mostra botão
}
/* ========= Eventos delegados ========= */
$(document).on('click', '.selecionar-produto', function () {
const codigo = $(this).data('codigo');
const descricao = $(this).data('descricao');
const preco = $(this).data('preco');
selecionarProduto(codigo, descricao, preco);
});
// Digitação em Qtd ou Preço Ref → recalcula automaticamente
$(document).on('input', 'input[name^="qtd___"], input[name^="Refpreco___"]', function () {
recalcLinha(this);
});
/* ========= Total / Quantidade ========= */
function recalcTotal() {
const q = parseFloat($('#quantidade').val() || '0');
const p = brMoneyToFloat($('#precoRef').val());
const t = (q * p) || 0;
$('#total').val(floatToBRL(t));
}
/* ========= Index das linhas ========= */
function atualizarIndex() {
$('#tbItens tbody tr.tableRow').each(function (i) {
if (!$(this).is('[detail=true]')) {
$(this).find('.tableIndex').text(i);
}
});
}
/* ========= Totais da tabela ========= */
function atualizarTotais() {
let totalItens = 0;
let totalGeral = 0;
// Pega todos os valores da tabela
const dadosTabela = $('#tbItens').find('input').serializeArray();
// Transforma em objeto chave:valor para fácil acesso
const mapValores = {};
dadosTabela.forEach(item => {
mapValores[item.name] = item.value;
});
// Percorre por linha (cada linha tem um sufixo ___1, ___2...)
let idx = 1;
while (mapValores['qtd___' + idx] !== undefined) {
const qtd = parseFloat(mapValores['qtd___' + idx] || 0);
const preco = brMoneyToFloat(mapValores['Refpreco___' + idx] || "0");
const total = qtd * preco;
totalItens += qtd;
totalGeral += total;
// Atualiza campo total da linha
if ($(`#totalValor___${idx}`).length) {
$(`#totalValor___${idx}`).val(floatToBRL(total));
}
if (name_item == "estabelecimento")
{ $("#filialDesc").val(selectedItem["CODIGO"]); }
idx++;
}
// Atualiza rodapé
$('#totalItens').val(totalItens);
$('#totalGeral').val(floatToBRL(totalGeral));
}
/* ========= Recalcula uma linha ========= */
function recalcLinha(el) {
const $linha = $(el).closest('tr');
const qtd = parseFloat($linha.find('input[name^="qtd___"]').val() || '0');
const preco = brMoneyToFloat($linha.find('input[name^="Refpreco___"]').val() || '0');
const total = qtd * preco;
$linha.find('input[name^="totalValor___"]').val(floatToBRL(total));
atualizarTotais();
}
/* ========= Remover linha ========= */
function fnWdkRemoveChild(el) {
$(el).closest('tr').remove();
atualizarIndex();
atualizarTotais();
}
/* ========= Adicionar Item ========= */
function adicionarItem() {
const cod = $('#produtoCod').val();
const desc = $('#DescoProduto').val();
const ult = $('#ultimoPreco').val() || '0,00';
const qtd = $('#quantidade').val();
const prec = $('#precoRef').val();
const tot = $('#total').val();
const dataN = $('#dataNec').val();
if (!cod) {
FLUIGC.toast({ title: 'Atenção', message: 'Selecione um produto/serviço', type: 'warning' });
return;
}
const idx = wdkAddChild('tbItens');
setTimeout(() => {
$('input[name="Codproduto___' + idx + '"]').val(cod);
const somenteDesc = desc.includes('-') ? desc.split('-').slice(1).join('-').trim() : desc;
$('input[name="produtoDesc___' + idx + '"]').val(somenteDesc);
$('input[name="ultimoPrecoItem___' + idx + '"]').val(ult);
$('input[name="qtd___' + idx + '"]').val(qtd).on('input', function () { recalcLinha(this); });
$('input[name="Refpreco___' + idx + '"]').val(prec).on('input', function () { recalcLinha(this); });
$('input[name="totalValor___' + idx + '"]').val(tot);
$('input[name="dataNecItem___' + idx + '"]').val(dataN);
atualizarIndex();
atualizarTotais();
}, 0);
FLUIGC.toast({ title: 'Item', message: 'Adicionado à lista', type: 'success' });
limparCamposPrincipais();
}
/* ========= Limpar campos principais ========= */
function limparCamposPrincipais() {
$('#produtoCod, #DescoProduto').val('');
$('#ultimoPreco').val('0,00');
$('#quantidade').val('0');
$('#precoRef').val('0,00');
$('#total').val('R$ 0,00');
$('#fnComprovante').val('');
toggleBotaoAddItem(); // 👈 mostra botão
}
@@ -0,0 +1,73 @@
.painel-ginseng > .panel-heading {
background-color: #7a113a !important; /* azul do logo */
color: #fff !important; /* texto branco */
border-color: #0b556b !important;
}
.painel-ginseng {
border-color: #0b556b !important;
}
/* tabela ocupa 100% */
#tbItens {
table-layout: fixed; /* força obedecer larguras definidas */
width: 100%;
}
/* todas as células centralizam por padrão */
#tbItens td, #tbItens th {
vertical-align: middle;
text-align: center;
white-space: nowrap;
}
/* coluna Código */
#tbItens th:nth-child(2),
#tbItens td:nth-child(2) {
width: 12%;
}
/* coluna Descrição (maior espaço) */
#tbItens th:nth-child(3),
#tbItens td:nth-child(3) {
width: 30%;
white-space: normal; /* deixa quebrar */
text-align: left;
}
/* Último Preço */
#tbItens th:nth-child(4),
#tbItens td:nth-child(4) {
width: 15%;
}
/* Preço Ref. */
#tbItens th:nth-child(5),
#tbItens td:nth-child(5) {
width: 10%;
}
/* Quantidade */
#tbItens th:nth-child(6),
#tbItens td:nth-child(6) {
width: 10%;
}
/* Total */
#tbItens th:nth-child(7),
#tbItens td:nth-child(7) {
width: 15%;
}
/* Data Necessidade */
#tbItens th:nth-child(8),
#tbItens td:nth-child(8) {
width: 18%;
}
/* Excluir */
#tbItens th:nth-child(9),
#tbItens td:nth-child(9) {
width: 10%;
}
@@ -1,3 +0,0 @@
.m-t-10 { margin-top: 10px; }
.m-t-15 { margin-top: 15px; }
.text-right { text-align: right; }
@@ -0,0 +1,71 @@
.componentAnexo{
display: flex;
align-items: center;
flex-wrap: nowrap;
}
.componentAnexo .input-group{
flex-grow: 1;
position: relative;
}
.componentAnexo .inputAnexo{
color: blue!important;
border-radius: 0!important;
}
.componentAnexo .icones{
display: flex;
align-items: center;
justify-content: flex-end;
}
.componentAnexo .icones .btn{
outline: none!important;
outline-offset: unset!important;
border-radius: 0;
padding-left: 7px;
padding-right: 7px;
margin-left: 3px;
}
.componentAnexo .icones .btn-default,
.componentAnexo .icones .btn-default:focus,
.componentAnexo .icones .btn-default:active,
.componentAnexo .icones .btn-default:hover{
border: solid 1px #58595b!important;
}
.componentAnexo .icones .btn-primary,
.componentAnexo .icones .btn-primary:focus,
.componentAnexo .icones .btn-primary:active,
.componentAnexo .icones .btn-primary:hover{
border: solid 1px #58595b!important;
}
.componentAnexo .icones .btn-success,
.componentAnexo .icones .btn-success:focus,
.componentAnexo .icones .btn-success:active,
.componentAnexo .icones .btn-success:hover{
border: solid 1px #1ab83f!important;
}
.componentAnexo .icones .btn-info,
.componentAnexo .icones .btn-info:focus,
.componentAnexo .icones .btn-info:active,
.componentAnexo .icones .btn-info:hover{
border: solid 1px #1eaad9!important;
}
.componentAnexo .icones .btn-warning,
.componentAnexo .icones .btn-warning:focus,
.componentAnexo .icones .btn-warning:active,
.componentAnexo .icones .btn-warning:hover{
border: solid 1px #e3b420!important;
}
.componentAnexo .icones .btn-danger,
.componentAnexo .icones .btn-danger:focus,
.componentAnexo .icones .btn-danger:active,
.componentAnexo .icones .btn-danger:hover{
border: solid 1px #cc3d3d!important;
}