Olá. Consegui resolver meu problema. Comecei a resolve-lo dividindo-o em partes.
As tabelas envolvidas(estão resumidas):
- tab_Produtos(ProdutoID, ProdutoDescricao, ProdutoSaldoEstoque)
- tab_Balancos(BalancoID, BalancoData)
- tab_BalancosItens (BalancoItem_ID, BalancoItem_BalancoID, BalancoItem_ProdutoID,
balancoItem_quantidade)
OBS: As tabelas de entradas(tab_Entradas, tab_EntradasItens),
saidas(tab_Saidas, tab_SaidasItens) e
vendas(tab_Vendas, tab_vendasItens) seguem o modelo das de balancos.
As tabelas de saídas, são usadas para saídas que não sejam vendas, estravio de produtos por exemplo.
Criei algumas funções para retornar os valores esperados:
- getDataUltBalanco():data - Retorna a data do ultimo balanço, a partir desta data serão contabilizadas as quantidades de entradas, saidas e vendas.
- getQtdBalanco(produto:inteiro):duplo - Retorna a quantidade total de um determinado produto informado no ultimo balanço.
- getQtdEntradas(produto:inteiro, dataInicial:data):duplo - Retorna a quantidade total de entradas de um determinado produto informado a partir da data obtida com a função getDataUltBalanco().
- getQtdSaidas(produto:inteiro, dataInicial:data):duplo - Retorna a quantidade total de saidas de um determinado produto informado a partir da data obtida com a função getDataUltBalanco().
- getQtdVendas(produto:inteiro, dataInicial:data):duplo - Retorna a quantidade total de vendas de um determinado produto informado a partir da data obtida com a função getDataUltBalanco().
OBS: Nas funções abaixo, o DECLARE ... HANDLER foi alterado, para que ao tentar selecionar um dado e este não for encontrado, um valor padrão seja selecionado, sem interromper assim a execução da função.
Note, também que o codigo das funções getQtdSaida e getQtdVenda não diferem da função getQtdEntrada (A não ser é claro pelas colunas das tabelas).
DROP FUNCTION getDataUltBalanco;
DELIMITER //
CREATE FUNCTION getDataUltBalanco() RETURNS DATE
BEGIN
DECLARE dataInicial DATE;
/* O HANDLER ALTERADO */
DECLARE CONTINUE HANDLER FOR NOT FOUND SET dataInicial = CONCAT(YEAR(CURDATE()),'/01/01');
SELECT BalancoData INTO dataInicial
FROM tab_Balancos
WHERE BalancoID = (SELECT MAX(BalancoID) FROM tab_Balancos);
RETURN dataInicial;
END//
DELIMITER ;
DROP FUNCTION getQtdBalanco;
DELIMITER //
CREATE FUNCTION getQtdBalanco(produto INTEGER) RETURNS DOUBLE
BEGIN
DECLARE qtdBalanco DOUBLE;
/* O HANDLER ALTERADO */
DECLARE CONTINUE HANDLER FOR NOT FOUND SET qtdBalanco = IFNULL(qtdBalanco,0);
SELECT SUM(BI.BalancoItem_Quantidade) INTO qtdBalanco
FROM tab_Balancos B, tab_BalancosItens BI
WHERE BI.BalancoItem_BalancoID = B.BalancoID AND
B.BalancoID = (SELECT MAX(balancoid) FROM tab_balancos) AND
BI.BalancoItem_ProdutoID = produto
GROUP BY BI.balancoItem_ProdutoID;
RETURN qtdBalanco;
END//
DELIMITER ;
DROP FUNCTION getQtdEntrada;
DELIMITER //
CREATE FUNCTION getQtdEntrada(produto INTEGER, dataInicial DATE) RETURNS DOUBLE
BEGIN
DECLARE qtdEntrada DOUBLE;
/* O HANDLER ALTERADO */
DECLARE CONTINUE HANDLER FOR NOT FOUND SET qtdEntrada = IFNULL(qtdEntrada,0);
SELECT SUM(EI.EntradaItem_Quantidade) INTO qtdEntrada
FROM tab_Entradas E, tab_EntradasItens EI
WHERE EI.EntradaItem_EntradaID = E.EntradaID AND
E.EntradaData >= dataInicial AND
EI.EntradaItem_ProdutoID = produto
GROUP BY EI.EntradaItem_ProdutoID;
RETURN qtdEntrada;
END//
DELIMITER ;
DROP FUNCTION getQtdSaida;
DELIMITER //
CREATE FUNCTION getQtdSaida(produto INTEGER, dataInicial DATE) RETURNS DOUBLE
BEGIN
DECLARE qtdSaida DOUBLE;
/* O HANDLER ALTERADO */
DECLARE CONTINUE HANDLER FOR NOT FOUND SET qtdSaida = IFNULL(qtdSaida,0);
SELECT SUM(SI.SaidaItem_Quantidade) INTO qtdSaida
FROM tab_Saidas S, tab_SaidasItens SI
WHERE SI.SaidaItem_SaidaID = S.SaidaID AND
S.SaidaData >= dataInicial AND
SI.SaidaItem_ProdutoID = produto
GROUP BY SI.SaidaItem_ProdutoID;
RETURN qtdSaida;
END//
DELIMITER ;
DROP FUNCTION getQtdVenda;
DELIMITER //
CREATE FUNCTION getQtdVenda(produto INTEGER, dataInicial DATE) RETURNS DOUBLE
BEGIN
DECLARE qtdVenda DOUBLE;
/* O HANDLER ALTERADO */
DECLARE CONTINUE HANDLER FOR NOT FOUND SET qtdVenda = IFNULL(qtdVenda,0);
SELECT SUM(VI.VendaItem_Quantidade) INTO qtdVenda
FROM tab_Vendas V, tab_VendasItens VI
WHERE VI.VendaItem_VendaID = V.VendaID AND
V.VendaData >= dataInicial AND
VI.VendaItem_ProdutoID = produto
GROUP BY VI.VendaItem_ProdutoID;
RETURN qtdVenda;
END//
DELIMITER ;
Finalmente os triggers. Nos triggers eu chamei as funções para retornar os valores que estas se destinam a retornar (dahhh, descobri isto sozinho, *risos*), armazenei os valores em variaveis, depois é só atualizar o saldo do estoque do produto onde:
saldo = (qtdBalancos + qtdEntradas) - (qtdSaidas + qtdVendas).
Note que no código dos triggers a única diferença será o ID do produto que se deseja atualizar, e o código do balanço pode ser aplicado para os triggers de entradas, saídas e vendas. Sendo assim para não repetir o código este pode ser encapsulado em um procedimento, por exemplo, setAtualizaEstoque(produto:inteiro). Assim o trigger só precisaria do codigo:
CALL setAtualizaEstoque(<ID_do_produto>);
Pela lógica, deve funcionar, mas não testei. Então vamos ao código dos triggers.
--TRIGGERS DA TABELA DE BALANCOS
DROP TRIGGER tab_Balancos_Deleta;
DELIMITER //
CREATE TRIGGER tab_Balancos_Deleta BEFORE DELETE ON tab_Balancos FOR EACH ROW
BEGIN
DELETE FROM tab_BalancosItens WHERE BalancoItem_BalancoID = OLD.BalancoID;
END//
DELIMITER ;
--TRIGGERS DA TABELA DE ITENS DE BALANCOS
DROP TRIGGER tab_BalancosItens_InsereSaldoProduto;
DELIMITER //
CREATE TRIGGER tab_BalancosItens_InsereSaldoProduto AFTER INSERT ON tab_BalancosItens FOR EACH ROW
BEGIN
DECLARE dataInicial DATE;
DECLARE qtdBalancos, qtdEntradas, qtdSaidas, qtdVendas DOUBLE;
SET dataInicial = getDataUltBalanco();
SET qtdBalancos = getQtdBalanco(NEW.BalancoItem_ProdutoID);
SET qtdEntradas = getQTdEntrada(NEW.BalancoItem_ProdutoID,dataInicial);
SET qtdSaidas = getQtdSaida(NEW.BalancoItem_ProdutoID,dataInicial);
SET qtdVendas = getQtdVenda(NEW.BalancoItem_ProdutoID,dataInicial);
UPDATE tab_Produtos SET ProdutoSaldoEstoque = (qtdBalancos + qtdEntradas) -
(qtdSaidas + qtdVendas) WHERE ProdutoID = NEW.BalancoItem_ProdutoID;
END//
DELIMITER ;
DROP TRIGGER tab_BalancosItens_AtualizaSaldoProduto;
DELIMITER //
CREATE TRIGGER tab_BalancosItens_AtualizaSaldoProduto AFTER UPDATE ON tab_BalancosItens FOR EACH ROW
BEGIN
DECLARE dataInicial DATE;
DECLARE qtdBalancos, qtdEntradas, qtdSaidas, qtdVendas DOUBLE;
SET dataInicial = getDataUltBalanco();
SET qtdBalancos = getQtdBalanco(NEW.BalancoItem_ProdutoID);
SET qtdEntradas = getQTdEntrada(NEW.BalancoItem_ProdutoID,dataInicial);
SET qtdSaidas = getQtdSaida(NEW.BalancoItem_ProdutoID,dataInicial);
SET qtdVendas = getQtdVenda(NEW.BalancoItem_ProdutoID,dataInicial);
UPDATE tab_Produtos SET ProdutoSaldoEstoque = (qtdBalancos + qtdEntradas) -
(qtdSaidas + qtdVendas) WHERE ProdutoID = NEW.BalancoItem_ProdutoID;
END//
DELIMITER ;
DROP TRIGGER tab_BalancosItens_DeletaSaldoProduto;
DELIMITER //
CREATE TRIGGER tab_BalancosItens_DeletaSaldoProduto AFTER DELETE ON tab_BalancosItens FOR EACH ROW
BEGIN
DECLARE dataInicial DATE;
DECLARE qtdBalancos, qtdEntradas, qtdSaidas, qtdVendas DOUBLE;
SET dataInicial = getDataUltBalanco();
SET qtdBalancos = getQtdBalanco(OLD.BalancoItem_ProdutoID);
SET qtdEntradas = getQTdEntrada(OLD.BalancoItem_ProdutoID,dataInicial);
SET qtdSaidas = getQtdSaida(OLD.BalancoItem_ProdutoID,dataInicial);
SET qtdVendas = getQtdVenda(OLD.BalancoItem_ProdutoID,dataInicial);
UPDATE tab_Produtos SET ProdutoSaldoEstoque = (qtdBalancos + qtdEntradas) -
(qtdSaidas + qtdVendas) WHERE ProdutoID = OLD.BalancoItem_ProdutoID;
END//
DELIMITER ;
Bom então é isso. Foi assim que resolvi meu problema.
A mesma lógica também pode ser utilizada para atualizar o valor de uma conta a receber, o valor de uma determinada venda atibuído ao caixa, por exemplo. Isto é útil para diminuir a necessidade do programador abrir o arquivo fonte, modificá-lo e depois compilar, depois atualizar no cliente, além da portabilidade, se mudar a linguagem de programação o código de atualização do estoque permanece o mesmo, já que este é armazenado no próprio banco de dados.
Obrigado e até a próxima.