Gerar parcelas dias da semana

24/10/2024

0

Pessoal, fiz algumas buscas, mas não consegui achar o que preciso.

Minha rotina de gerar parcelas tem 2 opções:

por período(usuário informa o intervalo de dias)
por dia fixo(sempre no dia 15, por exemplo, IncMonth)

Pego o valor total, divido pelo numero de parcelas e uso o intervalo selecionado para gerar as parcelas.

Quando cai no sábado ou domingo, jogo a primeira parcela para a segunda-feira(IncDay), o problema é que a segunda parcela pega a data da primeira como base para calcular o próximo vencimento, mas deveria pegar a data original + prazo

Alguém tem uma rotina que faça isso?
Renan

Renan

Responder

Post mais votado

27/10/2024

Exemplo:

DataOrigPrimParcela = 10/11/2024
NumParcelas = 10

for i:=0 to Pred(NumParcelas) do
  begin
    DataParcela:=AddMonths(DataOrigPrimParcela,i);
    case DayOfWeek(DataParcela) of
      1: DataParcela:=DataParcela+1;
      7: DataParcela:=DataParcela+2;
      end;
  end;


Obviamente, a rotina acima não checa se é feriado, que é muito mais complexa, pois depende de um cadastro de feriados.

Se houver uma função Feriado(Data) que retorna True se for feriado, você pode utilizar uma lógica simplificada, assim:

DataOrigPrimParcela = 10/11/2024
NumParcelas = 10

for i:=0 to Pred(NumParcelas) do
  begin
    DataParcela:=AddMonths(DataOrigPrimParcela,i);
    if ( (DayOfWeek(DataParcela) in [1,7]) or Feriado(DataParcela) ) then DataParcela:=DataParcela+1;
  end;

Arthur Heinrich

Arthur Heinrich
Responder

Mais Posts

28/10/2024

Renan

Obrigado Arthur.
Responder

28/10/2024

Arthur Heinrich

Onde eu havia sugerido um "if", na realidade seria um "while"

DataOrigPrimParcela = 10/11/2024
NumParcelas = 10
 
for i:=0 to Pred(NumParcelas) do
  begin
    DataParcela:=AddMonths(DataOrigPrimParcela,i);
    while ( (DayOfWeek(DataParcela) in [1,7]) or Feriado(DataParcela) ) do DataParcela:=DataParcela+1;
  end;
Responder

29/10/2024

Renan

Olá, Arthur

Seria mesmo interessante ter ao menos os feriados nacionais.
Vou criar uma tabela e cadastrar.

Nesse meio tempo vou pesquisar uma função para adaptar ao código que você postou.
Responder

29/10/2024

Arthur Heinrich

{ Carregar Feriados }
Q:=TQuery.Create;
Q.Lines.Add('select data from feriados order by 1');
Q.Open;
Feriados:=TStringList.Create;
while (not Q.EOF) do
  begin
    Feriados.Add( FormatDateTime( Q.FieldByName('data').AsDateTime, 'yyyy-mm-dd') );
    Q.Next;
  end;
Feriados.Sorted:=true;
Q.Close;
Q.Free;

{ Verificar se uma data é feriado }
if ( Feriados.IndexOf( FormatDateTime( DATA, 'yyyy-mm-dd') ) >=0 ) then  else {não feriado};

function Feriado( Data : TDateTime ):Boolean;
begin
  Result:=( Feriados.IndexOf( FormatDateTime( Data, 'yyyy-mm-dd') ) >=0 );
end;

Responder

30/10/2024

Renan

Legal, obrigado pela dica.

Logo que eu sobrar um tempo vou adaptar para usar na mesma instrução, a verificação de feriado e sabado e domingo.

abraço
Responder

05/11/2024

Renan

Arthur, estou tentando implementar teu exemplo, mas barrei em uns erros.

aqui recebo um erro de tipos incompativeis String e Integer, na ultima linha, na verificação do feriado

procedure TForm1.Button1Click(Sender: TObject);
var
 Feriados : TStringList;
 Qry : TIBQuery;
