Skip to content
This repository was archived by the owner on Dec 21, 2025. It is now read-only.
This repository was archived by the owner on Dec 21, 2025. It is now read-only.

Add checks for valid GTINs #140

@ferrisoxide

Description

@ferrisoxide

GTINs contain basic checksums we can use to verify that the data in the database is actually valid.

We could implement this at either the app or db level, run a script to mark any product with an invalid GTIN.

def valid_gtin?(gtin)
  return false unless gtin.match?(/^\d+$/) && [8, 12, 13, 14].include?(gtin.length)

  digits = gtin.chars.map(&:to_i)
  check_digit = digits.pop

  sum = digits.reverse.each_with_index.sum do |digit, index|
    index.odd? ? digit * 3 : digit
  end

  calculated_check_digit = (10 - sum % 10) % 10
  calculated_check_digit == check_digit
end

puts valid_gtin?("012345678905") # Example usage

or

create or replace function is_valid_gtin(gtin text) returns boolean as $$
declare
    gtin_length int;
    check_digit int;
    gtin_without_check_digit text;
    gtin_reverse text;
    gtin_digits int[];
    i int;
    sum int := 0;
    computed_check_digit int;
begin
    -- remove non-numeric characters
    gtin := regexp_replace(gtin, '\\d', '', 'g');
    gtin_length := length(gtin);

    -- ensure gtin length is valid (8, 12, 13, or 14 digits)
    if gtin_length not in (8, 12, 13, 14) then
      return false;
    end if;

    gtin_without_check_digit := left(gtin, gtin_length - 1);
    check_digit := cast(right(gtin, 1) as int);

    -- reverse the gtin (excluding check digit) and convert to an array of integers
    gtin_reverse := reverse(gtin_without_check_digit);
    gtin_digits := string_to_array(gtin_reverse, null)::int[];

    -- compute the check digit using the luhn algorithm
    for i in 1..array_upper(gtin_digits, 1) loop
      sum := sum + gtin_digits[i] * case i % 2 when 0 then 3 else 1 end;
    end loop;

    computed_check_digit := (10 - sum % 10) % 10;

    -- compare computed check digit with the actual check digit
    return computed_check_digit = check_digit;
end;
$$ language plpgsql;

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions