Relatório de Saldos e Movimentação
Introdução
O objetivo desse manual é documentar tudo o que envolve o processo do cálculo mas de uma forma mais técnica, para facilitar os testes do suporte ou futuras correções de bugs pelo desenvolvimento.
A forma como é calculado os Saldos e Movimentações dos produtos foi alterada. Anteriormente o relatório calculava toda a movimentação no momento da impressão, o que gerava lentidão quando o cliente possuía uma base de dados muito grande.
A partir de agora os saldos serão calculados diariamente, evitando que o relatório tenha uma gama de dados grande para processar.
Onde ficam armazenados os saldos?
Os saldos e movimentações serão armazenados na tabela SaldosEstoque que contém num único registro todas as movimentações do produto no dia, como: saldo anterior, entradas, saídas, ajustes e vendas.
Por exemplo:
SELECT * FROM SaldosEstoque
Resultado:
idSaldosEstoque salEmpresa salProduto salDataMovimento salSaldoAnterior
38705 1 1059 27/12/2016 0,00
salEntradas salSaidas salAjusteMais salAjusteMenos salVendas
300,00 0,00 0,00 0,00 0,00
A rotina que calcula os saldos e armazena na tabela SaldosEstoque é a sp dbo.spc_mov_processa_saldos_estoque.sql.
Quem executa a rotina de cálculo?
Quem executa a sp é o AutomatizeGO, através do plugin SaldoEstoque.dll.
O processamento é feito a cada 1 hora e filtrará somente movimentos que foram alterados a mais de meia hora, isso para evitar rodar a rotina com o cliente ainda digitando um movimento.
O resultado da execução do AutomatizeGO pode ser verificada no log do mesmo em: C:\Arquivos de Programas\Automatize\log\AutomatizeGO\.
Exemplo:
Caso o processamento ocorra com sucesso:
28/11 14:39:13,807 [30] INFO AutomatizeGo.ServiceGo - Info plugin AutomatizeGo.Plugins.SaldoEstoque, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null: EXEC SALDO ESTOQUE
Abaixo um exemplo caso ocorra um problema:
28/11 14:04:26,034 [6] INFO AutomatizeGo.ServiceGo - Info plugin AutomatizeGo.Plugins.SaldoEstoque, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null: NÃO ENCONTRADO TABELA E SP DE PROCESSAMENTO DO SALDO
Note que o erro foi “NÃO ENCONTRADO TABELA E…”. Para esse erro ocorrer, uma das seguintes situações deve ter ocorrido:
- A tabela
SaldosEstoquenão existir no banco; - A sp
dbo.spc_mov_processa_saldos_estoque.sqlnão exista; - O campo
MovimentoProdutoItem.mpiSaldoProcessadonão exista (abaixo esse campo é explicado); - O campo
MovimentoProdutoItemComposicao.mpcSaldoProcessadonão exista (abaixo esse campo é explicado).
Na primeira execução, caso a tabela SaldosEstoque esteja vazia, a sp irá recalcular toda a movimentação de todos os produtos e armazenar nela. Sendo assim, para fins de teste, caso haja a necessidade de se recalcular tudo novamente, basta apagar a tabela da seguinte forma:
TRUNCATE TABLE SaldosEstoque
O procedimento pode demorar um pouco (em alguns testes com bases grandes, menos de 3 minutos) dependendo da quantidade de movimentos do cliente, então é necessário verificar o log do AutomatizeGO se a mensagem “EXEC SALDO ESTOQUE” foi apresentada ou algum outro erro.
Em que momento serão calculados os saldos dos produtos?
O saldo é calculado nas seguintes situações:
- logo na primeira execução do plugin do AutomatizeGO ao ver que a tabela
SaldosEstoqueestá vazia - Ao alterar a situação do movimento
- Ao excluir um movimento
- Ao incluir um produto no movimento
- Ao alterar a quantidade de um produto dentro de um movimento
- Ao excluir um produto do movimento
Para permitir esses monitoramentos foram criados os seguintes gatilhos (triggers):
- dbo.tu_MovimentoProduto.sql
- é disparado quando houver alteração na situação do movimento.
- um registro será criado na tabela temporária
TmpSaldosEstoquepara que o AutomatizeGO processe essa alteração.
- dbo.td_MovimentoProduto.sql
- é disparado quando houver exclusão do movimento
- da mesma foram um registro será criado na tabela temporária
TmpSaldosEstoquepara que o AutomatizeGO processe essa exclusão.
- dbo.tu_MovimentoProdutoItem.sq
- é disparado ao alterar a quantidade de um produto dentro do movimento.
- os campos
MovimentoProdutoItem.mpiSaldoProcessadoeMovimentoProdutoItemComposicao.mpcSaldoProcessadoserão setados para 0 (zero), para que o AutomatizeGO processe a alteração
- dbo.td_MovimentoProdutoItem.sql
- é disparado excluir um produto do movimento.
- um registro será criado na tabela temporária
TmpSaldosEstoquepara cada produto e cada composição, para que o AutomatizeGO processe essa alteração.
Cliente possui centralização, como funciona?
A rotina de cálculo irá rodar nas filiais e, na central caso essa esteja indicada no campo Node.NodeDest (abaixo).
Após rodar a sp na filial os registros da tabela SaldosEstoque são enviados para a central para permitir visualizar nela o relatório de Saldos e Movimentação da filial.
Nunca os dados serão enviados da central para as filiais ou de uma filial para outra.
Não é possível visualizar o relatório de uma filial estando na outra. Por exemplo, a filial 1 não conseguirá ver da 2 e vice-versa. Na central será possível ver das duas filiais.
Bom, se a rotina sempre vai rodar nas filiais, então em que situação ela rodará também na central?
Para isso teremos que verificar a tabela Node na central.
Exemplo de central que irá rodar:
SELECT * FROM node
Resultado:
KEY = corp nodeDest = 4
Exemplo de central que não irá rodar:
SELECT * FROM node
Resultado:
KEY = corp nodeDest = NULL
Posso testar o cálculo sem que seja pelo AutomatizeGO?
Sim, como o AutomatizeGO irá rodar o cálculo dos saldos a cada 1 hora, talvez seja conveniente utilizar o script abaixo.
Não esqueça de trocar a filial no início do script, caso necessário:
SET NOCOUNT ON DECLARE @idEmpresa INT SET @idempresa = 1 -- << TROQUE A FILIAL AQUI >> -- se não tem registro na tabela então ja recalcula tudo pela primeira vez IF NOT EXISTS(SELECT top 1 1 FROM SaldosEstoque) BEGIN print 'Primeiro recalculo' EXEC spc_mov_processa_saldos_estoque @idempresa=@idEmpresa, @dataInicial='01/01/2000' UPDATE MovimentoProdutoItem SET mpiSaldoProcessado = 1 UPDATE MovimentoProdutoItemComposicao SET mpcSaldoProcessado = 1 RETURN END -- exclui os temporarios processados DELETE FROM TmpSaldosEstoque WHERE saeProcessado = 1 -- PROCURA pelos registros não processados do movimento para executar a spc_mov_processa_saldos_estoque DECLARE @curEmpresa INT, @curProduto INT, @curDt smalldatetime , @curIdMpi INT, @curIdMpc INT, @curProcessado bit, @curidTmpSaldos INT , @msgerro VARCHAR(2000) DECLARE @TBPROC TABLE ( idEmpresa INT, prod INT, dt datetime, idMpi INT, idMpc INT, sysInc datetime, processado bit, idTmpSaldos INT ) INSERT INTO @TBPROC (idEmpresa, prod, dt, idMpi, idMpc, sysInc, processado, idTmpSaldos) SELECT DISTINCT idEmpresa = mvpEmpresa , prod = MPI.mpiProduto , Dt = CAST(FLOOR(CAST(MVP.mvpData AS FLOAT)) AS DATETIME) --retira a hora da data , IDMPI = idMovimentoProdutoItem , NULL , isnull(mpi.sysRegistro, mpi.sysInc) , 0 , NULL FROM MovimentoProdutoItem MPI INNER JOIN MovimentoProduto MVP ON MVP.idMovimentoProduto = MPI.mpiMovimentoProduto WHERE MVP.mvpEmpresa = @idEmpresa AND isnull(MPI.mpiSaldoProcessado,0) = 0 AND (IsNull(MVP.mvpSituacao, 0) IN (0,9)) UNION ALL -- composição SELECT DISTINCT idEmpresa = mvpEmpresa , MPC.mpcProduto , Dt = CAST(FLOOR(CAST(MVP.mvpData AS FLOAT)) AS DATETIME) , NULL , idMovimentoProdutoItemComposicao , isnull(mpi.sysRegistro, mpi.sysInc) , 0 , NULL FROM MovimentoProdutoItemComposicao MPC INNER JOIN MovimentoProdutoItem MPI ON MPI.idMovimentoProdutoItem = MPC.mpcMovimentoProdutoItem INNER JOIN MovimentoProduto MVP ON MVP.idMovimentoProduto = MPI.mpiMovimentoProduto WHERE MVP.mvpEmpresa = @idEmpresa AND isnull(MPC.mpcSaldoProcessado,0) = 0 AND (IsNull(MVP.mvpSituacao, 0) IN (0,9)) UNION ALL SELECT DISTINCT saeEmpresa , saeProduto , CAST(FLOOR(CAST(saeData AS FLOAT)) AS DATETIME) , NULL , NULL , sysInc , 0 , idTmpSaldosEstoque FROM TmpSaldosEstoque WHERE saeProcessado = 0 AND saeEmpresa = @idEmpresa ORDER BY idEmpresa, prod, dt SELECT * FROM @TBPROC -- DEFINE UM CURSOR DINAMICO PORQUE VOU ALTERAR OS DADOS DA COLUNA -- PROCESSADO E QUERO QUE REFLITA NOS PROCESSAMENTOS SEGUINTES DECLARE curMov CURSOR LOCAL DYNAMIC FOR SELECT idEmpresa , prod , dt , processado FROM @TBPROC --WHERE (sysInc is null or sysInc < dateadd(minute, -30, current_timestamp)) -- pega somente o que foi modificado a mais de 30min -- -- ABRE O CURSOR E PERCORRE OS MOVIMENTOS -- OPEN curMov FETCH NEXT FROM curMov INTO @curEmpresa, @curProduto, @curDt, @curProcessado WHILE @@FETCH_STATUS = 0 BEGIN --print 'Processa?: ' + cast( @curProduto as varchar) + ' | ' + convert(varchar(12), @curDt, 103) + ' > ' + CAST(@curProcessado as varchar) IF @curProcessado = 0 BEGIN --print 'Calculando...' BEGIN try EXEC dbo.spc_mov_processa_saldos_estoque @idEmpresa = @curEmpresa , @DataInicial = @curDt , @DataFinal = NULL , @idProduto = @curProduto END try BEGIN catch SET @MSGErro = 'Erro processando salods: ' + ERROR_MESSAGE()+' /L:' + CAST(error_line() AS VARCHAR) RAISERROR(@MSGErro, 16, 1) GOTO SAIR END catch -- a spc_mov_processa_saldos_estoque processa todos os movimentos -- do produto a partir de uma data, então se existe mais algum -- registro do produto com uma data superior a @curDt, vou marcar como processado UPDATE MovimentoProdutoItem SET mpiSaldoProcessado = 1 WHERE IDMovimentoProdutoItem IN ( SELECT idMpi FROM @TBProc WHERE idEmpresa = @curEmpresa AND prod = @curProduto AND dt >= @curDt ) --composição UPDATE MovimentoProdutoItemComposicao SET mpcSaldoProcessado = 1 WHERE idMovimentoProdutoItemComposicao IN ( SELECT idMpc FROM @TBProc WHERE idEmpresa = @curEmpresa AND prod = @curProduto AND dt >= @curDt ) -- temporaria com os excluidos UPDATE TmpSaldosEstoque SET saeProcessado = 1 WHERE idTmpSaldosEstoque IN ( SELECT idTmpSaldos FROM @TBProc WHERE idEmpresa = @curEmpresa AND prod = @curProduto AND dt >= @curDt ) UPDATE @TBPROC SET processado = 1 WHERE idEmpresa = @curEmpresa AND prod = @curProduto AND dt >= @curDt END FETCH NEXT FROM curMov INTO @curEmpresa, @curProduto, @curDt, @curProcessado END SAIR: CLOSE curMov DEALLOCATE curMov
Considerações finais
Qual versão do AutomatizeGO que possui o plugin SaldoEstoque.dll?
A partir da 5.0.6.