quinta-feira, 8 de abril de 2010

Problemas Fracionários Delphi

Solucionando problemas fracionários

Observando uma longa discussão sobre este tema, resolvi apresentar minha experiência nesta área.
Eu tive muitos problemas parecidos com este há muito tempo, especialmente quando comecei a implantar sistemas para uso com ECF (emissor de cupom fiscal). Acontecia do programa apresentar um total de venda diferente do valor impresso pelo ECF. Então fui estudando a questão com carinho e cheguei a algumas conclusões que resolveram meus problemas:
1. O modo de arredondamento do FB é igual ao modo das calculadoras financeiras. Já no Delphi o arredondamento leva em consideração se a parte inteira é par ou ímpar quando a parte decimal termina em 5. Os ECFs também calculam como no FB. Para resolver as diferenças entre Delphi e FB escrevi a função abaixo (a conversão para string resolve alguns problemas de arredondamento do Delphi):
function ExRound(Value: Extended; Decimals: Integer): Extended;
var
  Factor, Fraction: Extended;
begin
  Factor := IntPower(10, Decimals);
  Value := StrToFloat(FloatToStr(Value * Factor));
  Result := Int(Value);
  Fraction := Frac(Value);
  if Fraction >= 0.5 then
    Result := Result + 1
  else if Fraction <= -0.5 then
    Result := Result - 1;
  Result := Result / Factor;
end;
2. Algumas vezes o Delphi gera uns problemas de arredondamento quando você faz vários cálculos (especialmente multiplicação e divisão) sem aplicar o arredondamento em cada etapa do cálculo. Isto ocorre devido ao modo que o Delphi armazena um valor de ponto flutuante numa variável. Procure fazer o arredondamento a cada etapa do cálculo, exceto se este arredondamento passo-a-passo for prejudicar o resultado do cálculo.
3. Sempre que possível use o tipo Currency para variáveis que receberão números reais com até 4 casas decimais. Lembre-se também de usar AsCurrency
ao acessar valores de campos de DataSets.
4. Use no FB campos do tipo NUMERIC(x,y) para armazenar valores financeiros e quantidades. Exemplos:
Quantidade NUMERIC(9,3)
Quantidade NUMERIC(18,3)
Preco NUMERIC(9,2)
Preco NUMERIC(18,2)
Preco NUMERIC(18,4)
Desconto NUMERIC(4,2)
5. Crie campos calculados no banco (COMPUTED BY) já com os devidos ajustes de arredondamento. No FB existe alguns problemas de arredondamento também
que geralmente se resolve com CASTs. Veja alguns exemplos:
Itens de venda:
ValorDescto   NUMERIC(9,2) COMPUTED(CAST(Qtd * PrecoVenda * Descto / 100    AS NUMERIC(9,2))),
Total         NUMERIC(9,2) COMPUTED(CAST(Qtd * PrecoVenda - ValorDescto AS    NUMERIC(9,2))),
ValorComissao NUMERIC(9,2) COMPUTED(CAST(Total * Comissao / 100 AS    NUMERIC(9,2))),
Contas a receber/recebidas:
Atraso INTEGER COMPUTED(
   CASE
     WHEN Recda = 'N' AND Vencto < CURRENT_DATE THEN
      CURRENT_DATE - Vencto
     WHEN Recda = 'S' AND Vencto < DataRecto THEN
      DataRecto - Vencto
    ELSE
     0
   END),
ValorJuro  NUMERIC(9,2) COMPUTED(CAST(Valor * Juro * Atraso / 100 / 30 AS NUMERIC(9,2))),
Total NUMERIC(9,2) COMPUTED(CAST(Valor + ValorJuro AS NUMERIC(9,2))),
TotalRecdo NUMERIC(9,2) COMPUTED(CAST(CapitalRecdo + JuroRecdo AS NUMERIC(9,2))),
Enfim, use CASTs no FB sempre que fizer cálculos envolvendo multiplicação e divisão para que o resultado tenha de fato as casas decimais desejadas.
Com estas técnicas acima resolvi completamente os problemas que eu tinha com relação aos arredondamentos, tanto no Delphi quanto no IB/FB.

Por:
Daniel P. Guimarães
Tecnobyte Informática

Migrando BD para o FB 2.1

Migrando um BD para o FB 2.1

O PROBLEMA

As versões anteriores ao Firebird 2.1 tem problemas relacionados com a extração de charsets e metadata:
  1. Durante a criação/alteração de objetos, não havia a conversão do código textual da metadata (procedures/triggers sources, descriptions, constraints/defaults texts, etc) do charset utilizado no cliente para o charset do sistema (UNICODE_FSS), inserindo bytes "não traduzidos" nas colunas de charset UNICODE_FSS.
  2. Quando lendo BLOBs textuais, não havia tradução do charset definido no BLOB para o charset do cliente.
Note que mesmo na versão 2.1, o item 1 acima poderá ocorrer caso você crie ou altere objetos no banco de dados utilizando uma conexão com charset NONE ou UNICODE_FSS, onde os dados contenham caracteres fora do padrão UNICODE_FSS.
Caso seu banco de dados tenha objetos definidos com caracteres fora do padrão ASCII, você deve repara-lo para que possa ser lido após a migração para o Firebird 2.1.
Abaixo estão descritos dois métodos para corrigir o problema. Escolha o que for melhor para o seu caso.
MÉTODO "A"
Para os passos a seguir, o banco de dados já deve estar na ODS 11.1 (significando que, caso seu banco tenha sido criado em uma versão anterior do FB, você deve fazer um backup e restaura-lo no FB 2.1).
Antes de começar, faça uma cópia do seu banco de dados.
Para cada um dos passos descritos a seguir, é aconselhável que você reconecte no banco de dados.
Passo 1. Criando as procedures no banco de dados
  1. isql database.fdb
  2. SQL> input 'misc/upgrade/metadata/metadata_charset_create.sql';
