Skip to content

Commit 108181b

Browse files
committed
chore: verify value in footer and payments
1 parent b7e45b5 commit 108181b

File tree

9 files changed

+140
-33
lines changed

9 files changed

+140
-33
lines changed

CHANGELOG

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
All notable changes to this project will be documented in this file.
44

55
## Version History
6-
- 1.3.2 (2023-21-06)
6+
- 1.3.3 (2023-17-07)
7+
- 1.3.2 (2023-14-07)
78
- 1.3.1 (2023-21-06)
89
- 1.3.0 (2023-29-05)
910
- 1.2.5 (2023-18-05)

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ In `mix.exs`, add the ExMachina dependency:
1414
```elixir
1515
def deps do
1616
[
17-
{:ex_cnab, "~> 1.3.2"},
17+
{:ex_cnab, "~> 1.3.3"},
1818
]
1919
end
2020
```

app/excnab/cnab240/services/build_details.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ defmodule ExCnab.Cnab240.Services.BuildDetails do
9090
%{header: header, detail: details, footer: footer} <- hd[detail_key_id],
9191
{:ok, builded_header} <- ChunkHeader.generate(header),
9292
{:ok, builded_details} <- Details.generate(details),
93-
{:ok, builded_footer} <- ChunkFooter.generate(footer),
93+
{:ok, builded_footer} <- ChunkFooter.generate(footer, builded_details),
9494
amount <- get_chunk_infos(builded_header, builded_footer) do
9595
details = [
9696
%{
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
defmodule ExCnab.Cnab240.Services.CalcAmount do
2+
@spec run(Map.t()) :: Integer.t()
3+
def run(details) do
4+
details
5+
|> hd()
6+
|> Map.get(:servico)
7+
|> Map.get(:segmento)
8+
|> calc_model(details)
9+
end
10+
11+
defp calc_model("A", details) do
12+
Enum.reduce(details, 0, fn detail, acc ->
13+
case detail.servico.segmento == "A" do
14+
true ->
15+
acc + String.to_integer(detail.credito.valor_pagamento)
16+
17+
false ->
18+
acc
19+
end
20+
end)
21+
end
22+
23+
defp calc_model("J", details) do
24+
Enum.reduce(details, 0, fn detail, acc ->
25+
case detail.servico.segmento == "J" do
26+
true ->
27+
acc + String.to_integer(detail.pagamento.valor_pagamento)
28+
29+
false ->
30+
acc
31+
end
32+
end)
33+
end
34+
35+
defp calc_model("N", details) do
36+
Enum.reduce(details, 0, fn detail, acc ->
37+
case detail.servico.segmento == "J" do
38+
true ->
39+
acc + String.to_integer(detail.pagamento.valor_pagamento)
40+
41+
false ->
42+
acc
43+
end
44+
end)
45+
end
46+
47+
defp calc_model("O", details) do
48+
Enum.reduce(details, 0, fn detail, acc ->
49+
case detail.servico.segmento == "O" do
50+
true ->
51+
acc + String.to_integer(detail.pagamento.valor_pagamento)
52+
53+
false ->
54+
acc
55+
end
56+
end)
57+
end
58+
end

app/excnab/cnab240/services/decode.ex

Lines changed: 32 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ defmodule ExCnab.Cnab240.Services.Decode do
99

1010
alias ExCnab.Cnab240.Services.GetFileInfo
1111

12+
alias ExCnab.Cnab240.Services.HasIdenticalInformations
13+
1214
@item_type %{
1315
"0" => :file_header,
1416
"1" => :chunk_header,
@@ -20,38 +22,46 @@ defmodule ExCnab.Cnab240.Services.Decode do
2022

2123
@spec run(String.t(), Map.t()) :: {:ok, Map.t()} | {:error, String.t()}
2224
def run(file, attrs \\ %{}) do
23-
list = read_file(file)
24-
25-
with {:ok, map} <- classify_by_type(list),
26-
{:ok, file_header} <- FileHeader.generate(map.file_header, attrs),
27-
{:ok, details} <- BuildDetails.run(map.chunks, attrs),
28-
{:ok, footer} <- FileFooter.generate(map.file_footer, attrs),
29-
{:ok, filename_info} <- GetFileInfo.run(file, attrs) do
30-
build_response(file_header, details, footer, filename_info, :ok)
31-
else
32-
{:error, error, line_error} ->
33-
line =
34-
list
35-
|> Enum.find_index(&(&1 == line_error))
36-
|> Kernel.+(1)
37-
38-
{:error, error, "Falha na linha #{line}"}
39-
40-
{:error, error} ->
41-
{:error, error}
25+
with list <- read_file(file),
26+
{:ok, content} <- generate_content(file, attrs, list) do
27+
build_response(
28+
content.file_header,
29+
content.details,
30+
content.footer,
31+
content.filename_info,
32+
:ok
33+
)
4234
end
4335
end
4436

4537
@spec run!(String.t(), Map.t()) :: Map.t() | {:error, String.t()}
4638
def run!(file, attrs) do
47-
list = read_file(file)
39+
with list <- read_file(file),
40+
{:ok, content} <- generate_content(file, attrs, list) do
41+
build_response(
42+
content.file_header,
43+
content.details,
44+
content.footer,
45+
content.filename_info,
46+
:no
47+
)
48+
end
49+
end
4850

49-
with {:ok, map} <- classify_by_type(list),
51+
defp generate_content(file, attrs, list) do
52+
with :ok <- HasIdenticalInformations.run(list),
53+
{:ok, map} <- classify_by_type(list),
5054
{:ok, file_header} <- FileHeader.generate(map.file_header, attrs),
5155
{:ok, details} <- BuildDetails.run(map.chunks, attrs),
5256
{:ok, footer} <- FileFooter.generate(map.file_footer, attrs),
5357
{:ok, filename_info} <- GetFileInfo.run(file, attrs) do
54-
build_response(file_header, details, footer, filename_info, :no)
58+
{:ok,
59+
%{
60+
file_header: file_header,
61+
details: details,
62+
footer: footer,
63+
filename_info: filename_info
64+
}}
5565
else
5666
{:error, error, line_error} ->
5767
line =
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
defmodule ExCnab.Cnab240.Services.HasIdenticalInformations do
2+
@moduledoc """
3+
Service to check if the CNAB 240 file has identical informations.
4+
"""
5+
6+
@spec run(list) :: :ok | {:error, String.t()}
7+
def run(list), do: find_list(list, list, :ok)
8+
9+
defp find_list(list, [hd | tl], :ok) do
10+
index = Enum.find_index(tl, &(&1 == hd))
11+
12+
currentIndex = Enum.find_index(list, &(&1 == hd))
13+
14+
case index do
15+
nil ->
16+
find_list(list, tl, :ok)
17+
18+
_ ->
19+
{:error, "A linha #{currentIndex + 1} está duplicada"}
20+
end
21+
end
22+
23+
defp find_list(_list, [], :ok), do: :ok
24+
end

app/excnab/cnab240/templates/chunk_footer.ex

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ defmodule ExCnab.Cnab240.Templates.ChunkFooter do
3535

3636
alias ExCnab.Cnab240.Validator.Details.ChunkFooter, as: ChunkFooterValidator
3737

38-
@spec generate(String.t()) :: {:ok, Map.t()} | {:error, String.t()}
39-
def generate(raw_string) do
38+
@spec generate(String.t(), Map.t()) :: {:ok, Map.t()} | {:error, String.t()}
39+
def generate(raw_string, builded_details) do
4040
control_field = control_fields(raw_string)
4141
total = total_fields(raw_string)
4242

@@ -48,7 +48,7 @@ defmodule ExCnab.Cnab240.Templates.ChunkFooter do
4848
uso_febraban_02: convert_position(raw_string, 66, 230),
4949
ocorrencias: convert_position(raw_string, 231, 240)
5050
}
51-
|> ChunkFooterValidator.call(raw_string)
51+
|> ChunkFooterValidator.call(raw_string, builded_details)
5252
end
5353

5454
defp control_fields(raw_string) do

app/validators/details/chunk_footer.validator.ex

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@ defmodule ExCnab.Cnab240.Validator.Details.ChunkFooter do
33
An implementation of chain of responsibility to validate the chunk footer.
44
"""
55

6-
@spec call(Map.t(), Map.t()) :: {:ok, Map.t()} | {:error, String.t()}
7-
def call(builded_footer, raw_footer) do
6+
@spec call(Map.t(), String.t(), Map.t()) :: {:ok, Map.t()} | {:error, String.t()}
7+
def call(builded_footer, raw_footer, builded_details) do
88
with :ok <- validate_length(raw_footer),
9-
:ok <- validate_record_type(builded_footer.controle.registro) do
9+
:ok <- validate_record_type(builded_footer.controle.registro),
10+
:ok <- validate_value_amount(builded_footer, builded_details) do
1011
{:ok, builded_footer}
1112
else
1213
{:error, reason} ->
@@ -35,4 +36,17 @@ defmodule ExCnab.Cnab240.Validator.Details.ChunkFooter do
3536
{:error, "Tipo de registro incorreto: #{record_type}, deveria ser #{@record_type}}"}
3637
end
3738
end
39+
40+
defp validate_value_amount(builded_footer, builded_details) do
41+
details_value = ExCnab.Cnab240.Services.CalcAmount.run(builded_details)
42+
footer_value = String.to_integer(builded_footer.total.valor)
43+
44+
case details_value == footer_value do
45+
true ->
46+
:ok
47+
48+
false ->
49+
{:error, "Valor total do lote incorreto: #{footer_value}, deveria ser #{details_value}"}
50+
end
51+
end
3852
end

mix.exs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ defmodule ExCnab.MixProject do
44
def project do
55
[
66
app: :ex_cnab,
7-
version: "1.3.2",
8-
elixir: "~> 1.14",
7+
version: "1.3.3",
8+
elixir: "~> 1.10",
99
elixirc_paths: elixirc_paths(Mix.env()),
1010
start_permanent: Mix.env() == :prod,
1111
description: description(),

0 commit comments

Comments
 (0)