Logo

Firebird: Linguagem PSQL

Otimização e centralização de regras de negócio com Procedural SQL.

Arredondamento do tipo ABNT em itens de serviço NFs

Vez ou outra somos obrigados a usar a matemática de um jeito diferente. Os matemáticos e pesquisadores tratam arredondamentos de uma maneira científica, enquanto o mundo civil pode trabalhar de uma maneira diferente. Este é o caso da norma ABNT para arredondamento de preços para itens de serviço. Elas funcionam do seguinte modo:

  1. Quando o algarismo a ser conservado for seguido de algarismo inferior a 5, o algarismo a ser conservado permanece sem alteração. Exemplo: 4,303 arredondado em duas casas decimais torna-se 4,30. Fala o teste:
    -- O arredondamento PSQL concorda com a regra ABNT retornando 4.3
    SELECT
      CAST(4.303 AS NUMERIC(18,2))
    FROM RDB$DATABASE
  2. Quando o algarismo a ser conservado for seguido de algarismo superior a 5, ou igual a 5 seguindo de um algarismo diferente de zero, soma-se uma unidade ao algarismo a ser conservado. Exemplo: 15,4875 arredondado em duas casas decimais torna-se 15,49.
    -- O arredondamento PSQL concorda com a regra ABNT retornando 15.49
    SELECT
      CAST(15.4875 AS NUMERIC(18,2))
    FROM RDB$DATABASE
  3. Quando o algarismo a ser conservado for ímpar, seguido de 5 e posteriormente de zeros, soma-se uma unidade ao algarismo a ser conservado. Exemplo: 25,7750 arredondado em duas casas decimais fica 25,78.
    -- O arredondamento PSQL concorda com a regra ABNT retornando 25.78
    SELECT
      CAST(25.7750 AS NUMERIC(18,2))
    FROM RDB$DATABASE
  4. Quando o algarismo a ser conservado for par, seguido de 5 e posteriormente de zeros, o algarismo a ser conservado permanece sem alteração. Exemplo: 31,7250 arredondado em duas casas decimais fica 31,72.
    -- No exemplo abaixo, o arredondamento PSQL FALHA COM A REGRA ABNT
    --   retornando 31,73 ao invés de 31.72 segundo a regra ABNT
    SELECT
      CAST(31.7250 AS NUMERIC(18,2))
    FROM RDB$DATABASE

Então é apenas no ponto "4" que o PSQL passa a falhar segundo esta norma. É claro que arredondar desse jeito é um pouco de preciosismo, mas ao rigor da lei e da norma ABNT para itens de prestação de serviço em NFs é preciso fazê-lo. Então como? A SQL Function abaixo aplica a regra ABNT que desejamos:

CREATE OR ALTER FUNCTION GET_ROUND_AS_ABNT (
    P_VALOR DOUBLE PRECISION,
    P_DECIMAIS SMALLINT)
RETURNS DOUBLE PRECISION
AS
DECLARE VARIABLE CDECIMAIS VARCHAR(100);
DECLARE VARIABLE STR_VALOR VARCHAR(100);
DECLARE VARIABLE NSUBSEQUENTE SMALLINT;
DECLARE VARIABLE POS_DEC SMALLINT;
-- Constante para indicar separador de decimal, 
--   mude para vírgula se for o seu caso.
DECLARE VARIABLE C_CHAR_DEC VARCHAR(1)='.';
BEGIN
 STR_VALOR = CAST(P_VALOR AS VARCHAR(100));
 POS_DEC = POSITION(C_CHAR_DEC,STR_VALOR);
 CDECIMAIS = SUBSTRING(STR_VALOR FROM POS_DEC+1 FOR CHAR_LENGTH(STR_VALOR));
 NSUBSEQUENTE = P_DECIMAIS+1;
 IF (:P_DECIMAIS < 1) THEN
  RETURN TRUNC(P_VALOR);
 ELSE
 IF (CHAR_LENGTH(CDECIMAIS) <= :P_DECIMAIS) THEN
  RETURN P_VALOR;
 ELSE
  BEGIN
   IF ((CAST(SUBSTRING(CDECIMAIS FROM NSUBSEQUENTE FOR 1) AS INTEGER) > 5) OR
       (CAST(SUBSTRING(CDECIMAIS FROM NSUBSEQUENTE FOR 1)AS DOUBLE PRECISION)  < 5))  THEN
    RETURN ROUND(P_VALOR,P_DECIMAIS);
   ELSE
   IF (CAST(SUBSTRING(CDECIMAIS FROM NSUBSEQUENTE FOR 1)AS DOUBLE PRECISION) = 5) THEN
    IF (MOD(CAST(SUBSTRING(CDECIMAIS FROM P_DECIMAIS FOR 1)AS DOUBLE PRECISION) ,2) <> 0) THEN
     RETURN ROUND(P_VALOR,P_DECIMAIS);
   ELSE
   IF (CAST(SUBSTRING(CDECIMAIS FROM NSUBSEQUENTE+1 FOR 1)AS DOUBLE PRECISION) > 0) THEN
    RETURN ROUND(P_VALOR,P_DECIMAIS);
   ELSE
    RETURN TRUNC(P_VALOR,P_DECIMAIS);
  END
END

Este código não é de minha autoria. Quando iria criar uma SQL Function para resolver tal problema de arredondamento ABNT, fiz o que sempre faço: procurei na internet para saber se alguém já não fez isso antes e encontrei as informações necessárias para este código que modifiquei levemente. Exemplo de uso:

SELECT
  GET_ROUND_AS_ABNT(31.7250, 2)
FROM RDB$DATABASE

Conclusão

Lembre-se de que este é um tipo de arredondamento especial, só é usado nas situações de arredondamento de itens de serviço em NFs. Portanto, descarte o uso dessa função para outras operações.

O material de referência desse artigo foi obtido em:

Documentação Senior - Arredondamento ABNT

← Voltar para PSQL