| 
			
				|  | ActiveDelphi .: O site do programador Delphi! :.
 
 |  
 
	
		| Exibir mensagem anterior :: Exibir próxima mensagem |  
		| Autor | Mensagem |  
		| pestana Colaborador
 
  
 
 Registrado: Sábado, 25 de Junho de 2005
 Mensagens: 3147
 Localização: Araras-SP
 
 | 
			
				|  Enviada: Sáb Jan 07, 2017 12:42 pm    Assunto: Erro ao tentar incluir (violation of PRIMARY...) [Resolvido] |   |  
				| 
 |  
				| Alguma coisa tem de errado neste código eu suspeito que seja esta trigger no bloco For Select ... Into Do, se eu retirar este bloco funciona mas logicamente somente se houver um item por pedido, quando eu adiciono este bloco For Select ... Into Do o erro acontece... 
 
  	  | Citação: |  	  | Invalid insert or update value(s): object columns are constrained - no 2 table rows can have duplicate column values.
 violation of PRIMARY or UNIQUE KEY constraint "PK_SALDOESTOQUE" on table "SALDO_ESTOQUE".
 Problematic key value is ("ID_MOVIMENTO" = 62, "ID_PRODUTO" = 1, "ID_EMBALAGEM" = 1).
 At procedure 'ADD_SALDO_ESTOQUE' line: 15, col: 4
 At procedure 'REAJUSTAR_SALDOESTQ' line: 41, col: 8
 At trigger 'ADICIONAR_ESTOQUE' line: 34, col: 11.
 | 
 
 Alguem poderia me dar um Help?
 
 
 Explicação do código:
 
 Existe as tabelas de pedido_entrada, item_entrada, movimento_estoque e saldo_estoque.
 pedido_entrada e item_entrada: armazena os pedidos de compra.
 movimento_estoque e saldo_estoque: armazena as movimentações e seu saldo de estoque, respectivamente.
 
 Esta Trigger é disparado depois de atualizar a tabela pedido_entrada (pedido de compra) aonde o campo status for alterado de (P)endente para (F)inalizado.
 A inteção é adicionar todos os itens do pedido de compra na tabela saldo de estoque (não esquecendo que a estrutura é um mestre-detalhe movimento_estoque 1-N saldo_estoque).
 
 
  	  | Código: |  	  | set term ^ ; 
 create trigger adicionar_estoque for pedido_entrada
 active after update position 0
 as
 declare variable idMovimento integer;
 declare variable idProduto integer;
 declare variable idEmbalagem integer;
 declare variable valorCusto numeric(13,2);
 declare variable margem numeric(7,2);
 declare variable valorVenda numeric(13,2);
 declare variable percDesc numeric(5,2);
 declare variable qtdeItem numeric(10,3);
 declare variable qtdeEstq numeric(13,3);
 declare variable valorTotal numeric(18,2);
 begin      /* se o status do pedido estiver como finalizado */
 if ((old.status = 'P') and (new.status = 'F')) then
 begin  /* inclui registro na tabela movimento_estoque */
 execute procedure add_movimento_estoque(old.id_pedido,NULL,2,NULL,'N')
 returning_values :idMovimento;
 /* retorna o último saldo de estoque em que o código do produro e embalagem seja igual ao do item do pedido */
 for select s.id_produto, s.id_embalagem, i.valorUnitario, s.percentual,
 s.valorVenda, s.percentDesc, s.qtdeAtual, i.quantidade, s.valorTotal
 from item_entrada i
 inner join saldo_estoque s on(i.id_produto = s.id_produto and
 i.id_embalagem = s.id_embalagem)
 where (i.id_pedido = old.id_pedido)
 and (s.id_movimento = (select max(id_movimento)
 from saldo_estoque
 where (id_produto = s.id_produto)
 and (id_embalagem = s.id_embalagem)))
 into :idProduto, :idEmbalagem, :valorCusto, :margem, :valorVenda, :percDesc,
 :qtdeEstq, :qtdeItem, :valorTotal
 do
 begin
 execute procedure reajustar_saldoEstq('E',2,:idMovimento,:idProduto,:idEmbalagem,
 :qtdeItem,:qtdeEstq,:valorCusto,:margem,
 :valorVenda,:percDesc,:valorTotal);
 end
 end
 end ^
 
 set term ; ^
 
 
 set term ^ ;
 
 create procedure reajustar_saldoEstq(
 operacao char(1) character set win1252,
 idTipo integer,
 idMovimento integer,
 idProduto integer,
 idEmbalagem integer,
 qtde numeric(10,3),
 qtdeEstq numeric(13,3),
 valorCusto numeric(13,2),
 margem numeric(7,2),
 valorVenda numeric(13,2),
 percDesc numeric(5,2),
 valorTotal numeric(18,2))
 as
 declare variable custoMedio numeric(13,2);
 declare variable valorMov numeric(15,2);
 begin  /* reajusta a quantidade em estoque e o valor total de custo. */
 if (operacao in ('E','S','I')) then
 begin
 if (operacao = 'E') then     /* entrada.    */
 begin
 qtdeEstq = (:qtdeEstq + :qtde);
 valorMov = (:valorCusto * :qtde);
 valorTotal = (:valorTotal + :valorMov);
 end
 else
 if (operacao = 'S') then     /* saída.      */
 begin
 qtdeEstq = (:qtdeEstq - :qtde);
 valorMov = (:valorCusto * :qtde);
 valorTotal = (:valorTotal - :valorMov);
 end
 else
 if (operacao = 'I') then     /* inicializa. */
 begin
 qtdeEstq = :qtde;
 valorMov = (:valorCusto * :qtde);
 valorTotal = :valorMov;
 end
 /* inclui registro na tabela saldo_estoque. */
 execute procedure add_saldo_estoque (:idMovimento,:idProduto,:idEmbalagem,
 :valorCusto,:margem,:valorVenda,:percDesc,
 :qtde,:valorMov,:qtdeEstq,:valorTotal);
 /* idTipo 2: entrada por compra, 4: devolução de venda, 5: devolução de compra                                */
 /* devolução de ... para casos em que o valor de custo da "devolução" seja diferente do valor de custo atual. */
 if ((operacao in ('E','S')) and (idTipo in (2,4,5))) then
 begin  /* se o custo médio for alterado, recalcula o custo e inclui um novo movimento. */
 custoMedio = (:valorTotal / :qtdeEstq);
 if (:valorCusto <> :custoMedio) then
 begin
 execute procedure add_movimento_estoque (NULL,NULL,1,NULL,'N')
 returning_values :idMovimento;
 execute procedure add_saldo_estoque (:idMovimento,:idProduto,:idEmbalagem,
 :custoMedio,:margem,:valorVenda,:percDesc,
 0,0,:qtdeEstq,:valorTotal);
 end
 end
 end
 end ^
 
 set term ; ^
 | 
 
 
 Obrigado!
 _________________
 Ao invés de ficar desanimado no que deu de errado, olhe para frente, aprenda com os erros e veja o que ainda pode ser feito. A determinação e a persistência é uma das etapas para o sucesso.
 
 Editado pela última vez por pestana em Sex Jan 13, 2017 11:41 am, num total de 2 vezes
 |  |  
		| Voltar ao Topo |  |  
		|  |  
		| pestana Colaborador
 
  
 
 Registrado: Sábado, 25 de Junho de 2005
 Mensagens: 3147
 Localização: Araras-SP
 
 | 
			
				|  Enviada: Ter Jan 10, 2017 6:19 am    Assunto: |   |  
				| 
 |  
				| Se eu executar o select no banco o resultado são estas duas linhas: 
 id_produto = 1 | id_embalagem = 1 | valorUnitario = 25 | percentual = 0 | valorVenda = 0 | percentDesc = 0 | qtdeAtual = 14 | quantidade = 2 | valorTotal = 325
 id_produto = 2 | id_embalagem = 1 | valorUnitario = 75 | percentual = 0 | valorVenda = 0 | percentDesc = 0 | qtdeAtual = 3 | quantidade = 3 | valorTotal = 225
 
 Eu não consigo entender o porque desta mensagem!
 
 Alguém poderia me ajudar?
 
 
 Obrigado!
 _________________
 Ao invés de ficar desanimado no que deu de errado, olhe para frente, aprenda com os erros e veja o que ainda pode ser feito. A determinação e a persistência é uma das etapas para o sucesso.
 |  |  
		| Voltar ao Topo |  |  
		|  |  
		| pestana Colaborador
 
  
 
 Registrado: Sábado, 25 de Junho de 2005
 Mensagens: 3147
 Localização: Araras-SP
 
 | 
			
				|  Enviada: Qua Jan 11, 2017 1:18 pm    Assunto: |   |  
				| 
 |  
				| Pessoal desconsidere os códigos acima e verifique esta Trigger. Fazendo os testes eu descobri que ao incluir registros na tabela "saldo_estoque" dentro do For Select Do ocorre este erro. 
 Parece que o loop For Select tenta incluir duas vezes o mesmo registro na tabela saldo_estoque, deem uma olhada na declaração select ...
 
 
  	  | Código: |  	  | SET TERM ^ ; 
 CREATE OR ALTER TRIGGER ADICIONAR_ESTOQUE FOR PEDIDO_ENTRADA
 ACTIVE AFTER UPDATE POSITION 0
 as
 declare variable idMovimento integer;
 declare variable idProduto integer;
 declare variable idEmbalagem integer;
 declare variable valorCusto numeric(13,2);
 declare variable margem numeric(7,2);
 declare variable valorVenda numeric(13,2);
 declare variable percDesc numeric(5,2);
 declare variable qtdeItem numeric(10,3);
 declare variable qtdeEstq numeric(13,3);
 declare variable valorTotal numeric(18,2);
 begin      /* se o status do pedido estiver como finalizado */
 if ((old.status = 'P') and (new.status = 'F')) then
 begin  /* inclui registro na tabela movimento_estoque */
 execute procedure add_movimento_estoque (old.id_pedido,NULL,2,NULL,'N')
 returning_values :idMovimento;
 /* retorna o último saldo de estoque em que o código do produro e embalagem seja igual ao do item do pedido */
 for select s.id_produto, s.id_embalagem, i.valorUnitario, s.percentual,
 s.valorVenda, s.percentDesc, s.qtdeAtual, i.quantidade, s.valorTotal
 from item_entrada i
 inner join saldo_estoque s on(i.id_produto = s.id_produto and
 i.id_embalagem = s.id_embalagem)
 where (i.id_pedido = old.id_pedido)
 and (s.id_movimento = (select max(id_movimento)
 from saldo_estoque
 where (id_produto = s.id_produto)
 and (id_embalagem = s.id_embalagem)))
 into :idProduto, :idEmbalagem, :valorCusto, :margem, :valorVenda, :percDesc,
 :qtdeEstq, :qtdeItem, :valorTotal
 do
 begin
 insert into saldo_estoque (id_movimento, id_produto, id_embalagem, valorCusto,
 percentual, valorVenda, percentDesc, qtdeMov,
 valorMov, qtdeAtual, valorTotal)
 values (:idMovimento, :idProduto, :idEmbalagem, :valorCusto, :margem,
 :valorVenda, :percDesc, 0, 0, :qtdeEstq, :valorTotal);
 end
 end
 end ^
 
 SET TERM ; ^
 | 
 
 Agora estou tentando encontrar uma solução para este problema, quem puder me ajudar eu agradeço!
 
 
 Obrigado!
 _________________
 Ao invés de ficar desanimado no que deu de errado, olhe para frente, aprenda com os erros e veja o que ainda pode ser feito. A determinação e a persistência é uma das etapas para o sucesso.
 |  |  
		| Voltar ao Topo |  |  
		|  |  
		| imex Moderador
 
  
 
 Registrado: Sexta-Feira, 7 de Janeiro de 2011
 Mensagens: 11666
 
 
 | 
			
				|  Enviada: Qua Jan 11, 2017 2:03 pm    Assunto: |   |  
				| 
 |  
				| Boa tarde, 
 Experimente fazer uns testes dessa forma para ver se é obtido o resultado esperado:
 
 
  	  | Código: |  	  | for select i.id_produto, i.id_embalagem, i.valorUnitario, i.quantidade from item_entrada i
 where i.id_pedido = old.id_pedido
 into :idProduto, :idEmbalagem, :valorCusto, :qtdeItem
 do
 begin
 select first 1 s.percentual, s.valorVenda, s.percentDesc, s.qtdeAtual, s.valorTotal
 from saldo_estoque s
 where s.id_produto = :idProduto and
 s.id_embalagem = :idEmbalagem
 order by s.id_movimento desc
 into :margem, :valorVenda, :percDesc, :qtdeEstq, :valorTotal;
 
 insert -- ...
 end
 | 
 
 Espero que ajude
 
 _________________
 Assinatura: http://www.imoveisemexposicao.com.br/imoveis-alugar-guarulhos-residencial-apartamento
 |  |  
		| Voltar ao Topo |  |  
		|  |  
		| pestana Colaborador
 
  
 
 Registrado: Sábado, 25 de Junho de 2005
 Mensagens: 3147
 Localização: Araras-SP
 
 | 
			
				|  Enviada: Qui Jan 12, 2017 11:04 am    Assunto: |   |  
				| 
 |  
				| Muito obrigado Imex, resolvi com a sua dica de separar o select! 
 Eu não sabia disso, porque será que com um único select declarado no loop "For Select" não era possível inserir os registros na tabela saldo_estoque (ocorria erro de violação de chave primaria) ?? ao separar o select não acontece mais o erro e insere os registros na tabela.
 
 
 Foi de grande ajuda, valeu!!!
 _________________
 Ao invés de ficar desanimado no que deu de errado, olhe para frente, aprenda com os erros e veja o que ainda pode ser feito. A determinação e a persistência é uma das etapas para o sucesso.
 |  |  
		| Voltar ao Topo |  |  
		|  |  
		| imex Moderador
 
  
 
 Registrado: Sexta-Feira, 7 de Janeiro de 2011
 Mensagens: 11666
 
 
 | 
			
				|  Enviada: Qui Jan 12, 2017 1:52 pm    Assunto: |   |  
				| 
 |  
				| Não tenho certeza, mas desconfio que ao inserir um novo registro na tabela saldo_estoque dentro desse looping, esse própio registro era retornado na próxima interação do looping que só não se tornava infinito porque ocorria o erro de violação da PK. 
 Espero que ajude
 |  |  
		| Voltar ao Topo |  |  
		|  |  
		| pestana Colaborador
 
  
 
 Registrado: Sábado, 25 de Junho de 2005
 Mensagens: 3147
 Localização: Araras-SP
 
 | 
			
				|  Enviada: Sex Jan 13, 2017 10:15 am    Assunto: |   |  
				| 
 |  
				| Imex eu acho que é isso mesmo, porque nos testes que eu tinha feito eu retirei a chave primaria pk_saldoEstoque e deu a impressão que a execução entrou em um looping infinito e só consegui sair fazendo a finalização forçada. 
 Eu acho estranho porque a inclusão de registro na tabela interfere no select, sabendo que a cada iteração dentro do loop é uma nova linha.
 p.ex: a chave seria formado por id_movimento, id_produto, id_embalagem
 
 
  	  | Código: |  	  | execute procedure add_movimento_estoque (old.id_pedido,NULL,2,NULL,'N') returning_values :idMovimento;
 for select s.id_produto, s.id_embalagem, i.valorUnitario, s.percentual,
 s.valorVenda, s.percentDesc, s.qtdeAtual, i.quantidade, s.valorTotal
 from item_entrada i
 inner join saldo_estoque s on(i.id_produto = s.id_produto and
 i.id_embalagem = s.id_embalagem)
 where (i.id_pedido = old.id_pedido)
 and (s.id_movimento = (select max(id_movimento)
 from saldo_estoque
 where (id_produto = s.id_produto)
 and (id_embalagem = s.id_embalagem)))
 into :idProduto, :idEmbalagem, :valorCusto, :margem, :valorVenda, :percDesc,
 :qtdeEstq, :qtdeItem, :valorTotal
 do
 begin
 insert into saldo_estoque (id_movimento, id_produto, id_embalagem, valorCusto,
 percentual, valorVenda, percentDesc, qtdeMov,
 valorMov, qtdeAtual, valorTotal)
 values (:idMovimento, :idProduto, :idEmbalagem, :valorCusto, :margem,
 :valorVenda, :percDesc, 0, 0, :qtdeEstq, :valorTotal);
 end
 | 
 
 Ao incluir o registro na tabela saldo_estoque:
 
 1º Linha
 id_movimento = 1, id_produto = 1, id_embalagem = 1
 
 2º Linha
 id_movimento = 1, id_produto = 2, id_embalagem = 1
 
 Observe que os valores das chaves são diferentes
 
 Mas pelo que eu vejo na prática não é assim que ocorre, acontece como você mesmo disse Imex.
 
 
 Mais uma vez muito obrigado!
 _________________
 Ao invés de ficar desanimado no que deu de errado, olhe para frente, aprenda com os erros e veja o que ainda pode ser feito. A determinação e a persistência é uma das etapas para o sucesso.
 |  |  
		| Voltar ao Topo |  |  
		|  |  
		| imex Moderador
 
  
 
 Registrado: Sexta-Feira, 7 de Janeiro de 2011
 Mensagens: 11666
 
 
 | 
			
				|  Enviada: Sex Jan 13, 2017 10:55 am    Assunto: |   |  
				| 
 |  
				| Acredito que com o For Select o banco de dados não retorna todas as linhas (no caso 2) de uma vez e depois processa, como ocorreria por exemplo se esse looping fosse na aplicação. O banco de dados provavelmente retorna 1 linha de cada vez, sem saber se quer se existe uma próxima linha, então quando vai procurar a próxima linha acaba retornando a linha que acabou de ser inserida na  interação anterior do looping (mesmo produto) formando o looping infinito. 
 Espero que ajude
 |  |  
		| Voltar ao Topo |  |  
		|  |  
		| pestana Colaborador
 
  
 
 Registrado: Sábado, 25 de Junho de 2005
 Mensagens: 3147
 Localização: Araras-SP
 
 | 
			
				|  Enviada: Sex Jan 13, 2017 11:38 am    Assunto: |   |  
				| 
 |  
				| Imex, só pode ser isso mesmo, 
 mas uma vez muito obrigado!
 _________________
 Ao invés de ficar desanimado no que deu de errado, olhe para frente, aprenda com os erros e veja o que ainda pode ser feito. A determinação e a persistência é uma das etapas para o sucesso.
 |  |  
		| Voltar ao Topo |  |  
		|  |  
		|  |  
  
	| 
 
 | Enviar Mensagens Novas: Proibido. Responder Tópicos Proibido
 Editar Mensagens: Proibido.
 Excluir Mensagens: Proibido.
 Votar em Enquetes: Proibido.
 
 |  |