none
Atualização do Saldo de Conta Corrente RRS feed

  • Pergunta

  • Bom dia pessoal !

    Tenho certeza que neste grupo só tem feras em BD SQL Server, então estou recorrendo a vocês para uma ajuda.

    Se alguém puder examinar a query abaixo e me dizer se tem algum erro ou se esta correta, ficarei muito grato.

    ************
    ESTA ROTINA EU USO PARA ATUALIZAR O SALDO DO CONTA CORRENTE CONTÁBIL, passando os dados referente ao IdConta e DtLancto a partir da qual os saldos de cada linha de lançamento serão atualizados. Ex: Conta = 57, Data 24/09/2019, os saldos dos lançamentos serão atualizados do dia 24/09/2019 em diante, cuja conta seja 57.
    *************

    O problema que estou tendo é "as vezes o saldo do meu conta corrente é atualizado errado", pois pega o saldo inicial de uma data inferior a data inicial informada.

    Para leitura eu uso o indice IX_Lancto (IdConta (ASC), DtLancto (ASC), NatLancto (ASC), IdLote (ASC), Ordem (ASC)).

    --************************

    USE [dbc0102001]
    GO

    DECLARE @IdConta as int,
            @DtLancto as date,
        @VlrSaldo as decimal(16,2);

    SET @IdConta = 57;  -- somente exemplo
    SET @DtLancto = '2019-09-24';  -- somente exemplo
    SET @VlrSaldo = 0;

    SELECT TOP(1) @VlrSaldo = VlrSaldo 
      FROM ctLancto WITH (INDEX(IX_ctLancto)) 
     WHERE IdConta = @IdConta AND DtLancto < @DtLancto
     ORDER BY DtLancto DESC, NatLancto ASC, IdLote ASC, Ordem ASC

    UPDATE ctLancto
       SET @VlrSaldo = @VlrSaldo + (CASE WHEN TipoPartida = 'D' THEN (VlrLancto) WHEN TipoPartida = 'C' THEN (VlrLancto * -1) ELSE 0 END), VlrSaldo = @VlrSaldo
      FROM ctLancto WITH (INDEX(IX_ctLancto)) 
     WHERE IdConta = @IdConta AND DtLancto >= @DtLancto
    GO

    --************************

    obrigado !



    • Editado Pedro Palma terça-feira, 15 de outubro de 2019 18:11
    terça-feira, 15 de outubro de 2019 14:51

Respostas

  • (...)  as vezes o saldo inicial é pego corretamente, as vezes o saldo inicial é pego de outra data ficando errado, isso acontece no comando SELECT,

    Pedro Palma, para que o índice seja lido em ordem inversa à de sua ordenação (ou seja, de trás para frente), a cláusula ORDER BY deve ficar assim:

      order by IdConta desc, DtLancto desc, NatLancto desc, IdLote desc, Ordem desc;

    Isto pode ser confirmado pelos atributos IndexScan Ordered="true" e ScanDirection="BACKWARD", presentes no plano de execução; detalhes no artigo O Plano Perfeito.

    No código que publicou está assim:

       ORDER BY DtLancto DESC, NatLancto ASC, IdLote ASC, Ordem ASC

    o que faz com que o índice seja lido do início para o fim e as linhas retornadas então ordenadas pela sequência definida na cláusula ORDER BY.

     


    José Diz     Belo Horizonte, MG - Brasil     [query performance tuning: Porto SQL]


    Este conteúdo é fornecido sem garantias de qualquer tipo, seja expressa ou implícita.

    • Marcado como Resposta Pedro Palma terça-feira, 15 de outubro de 2019 20:44
    • Editado José Diz terça-feira, 15 de outubro de 2019 21:06
    terça-feira, 15 de outubro de 2019 19:58

