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:
- 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 - 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 - 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 - 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: