test_gs1_128.doctest - more detailed doctests for the stdnum.gs1_128 module Copyright (C) 2019 Sergi Almacellas Abellaan Copyright (C) 2020-2025 Arthur de Jong This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA This file contains more detailed doctests for the stdnum.gs1_128 module. It tries to test more corner cases and detailed functionality that is not really useful as module documentation. >>> from decimal import Decimal >>> import datetime >>> import pprint >>> from stdnum import gs1_128 >>> gs1_128.compact('(01)38425876095074(17)181119(37)1 ') '013842587609507417181119371' We can create a GS1-128 code based on data we provide. Various data types will be converted to the correct representation. >>> gs1_128.encode({'01': '38425876095074', '17': datetime.date(2018, 11, 19), '37': 1}, parentheses=True) '(01)38425876095074(17)181119(37)1' >>> gs1_128.encode({'02': '98412345678908', '3100': 17.23, '37': 32}) '029841234567890831020017233732' >>> gs1_128.encode({'02': '98412345678908', '3100': 17.23, '37': 32}, parentheses=True) '(02)98412345678908(3102)001723(37)32' >>> gs1_128.encode({'253': '1234567890005000123'}) '2531234567890005000123' >>> gs1_128.encode({'09': '1234'}) # unknown AI Traceback (most recent call last): ... InvalidComponent: ... >>> gs1_128.encode({'310': 17.23}) # no longer detected as part of the 3100-3105 range Traceback (most recent call last): ... InvalidComponent: ... If we have a separator we use it to separate variable-length values, otherwise we pad all variable-length values to the maximum length (except the last one). >>> gs1_128.encode({'01': '58425876097843', '10': '123456', '37': 18, '3900': 42, '3920': 12}, parentheses=True) '(01)58425876097843(10)123456 (37)00000018(3900)000000000000042(3920)12' >>> gs1_128.encode({'01': '58425876097843', '10': '123456', '37': 18, '3900': 42, '3920': 12}, parentheses=True, separator='[FNC1]') '(01)58425876097843(10)123456[FNC1](37)18[FNC1](3900)42[FNC1](3920)12' Numeric values can be provided in several forms and precision is encoded properly. >>> gs1_128.encode({ ... '3100': 17.23, # float ... '3110': 456, # int ... '3120': 1.0 / 3.0, # float with lots of digits ... '3130': '123.456', # str ... '3910': ('123', Decimal('123.456')), # currency number combo ... }, parentheses=True) '(3102)001723(3110)000456(3125)033333(3133)123456(3913)123123456' >>> gs1_128.encode({'01': '98456789014533', '3100': Decimal('0.035')}, parentheses=True) '(01)98456789014533(3103)000035' We generate dates in various formats, depending on the AI. >>> gs1_128.encode({ ... '11': datetime.datetime(2018, 11, 19, 0, 0, 0), ... '12': '181119', # if you provide a string value, it is expected to be correct ... '7003': datetime.datetime(2018, 11, 19, 12, 45, 13), ... '7007': (datetime.date(2018, 11, 19), datetime.date(2018, 11, 21)), ... }, parentheses=True) '(11)181119(12)181119(7003)1811191245(7007)181119181121' >>> gs1_128.encode({'8008': datetime.datetime(2018, 11, 19, 12, 45, 13)}, parentheses=True) '(8008)181119124513' >>> gs1_128.encode({'8008': datetime.datetime(2018, 11, 19, 12, 45)}, parentheses=True) '(8008)1811191245' >>> gs1_128.encode({'8008': datetime.datetime(2018, 11, 19, 12, 0)}, parentheses=True) '(8008)18111912' >>> gs1_128.encode({'8008': datetime.datetime(2018, 11, 19, 0, 0)}, parentheses=True) '(8008)18111900' >>> gs1_128.encode({'7011': datetime.date(2018, 11, 19)}, parentheses=True) '(7011)181119' >>> gs1_128.encode({'7011': datetime.datetime(2018, 11, 19, 12, 45)}, parentheses=True) '(7011)1811191245' If we try to encode an invalid EAN we will get an error. >>> gs1_128.encode({'01': '38425876095079'}, parentheses=True) Traceback (most recent call last): ... InvalidChecksum: ... We can decode (parse) the GS1-128 code to a dictionary with information about the structure of the number. 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(3103)017230(37)32')) {'02': '98412345678908', '3103': 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} >>> pprint.pprint(gs1_128.info('|(01)58425876097843|(10)123456|(17)181119(37)18', separator='|')) {'01': '58425876097843', '10': '123456', '17': datetime.date(2018, 11, 19), '37': 18} >>> pprint.pprint(gs1_128.info('(01)38425876095074(17)260400(10)123456')) {'01': '38425876095074', '10': '123456', '17': datetime.date(2026, 4, 30)} >>> pprint.pprint(gs1_128.info('(01)38425876095074(17)261200(10)123456')) {'01': '38425876095074', '10': '123456', '17': datetime.date(2026, 12, 31)} >>> gs1_128.info('(09)38425876095074') # unknown AI Traceback (most recent call last): ... InvalidComponent: ... >>> pprint.pprint(gs1_128.info('(253)1234567890005000123')) {'253': '1234567890005000123'} We can decode decimal values from various formats. >>> pprint.pprint(gs1_128.info('(3105)033333')) {'3105': Decimal('0.33333')} >>> pprint.pprint(gs1_128.info('(3103)000035')) {'3103': Decimal('0.035')} >>> pprint.pprint(gs1_128.info('(3100)033333')) {'3100': Decimal('33333')} >>> pprint.pprint(gs1_128.info('(3913)123123456')) {'3913': ('123', Decimal('123.456'))} We an decode date files from various formats. >>> pprint.pprint(gs1_128.info('(11)181119')) {'11': datetime.date(2018, 11, 19)} >>> pprint.pprint(gs1_128.info('(7003)1811191245')) {'7003': datetime.datetime(2018, 11, 19, 12, 45)} >>> pprint.pprint(gs1_128.info('(7007)181119')) {'7007': datetime.date(2018, 11, 19)} >>> pprint.pprint(gs1_128.info('(7007)181119181121')) {'7007': (datetime.date(2018, 11, 19), datetime.date(2018, 11, 21))} >>> pprint.pprint(gs1_128.info('(8008)18111912')) {'8008': datetime.datetime(2018, 11, 19, 12, 0)} >>> pprint.pprint(gs1_128.info('(7011)181119')) {'7011': datetime.date(2018, 11, 19)} >>> pprint.pprint(gs1_128.info('(7011)1811191245')) {'7011': datetime.datetime(2018, 11, 19, 12, 45)} While the compact() function can clean up the number somewhat the validate() function calls info() and then encode() to ensure an even more compact and consistent format. >>> gs1_128.compact('(01)58425876097843(10)123456 (37)00000018') '015842587609784310123456 3700000018' >>> gs1_128.validate('(01)58425876097843(10)123456 (37)00000018') '015842587609784310123456 3718' >>> gs1_128.validate('(01)58425876097843(10)123456 (37)00000018', separator='|') '015842587609784310123456|3718' >>> gs1_128.validate('30aa') Traceback (most recent call last): ... InvalidFormat: ...