begin
  { Carregar Feriados }
  Qry := TIBQuery.Create(Self);
  Qry.SQL.Add('SELECT DATA FROM FERIADOS ORDER BY 1');
  Qry.Open;

  Feriados := TStringList.Create;

  while not Qry.EOF do
  begin
    Feriados.Add(FormatDateTime('dd/mm/yyyy', Qry.FieldByName('DATA').AsDateTime));
    Qry.Next;
  end;

  Feriados.Sorted:=true;
  FreeAndNil(Qry);

  { Verificar se uma data é feriado }
  if Feriados.IndexOf(FormatDateTime('dd/mm/yyyy', Qry.FieldByName('DATA').AsDateTime) >= 0)  then

  else {não feriado};
end;


Aqui na função, ela não reconhece Feriados e a Qry(devo declarar essas variaveis como globais?)

function TForm1.Feriado(Data: TDateTime): Boolean;
begin
  Result := (Feriados.IndexOf(FormatDateTime('dd/mm/yyyy', Qry.FieldByName('DATA').AsDateTime)) >= 0);
end;
Responder

05/11/2024

Arthur Heinrich

Eu coloquei o parêntesis no lugar errado:

if Feriados.IndexOf(FormatDateTime('dd/mm/yyyy', Qry.FieldByName('DATA').AsDateTime) >= 0)  then


Deveria ser:

if Feriados.IndexOf(FormatDateTime('dd/mm/yyyy', Qry.FieldByName('DATA').AsDateTime)) >= 0 then


Eu declarei a variável Feriados, no exemplo, como uma variável local.

Mas você pode declarar como uma propriedade da classe que contém o método/função Feriados(), para que a função tenha acesso à lista carregada no início.

Ou, pode utilizar uma variável global, em uma unit utilizada para gerenciar os feriados, onde seja colocada a função Feriados().

A escolha tem mais a ver com o seu estilo de organização e o escopo em que a função feriados irá funcionar.
Responder

05/11/2024

Renan

Entendi, mas fiquei na dúvida de como verificar se é feriado ou final de semana na mesma rotina, conforme o codigo a seguir.

if ( (DayOfWeek(DataParcela) in [1,7]) or Feriado(DataParcela) ) then 
  DataParcela:=DataParcela+1;
Responder

07/11/2024

Emerson Nascimento

Porque você quer trocar a data? É uma política da empresa?
Os boletos que eu recebo (são muitos, hahahaha) têm vencimentos em qualquer dia, seja final de semana ou feriado.

Porque?
Se o vencimento do boleto for no sábado, eu pago na segunda (dia útil) e não há juros nem mora.
Mas se eu pagar na terça-feira serão cobrados juros e 3 dias de mora, porque o vencimento era sábado (cliente - eu - no "prejuízo").

Se esse boleto tivesse a data de vencimento alterada para segunda, ao pagar na terça eu pagaria menos mora (credor recebe menos pelo meu atrazo).

Boletos com vencimento em finais de semana ou feriados são aceitos no primeiro dia útil seguinte sem cobrança de juros ou mora (artigo 1º da Lei 7089/83).

Impostos e taxas são diferentes: caso o vencimento seja no final de semana ou feriado, o pagamento deve ser antecipado.

https://www.bcb.gov.br/meubc/faqs/p/boleto-com-vencimento-em-dia-nao-util-e-boleto-vencido







Responder

07/11/2024

Renan

Olá, Emerson.

Vou alterar para dias uteis, para fins de relatórios e fluxos de caixa.
Responder

08/11/2024

Emerson Nascimento


eu trabalho com 2 campos de vencimento: 1 para o vencimento real (aquele obtido a partir condição de pagamento) e outro para gravar a data útil, aquela que corresponde ao primeiro dia útil seguinte. caso o vencimento real já seja um dia útil, o conteúdo dos campos é o mesmo.

desta forma posso trabalhar com uma data para envio ao banco e outra data para relatórios. inclusive os relatórios são parametrizáveis quanto ao vencimento utilizado no filtro e o vencimento apresentado.


Responder

08/11/2024

Emerson Nascimento


eu trabalho com 2 campos de vencimento: 1 para o vencimento real (aquele obtido a partir condição de pagamento) e outro para gravar a data útil, aquela que corresponde ao primeiro dia útil seguinte. caso o vencimento real já seja um dia útil, o conteúdo dos campos é o mesmo.

desta forma posso trabalhar com uma data para envio ao banco e outra data para relatórios. inclusive os relatórios são parametrizáveis quanto ao vencimento utilizado no filtro e o vencimento apresentado.


Responder

08/11/2024

Renan

É uma opção.
No meu caso não vou gerar remessa, apenas controlar contas a pagar e a receber.

Posso controlar o vencimento ao gerar o relatório, mas ainda acho que vou jogar o registo para o próximo dia útil.

Tenho parte do código pronto, usando dia fixo. Ainda preciso adaptar o intervalo de dias que o usuário pode informar, como por exemplo, parcelas a cada 15 dias.

A questão do feriado, ainda não consegui adaptar a função do Arthur.
Responder

15/11/2024

Emerson Nascimento

a questão do feriado é simples: você cria uma tabela de feriados com a data, descrição e tipo de feriado (nacional/estadual/municipal).
CREATE TABLE [dbo].[Feriado](
[Data] [date] NOT NULL,
[Descricao] [varchar](50) NOT NULL,
[Tipo] [char](1) NULL,
) ON [PRIMARY]
GO

após obter a data a partir da condição de pagamento você terá dois procedimentos obrigatórios:
- verificar se é fim de semana e
- verificar se é feriado.
deverá ser um loop até encontrar um dia útil.

imagine que pela condição de pagamento, a data de vencimento caiu no dia 30/12/2023. é um sábado. dia 01/01/2024 é feriado, e esta data deve ser incluída na tabela Feriado.
deixe a query que acessa a tabela Feriado sempre aberta - num DM, se possível.

então você precisa ter um loop que seja executado enquanto NÃO for um dia útil:

while not DiaUtil(vencimento) do
vencimento := vencimento + 1;

então, na função/método DiaUtil() você verifica se é fim de semana ou feriado:

function DiaUtil(dia: date): boolean;
begin
result := not( (DayOfWeek(dia) in [1,7]) or qryFeriado.Locate('Data', dia) );
end;

se você usar um DM, coloque a função DiaUtil nele ou em uma unit de funções (lib).
Responder

15/11/2024

Emerson Nascimento

a questão do feriado é simples: você cria uma tabela de feriados com a data, descrição e tipo de feriado (nacional/estadual/municipal).
CREATE TABLE [dbo].[Feriado](
      [Data] [date] NOT NULL,
      [Descricao] [varchar](50) NOT NULL,
      [Tipo] [char](1) NULL
) ON [PRIMARY]
GO

após obter a data a partir da condição de pagamento você terá dois procedimentos obrigatórios:
- verificar se é fim de semana e
- verificar se é feriado.
deverá ser um loop até encontrar um dia útil.

imagine que pela condição de pagamento, a data de vencimento caiu no dia 30/12/2023. é um sábado. dia 01/01/2024 é feriado, e esta data deve ser incluída na tabela Feriado.
deixe a query que acessa a tabela Feriado sempre aberta - num DM, se possível.

você precisa ter um loop que seja executado enquanto NÃO for um dia útil:
while not DiaUtil(vencimento) do
   vencimento := vencimento + 1;

na função/método DiaUtil() você verifica se é fim de semana ou feriado:
function DiaUtil(dia: date): boolean;
begin
   result := not( (DayOfWeek(dia) in [1,7]) or qryFeriado.Locate('Data', dia) );
end;

se você usar um DM, coloque a função DiaUtil nele ou em uma unit de funções (lib).

utilizando no código que eu vi um pouco acima:
for i := 0 to Pred(NumParcelas) do
begin
    DataParcela := AddMonths(DataOrigPrimParcela,i);

    while not DiaUtil(DataParcela) do
        DataParcela := DataParcela + 1;

    // Saiu do loop, então faça o que for necessário com a DataParcela
    // to-do
end;




Responder

Utilizamos cookies para fornecer uma melhor experiência para nossos usuários, consulte nossa política de privacidade.

Aceitar