gs1 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|