Todas as Respostas

  • Pedro Palma, a tabela ctLancto possui coluna que identifique cada lançamento de forma única?

    ---

    Sugiro que substitua a construção

      SET @VlrSaldo = @VlrSaldo + (CASE WHEN TipoPartida = 'D' THEN (VlrLancto) WHEN TipoPartida = 'C' THEN (VlrLancto * -1) ELSE 0 END),
              VlrSaldo = @VlrSaldo

    por

      SET @VlrSaldo= VlrSaldo=  @VlrSaldo + (CASE WHEN TipoPartida = 'D' THEN (VlrLancto) WHEN TipoPartida = 'C' THEN (VlrLancto * -1) ELSE 0 END)

    ---

    Avalie também a seguinte construção:

    -- código #1 v3
    ...
    with Linha_Lancto as (
    SELECT top (1) * --IdConta, DtLancto, TipoPartida, VlrLancto, VlrSaldo
      FROM ctLancto WITH (INDEX(IX_ctLancto)) 
     WHERE IdConta = @IdConta AND DtLancto < @DtLancto
     ORDER BY DtLancto DESC, NatLancto ASC, IdLote ASC, Ordem ASC
    )
    UPDATE Linha_Lancto
       set  @VlrSaldo= VlrSaldo= (VlrSaldo + (CASE WHEN TipoPartida = 'D' THEN (VlrLancto) WHEN TipoPartida = 'C' THEN (VlrLancto * -1) ELSE 0 END));
    ...
    Não testei; pode conter erro(s).

     

    Lembre-se de marcar esta resposta se ela te ajudou a resolver o problema.


    José Diz     Belo Horizonte, MG - Brasil     [query performance tuning: Porto SQL]


    Este conteúdo é fornecido sem garantias de qualquer tipo, seja expressa ou implícita.

    • Editado José Diz terça-feira, 15 de outubro de 2019 18:15
    terça-feira, 15 de outubro de 2019 15:04
  • Obrigado Diniz.
    Vou testar e retornarei com o resultado.
    terça-feira, 15 de outubro de 2019 15:28
  • O problema que estou tendo é "as vezes o saldo do meu conta corrente é atualizado errado", pois pega o saldo inicial de uma data inferior a data inicial informada.

    Algumas observações:

    1. o primeiro comando, com a instrução SELECT, utiliza as colunas DtLancto, NatLancto, IdLote e Ordem para descobrir qual é o lançamento mais recente. Já no segundo comando, com a instrução UPDATE, somente é utilizada a coluna DtLancto para definir qual lançamento será atualizado. Ou seja, regras diferentes para localizar e para atualizar;  
       
    2. entre o primeiro comando e o segundo comando outro processo pode ter incluído mais um lançamento para a mesma conta, por causa da concorrência de processos. Ao utilizar o proposto no código #1 não haverá esse problema, pois a instrução UPDATE altera a linha que foi selecionada.
       



    José Diz     Belo Horizonte, MG - Brasil     [query performance tuning: Porto SQL]


    Este conteúdo é fornecido sem garantias de qualquer tipo, seja expressa ou implícita.

    • Editado José Diz terça-feira, 15 de outubro de 2019 19:59
    terça-feira, 15 de outubro de 2019 15:28
  • Oi Luiz,

    Obrigado pela explicação que vc me deu.
    Tenho um pouco de dificuldade em SQL, meu foco é em programação C#.
    Quero ver se entendi bem a sua explicação (código #1 v3).

    1) Quando o SELECT é executado, o registro encontrado fica na agulha, ou melhor, é o registro corrente; e
    2) Quando o UPDATE é executado, inicia a atualização dos registros a partir do registro corrente.

    Correto ?
    Sendo correto, então não preciso guardar o valor do saldo anterior.

    obrigado,

    terça-feira, 15 de outubro de 2019 16:36
  • Pedro,

    Estou entrando no post para colaborar, pois o mesmo já esta bem conduzido.

    Em relação as suas perguntas perguntas, vou tentar responder:

    1) Quando o SELECT é executado, o registro encontrado fica na agulha, ou melhor, é o registro corrente; e

    Como o Select esta utilizando o operador Top 1, neste caso somente uma única linha esta sendo retornada, sim este é o registro corrente o qual foi pesquisado, identificado e apresentando em tela pelo SQL Server.
    2) Quando o UPDATE é executado, inicia a atualização dos registros a partir do registro corrente.

    Isso mesmo, como esta sendo utilizando uma CTE, conhecida como tabela de expressão comum, toda estrutura deste objeto enquanto o mesmo encontra-se em uso será composta única e especificamente por este registro e seu conjunto de valores, o Update vai ser processado para atender o que foi colocado como condição no comando Case atulizando o valor da variável ou parâmetro @VlrSaldo para o conjunto de linhas de registros existentes na CTE Linha_Lancto, ou seja, basicamente o registro único que esta contido dentro dela.

    Pois bem, espero ter ajudado, abraços.


    Pedro Antonio Galvão Junior [MVP | MCC | MSTC | MIE | Microsoft Evangelist | Microsoft Partner | Engenheiro de Softwares | Especialista em Banco de Dados Relacional e Data Warehouse | Professor Universitário | @JuniorGalvaoMVP | http://pedrogalvaojunior.wordpress.com]

    terça-feira, 15 de outubro de 2019 17:31
  • 1) Quando o SELECT é executado, o registro encontrado fica na agulha, ou melhor, é o registro corrente;

    Pedro Palma, como o SELECT retorna uma única linha, então somente uma linha é atualizada.

    É uma forma não padrão do SQL, pois a atualização não utiliza diretamente a tabela mas sim a CTE. Ao analisar o plano de execução é possível perceber como ocorre o processamento.

    Suponha que exista uma coluna que identifique cada lançamento de forma única: idLancto. O código #1 seria a mesma coisa que

    -- código #2
    with Linha_Lancto as (
    SELECT top (1) IdLancto
      FROM ctLancto WITH (INDEX(IX_ctLancto)) 
     WHERE IdConta = @IdConta AND DtLancto < @DtLancto
     ORDER BY DtLancto DESC, NatLancto ASC, IdLote ASC, Ordem ASC
    )
    UPDATE L
       set  VlrSaldo= (L.VlrSaldo + (CASE WHEN L.TipoPartida = 'D' THEN (L.VlrLancto) WHEN L.TipoPartida = 'C' THEN (L.VlrLancto * -1) ELSE 0 END));
      from ctLancto as L
           inner join Linha_Lancto as LL on LL.IdLancto = L.IdLancto;
     

    Mas se o objetivo é atualizar a partir de, então é provável que a solução tenha que envolver CTE recursiva. A solução que você utilizou é engenhosa, mas estou aqui a avaliar se a tabela sempre será lida na ordem desejada (índice IX_ctLancto).


    José Diz     Belo Horizonte, MG - Brasil     [query performance tuning: Porto SQL]


    Este conteúdo é fornecido sem garantias de qualquer tipo, seja expressa ou implícita.

    • Editado José Diz terça-feira, 15 de outubro de 2019 18:16
    terça-feira, 15 de outubro de 2019 17:34
  • Obrigado Junior !

    Eu fiz uma alteração no post inicial, incluindo uma observação sobre o uso da rotina, assim fica mais claro a necessidade da rotina.

    Bom, eu entendi bem a sua explicação e agradeço a sua interação neste post.

    abs,
    terça-feira, 15 de outubro de 2019 18:16
  • Obrigado José Diz !

    Peço desculpas por ter direcionado a você com outro nome (Diniz e Luiz), não sei onde estava com a cabeça (kkkk), acho que esta rotina esta me deixando louco.
    Eu fiz uma alteração no post inicial, incluindo uma observação sobre o uso da rotina, assim fica mais claro a necessidade dela.
    Bom, eu entendi bem a sua explicação e agradeço muito.

    Fiz vários testes e de fato o registro corrente é atualizado, mas minha necessidade é que atualize todos os registros a partir de uma informação (que sejam iguais ao IdConta e igual ou maior que a DtLancto informada), seguindo a ordem do indice IX_ctLancto.

    att,
    terça-feira, 15 de outubro de 2019 18:24
  • Obrigado Junior !

    Eu fiz uma alteração no post inicial, incluindo uma observação sobre o uso da rotina, assim fica mais claro a necessidade da rotina.

    Bom, eu entendi bem a sua explicação e agradeço a sua interação neste post.

    abs,

    Pedro,

    Ok, obrigado.


    Pedro Antonio Galvão Junior [MVP | MCC | MSTC | MIE | Microsoft Evangelist | Microsoft Partner | Engenheiro de Softwares | Especialista em Banco de Dados Relacional e Data Warehouse | Professor Universitário | @JuniorGalvaoMVP | http://pedrogalvaojunior.wordpress.com]

    terça-feira, 15 de outubro de 2019 18:35
  • Pedro,

    Então neste caso, você não deseja que seja retornado somente uma linha, mas sim todas que satisfazem a sua condição?

    Se for isso, acredito que ao remover o comando Top 1, você terá o retorno de todas as linhas, com isso o Update que será realizado vai justamente fazer a atualização na coluna VlrSaldo de acordo com as regras do comando Case e respeitando a junção aplicada entre a tabela ctLancto e a CTE.


    Pedro Antonio Galvão Junior [MVP | MCC | MSTC | MIE | Microsoft Evangelist | Microsoft Partner | Engenheiro de Softwares | Especialista em Banco de Dados Relacional e Data Warehouse | Professor Universitário | @JuniorGalvaoMVP | http://pedrogalvaojunior.wordpress.com]

    terça-feira, 15 de outubro de 2019 18:39
  • Junior,

    Da forma que eu postei logo no início, as vezes o saldo inicial é pego corretamente, as vezes o saldo inicial é pego de outra data ficando errado, isso acontece no comando SELECT, dai em diante, comando UPDATE, os saldos de todos os registros que satisfaçam a igualdade do IdConta são atualizados, tomando como base o saldo inicial obtido no comando SELECT.  

    Uma pergunta de leigo:
    Será que o Indice pode estar provocando esta situação ?



    • Editado Pedro Palma terça-feira, 15 de outubro de 2019 19:08
    terça-feira, 15 de outubro de 2019 18:56
  • Fiz vários testes e de fato o registro corrente é atualizado, mas minha necessidade é que atualize todos os registros a partir de uma informação (que sejam iguais ao IdConta e igual ou maior que a DtLancto informada), seguindo a ordem do indice IX_ctLancto.

    Além da solução que você propôs, de imediato vejo duas outras soluções e uma delas é através do uso de CTE recursiva. Eis o código SQL proposto:

    -- código #3 v3
    declare @IdConta int, @DtLancto date, @VlrSaldo decimal(16,2);
    set @IdConta= 57;  
    set @DtLancto= '20190924'; 
    set @VlrSaldo= 0;
    
    -- obtém saldo anterior da conta
    SELECT top (1) @VlrSaldo= VlrSaldo 
      from ctLancto 
      where IdConta = @IdConta 
            and DtLancto < @DtLancto
      order by IdConta desc, DtLancto desc, NatLancto desc, IdLote desc, Ordem desc;
    
    PRINT @VlrSaldo; -- debug
    -- with -- sequencia os lançamentos da conta pela ordem do índice Seq_ctLancto as ( SELECT IdConta, DtLancto, NatLancto,  IdLote, Ordem,
    VlrLancto, TipoPartida, seq= row_number() over (order by IdConta, DtLancto, NatLancto, IdLote, Ordem) from ctLancto where IdConta = @IdConta and DtLancto >= @DtLancto ), -- CTE recursiva que soma os lançamentos, pela ordem Le_ctLancto as ( SELECT top (1) seq, IdConta, DtLancto, NatLancto, IdLote, Ordem, VlrLancto, TipoPartida, (@VlrSaldo + case TipoPartida when 'D' then VlrLancto when 'C' then -VlrLancto else 0 end) as Saldo from Seq_ctLancto where seq = 1 union all SELECT T2.seq, T2.IdConta, T2.DtLancto, T2.NatLancto, T2.IdLote, T2.Ordem, T2.VlrLancto, T2.TipoPartida, (T1.Saldo + case T2.TipoPartida when 'D' then T2.VlrLancto when 'C' then -T2.VlrLancto else 0 end) from Le_ctLancto as T1 inner join Seq_ctLancto as T2 on T2.seq = (T1.seq +1) ) UPDATE L set VlrSaldo = LL.Saldo output LL.seq, deleted.VlrLancto, deleted.VlrSaldo, inserted.VlrSaldo from ctLancto as L inner join Le_ctLancto as LL on LL.IdConta = L.IdConta and LL.DtLancto = L.DtLancto and LL.NatLancto = L.NatLancto
    and LL.IdLote = L.Lote
    and LL.Ordem = L.Ordem;

    Eu testei o código acima em uma versão simplificada e funcionou; ao converter para a versão completa, posso ter esquecido de algum detalhe.

    --

    Qual é a versão do SQL Server em uso?

    Qual o número aproximado de lançamentos da conta?

    Esse código SQL será executado uma única vez ou será algo que ficará em produção?

    O índice IX_Lancto é unique? Isto é, ele não aceita linhas repetidas?

    Para que o código acima processe de forma correta é necessário que não haja outros processos em paralelo incluindo lançamentos para a mesma conta e no mesmo período.

      

    Lembre-se de marcar esta resposta se ela te ajudou a resolver o problema. 


    José Diz     Belo Horizonte, MG - Brasil     [query performance tuning: Porto SQL]


    Este conteúdo é fornecido sem garantias de qualquer tipo, seja expressa ou implícita.

    • Editado José Diz terça-feira, 15 de outubro de 2019 21:06
    terça-feira, 15 de outubro de 2019 19:22
  • (...)  as vezes o saldo inicial é pego corretamente, as vezes o saldo inicial é pego de outra data ficando errado, isso acontece no comando SELECT,

    Pedro Palma, para que o índice seja lido em ordem inversa à de sua ordenação (ou seja, de trás para frente), a cláusula ORDER BY deve ficar assim:

      order by IdConta desc, DtLancto desc, NatLancto desc, IdLote desc, Ordem desc;

    Isto pode ser confirmado pelos atributos IndexScan Ordered="true" e ScanDirection="BACKWARD", presentes no plano de execução; detalhes no artigo O Plano Perfeito.

    No código que publicou está assim:

       ORDER BY DtLancto DESC, NatLancto ASC, IdLote ASC, Ordem ASC

    o que faz com que o índice seja lido do início para o fim e as linhas retornadas então ordenadas pela sequência definida na cláusula ORDER BY.

     


    José Diz     Belo Horizonte, MG - Brasil     [query performance tuning: Porto SQL]


    Este conteúdo é fornecido sem garantias de qualquer tipo, seja expressa ou implícita.

    • Marcado como Resposta Pedro Palma terça-feira, 15 de outubro de 2019 20:44
    • Editado José Diz terça-feira, 15 de outubro de 2019 21:06
    terça-feira, 15 de outubro de 2019 19:58
  • Bingo José Diz !

    Antes de ler o seu comentário, eu estava analisando os dados de um cliente que continha o erro em pauta.
    Após uma analise minuciosa, acabei enxergando o erro que é exatamente o que vc postou, ou seja, a ORDER BY do SELECT estava errada.
    Alterando a ORDER BY a rotina ficou TOP, também fiz a alteração que vc propôs   
    SET @VlrSaldo= VlrSaldo=  @VlrSaldo + (CASE WHEN TipoPartida = 'D' THEN (VlrLancto) WHEN TipoPartida = 'C' THEN (VlrLancto * -1) ELSE 0 END)

    Vou colocar em produção o seguinte comando:

    USE [dbc0102001]
    GO

    DECLARE @IdConta as int,
                 @DtLancto as date,
                  @VlrSaldo as decimal(16,2);

    SET @IdConta = 57;  -- somente exemplo
    SET @DtLancto = '2019-09-24';  -- somente exemplo
    SET @VlrSaldo = 0;

    SELECT TOP(1) @VlrSaldo = VlrSaldo 
      FROM ctLancto WITH (INDEX(IX_ctLancto)) 
     WHERE IdConta = @IdConta AND DtLancto < @DtLancto
     ORDER BY DtLancto DESC, NatLancto DESC, IdLote DESC, Ordem DESC

    UPDATE ctLancto
       SET @VlrSaldo = VlrSaldo = @VlrSaldo + (CASE WHEN TipoPartida = 'D' THEN (VlrLancto) WHEN TipoPartida = 'C' THEN (VlrLancto * -1) ELSE 0 END)
      FROM ctLancto WITH (INDEX(IX_ctLancto)) 
     WHERE IdConta = @IdConta AND DtLancto >= @DtLancto
    GO

    att,

    terça-feira, 15 de outubro de 2019 20:42
  • Oi José Diz,

    1) A versão do BD é 2014;
    2) Número aproximado de lançamentos por conta, depende muito da empresa. Tem conta que é pouquissimo lançamento (até 100), tem conta que chega a 10 mil. O UPDADE ocorre em poucas contas, pois atualiza o saldo a partir dos dados informados (IdConta e DtLancto).
    3) Este código ficará em produção.
    4) Não tem problema com concorrência pois o código é executado dentro de um bloco TransactionScope.
    5) Eu vou testar todas as alternativas que passou.

    Mais uma vez, meu muito obrigado. Voce me quebrou um galhão e me explicou muita coisa, por isso serei sempre grato.

    obrigado,

    terça-feira, 15 de outubro de 2019 21:02
  • Bingo José Diz !

    Antes de ler o seu comentário, eu estava analisando os dados de um cliente que continha o erro em pauta.
    Após uma analise minuciosa, acabei enxergando o erro que é exatamente o que vc postou, ou seja, a ORDER BY do SELECT estava errada.
    Alterando a ORDER BY a rotina ficou TOP

    Boas notícias, Pedro Palma.

    Mas um detalhe: na cláusula ORDER BY coloque todas as colunas que fazem parte do índice, inclusive a coluna IdConta, de modo que o índice seja lido em ordem inversa ("backward"). Para confirmar se o acesso ao índice está correto, basta analisar o plano de execução.

    Com relação ao UPDATE da forma que você propôs, eu achei a solução engenhosa mas ainda estou com um pé atrás; é por causa do Halloween Problem.

    Se possível teste o código #3 v3, para ter uma solução alternativa.


    José Diz     Belo Horizonte, MG - Brasil     [query performance tuning: Porto SQL]


    Este conteúdo é fornecido sem garantias de qualquer tipo, seja expressa ou implícita.

    terça-feira, 15 de outubro de 2019 21:06
  • Olá José Diz,

    Acabei de subir a versão que eu propus.
    Fiz vários testes e esta rodando maravilhosamente bem.
    Amanhã o cliente é quem vai testar, kkkk, pois estará fazendo lançamentos de diversas formas, então poderei estar monitorando para saber se esta tudo OK.
    A respeito dos seus códigos, em especial ao #3 v3, também estarei testando em ambiente de desenvolvimento e darei o feedback do resultado.

    abs,
    terça-feira, 15 de outubro de 2019 22:15
  • Junior,

    Da forma que eu postei logo no início, as vezes o saldo inicial é pego corretamente, as vezes o saldo inicial é pego de outra data ficando errado, isso acontece no comando SELECT, dai em diante, comando UPDATE, os saldos de todos os registros que satisfaçam a igualdade do IdConta são atualizados, tomando como base o saldo inicial obtido no comando SELECT.  

    Uma pergunta de leigo:
    Será que o Indice pode estar provocando esta situação ?



    Pedro,

    O índice não iria fazer isso não, mas sim a maneira com que você esta pesquisando os dados ou até mesmo ordenando.


    Pedro Antonio Galvão Junior [MVP | MCC | MSTC | MIE | Microsoft Evangelist | Microsoft Partner | Engenheiro de Softwares | Especialista em Banco de Dados Relacional e Data Warehouse | Professor Universitário | @JuniorGalvaoMVP | http://pedrogalvaojunior.wordpress.com]

    quarta-feira, 16 de outubro de 2019 13:30
  • Oi Junior,

    Obrigado pela sua ajuda e participação neste post.

    Ontem a noite eu subi as alterações e coloquei em produção conforme comentário feito com o José Diz.
    Hoje dois clientes usaram o dia todo sem nenhuma reclamação.
    A poucos minutos atrás, fiz uma analise dos dados a respeito dos saldos, pude constatar que estão corretos. Isto significa que o código inserido esta funcionando.

    abs,

    quarta-feira, 16 de outubro de 2019 21:09
  • Oi Junior,

    Obrigado pela sua ajuda e participação neste post.

    Ontem a noite eu subi as alterações e coloquei em produção conforme comentário feito com o José Diz.
    Hoje dois clientes usaram o dia todo sem nenhuma reclamação.
    A poucos minutos atrás, fiz uma analise dos dados a respeito dos saldos, pude constatar que estão corretos. Isto significa que o código inserido esta funcionando.

    abs,

    Pedro,

    Que bom, fico feliz em poder ajudar, estou neste dias muito sobrecarregado devido a questões profissionais, mas dentro do possível vou acompanhando.

    Agradeço a atenção e no que for possível contem comigo.


    Pedro Antonio Galvão Junior [MVP | MCC | MSTC | MIE | Microsoft Evangelist | Microsoft Partner | Engenheiro de Softwares | Especialista em Banco de Dados Relacional e Data Warehouse | Professor Universitário | @JuniorGalvaoMVP | http://pedrogalvaojunior.wordpress.com]

    quarta-feira, 16 de outubro de 2019 22:17