Skip to content

Commit 5b0a67e

Browse files
author
Adrià Tarroja Caubet
committed
Fix #471: Validate GS1-128 AIs for trade measures (31nn, 32nn, 35nn, 36nn)
1 parent ad4af91 commit 5b0a67e

File tree

2 files changed

+28
-4
lines changed

2 files changed

+28
-4
lines changed

stdnum/gs1_128.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,10 @@
7575
'8007': 'stdnum.iban',
7676
}
7777

78+
_TRADE_MEASURE_PREFIXES = {
79+
'31', '32', '35', '36',
80+
}
81+
7882

7983
def compact(number: str) -> str:
8084
"""Convert the GS1-128 to the minimal representation.
@@ -195,6 +199,9 @@ def info(number: str, separator: str = '') -> dict[str, Any]:
195199
If a `separator` is provided it will be used as FNC1 to determine the end
196200
of variable-sized values.
197201
"""
202+
for grp in re.findall(r"\((\d+)\)", number):
203+
if grp[:2] in _TRADE_MEASURE_PREFIXES and len(grp) != 4:
204+
raise InvalidComponent()
198205
number = compact(number)
199206
data = {}
200207
identifier = ''
@@ -253,7 +260,14 @@ def encode(data: Mapping[str, object], separator: str = '', parentheses: bool =
253260
if ai in _ai_validators:
254261
mod = __import__(_ai_validators[ai], globals(), locals(), ['validate'])
255262
mod.validate(value)
256-
value = _encode_value(info['format'], info['type'], value)
263+
raw = _encode_value(info['format'], info['type'], value)
264+
if info['type'] == 'decimal' and info['format'] == 'N6' and ai[:2] in _TRADE_MEASURE_PREFIXES:
265+
# insert decimal count into AI and drop it from the value payload
266+
dec = raw[0]
267+
ai = ai + dec
268+
value = raw[1:]
269+
else:
270+
value = raw
257271
# store variable-sized values separate from fixed-size values
258272
if info.get('fnc1'):
259273
variable_values.append((ai_fmt % ai, info['format'], info['type'], value))

tests/test_gs1_128.doctest

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ properly.
6565
... '313': '123.456', # str
6666
... '391': ('123', Decimal('123.456')), # currency number combo
6767
... }, parentheses=True)
68-
'(310)2001723(311)0000456(312)5033333(313)3123456(391)3123123456'
68+
'(3102)001723(3110)000456(3125)033333(3133)123456(391)3123123456'
6969

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

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

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

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

126128
>>> pprint.pprint(gs1_128.info('(310)5033333'))
129+
Traceback (most recent call last):
130+
...
131+
InvalidComponent: ...
132+
>>> pprint.pprint(gs1_128.info('(3105)033333'))
133+
{'310': Decimal('0.33333')}
134+
>>> pprint.pprint(gs1_128.info('(01)98456789014533(3103)000035'))
135+
{'01': '98456789014533', '310': Decimal('0.035')}
136+
>>> pprint.pprint(gs1_128.info('(3105)033333'))
127137
{'310': Decimal('0.33333')}
128-
>>> pprint.pprint(gs1_128.info('(310)0033333'))
138+
>>> pprint.pprint(gs1_128.info('(3100)033333'))
129139
{'310': Decimal('33333')}
130140
>>> pprint.pprint(gs1_128.info('(391)3123123456'))
131141
{'391': ('123', Decimal('123.456'))}

0 commit comments

Comments
 (0)