Skip to content

Fix #471: Enforce correct format for GS1-128 trade measure AIs #472

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion stdnum/gs1_128.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@
'8007': 'stdnum.iban',
}

_TRADE_MEASURE_PREFIXES = {
'31', '32', '35', '36',
}


def compact(number: str) -> str:
"""Convert the GS1-128 to the minimal representation.
Expand Down Expand Up @@ -195,6 +199,9 @@ def info(number: str, separator: str = '') -> dict[str, Any]:
If a `separator` is provided it will be used as FNC1 to determine the end
of variable-sized values.
"""
for grp in re.findall(r'\((\d+)\)', number):
if grp[:2] in _TRADE_MEASURE_PREFIXES and len(grp) != 4:
raise InvalidComponent()
number = compact(number)
data = {}
identifier = ''
Expand Down Expand Up @@ -253,7 +260,14 @@ def encode(data: Mapping[str, object], separator: str = '', parentheses: bool =
if ai in _ai_validators:
mod = __import__(_ai_validators[ai], globals(), locals(), ['validate'])
mod.validate(value)
value = _encode_value(info['format'], info['type'], value)
raw = _encode_value(info['format'], info['type'], value)
if info['type'] == 'decimal' and info['format'] == 'N6' and ai[:2] in _TRADE_MEASURE_PREFIXES:
# insert decimal count into AI and drop it from the value payload
dec = raw[0]
ai = ai + dec
value = raw[1:]
else:
value = raw
# store variable-sized values separate from fixed-size values
if info.get('fnc1'):
variable_values.append((ai_fmt % ai, info['format'], info['type'], value))
Expand Down
16 changes: 13 additions & 3 deletions tests/test_gs1_128.doctest
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ properly.
... '313': '123.456', # str
... '391': ('123', Decimal('123.456')), # currency number combo
... }, parentheses=True)
'(310)2001723(311)0000456(312)5033333(313)3123456(391)3123123456'
'(3102)001723(3110)000456(3125)033333(3133)123456(391)3123123456'

We generate dates in various formats, depending on the AI.

Expand All @@ -88,6 +88,8 @@ We generate dates in various formats, depending on the AI.
'(7011)181119'
>>> gs1_128.encode({'7011': datetime.datetime(2018, 11, 19, 12, 45)}, parentheses=True)
'(7011)1811191245'
>>> gs1_128.encode({'01': '98456789014533', '310': Decimal('0.035')}, parentheses=True)
'(01)98456789014533(3103)000035'

If we try to encode an invalid EAN we will get an error.

Expand All @@ -104,7 +106,7 @@ pprint.pprint(gs1_128.info('(01)38425876095074(17)181119(37)1 '))
{'01': '38425876095074', '17': datetime.date(2018, 11, 19), '37': 1}
>>> pprint.pprint(gs1_128.info('013842587609507417181119371'))
{'01': '38425876095074', '17': datetime.date(2018, 11, 19), '37': 1}
>>> pprint.pprint(gs1_128.info('(02)98412345678908(310)3017230(37)32'))
>>> pprint.pprint(gs1_128.info('(02)98412345678908(3103)017230(37)32'))
{'02': '98412345678908', '310': Decimal('17.230'), '37': 32}
>>> pprint.pprint(gs1_128.info('(01)58425876097843(10)123456 (17)181119(37)18'))
{'01': '58425876097843', '10': '123456', '17': datetime.date(2018, 11, 19), '37': 18}
Expand All @@ -124,8 +126,16 @@ InvalidComponent: ...
We can decode decimal values from various formats.

>>> pprint.pprint(gs1_128.info('(310)5033333'))
Traceback (most recent call last):
...
InvalidComponent: ...
>>> pprint.pprint(gs1_128.info('(3105)033333'))
{'310': Decimal('0.33333')}
>>> pprint.pprint(gs1_128.info('(01)98456789014533(3103)000035'))
{'01': '98456789014533', '310': Decimal('0.035')}
>>> pprint.pprint(gs1_128.info('(3105)033333'))
{'310': Decimal('0.33333')}
>>> pprint.pprint(gs1_128.info('(310)0033333'))
>>> pprint.pprint(gs1_128.info('(3100)033333'))
{'310': Decimal('33333')}
>>> pprint.pprint(gs1_128.info('(391)3123123456'))
{'391': ('123', Decimal('123.456'))}
Expand Down
Loading