def test_parse_gtin_8(value: str) -> None: assert Gtin.parse(value) == Gtin( value=value, format=GtinFormat.GTIN_8, prefix=GS1Prefix(value="00009", usage="GS1 US"), payload="9638507", check_digit=4, )
def test_parse_gtin_12_without_leading_zero(value: str) -> None: assert Gtin.parse(value) == Gtin( value=value, format=GtinFormat.GTIN_12, prefix=GS1Prefix(value="061", usage="GS1 US"), payload="61414100003", check_digit=6, )
def test_parse_gtin_13(value: str) -> None: assert Gtin.parse(value) == Gtin( value=value, format=GtinFormat.GTIN_13, prefix=GS1Prefix(value="590", usage="GS1 Poland"), payload="590123412345", check_digit=7, )
def test_parse_gtin_12_with_3_leading_zero(value: str) -> None: assert Gtin.parse(value) == Gtin( value=value, format=GtinFormat.GTIN_12, prefix=GS1Prefix(value="00009", usage="GS1 US"), payload="00090291451", check_digit=1, )
def test_parse_gtin_14() -> None: assert Gtin.parse("98765432109213") == Gtin( value="98765432109213", format=GtinFormat.GTIN_14, prefix=GS1Prefix(value="876", usage="GS1 Netherlands"), payload="9876543210921", check_digit=3, packaging_level=9, )
def test_parse() -> None: sscc = Sscc.parse("376130321109103420") assert sscc == Sscc( value="376130321109103420", prefix=GS1Prefix(value="761", usage="GS1 Schweiz, Suisse, Svizzera"), extension_digit=3, payload="37613032110910342", check_digit=0, )
def parse(cls: Type[Sscc], value: str) -> Sscc: """Parse the given value into a :class:`Sscc` object. Args: value: The value to parse. Returns: SSCC data structure with the successfully extracted data. The checksum is guaranteed to be valid if an SSCC object is returned. Raises: ParseError: If the parsing fails. """ value = value.strip() if len(value) != 18: raise ParseError(f"Failed to parse {value!r} as SSCC: " f"Expected 18 digits, got {len(value)}.") if not value.isnumeric(): raise ParseError( f"Failed to parse {value!r} as SSCC: Expected a numerical value." ) value_without_extension_digit = value[1:] prefix = GS1Prefix.extract(value_without_extension_digit) extension_digit = int(value[0]) payload = value[:-1] check_digit = int(value[-1]) calculated_check_digit = numeric_check_digit(payload) if check_digit != calculated_check_digit: raise ParseError( f"Invalid SSCC check digit for {value!r}: " f"Expected {calculated_check_digit!r}, got {check_digit!r}.") return cls( value=value, prefix=prefix, extension_digit=extension_digit, payload=payload, check_digit=check_digit, )
from biip.gtin import Gtin, GtinFormat from biip.sscc import Sscc @pytest.mark.parametrize( "value, expected", [ ( "00373400306809981733", GS1ElementString( ai=GS1ApplicationIdentifier.extract("00"), value="373400306809981733", pattern_groups=["373400306809981733"], sscc=Sscc( value="373400306809981733", prefix=GS1Prefix(value="734", usage="GS1 Sweden"), extension_digit=3, payload="37340030680998173", check_digit=3, ), ), ), ( "0107032069804988", GS1ElementString( ai=GS1ApplicationIdentifier.extract("01"), value="07032069804988", pattern_groups=["07032069804988"], gtin=Gtin( value="07032069804988", format=GtinFormat.GTIN_13,
element_strings=[ GS1ElementString( ai=GS1ApplicationIdentifier( ai="01", description="Global Trade Item Number (GTIN)", data_title="GTIN", fnc1_required=False, format="N2+N14", pattern="^01(\\d{14})$", ), value="07032069804988", pattern_groups=["07032069804988"], gtin=Gtin( value="07032069804988", format=GtinFormat.GTIN_13, prefix=GS1Prefix(value="703", usage="GS1 Norway"), payload="703206980498", check_digit=8, ), ), GS1ElementString( ai=GS1ApplicationIdentifier( ai="15", description="Best before date (YYMMDD)", data_title="BEST BEFORE or BEST BY", fnc1_required=False, format="N2+N6", pattern="^15(\\d{6})$", ), value="210526", pattern_groups=["210526"],
def parse( cls: Type[Gtin], value: str, *, rcn_region: Optional[RcnRegion] = None ) -> Gtin: """Parse the given value into a :class:`Gtin` object. Both GTIN-8, GTIN-12, GTIN-13, and GTIN-14 are supported. Args: value: The value to parse. rcn_region: The geographical region whose rules should be used to interpret Restricted Circulation Numbers (RCN). Needed to extract e.g. variable weight/price from GTIN. Returns: GTIN data structure with the successfully extracted data. The checksum is guaranteed to be valid if a GTIN object is returned. Raises: ParseError: If the parsing fails. """ from biip.gtin import Rcn value = value.strip() if len(value) not in (8, 12, 13, 14): raise ParseError( f"Failed to parse {value!r} as GTIN: " f"Expected 8, 12, 13, or 14 digits, got {len(value)}." ) if not value.isnumeric(): raise ParseError( f"Failed to parse {value!r} as GTIN: Expected a numerical value." ) stripped_value = _strip_leading_zeros(value) assert len(stripped_value) in (8, 12, 13, 14) num_significant_digits = len(stripped_value) gtin_format = GtinFormat(num_significant_digits) payload = stripped_value[:-1] check_digit = int(stripped_value[-1]) packaging_level: Optional[int] = None if gtin_format == GtinFormat.GTIN_14: packaging_level = int(stripped_value[0]) value_without_packaging_level = stripped_value[1:] prefix = GS1Prefix.extract(value_without_packaging_level) elif gtin_format == GtinFormat.GTIN_12: # Add a zero to convert U.P.C. Company Prefix to GS1 Company Prefix prefix = GS1Prefix.extract(stripped_value.zfill(13)) elif gtin_format == GtinFormat.GTIN_8: prefix = GS1Prefix.extract(stripped_value.zfill(12)) else: prefix = GS1Prefix.extract(stripped_value) calculated_check_digit = numeric_check_digit(payload) if check_digit != calculated_check_digit: raise ParseError( f"Invalid GTIN check digit for {value!r}: " f"Expected {calculated_check_digit!r}, got {check_digit!r}." ) gtin_type: Type[Union[Gtin, Rcn]] if "Restricted Circulation Number" in prefix.usage: gtin_type = Rcn else: gtin_type = Gtin gtin = gtin_type( value=value, format=gtin_format, prefix=prefix, payload=payload, check_digit=check_digit, packaging_level=packaging_level, ) if isinstance(gtin, Rcn) and rcn_region is not None: gtin._parse_with_regional_rules(rcn_region) return gtin
def test_invalid_gs1_prefix(bad_value: str) -> None: with pytest.raises(ParseError) as exc_info: GS1Prefix.extract(bad_value) assert str(exc_info.value) == f"Failed to get GS1 Prefix from {bad_value!r}."
def test_is_hashable() -> None: prefix = GS1Prefix.extract("978") assert hash(prefix) is not None
def test_gs1_prefix(value: str, expected: GS1Prefix) -> None: assert GS1Prefix.extract(value) == expected
@pytest.mark.parametrize("bad_value", ["abcdef", "1a2b3c"]) def test_invalid_gs1_prefix(bad_value: str) -> None: with pytest.raises(ParseError) as exc_info: GS1Prefix.extract(bad_value) assert str(exc_info.value) == f"Failed to get GS1 Prefix from {bad_value!r}." @pytest.mark.parametrize( "value, expected", [ ( "0000001999", GS1Prefix(value="0000001", usage="Unused to avoid collision with GTIN-8"), ), ("060999", GS1Prefix(value="060", usage="GS1 US")), ("139999", GS1Prefix(value="139", usage="GS1 US")), ("6712670000276", None), # Unassigned prefix ("701999", GS1Prefix(value="701", usage="GS1 Norway")), ("978-1-492-05374-3", GS1Prefix(value="978", usage="Bookland (ISBN)")), ], ) def test_gs1_prefix(value: str, expected: GS1Prefix) -> None: assert GS1Prefix.extract(value) == expected def test_is_hashable() -> None: prefix = GS1Prefix.extract("978")
from biip.sscc import Sscc from biip.symbology import Symbology, SymbologyIdentifier @pytest.mark.parametrize( "value, expected", [ ( # GTIN-8 "96385074", ParseResult( value="96385074", gtin=Gtin( value="96385074", format=GtinFormat.GTIN_8, prefix=GS1Prefix(value="00009", usage="GS1 US"), payload="9638507", check_digit=4, ), gs1_message=GS1Message( value="96385074", element_strings=[ GS1ElementString( ai=GS1ApplicationIdentifier.extract("96"), value="385074", pattern_groups=["385074"], ) ], ), sscc_error= ("Failed to parse '96385074' as SSCC: Expected 18 digits, got 8."