Skip to content

Commit 73015b8

Browse files
authored
Adiciona utilitário de formatação monetária (#434)
* feat: adiciona formatação monetária * Atualiza changelog * Update CHANGELOG.md
1 parent 60c8937 commit 73015b8

6 files changed

Lines changed: 165 additions & 96 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1111
- Utilitário `convert_code_to_uf` [#397](https://github.com/brazilian-utils/brutils-python/pull/410)
1212
- Utilitário `convert_date_to_text`[#394](https://github.com/brazilian-utils/brutils-python/pull/415)
1313
- Utilitário `get_municipality_by_code` [412](https://github.com/brazilian-utils/brutils-python/pull/412)
14-
1514
- Utilitário `get_code_by_municipality_name` [#399](https://github.com/brazilian-utils/brutils-python/issues/399)
15+
- Utilitário `format_currency` [#426](https://github.com/brazilian-utils/brutils-python/issues/426)
1616

1717
## [2.2.0] - 2024-09-12
1818

README.md

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -81,18 +81,20 @@ False
8181
- [remove\_symbols\_pis](#remove_symbols_pis)
8282
- [generate\_pis](#generate_pis)
8383
- [Processo Jurídico](#processo-jurídico)
84-
- [is\_valid\_legal\_process](#is_valid_legal_process)
84+
- [is\_valid\_legal\_process](#is_valid_legal_process)
8585
- [format\_legal\_process](#format_legal_process)
8686
- [remove\_symbols\_legal\_process](#remove_symbols_legal_process)
8787
- [generate\_legal\_process](#generate_legal_process)
88-
- [Título Eleitoral](#titulo-eleitoral)
89-
- [is_valid_voter_id](#is_valid_voter_id)
90-
- [format_voter_id](#format_voter_id)
91-
- [generate_voter_id](#generate_voter_id)
88+
- [Titulo Eleitoral](#titulo-eleitoral)
89+
- [is\_valid\_voter\_id](#is_valid_voter_id)
90+
- [format\_voter\_id](#format_voter_id)
91+
- [generate\_voter\_id](#generate_voter_id)
9292
- [IBGE](#ibge)
9393
- [get_code_by_municipality_name](#get_code_by_municipality_name)
9494
- [convert_code_to_uf](#convert_code_to_uf)
9595
- [get\_municipality\_by\_code](#get_municipality_by_code)
96+
- [Monetário](#monetário)
97+
- [format\_currency](#format_currency)
9698

9799
## CPF
98100

@@ -1190,6 +1192,33 @@ None
11901192
>>> get_code_by_municipality_name("Municipio Inexistente", "RS")
11911193
None
11921194
```
1195+
1196+
## Monetário
1197+
1198+
### format_currency
1199+
1200+
Formata um número seguindo o padrão monetário brasileiro. O número será formatado
1201+
adicionando o símbolo R$ como prefixo, vírgula como separador decimal, e ponto como
1202+
agrupador de milhar.
1203+
1204+
Argumentos:
1205+
* float ou Decimal: Um número com ou sem casas decimais.
1206+
1207+
Retorna:
1208+
* str ou None: O número formatado seguindo o padrão brasileiro.
1209+
1210+
Exemplo:
1211+
1212+
```python
1213+
>>> from brutils.currency import format_currency
1214+
>>> format_currency(1259.03)
1215+
'R$ 1.259,03'
1216+
>>> format_currency(0)
1217+
'R$ 0,00'
1218+
>>> format_currency("not a number")
1219+
None
1220+
```
1221+
11931222
# Novos Utilitários e Reportar Bugs
11941223

11951224
Caso queira sugerir novas funcionalidades ou reportar bugs, basta criar

README_EN.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ False
9393
- [convert_code_to_uf](#convert_code_to_uf)
9494
- [get\_municipality\_by\_code](#get_municipality_by_code)
9595
- [get_code_by_municipality_name](#get_code_by_municipality_name)
96+
- [Monetary](#monetary)
97+
- [format_currency](#format_currency)
9698

9799
## CPF
98100

@@ -1195,6 +1197,32 @@ None
11951197
None
11961198
```
11971199

1200+
## Monetary
1201+
1202+
### format_currency
1203+
1204+
Formats a number following the Brazilian monetary standard. The number will be
1205+
formatted by adding the R$ symbol as a prefix, a comma as a decimal separator, and a
1206+
period as a thousands grouper.
1207+
1208+
Args:
1209+
* float or Decimal: A number with or without decimal places.
1210+
1211+
Returns:
1212+
* str or None: The number formatted following the Brazilian standard.
1213+
1214+
Example:
1215+
1216+
```python
1217+
>>> from brutils.currency import format_currency
1218+
>>> format_currency(1259.03)
1219+
'R$ 1.259,03'
1220+
>>> format_currency(0)
1221+
'R$ 0,00'
1222+
>>> format_currency("not a number")
1223+
None
1224+
```
1225+
11981226
# Feature Request and Bug Report
11991227

12001228
If you want to suggest new features or report bugs, simply create

brutils/__init__.py

Lines changed: 35 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -4,43 +4,24 @@
44
get_address_from_cep,
55
get_cep_information_from_address,
66
)
7-
from brutils.cep import (
8-
generate as generate_cep,
9-
)
10-
from brutils.cep import (
11-
is_valid as is_valid_cep,
12-
)
13-
from brutils.cep import (
14-
remove_symbols as remove_symbols_cep,
15-
)
7+
from brutils.cep import generate as generate_cep
8+
from brutils.cep import is_valid as is_valid_cep
9+
from brutils.cep import remove_symbols as remove_symbols_cep
1610

1711
# CNPJ Imports
18-
from brutils.cnpj import (
19-
format_cnpj,
20-
)
21-
from brutils.cnpj import (
22-
generate as generate_cnpj,
23-
)
24-
from brutils.cnpj import (
25-
is_valid as is_valid_cnpj,
26-
)
27-
from brutils.cnpj import (
28-
remove_symbols as remove_symbols_cnpj,
29-
)
12+
from brutils.cnpj import format_cnpj
13+
from brutils.cnpj import generate as generate_cnpj
14+
from brutils.cnpj import is_valid as is_valid_cnpj
15+
from brutils.cnpj import remove_symbols as remove_symbols_cnpj
3016

3117
# CPF Imports
32-
from brutils.cpf import (
33-
format_cpf,
34-
)
35-
from brutils.cpf import (
36-
generate as generate_cpf,
37-
)
38-
from brutils.cpf import (
39-
is_valid as is_valid_cpf,
40-
)
41-
from brutils.cpf import (
42-
remove_symbols as remove_symbols_cpf,
43-
)
18+
from brutils.cpf import format_cpf
19+
from brutils.cpf import generate as generate_cpf
20+
from brutils.cpf import is_valid as is_valid_cpf
21+
from brutils.cpf import remove_symbols as remove_symbols_cpf
22+
23+
# Currency
24+
from brutils.currency import format_currency
4425

4526
# Date imports
4627
from brutils.date import convert_date_to_text
@@ -53,81 +34,43 @@
5334
get_code_by_municipality_name,
5435
get_municipality_by_code,
5536
)
56-
from brutils.ibge.uf import (
57-
convert_code_to_uf,
58-
)
37+
from brutils.ibge.uf import convert_code_to_uf
5938

6039
# Legal Process Imports
61-
from brutils.legal_process import (
62-
format_legal_process,
63-
)
64-
from brutils.legal_process import (
65-
generate as generate_legal_process,
66-
)
67-
from brutils.legal_process import (
68-
is_valid as is_valid_legal_process,
69-
)
70-
from brutils.legal_process import (
71-
remove_symbols as remove_symbols_legal_process,
72-
)
40+
from brutils.legal_process import format_legal_process
41+
from brutils.legal_process import generate as generate_legal_process
42+
from brutils.legal_process import is_valid as is_valid_legal_process
43+
from brutils.legal_process import remove_symbols as remove_symbols_legal_process
7344

7445
# License Plate Imports
7546
from brutils.license_plate import (
7647
convert_to_mercosul as convert_license_plate_to_mercosul,
7748
)
78-
from brutils.license_plate import (
79-
format_license_plate,
80-
)
81-
from brutils.license_plate import (
82-
generate as generate_license_plate,
83-
)
84-
from brutils.license_plate import (
85-
get_format as get_format_license_plate,
86-
)
87-
from brutils.license_plate import (
88-
is_valid as is_valid_license_plate,
89-
)
90-
from brutils.license_plate import (
91-
remove_symbols as remove_symbols_license_plate,
92-
)
49+
from brutils.license_plate import format_license_plate
50+
from brutils.license_plate import generate as generate_license_plate
51+
from brutils.license_plate import get_format as get_format_license_plate
52+
from brutils.license_plate import is_valid as is_valid_license_plate
53+
from brutils.license_plate import remove_symbols as remove_symbols_license_plate
9354

9455
# Phone Imports
9556
from brutils.phone import (
9657
format_phone,
9758
remove_international_dialing_code,
9859
remove_symbols_phone,
9960
)
100-
from brutils.phone import (
101-
generate as generate_phone,
102-
)
103-
from brutils.phone import (
104-
is_valid as is_valid_phone,
105-
)
61+
from brutils.phone import generate as generate_phone
62+
from brutils.phone import is_valid as is_valid_phone
10663

10764
# PIS Imports
108-
from brutils.pis import (
109-
format_pis,
110-
)
111-
from brutils.pis import (
112-
generate as generate_pis,
113-
)
114-
from brutils.pis import (
115-
is_valid as is_valid_pis,
116-
)
117-
from brutils.pis import (
118-
remove_symbols as remove_symbols_pis,
119-
)
65+
from brutils.pis import format_pis
66+
from brutils.pis import generate as generate_pis
67+
from brutils.pis import is_valid as is_valid_pis
68+
from brutils.pis import remove_symbols as remove_symbols_pis
12069

12170
# Voter ID Imports
122-
from brutils.voter_id import (
123-
format_voter_id,
124-
)
125-
from brutils.voter_id import (
126-
generate as generate_voter_id,
127-
)
128-
from brutils.voter_id import (
129-
is_valid as is_valid_voter_id,
130-
)
71+
from brutils.voter_id import format_voter_id
72+
from brutils.voter_id import generate as generate_voter_id
73+
from brutils.voter_id import is_valid as is_valid_voter_id
13174

13275
# Defining __all__ to expose the public methods
13376
__all__ = [
@@ -183,4 +126,6 @@
183126
"convert_code_to_uf",
184127
"get_municipality_by_code",
185128
"get_code_by_municipality_name",
129+
# Currency
130+
"format_currency",
186131
]

brutils/currency.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
from decimal import Decimal, InvalidOperation
2+
3+
4+
def format_currency(value): # type: (float) -> str | None
5+
"""
6+
Formats a given float as Brazilian currency (R$).
7+
8+
This function takes a float value and returns a formatted string representing
9+
the value as Brazilian currency, using the correct thousand and decimal separators.
10+
11+
Args:
12+
value (float or Decimal): The numeric value to be formatted.
13+
14+
Returns:
15+
str or None: A string formatted as Brazilian currency, or None if the input is invalid.
16+
17+
Example:
18+
>>> format_currency(1234.56)
19+
"R$ 1.234,56"
20+
>>> format_currency(0)
21+
"R$ 0,00"
22+
>>> format_currency(-9876.54)
23+
"R$ -9.876,54"
24+
>>> format_currency(None)
25+
None
26+
>>> format_currency("not a number")
27+
None
28+
"""
29+
30+
try:
31+
decimal_value = Decimal(value)
32+
formatted_value = (
33+
f"R$ {decimal_value:,.2f}".replace(",", "_")
34+
.replace(".", ",")
35+
.replace("_", ".")
36+
)
37+
38+
return formatted_value
39+
except InvalidOperation:
40+
return None

tests/test_currency.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
from decimal import Decimal
2+
from unittest import TestCase
3+
4+
from brutils.currency import format_currency
5+
6+
7+
class TestFormatCurrency(TestCase):
8+
def test_when_value_is_a_decimal_value(self):
9+
assert format_currency(Decimal("123236.70")) == "R$ 123.236,70"
10+
11+
def test_when_value_is_a_float_value(self):
12+
assert format_currency(123236.70) == "R$ 123.236,70"
13+
14+
def test_when_value_is_negative(self):
15+
assert format_currency(-123236.70) == "R$ -123.236,70"
16+
17+
def test_when_value_is_zero(self):
18+
print(format_currency(0))
19+
assert format_currency(0) == "R$ 0,00"
20+
21+
def test_value_decimal_replace_rounding(self):
22+
assert format_currency(-123236.7676) == "R$ -123.236,77"
23+
24+
def test_when_value_is_not_a_valid_currency(self):
25+
assert format_currency("not a number") is None
26+
assert format_currency("09809,87") is None
27+
assert format_currency("897L") is None

0 commit comments

Comments
 (0)