diff --git a/README.md b/README.md index a77c0d0..76a8cae 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,5 @@ # ABA File Validator -[![CI](https://github.com/alt-ctrl-dev/aba_file_validator/actions/workflows/ci.yml/badge.svg)](https://github.com/alt-ctrl-dev/aba_file_validator/actions/workflows/ci.yml) - +[![Unit tests](https://github.com/alt-ctrl-dev/aba_file_validator/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/alt-ctrl-dev/aba_file_validator/actions/workflows/ci.yml) ## Installation diff --git a/lib/aba_file_validator.ex b/lib/aba_file_validator.ex index 40e7eb8..d862f24 100644 --- a/lib/aba_file_validator.ex +++ b/lib/aba_file_validator.ex @@ -99,6 +99,114 @@ defmodule AbaFileValidator do end end + @doc """ + Get the entries as part of the file total record + + ## Examples + + iex> AbaFileValidator.get_file_total_record(1) + {:error, :invalid_input} + + iex> AbaFileValidator.get_file_total_record("11") + {:error, :incorrect_length} + + iex> AbaFileValidator.get_file_total_record("01") + {:error, :incorrect_length} + + iex> AbaFileValidator.get_file_total_record("1 01CBA test 301500221212121227121222 ") + {:error, :incorrect_starting_code} + + iex> AbaFileValidator.get_file_total_record("7999 999 000000000000000353890000035388 000000 ") + {:error, :invalid_format, [:bsb_filler, :net_total_mismatch ]} + + iex> AbaFileValidator.get_file_total_record("7 ") + {:error, :invalid_format, [:bsb_filler, :net_total, :total_credit, :total_debit, :record_count]} + + iex> AbaFileValidator.get_file_total_record("7999 999 000000000000000353890000035388 000002 ") + {:error, :invalid_format, [:bsb_filler, :net_total_mismatch, :records_mismatch]} + + iex> AbaFileValidator.get_file_total_record("7999-999 000000000000000353890000035389 000000 ") + {:ok, 0, 35389, 35389, 0} + + """ + def get_file_total_record(entry, records \\ 0) + + def get_file_total_record(entry, _records) when not is_binary(entry) do + {:error, :invalid_input} + end + + def get_file_total_record(entry, records) when is_number(records) do + if not correct_length?(entry, 120) do + {:error, :incorrect_length} + else + {code, entry} = String.split_at(entry, 1) + + if code != "7" do + {:error, :incorrect_starting_code} + else + {bsb_filler, entry} = String.split_at(entry, 7) + {first_blank, entry} = String.split_at(entry, 12) + {net_total, entry} = String.split_at(entry, 10) + {total_credit, entry} = String.split_at(entry, 10) + {total_debit, entry} = String.split_at(entry, 10) + {mid_blank, entry} = String.split_at(entry, 24) + {record_count, last_blank} = String.split_at(entry, 6) + + errors = [] + + errors = if bsb_filler !== "999-999", do: errors ++ [:bsb_filler], else: errors + + errors = + if not correct_length?(first_blank, 12), do: errors ++ [:first_blank], else: errors + + errors = if not correct_length?(last_blank, 40), do: errors ++ [:last_blank], else: errors + + errors = if not correct_length?(mid_blank, 24), do: errors ++ [:mid_blank], else: errors + + errors = if string_empty?(net_total), do: errors ++ [:net_total], else: errors + + errors = if string_empty?(total_credit), do: errors ++ [:total_credit], else: errors + + errors = + if string_empty?(total_debit), + do: errors ++ [:total_debit], + else: errors + + errors = if string_empty?(record_count), do: errors ++ [:record_count], else: errors + + errors = + unless string_empty?(net_total) and string_empty?(total_credit) and + string_empty?(total_debit) do + net_amount = String.to_integer(net_total) + credit_amount = String.to_integer(total_credit) + debit_amount = String.to_integer(total_debit) + + if net_amount !== credit_amount - debit_amount, + do: errors ++ [:net_total_mismatch], + else: errors + else + errors + end + + errors = + unless string_empty?(record_count) do + if records !== String.to_integer(record_count), + do: errors ++ [:records_mismatch], + else: errors + else + errors + end + + if(length(errors) > 0) do + {:error, :invalid_format, errors} + else + {:ok, String.to_integer(net_total), String.to_integer(total_credit), + String.to_integer(total_debit), String.to_integer(record_count)} + end + end + end + end + @spec get_transaction_code_description(any) :: :error | binary() @doc """ Get a description for a given transaction code diff --git a/lib/util.ex b/lib/util.ex index 58abf31..d660b88 100644 --- a/lib/util.ex +++ b/lib/util.ex @@ -3,12 +3,18 @@ defmodule AbaFileValidator.Utils do def string_empty?(entry) when is_binary(entry), do: String.trim(entry) |> String.length() == 0 def valid_date?(<>) do - [yy, mm, dd] = for i <- [yy, mm, dd], do: String.to_integer(i) + if string_empty?(dd) or + string_empty?(mm) or + string_empty?(yy) do + false + else + [yy, mm, dd] = for i <- [yy, mm, dd], do: String.to_integer(i) - NaiveDateTime.new(yy, mm, dd, 0, 0, 0) - |> case do - {:ok, _} -> true - {:error, _} -> false - end + NaiveDateTime.new(2000 + yy, mm, dd, 0, 0, 0) + |> case do + {:ok, _} -> true + {:error, _} -> false + end + end end end diff --git a/test/aba_file_validator_test.exs b/test/aba_file_validator_test.exs index 811a35e..1bbf871 100644 --- a/test/aba_file_validator_test.exs +++ b/test/aba_file_validator_test.exs @@ -70,5 +70,80 @@ defmodule AbaFileValidatorTest do assert AbaFileValidator.get_descriptive_record(entry) == {:error, :invalid_format, [:reel_sequence_number]} end + + test "returns an error if empty string" do + entry = + "0 " + + assert AbaFileValidator.get_descriptive_record(entry) == + {:error, :invalid_format, + [ + :reel_sequence_number, + :bank_abbreviation, + :user_preferred_specification, + :user_id_number, + :description, + :date + ]} + end + end + + describe "AbaFileValidator.get_file_total_record/1" do + test "validates succesfully" do + entry = + "7999-999 000000000000000353890000035389 000000 " + + assert AbaFileValidator.get_file_total_record(entry) == + {:ok, 0, 35389, 35389, 0} + end + + test "returns an error if incorrect length with correct starting code" do + assert AbaFileValidator.get_file_total_record("7") == {:error, :incorrect_length} + end + + test "returns an error if incorrect length with incorrect starting code" do + assert AbaFileValidator.get_file_total_record("1") == {:error, :incorrect_length} + end + + test "returns an error if incorrect starting code" do + entry = + "1 01CBA test 301500221212121227121222 " + + assert AbaFileValidator.get_file_total_record(entry) == + {:error, :incorrect_starting_code} + end + + test "returns an error if invalid string" do + entry = + "7999 999 000000000000000353890000035389 000000 " + + assert AbaFileValidator.get_file_total_record(entry) == + {:error, :invalid_format, [:bsb_filler]} + end + + test "returns an error if empty string" do + entry = + "7 " + + assert AbaFileValidator.get_file_total_record(entry) == + {:error, :invalid_format, + [:bsb_filler, :net_total, :total_credit, :total_debit, :record_count]} + end + + test "returns an error if balance don't match" do + entry = + "7999 999 000000000000000353890000035388 000000 " + + assert AbaFileValidator.get_file_total_record(entry) == + {:error, :invalid_format, [:bsb_filler, :net_total_mismatch]} + end + + test "returns an error if records don't match" do + entry = + "7999 999 000000000000000353890000035389 000002 " + + assert AbaFileValidator.get_file_total_record(entry) == + {:error, :invalid_format, [:bsb_filler, :records_mismatch]} + end end end