Repository: staticdev/irpf-cei

Issue:
Change range limitations for reports
* Add 2020 months to emoluments available time-series.
* Change validation from 1st jan to 31th to any values in emoluments available time-series.
* Refactor tests

Discussion:

Patch:
diff --git a/README.rst b/README.rst
index a9e01bd..a1c1b23 100644
--- a/README.rst
+++ b/README.rst
@@ -35,7 +35,7 @@ IRPF CEI
 
 Programa auxiliar para calcular custos de ações, ETFs e FIIs. Este programa foi feito para calcular emolumentos, taxa de liquidação e custo total para a declaração de Bens e Direitos do Imposto de Renda Pessoa Física.
 
-**Essa aplicação foi testada e configurada para calcular tarifas referentes ao ano de 2019-2020 (IRPF 2020) e não faz cálculos para compra e venda no mesmo dia (Day Trade), contratos futuros e Índice Brasil 50.**
+**Essa aplicação foi testada e configurada para calcular tarifas referentes aos anos de 2019 a 2020 (IRPF 2020/2021) e não faz cálculos para compra e venda no mesmo dia (Day Trade), contratos futuros e Índice Brasil 50.**
 
 
 Requisitos
@@ -43,7 +43,7 @@ Requisitos
 
 1. Python
 
-Instale na sua máquina o Python 3.8.0 ou superior para o seu sistema operacional em python.org_.
+Instale na sua máquina o Python 3.8.0 ou superior (versão 3.9 recomendada) para o seu sistema operacional em python.org_.
 
 Usuários do Windows devem baixar a versão `Windows x86-64 executable installer` e na tela de instalação marcar a opção `Add Python 3.8 to PATH`:
 
@@ -74,6 +74,9 @@ Uso
 ---
 
 1. Entre no `site do CEI`_, faça login e entre no menu Extratos e Informativos → Negociação de Ativos → Escolha uma corretora e as datas 1 de Janeiro e 31 de Dezembro do ano em que deseja declarar. Em seguida clique no botão “Exportar para EXCEL”. Ele irá baixar o arquivo “InfoCEI.xls”.
+
+Você pode combinar lançamentos de anos diferentes em um mesmo documento colando as linhas de um relatório em outro, mas mantenha a ordem cronológica.
+
 2. Execute o programa através do comando:
 
 .. code:: console
diff --git a/src/irpf_cei/b3.py b/src/irpf_cei/b3.py
index e7aabed..63cccff 100644
--- a/src/irpf_cei/b3.py
+++ b/src/irpf_cei/b3.py
@@ -40,7 +40,37 @@ EMOLUMENTOS_PERIODS = [
         datetime.datetime(2019, 11, 4), datetime.datetime(2019, 12, 2), 0.0000411
     ),
     RatePeriod(
-        datetime.datetime(2019, 12, 3), datetime.datetime(2020, 1, 2), 0.00003802
+        datetime.datetime(2019, 12, 3), datetime.datetime(2020, 1, 1), 0.00003802
+    ),
+    RatePeriod(datetime.datetime(2020, 1, 1), datetime.datetime(2020, 2, 1), 0.0000366),
+    RatePeriod(
+        datetime.datetime(2020, 2, 1), datetime.datetime(2020, 3, 1), 0.00003462
+    ),
+    RatePeriod(
+        datetime.datetime(2020, 3, 1), datetime.datetime(2020, 4, 1), 0.00003248
+    ),
+    RatePeriod(
+        datetime.datetime(2020, 4, 1), datetime.datetime(2020, 5, 1), 0.00003006
+    ),
+    RatePeriod(datetime.datetime(2020, 5, 1), datetime.datetime(2020, 6, 3), 0.0000334),
+    RatePeriod(
+        datetime.datetime(2020, 6, 1), datetime.datetime(2020, 7, 1), 0.00003291
+    ),
+    RatePeriod(
+        datetime.datetime(2020, 7, 1), datetime.datetime(2020, 8, 1), 0.00003089
+    ),
+    RatePeriod(datetime.datetime(2020, 8, 1), datetime.datetime(2020, 9, 1), 0.0000318),
+    RatePeriod(
+        datetime.datetime(2020, 9, 1), datetime.datetime(2020, 10, 1), 0.00003125
+    ),
+    RatePeriod(
+        datetime.datetime(2020, 10, 1), datetime.datetime(2020, 11, 1), 0.00003219
+    ),
+    RatePeriod(
+        datetime.datetime(2020, 11, 1), datetime.datetime(2020, 12, 1), 0.00003247
+    ),
+    RatePeriod(
+        datetime.datetime(2020, 12, 1), datetime.datetime(2021, 1, 1), 0.00003020
     ),
 ]
 EMOLUMENTOS_AUCTION_RATE = 0.00007
diff --git a/src/irpf_cei/cei.py b/src/irpf_cei/cei.py
index 9b7ab7e..2cdccdd 100644
--- a/src/irpf_cei/cei.py
+++ b/src/irpf_cei/cei.py
@@ -43,17 +43,14 @@ def date_parse(value: str) -> datetime.datetime:
 
 def validate_period(first: str, second: str) -> int:
     """Consider the year from the first trade date."""
-    if first.startswith("01/01") and second.startswith("31/12"):
-        first_year = int(first[-4:])
-        second_year = int(second[-4:])
-    else:
-        return sys.exit("Erro: emitir relatório do dia 1 de janeiro a 31 de dezembro.")
-    if first_year == second_year and first_year >= 2019:
-        return first_year
+    first_year = int(first[-4:])
+    second_year = int(second[-4:])
+    if first_year <= second_year and first_year > 2018 and second_year < 2021:
+        return second_year
     return sys.exit(
         (
-            "Erro: o período de {} a {} não é válido, favor verificar instruções "
-            "na documentação."
+            f"Erro: o período de {first} a {second} não é válido, favor verificar "
+            "instruções na documentação."
         )
     )
 


Test patch:
diff --git a/tests/test_cei.py b/tests/test_cei.py
index 90196dd..eaa1be6 100644
--- a/tests/test_cei.py
+++ b/tests/test_cei.py
@@ -89,19 +89,23 @@ def test_get_xls_filename_download_folder(
 
 def test_validate_period_success() -> None:
     """Return reference year."""
-    assert cei.validate_period("01/01/2020", "31/12/2020") == 2020
+    first_date = "01/01/2019"
+    second_date = "31/12/2020"
+
+    assert cei.validate_period(first_date, second_date) == 2020
 
 
 def test_validate_period_wrong_start_finish() -> None:
     """Raise `SystemExit` from wrong start date."""
-    with pytest.raises(SystemExit):
-        assert cei.validate_period("01/12/2020", "31/12/2020")
-
-
-def test_validate_period_different_years() -> None:
-    """Raise `SystemExit` from different years."""
-    with pytest.raises(SystemExit):
-        assert cei.validate_period("01/01/2019", "31/12/2020")
+    first_date = "01/01/2018"
+    second_date = "31/12/2020"
+
+    with pytest.raises(SystemExit) as ex:
+        cei.validate_period(first_date, second_date)
+    assert str(ex.value) == (
+        f"Erro: o período de {first_date} a {second_date} não é válido, favor "
+        "verificar instruções na documentação."
+    )
 
 
 def test_validate_header_empty_file(fs: Mock, cwd: Mock) -> None: