3
\$\begingroup\$

Similar to other GTIN validations, the function should take the entire string of the read in barcode, calculate the check digit, and compare it to the check digit on the code. For example, the code 6291041500213 would take the first 12 digits (629104150021) and calculate the answer of 3.

this code should work for GTIN-8, GTIN-12, GTIN-13, GTIN-14, GSIN, SSCC.

I was hoping to get some feedback on the test cases and on the code itself on where it can be DRYed or made more explicit. I realized later on that Elixir now has a built in mod fuction which I'll switch to (I read an older Jose article that said it did not)...I think I need to also clean up the areas where it returns {:ok} and {:error}.

Code:

  @doc """
  Check for valid  GTIN-8, GTIN-12, GTIN-13, GTIN-14, GSIN, SSCC codes

  Returns `{:ok}` or `{:error}`

  ## Examples

      iex> gtin_check_digit("6291041500213")
      {:ok}

      iex> gtin_check_digit("6291041500214")
      {:error}
  """
  @spec gtin_check_digit(string) :: { atom }
  def gtin_check_digit(number) do
    {code, check_digit} = String.split_at(number, String.length(number) - 1)
    calculated_check_digit = code
      |> multiply_and_sum_array
      |> subtract_from_nearest_multiple_of_ten

    case calculated_check_digit == String.to_integer(check_digit) do
      true -> {:ok}
      _ -> {:error}
    end
  end

  @doc """
  Calculates the sum of the digits in a string and multiplied value based on index order

  Returns sum of values

  ## Examples

      iex> multiply_and_sum_array("629104150021")
      57

  """
  @spec multiply_and_sum_array(string) :: number
  def multiply_and_sum_array(number) do
    number
      |> String.codepoints
      |> Enum.map(fn(x) -> String.to_integer(x) end)
      |> Enum.reverse
      |> Stream.with_index
      |> Enum.reduce(0, fn({num, idx}, acc) ->
        acc + (num * mult_by_index_code(idx))
      end)
  end


  @doc """
  Calculates the difference of the highest rounded multiple of 10

  Returns the difference of the highest rounded multiple of 10

  ## Examples

      iex> subtract_from_nearest_multiple_of_ten(57)
      3

  """
  @spec subtract_from_nearest_multiple_of_ten(number) :: number
  def subtract_from_nearest_multiple_of_ten(number) do
    mod(10 - mod(number, 10), 10)
  end

  #def mult_by_index_code(index) when mod(index, 2) == 0, do: 3 #these did not work because I cannot call mod within the `when`
  #def mult_by_index_code(index), do: 1
  def mult_by_index_code(index) do
    case mod(index, 2) do
      0 -> 3
      _ -> 1
    end
  end

  defp mod(x,y) when x > 0, do: rem(x, y);
  defp mod(x,y) when x < 0, do: rem(y+x, y);
  defp mod(0,_y), do: 0

Tests

 test "gtin_check_digit function with valid number" do
    number = "6291041500213"
    assert {:ok} == gtin_check_digit(number)
  end

  test "gtin_check_digit function with invalid number" do
    number = "6291041500214"
    assert {:error} == gtin_check_digit(number)
    assert {:error} == gtin_check_digit("6291041533213")
  end

  test "mult_by_index_code function" do
    assert mult_by_index_code(1) == 1
    assert mult_by_index_code(2) == 3
    assert mult_by_index_code(3) == 1
    assert mult_by_index_code(4) == 3
  end

  test "multiply_and_sum_array function" do
    number = "629104150021"
    #number = [6,2,9,1,0,4,1,5,0,0,2,1]
    assert 57 == multiply_and_sum_array(number)
  end

  test "subtract_from_nearest_multiple_of_ten function" do
    assert 3 == subtract_from_nearest_multiple_of_ten(57)
  end

As I refine this, I'll create a hex package to use a common utility library for the elixir community.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.