gs1 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +13 -0
- data/.rspec +3 -0
- data/.rubocop.yml +13 -0
- data/.ruby-version +1 -0
- data/.travis.yml +8 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +21 -0
- data/README.md +83 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/gs1.gemspec +42 -0
- data/lib/gs1/ai.rb +294 -0
- data/lib/gs1/barcode/base.rb +83 -0
- data/lib/gs1/barcode/definitions.rb +26 -0
- data/lib/gs1/barcode/healthcare.rb +53 -0
- data/lib/gs1/barcode.rb +4 -0
- data/lib/gs1/batch.rb +33 -0
- data/lib/gs1/check_digit_calculator.rb +49 -0
- data/lib/gs1/content.rb +47 -0
- data/lib/gs1/definitions.rb +79 -0
- data/lib/gs1/expiration_date.rb +55 -0
- data/lib/gs1/extensions/date.rb +26 -0
- data/lib/gs1/extensions/gtin.rb +23 -0
- data/lib/gs1/extensions.rb +2 -0
- data/lib/gs1/gtin.rb +39 -0
- data/lib/gs1/record.rb +55 -0
- data/lib/gs1/serial_number.rb +31 -0
- data/lib/gs1/sscc.rb +43 -0
- data/lib/gs1/validations/check_digit_validation.rb +17 -0
- data/lib/gs1/validations/date_validation.rb +23 -0
- data/lib/gs1/validations/length_validation.rb +25 -0
- data/lib/gs1/validations.rb +49 -0
- data/lib/gs1/version.rb +3 -0
- data/lib/gs1.rb +47 -0
- metadata +179 -0
@@ -0,0 +1,26 @@
|
|
1
|
+
module GS1
|
2
|
+
module Barcode
|
3
|
+
# Module for handling definitions.
|
4
|
+
#
|
5
|
+
module Definitions
|
6
|
+
def self.included(base)
|
7
|
+
base.extend ClassMethods
|
8
|
+
end
|
9
|
+
|
10
|
+
# Adding defintion class methods.
|
11
|
+
#
|
12
|
+
module ClassMethods
|
13
|
+
attr_reader :records
|
14
|
+
|
15
|
+
def define_records(*records)
|
16
|
+
@records ||= []
|
17
|
+
@records = records
|
18
|
+
|
19
|
+
records.each do |record|
|
20
|
+
attr_reader record.underscore_name
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module GS1
|
2
|
+
module Barcode
|
3
|
+
# Barcode for boxes in healthcare business.
|
4
|
+
#
|
5
|
+
class Healthcare < Base
|
6
|
+
define_records GTIN, ExpirationDate, Batch, SerialNumber
|
7
|
+
|
8
|
+
def to_s(level: AIDCMarketingLevels::ENHANCED)
|
9
|
+
return unless valid?(level: level)
|
10
|
+
|
11
|
+
[gtin.to_ai,
|
12
|
+
expiration_date&.to_ai,
|
13
|
+
batch&.to_ai,
|
14
|
+
serial_number&.to_ai].compact.join
|
15
|
+
end
|
16
|
+
|
17
|
+
def valid?(level: AIDCMarketingLevels::ENHANCED)
|
18
|
+
return false unless AIDCMarketingLevels::ALL.include?(level)
|
19
|
+
|
20
|
+
validate(level)
|
21
|
+
|
22
|
+
errors.empty?
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def validate(level)
|
28
|
+
errors.clear
|
29
|
+
|
30
|
+
validate_minimum
|
31
|
+
return if level == AIDCMarketingLevels::MINIMUM
|
32
|
+
|
33
|
+
validate_enhanced
|
34
|
+
return if level == AIDCMarketingLevels::ENHANCED
|
35
|
+
|
36
|
+
validate_highest
|
37
|
+
end
|
38
|
+
|
39
|
+
def validate_minimum
|
40
|
+
errors << 'Invalid gtin' unless gtin.valid?
|
41
|
+
end
|
42
|
+
|
43
|
+
def validate_enhanced
|
44
|
+
errors << 'Invalid batch' unless batch&.valid?
|
45
|
+
errors << 'Invalid expiration date' unless expiration_date&.valid?
|
46
|
+
end
|
47
|
+
|
48
|
+
def validate_highest
|
49
|
+
errors << 'Invalid serial number' unless serial_number&.valid?
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
data/lib/gs1/barcode.rb
ADDED
data/lib/gs1/batch.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
module GS1
|
2
|
+
# Batch or lot number: AI (10)
|
3
|
+
#
|
4
|
+
# The GS1 Application Identifier (10) indicates that the GS1 Application Identifier data field contains a
|
5
|
+
# batch or lot number. The batch or lot number associates an item with information the manufacturer
|
6
|
+
# considers relevant for traceability of the trade item to which the element string is applied. The data may
|
7
|
+
# refer to the trade item itself or to items contained. The number may be, for example, a production lot
|
8
|
+
# number, a shift number, a machine number, a time, or an internal production code. The data is
|
9
|
+
# alphanumeric and may include all characters contained in figure 7.11-1.
|
10
|
+
#
|
11
|
+
# Note: The batch or lot number is not part of the unique identification of a trade item.
|
12
|
+
#
|
13
|
+
# Figure 3.4.1-1. Format of the element string
|
14
|
+
# |----|------------------------------------------------------|
|
15
|
+
# | AI | Batch or lot number |
|
16
|
+
# |----|------------------------------------------------------|
|
17
|
+
# | 10 | X1 -------------> variable length -------------> X20 |
|
18
|
+
# |----|------------------------------------------------------|
|
19
|
+
#
|
20
|
+
# The data transmitted by the barcode reader means that the element string denoting a batch or lot
|
21
|
+
# number has been captured. As this element string is an attribute of a particular item, it must be
|
22
|
+
# processed together with the GTIN of the trade item to which it relates. When indicating this element
|
23
|
+
# string in the non-HRI text section of a barcode label, the following data title SHOULD be used (see
|
24
|
+
# also section 3.2): BATCH/LOT
|
25
|
+
#
|
26
|
+
# Source: https://www.gs1.org/sites/default/files/docs/barcodes/GS1_General_Specifications.pdf
|
27
|
+
#
|
28
|
+
class Batch < Record
|
29
|
+
AI = AI::BATCH_LOT
|
30
|
+
|
31
|
+
define :length, allowed: 1..20
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module GS1
|
2
|
+
# GS1 check digit calculation
|
3
|
+
#
|
4
|
+
# Implementation of: http://www.gs1.org/how-calculate-check-digit-manually
|
5
|
+
# Notice! This class does not validate the format of the given sequence,
|
6
|
+
# only the length.
|
7
|
+
#
|
8
|
+
class CheckDigitCalculator
|
9
|
+
MULTIPLIER_ARRAY = [3, 1] * 9
|
10
|
+
VALID_LENGTHS = [7, 11, 12, 13, 17].freeze
|
11
|
+
|
12
|
+
def initialize(sequence)
|
13
|
+
@sequence = sequence
|
14
|
+
@reverse_sequence = sequence.chars.reverse
|
15
|
+
|
16
|
+
raise ArgumentError, 'Invalid length' unless VALID_LENGTHS.include?(sequence.size)
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.from_sequence(sequence)
|
20
|
+
new(sequence).check_digit
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.with_sequence(sequence)
|
24
|
+
new(sequence).calculate_sequence_with_digit
|
25
|
+
end
|
26
|
+
|
27
|
+
def check_digit
|
28
|
+
sub_from_nearest_higher_ten.to_s
|
29
|
+
end
|
30
|
+
|
31
|
+
def calculate_sequence_with_digit
|
32
|
+
sequence + check_digit
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
attr_reader :sequence, :reverse_sequence
|
38
|
+
|
39
|
+
def multiplied_sequence
|
40
|
+
@multiplied_sequence ||= reverse_sequence.each_with_index.map do |digit, idx|
|
41
|
+
digit.to_i * MULTIPLIER_ARRAY[idx]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def sub_from_nearest_higher_ten
|
46
|
+
(10 - (multiplied_sequence.inject(:+) % 10)) % 10
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
data/lib/gs1/content.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
module GS1
|
2
|
+
# Identification of trade items contained in a logistic unit: AI (02)
|
3
|
+
#
|
4
|
+
# The GS1 Application Identifier (02) indicates that the GS1 Application Identifier data field includes
|
5
|
+
# the GTIN of the contained trade items. The GTIN is used to identify trade items (see section 4).
|
6
|
+
#
|
7
|
+
# The GTIN for trade items may be a GTIN-8, GTIN-12, GTIN-13 or a GTIN-14. See section 2 for the
|
8
|
+
# rules for GTIN formats and mandatory or optional attributes in the various trade item applications.
|
9
|
+
#
|
10
|
+
# The GTIN of the trade items contained is the GTIN of the highest level of trade item contained in the
|
11
|
+
# logistic unit.
|
12
|
+
#
|
13
|
+
# Note: This element string SHALL be used only on a logistic unit if:
|
14
|
+
#
|
15
|
+
# - the logistic unit is not itself a trade item; and
|
16
|
+
# - all trade items that are contained at the highest level have the same GTIN
|
17
|
+
#
|
18
|
+
# The check digit is explained in section 7.9. Its verification, which must be carried out in the
|
19
|
+
# application software, ensures that the number is correctly composed.
|
20
|
+
#
|
21
|
+
# Figure 3.3.3-1. Format of the element string
|
22
|
+
# |----|-------------------------------------------------------------|
|
23
|
+
# | | Global Trade Item Number (GTIN) |
|
24
|
+
# | |-----------------------------------------------------|-------|
|
25
|
+
# | AI | GS1-8 Prefix or GS1 Company Prefix Item reference | Check |
|
26
|
+
# | | -----------------------------> <-----------------| digit |
|
27
|
+
# |----|-----------------------------------------------------|-------|
|
28
|
+
# GTIN-8 | 02 | 0 0 0 0 0 0 N1 N2 N3 N4 N5 N6 N7 | N8 |
|
29
|
+
# GTIN-12 | 02 | 0 0 N1 N2 N3 N4 N5 N6 N7 N8 N9 N10 N11 | N12 |
|
30
|
+
# GTIN-13 | 02 | 0 N1 N2 N3 N4 N5 N6 N7 N8 N9 N10 N11 N12 | N13 |
|
31
|
+
# GTIN-14 | 02 | N1 N2 N3 N4 N5 N6 N7 N8 N9 N10 N11 N12 N13 | N14 |
|
32
|
+
# |----|-----------------------------------------------------|-------|
|
33
|
+
#
|
34
|
+
# The data transmitted from the barcode reader means that the element string denoting the GTIN of
|
35
|
+
# trade items contained in a logistic unit has been captured. This element string must be processed
|
36
|
+
# together with the count of trade items, AI (37), which must appear on the same unit (see section
|
37
|
+
# 3.6.5). When indicating this element string in the non-HRI text section of a barcode label, the
|
38
|
+
# following data title SHOULD be used (see also section 3.2): CONTENT
|
39
|
+
#
|
40
|
+
# Source: https://www.gs1.org/sites/default/files/docs/barcodes/GS1_General_Specifications.pdf
|
41
|
+
#
|
42
|
+
class Content < Record
|
43
|
+
include Extensions::GTIN
|
44
|
+
|
45
|
+
AI = AI::CONTENT
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module GS1
|
2
|
+
# Module for handling definitions.
|
3
|
+
#
|
4
|
+
module Definitions
|
5
|
+
class UnknownDefinition < StandardError; end
|
6
|
+
|
7
|
+
def self.included(base)
|
8
|
+
base.extend ClassMethods
|
9
|
+
end
|
10
|
+
|
11
|
+
# Adding defintion class methods.
|
12
|
+
#
|
13
|
+
module ClassMethods
|
14
|
+
attr_reader :definitions
|
15
|
+
|
16
|
+
DEFINITIONS = %i[check_digit date length].freeze
|
17
|
+
|
18
|
+
def define(key, options = {})
|
19
|
+
raise UnknownDefinition, "#{key} is not a valid definition" unless DEFINITIONS.include?(key)
|
20
|
+
|
21
|
+
@definitions ||= {}
|
22
|
+
|
23
|
+
definitions[key] = send("normalize_#{key}_options", options)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Currently no support for options.
|
27
|
+
def normalize_check_digit_options(_options)
|
28
|
+
{}
|
29
|
+
end
|
30
|
+
|
31
|
+
# Currently no support for options.
|
32
|
+
def normalize_date_options(_options)
|
33
|
+
{}
|
34
|
+
end
|
35
|
+
|
36
|
+
# Defaults barcode length to allowed length if not explicitly defined, only
|
37
|
+
# if there is one significant allowed.
|
38
|
+
def normalize_length_options(options)
|
39
|
+
options[:allowed] = normalize_multiple_option(options[:allowed])
|
40
|
+
options[:barcode] = normalize_singlural_option(options[:barcode])
|
41
|
+
options[:max_barcode] = normalize_singlural_option(options[:barcode] || options[:allowed])
|
42
|
+
|
43
|
+
options
|
44
|
+
end
|
45
|
+
|
46
|
+
def normalize_multiple_option(option_value)
|
47
|
+
case option_value
|
48
|
+
when nil then nil
|
49
|
+
when Range then option_value.to_a
|
50
|
+
when Array then option_value
|
51
|
+
else [option_value]
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def normalize_singlural_option(option_value)
|
56
|
+
case option_value
|
57
|
+
when Array then option_value.last
|
58
|
+
else option_value
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def barcode_length
|
63
|
+
lengths[:barcode]
|
64
|
+
end
|
65
|
+
|
66
|
+
def barcode_max_length
|
67
|
+
lengths[:max_barcode]
|
68
|
+
end
|
69
|
+
|
70
|
+
def allowed_lengths
|
71
|
+
lengths[:allowed]
|
72
|
+
end
|
73
|
+
|
74
|
+
def lengths
|
75
|
+
definitions[:length]
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module GS1
|
2
|
+
# Expiration date: AI (17)
|
3
|
+
#
|
4
|
+
# The GS1 Application Identifier (17) indicates that the GS1 Application Identifier data fields contain
|
5
|
+
# an expiration date. The expiration date is the date that determines the limit of consumption or use
|
6
|
+
# of a product/coupon. Its meaning is determined based on the trade item context (e.g., for food, the
|
7
|
+
# date will indicate the possibility of a direct health risk resulting from use of the product after the
|
8
|
+
# date, for pharmaceutical products, it will indicate the possibility of an indirect health risk resulting
|
9
|
+
# from the ineffectiveness of the product after the date). It is often referred to as "use by date" or
|
10
|
+
# "maximum durability date."
|
11
|
+
#
|
12
|
+
# Note: A retailer may use this to determine a date that after which, they will no longer
|
13
|
+
# merchandise the product. Currently, there are implementations of best before date which are
|
14
|
+
# interpreted in their processes as date to Sell By.
|
15
|
+
#
|
16
|
+
# The structure is:
|
17
|
+
#
|
18
|
+
# - Year: the tens and units of the year (e.g., 2003 = 03), which is mandatory.
|
19
|
+
# - Month: the number of the month (e.g., January = 01), which is mandatory.
|
20
|
+
# - Day: the number of the day of the relevant month (e.g., second day = 02); if it is not necessary
|
21
|
+
# to specify the day, the field must be filled with two zeros.
|
22
|
+
#
|
23
|
+
# Note: When it is not necessary to specify the day (the Day field is filled with two zeros), the
|
24
|
+
# resultant data string SHALL be interpreted as the last day of the noted month including any
|
25
|
+
# adjustment for leap years (e.g., "130200" is "2013 February 28", "160200" is "2016 February
|
26
|
+
# 29", etc.).
|
27
|
+
#
|
28
|
+
# Note: This element string can only specify dates ranging from 49 years in the past to 50
|
29
|
+
# years in the future. Determination of the correct century is explained in section 7.12.
|
30
|
+
#
|
31
|
+
# Figure 3.4.5-1. Format of the element string
|
32
|
+
# |----|-----------------------------------|
|
33
|
+
# | | Best before date |
|
34
|
+
# | |-----------|-----------|-----------|
|
35
|
+
# | AI | Year | Month | Day |
|
36
|
+
# | | | | |
|
37
|
+
# |----|-----------|-----------|-----------|
|
38
|
+
# | 15 | N1 N2 | N3 N4 | N5 N6 |
|
39
|
+
# |----|-----------|-----------|-----------|
|
40
|
+
#
|
41
|
+
# The data transmitted from the barcode reader means that the element string denoting a best before
|
42
|
+
# date has been captured. As this element string is an attribute of a trade item, it must be processed
|
43
|
+
# together with the GTIN of the trade item to which it relates.
|
44
|
+
#
|
45
|
+
# When indicating this element string in the non-HRI text section of a barcode label, the following data
|
46
|
+
# title SHOULD be used (see also section 3.2): BEST BEFORE or BEST BY
|
47
|
+
#
|
48
|
+
# Source: https://www.gs1.org/sites/default/files/docs/barcodes/GS1_General_Specifications.pdf
|
49
|
+
#
|
50
|
+
class ExpirationDate < Record
|
51
|
+
include Extensions::Date
|
52
|
+
|
53
|
+
AI = AI::USE_BY
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'date'
|
2
|
+
|
3
|
+
module GS1
|
4
|
+
module Extensions
|
5
|
+
# Extension for a GS1 date. Ensures correct formating and validation.
|
6
|
+
#
|
7
|
+
module Date
|
8
|
+
def self.included(base)
|
9
|
+
base.define :date
|
10
|
+
base.define :length, barcode: 6
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_s
|
14
|
+
return data.strftime('%y%m%d') if data.is_a?(::Date)
|
15
|
+
|
16
|
+
data
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_date
|
20
|
+
return data if data.is_a?(::Date)
|
21
|
+
|
22
|
+
::Date.parse(data)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module GS1
|
2
|
+
module Extensions
|
3
|
+
# Extension for a GS1 GTIN. Ensures correct formating and validation.
|
4
|
+
#
|
5
|
+
module GTIN
|
6
|
+
def self.included(base)
|
7
|
+
base.define :check_digit
|
8
|
+
base.define :length, allowed: [8, 12, 13, 14].freeze, barcode: 14
|
9
|
+
|
10
|
+
base.allowed_lengths.each do |length|
|
11
|
+
define_method "to_gtin_#{length}" do
|
12
|
+
data.to_s.rjust(length, '0')
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Default to GTIN-14 since it is the most common format.
|
18
|
+
def to_s
|
19
|
+
to_gtin_14
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/gs1/gtin.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
module GS1
|
2
|
+
# Identification of a trade item (GTIN): AI (01)
|
3
|
+
#
|
4
|
+
# The GS1 Application Identifier (01) indicates that the GS1 Application Identifier data field contains a
|
5
|
+
# GTIN. The GTIN is used to identify trade items (see section 4).
|
6
|
+
#
|
7
|
+
# The GTIN for trade items may be a GTIN-8, GTIN-12, GTIN-13 or a GTIN-14. See section 2.1 for the
|
8
|
+
# rules for GTIN formats and mandatory or optional attributes in the various trade item applications.
|
9
|
+
#
|
10
|
+
# The check digit is explained in section 7.9. Its verification, which must be carried out in the
|
11
|
+
# application software, ensures that the number is correctly composed.
|
12
|
+
#
|
13
|
+
# Figure 3.3.2-1. Format of the element string
|
14
|
+
# |----|-------------------------------------------------------------|
|
15
|
+
# | | Global Trade Item Number (GTIN) |
|
16
|
+
# | |-----------------------------------------------------|-------|
|
17
|
+
# | AI | GS1-8 Prefix or GS1 Company Prefix Item reference | Check |
|
18
|
+
# | | -----------------------------> <-----------------| digit |
|
19
|
+
# |----|-----------------------------------------------------|-------|
|
20
|
+
# GTIN-8 | 02 | 0 0 0 0 0 0 N1 N2 N3 N4 N5 N6 N7 | N8 |
|
21
|
+
# GTIN-12 | 02 | 0 0 N1 N2 N3 N4 N5 N6 N7 N8 N9 N10 N11 | N12 |
|
22
|
+
# GTIN-13 | 02 | 0 N1 N2 N3 N4 N5 N6 N7 N8 N9 N10 N11 N12 | N13 |
|
23
|
+
# GTIN-14 | 02 | N1 N2 N3 N4 N5 N6 N7 N8 N9 N10 N11 N12 N13 | N14 |
|
24
|
+
# |----|-----------------------------------------------------|-------|
|
25
|
+
#
|
26
|
+
# The data transmitted from the barcode reader means that the element string denoting the GTIN of a
|
27
|
+
# fixed measure trade item has been captured.
|
28
|
+
#
|
29
|
+
# When indicating this element string in the non-HRI text section of a barcode label, the following data
|
30
|
+
# title SHOULD be used (see also section 3.2): GTIN
|
31
|
+
#
|
32
|
+
# Source: https://www.gs1.org/sites/default/files/docs/barcodes/GS1_General_Specifications.pdf
|
33
|
+
#
|
34
|
+
class GTIN < Record
|
35
|
+
include Extensions::GTIN
|
36
|
+
|
37
|
+
AI = AI::GTIN
|
38
|
+
end
|
39
|
+
end
|
data/lib/gs1/record.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
module GS1
|
2
|
+
# Base class for a GS1 record.
|
3
|
+
#
|
4
|
+
class Record
|
5
|
+
include Definitions
|
6
|
+
include Validations
|
7
|
+
|
8
|
+
attr_reader :data
|
9
|
+
|
10
|
+
def initialize(data)
|
11
|
+
super
|
12
|
+
@data = data
|
13
|
+
end
|
14
|
+
|
15
|
+
singleton_class.send(:attr_reader, :descendants)
|
16
|
+
@descendants = []
|
17
|
+
|
18
|
+
class << self
|
19
|
+
def inherited(subclass)
|
20
|
+
descendants << subclass
|
21
|
+
end
|
22
|
+
|
23
|
+
def ai
|
24
|
+
self::AI
|
25
|
+
end
|
26
|
+
|
27
|
+
def underscore_name
|
28
|
+
name.split('::')
|
29
|
+
.last
|
30
|
+
.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
31
|
+
.gsub(/([a-z\d])([A-Z])/, '\1_\2')
|
32
|
+
.tr('-', '_')
|
33
|
+
.downcase
|
34
|
+
.to_sym
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def ai
|
39
|
+
self.class.ai
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_s
|
43
|
+
data&.to_s
|
44
|
+
end
|
45
|
+
|
46
|
+
def to_ai
|
47
|
+
"#{ai}#{data}"
|
48
|
+
end
|
49
|
+
|
50
|
+
def ==(other)
|
51
|
+
self.class == other.class &&
|
52
|
+
to_s == other.to_s
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module GS1
|
2
|
+
# Serial number: AI (21)
|
3
|
+
#
|
4
|
+
# The GS1 Application Identifier (21) indicates that the GS1 Application Identifier data field contains a
|
5
|
+
# serial number. A serial number is assigned to an entity for its lifetime. When combined with a GTIN,
|
6
|
+
# a serial number uniquely identifies an individual item. The serial number field is alphanumeric and
|
7
|
+
# may include all characters contained in figure 7.11-1. The manufacturer determines the serial
|
8
|
+
# number.
|
9
|
+
#
|
10
|
+
# Figure 3.5.2-1. Format of the element string
|
11
|
+
# |----|------------------------------------------------------|
|
12
|
+
# | AI | Serial number |
|
13
|
+
# |----|------------------------------------------------------|
|
14
|
+
# | 21 | X1 -------------> variable length -------------> X20 |
|
15
|
+
# |----|------------------------------------------------------|
|
16
|
+
#
|
17
|
+
# The data transmitted from the barcode reader means that the element string denoting a serial
|
18
|
+
# number has been captured. As this element string is an attribute of a trade item, it must be
|
19
|
+
# processed together with the GTIN of the trade item to which it relates.
|
20
|
+
#
|
21
|
+
# When indicating this element string in the non-HRI text section of a barcode label, the following data
|
22
|
+
# title SHOULD be used (see also section 3.2): SERIAL
|
23
|
+
#
|
24
|
+
# Source: https://www.gs1.org/sites/default/files/docs/barcodes/GS1_General_Specifications.pdf
|
25
|
+
#
|
26
|
+
class SerialNumber < Record
|
27
|
+
AI = AI::SERIAL
|
28
|
+
|
29
|
+
define :length, allowed: 1..20
|
30
|
+
end
|
31
|
+
end
|
data/lib/gs1/sscc.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
module GS1
|
2
|
+
#
|
3
|
+
# Identification of a logistic unit (SSCC): AI (00)
|
4
|
+
#
|
5
|
+
# The GS1 Application Identifier (00) indicates that the GS1 Application Identifier data field contains an
|
6
|
+
# SSCC (Serial Shipping Container Code). The SSCC is used to identify logistic units (see section 2.2).
|
7
|
+
#
|
8
|
+
# The extension digit is used to increase the capacity of the serial reference within the SSCC. It is
|
9
|
+
# assigned by the company that constructs the SSCC. The extension digit ranges from 0-9.
|
10
|
+
#
|
11
|
+
# The GS1 Company Prefix is allocated by GS1 Member Organisations to the company that allocates
|
12
|
+
# the SSCC - here the physical builder or the brand owner of the logistic unit (see section 1.4.4). It
|
13
|
+
# makes the SSCC unique worldwide but does not identify the origin of the unit.
|
14
|
+
#
|
15
|
+
# The structure and content of the serial reference is at the discretion of owner of the GS1 Company
|
16
|
+
# Prefix to uniquely identify each logistic unit.
|
17
|
+
#
|
18
|
+
# The check digit is explained in section 7.9. Its verification, which must be carried out in the
|
19
|
+
# application software, ensures that the number is correctly composed.
|
20
|
+
#
|
21
|
+
# Figure 3.3.1-1. Format of the element string
|
22
|
+
# |----|-----------------------------------------------------------------------------|
|
23
|
+
# | | SSCC (Serial Shipping Container Code) |
|
24
|
+
# | |-----------|---------------------------------------------------------|-------|
|
25
|
+
# | AI | Extension | GS1 Company Prefix Serial reference | Check |
|
26
|
+
# | | digit | --------------> <-----------------------| digit |
|
27
|
+
# |----|-----------|---------------------------------------------------------|-------|
|
28
|
+
# | 00 | N1 | N2 N3 N4 N5 N6 N7 N8 N9 N10 N11 N12 N13 N14 N15 N16 N17 | N18 |
|
29
|
+
# |----|-----------|---------------------------------------------------------|-------|
|
30
|
+
#
|
31
|
+
# The data transmitted from the barcode reader means that the element string denoting the SSCC of
|
32
|
+
# a logistic unit has been captured. When indicating this element string in the non-HRI text section of
|
33
|
+
# a barcode label, the following data title SHOULD be used (see also section 3.2): SSCC
|
34
|
+
#
|
35
|
+
# Source: https://www.gs1.org/sites/default/files/docs/barcodes/GS1_General_Specifications.pdf
|
36
|
+
#
|
37
|
+
class SSCC < Record
|
38
|
+
AI = AI::SSCC
|
39
|
+
|
40
|
+
define :check_digit
|
41
|
+
define :length, allowed: 18
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module GS1
|
2
|
+
module Validations
|
3
|
+
# Ensures correct check digit validation.
|
4
|
+
#
|
5
|
+
module CheckDigitValidation
|
6
|
+
def validate_check_digit
|
7
|
+
errors << 'Check digit mismatch' unless valid_check_digit?
|
8
|
+
end
|
9
|
+
|
10
|
+
def valid_check_digit?
|
11
|
+
GS1::CheckDigitCalculator.with_sequence(data[0..-2]) == data
|
12
|
+
rescue ArgumentError
|
13
|
+
false
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'date'
|
2
|
+
|
3
|
+
module GS1
|
4
|
+
module Validations
|
5
|
+
# Ensures correct date validation.
|
6
|
+
#
|
7
|
+
module DateValidation
|
8
|
+
def validate_date(_options = {})
|
9
|
+
errors << 'Invalid date' unless valid_date?
|
10
|
+
end
|
11
|
+
|
12
|
+
def valid_date?
|
13
|
+
return true if data.is_a?(::Date)
|
14
|
+
|
15
|
+
::Date.parse(data)
|
16
|
+
|
17
|
+
true
|
18
|
+
rescue TypeError, ArgumentError
|
19
|
+
false
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module GS1
|
2
|
+
module Validations
|
3
|
+
# Ensures correct length validation.
|
4
|
+
#
|
5
|
+
module LengthValidation
|
6
|
+
def validate_length
|
7
|
+
errors << 'Invalid length' unless valid_length?
|
8
|
+
end
|
9
|
+
|
10
|
+
def valid_length?
|
11
|
+
return false unless data
|
12
|
+
|
13
|
+
valid_barcode_length? || valid_allowed_length?
|
14
|
+
end
|
15
|
+
|
16
|
+
def valid_allowed_length?
|
17
|
+
self.class.allowed_lengths.include?(data.size)
|
18
|
+
end
|
19
|
+
|
20
|
+
def valid_barcode_length?
|
21
|
+
self.class.barcode_length == data.size if self.class.barcode_length
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|