Passo 2. Verificando o banco de dados
  1. isql database.fdb
  2. SQL> select * from rdb$check_metadata;
A procedure rdb$check_metadata vai retornar todos os objetos que foram afetados por ela.
Caso não retorne um erro/exceção, significa que sua metadata está OK e você pode ir direto para o passo 4 (abaixo). Caso contrário, o primeiro objeto com problemas é o último listado antes de ocorrer a exceção.
Para corrigir a metadata, é necessário saber com qual charset o objeto foi criado. O script de upgrade funcionará corretamente apenas se toda a metadata foi criada com o mesmo charset.
Passo 3. Corrigindo a metadata
  1. isql database.fdb
  2. SQL> input 'misc/upgrade/metadata/metadata_charset_create.sql';
  3. SQL> select * from rdb$fix_metadata('WIN1252');  -- troque WIN1252 pelo charset apropriado
  4. SQL> commit;
A procedure rdb$fix_metadata retornará os mesmos dados que a procedure rdb$check_metadata, mas irá alterar o texto da metadata.
A procedure deve ser executada apenas uma vez! Depois disso, poderá ser apagada.
Passo 4. Removendo as procedures de atualização
  1. isql database.fdb
  2. SQL> input 'misc/upgrade/metadata/metadata_charset_drop.sql';

MÉTODO "B"

Este método tem a vantagem de criar um banco de dados com estrutura 100% válida, visto que qualquer problema no código de procedures, triggers, etc., como por exemplo o uso de palavras que passaram a ser reservadas, acusará erros durante a execução do script, que deverá ser corrigido a fim de deixar o banco de dados com uma metadata perfeita.
  1. Sempre trabalhe com uma cópia da base de dados original, para evitar qualquer tipo de problema.
  2. Antes de migrar para o FB 2.1, extraia o script de criação do banco de dados que será migrado.
  3. Crie um novo banco de dados já no Firebird 2.1, utilizando o script gerado anteriormente. Caso ocorram erros durante a execução do script, corrija os problemas reportados.
  4. Através de uma ferramenta de PUMP (exemplo: IB DataPump), copie os dados da base antiga para o novo banco de dados.
Autor: Carlos Henrique Cantu - Método "A" traduzido e adaptado do arquivo metadata_charset.txt que acompanha o FB 2.1

Dois Firebird 2.x e 2.5 Instanciados

Instalando o FB 2.5 com outras versões do FB


Neste artigo, demonstrarei como podemos instalar a versão 2.5 do Firebird junto às outras versões – isto graças à possibilidade que o FB 2.5 nos dá de renomear o serviço durante o processo de instanciar o mesmo. Os passos iniciais são os mesmo descritos no artigo anterior. Mostrarei aqui o que devemos modificar a partir da instalação da versão 2.5 quando alguma versão anterior já estiver instalada, conforme demonstro na imagem abaixo (versão 1.0 e 1.5 instaladas):
Programas instalados.JPG
Como podemos ver abaixo, ambas estão instaladas como serviço.
Serviços.JPG
Durante o processo de instalação do FB 2.5 será exibido um alerta informado que se encontra em execução um servidor Firebird 1.5 ou posterior, e que o serviço/aplicação deve ser finalizado.
a
Após clicar em Ok, a instalação será abortada. Para que consigamos prosseguir é necessário que seja parado o serviço do FB 1.5 ou 2.0 (vá em Ferramentas Administrativas/Serviços e pare o mesmo). Feito isto, execute novamente o processo de instalação do FB 2.1.
Concluída a instalação da versão 2.1 – partiremos agora para instanciar o serviço e iniciar o mesmo, isto será feito manualmente através do utilitário de linha de comando instsvc que faz parte do pacote de instalação e encontra-se na pasta bin.
b
Abra um prompt de comando (Iniciar/Executar – cmd ou Iniciar/Programas/Promp de Comando). No prompt, digite:
cd\Arquivos de Programas\Firebird\Firebird_2_1
Já dentro do diretório onde o FB foi instalado, digite:
edit firebird.conf
Será aberta a tela do editor de texto Edit, com o conteúdo do referido arquivo carregado. Localize a linha #RemoteServicePort=3050 e faça as seguintes alterações:
c
Remova o caractere # da frente da linha, e depois altere o número da porta 3050 para qualquer outro número de porta permitida (que não esteja sendo usada por outros programas). Salve as alterações feitas no documento e saia do editor de textos.
Agora iniciaremos o processo de criação da instância do serviço e a inicialização do mesmo.
Vamos agora para o diretório onde esta localizado o instsvc...
d
Digite cd bin , depois digite instsvc i –s –a –g –n Firebird_2_1 (observe o parâmetro –n e logo em seguida o nome Firebird_2_1 – este será o nome do serviço do FB 2.1 no Windows).
Serão exibidas duas linhas de mensagens informando que foram criados com sucesso os serviços do Server e Guardian. Digite instsvc sta , será mostrada uma nova linha de mensagem informado que o serviço foi iniciado com sucesso.
e
Se tudo ocorreu bem, agora você terá as versões 1.0, 1.5 ou 2.0 e 2.1 instaladas e em execução como serviço em uma mesma máquina!