Restricted Circulation Number (RCN) is a subset of GTIN.
Both RCN-8, RCN-12, and RCN-13 are supported. There is no 14 digit version
of RCN.
RCN-12 with prefix 2 and RCN-13 with prefix 02 or 20-29 have the same
semantics across a geographic region, defined by the local GS1 Member
Organization.
RCN-8 with prefix 0 or 2, RCN-12 with prefix 4, and RCN-13 with prefix 04 or
40-49 have semantics that are only defined within a single company.
Use Gtin.parse()
to parse potential RCNs. This
subclass is returned if the GS1 Prefix signifies that the value is an RCN.
References
GS1 General Specifications, section 2.1.11-2.1.12
Rcn
dataclass
Bases: Gtin
Data class containing an RCN.
This is a subclass of Gtin
. To create an Rcn
instance,
use Gtin.parse()
with an RCN string as the value
to parse.
Source code in src/biip/rcn.py
| @dataclass(frozen=True)
class Rcn(Gtin):
"""Data class containing an RCN.
This is a subclass of [`Gtin`][biip.gtin.Gtin]. To create an `Rcn` instance,
use [`Gtin.parse()`][biip.gtin.Gtin.parse] with an RCN string as the value
to parse.
"""
usage: RcnUsage | None = field(default=None)
"""Where the RCN can be circulated, in a geographical region or within a company."""
region: RcnRegion | None = field(default=None)
"""The geographical region.
The region's rules are used to interpret the contents of the RCN.
"""
weight: Decimal | None = field(default=None)
"""A variable weight value extracted from the GTIN."""
count: int | None = field(default=None)
"""A variable count extracted from the GTIN."""
price: Decimal | None = field(default=None)
"""A variable weight price extracted from the GTIN."""
money: moneyed.Money | None = field(default=None)
"""A Money value created from the variable weight price.
Only set if [`py-moneyed`](https://pypi.org/project/py-moneyed/) is
installed and the currency is known.
"""
def __rich_repr__(self) -> Iterator[tuple[str, Any] | tuple[str, Any, Any]]: # noqa: D105
# Skip printing fields with default values
yield from super().__rich_repr__()
yield "usage", self.usage, None
yield "region", self.region, None
yield "weight", self.weight, None
yield "count", self.count, None
yield "price", self.price, None
yield "money", self.money, None
def _with_usage(self) -> Rcn:
# Classification as RCN depends on the prefix being known, so we won't
# get here unless it is known.
assert self.prefix is not None
usage = None
if "within a geographic region" in self.prefix.usage:
usage = RcnUsage.GEOGRAPHICAL
if "within a company" in self.prefix.usage:
usage = RcnUsage.COMPANY
return replace(self, usage=usage)
def _parsed_with_regional_rules(self, *, config: ParseConfig) -> Rcn:
rcn = self
if rcn.usage == RcnUsage.COMPANY:
# The value is an RCN, but it is intended for use within a company,
# so we can only interpret it as an opaque GTIN.
return rcn
if config.rcn_region is None:
# The region is not known, so we cannot interpret the variable measure.
return rcn
rcn = replace(rcn, region=RcnRegion(config.rcn_region))
strategy = _Strategy.get_for_rcn(rcn)
if strategy is None:
# Without a strategy, we cannot extract anything.
return rcn
if config.rcn_verify_variable_measure:
strategy.verify_check_digit(rcn)
if strategy.measure_type == _MeasureType.WEIGHT:
weight = strategy.get_variable_measure(self)
else:
weight = None
if strategy.measure_type == _MeasureType.COUNT:
count = int(strategy.get_variable_measure(self))
else:
count = None
if strategy.measure_type == _MeasureType.PRICE:
price = strategy.get_variable_measure(self)
else:
price = None
assert rcn.region
currency_code = rcn.region.get_currency_code()
if price is not None and have_moneyed and currency_code is not None:
import moneyed
money = moneyed.Money(amount=price, currency=currency_code)
else:
money = None
return replace(
rcn,
weight=weight,
count=count,
price=price,
money=money,
)
def without_variable_measure(self) -> Gtin:
"""Create a new RCN where the variable measure is zeroed out.
This provides us with a number which still includes the item
reference, but does not vary with weight/price, and can thus be used
to lookup the relevant trade item in a database or similar.
This has no effect on RCNs intended for use within a company, as
the semantics of those numbers vary from company to company.
Returns:
A RCN instance with zeros in the variable measure places.
Raises:
EncodeError: If the rules for variable measures in the region are unknown.
"""
if self.usage == RcnUsage.COMPANY:
# The value is an RCN, but it is intended for use within a company,
# so we can only interpret it as an opaque GTIN.
return self
if self.region is None:
msg = (
f"Cannot zero out the variable measure part of {self.value!r} as the "
f"RCN rules for the geographical region {self.region!r} are unknown."
)
raise EncodeError(msg)
strategy = _Strategy.get_for_rcn(self)
if strategy is None:
# This prefix has no rules for removing variable parts.
return self
return strategy.without_variable_measure(self)
|
check_digit
instance-attribute
Check digit used to check if the GTIN as a whole is valid.
company_prefix
instance-attribute
company_prefix: GS1CompanyPrefix | None
The GS1 Company Prefix.
Identifying the company that issued the GTIN.
count
class-attribute
instance-attribute
count: int | None = field(default=None)
A variable count extracted from the GTIN.
GTIN format, either GTIN-8, GTIN-12, GTIN-13, or
GTIN-14.
Classification is done after stripping leading zeros.
money
class-attribute
instance-attribute
money: Money | None = field(default=None)
A Money value created from the variable weight price.
Only set if py-moneyed
is
installed and the currency is known.
packaging_level
class-attribute
instance-attribute
packaging_level: int | None = None
Packaging level is the first digit in GTIN-14 codes.
This digit is used for wholesale shipments, e.g. the GTIN-14 product
identifier in GS1-128 barcodes, but not in the GTIN-13 barcodes used for
retail products.
payload
instance-attribute
The actual payload.
Including packaging level if any, company prefix, and item reference.
Excludes the check digit.
prefix
instance-attribute
The GS1 Prefix.
Indicating what GS1 country organization that assigned
code range.
price
class-attribute
instance-attribute
price: Decimal | None = field(default=None)
A variable weight price extracted from the GTIN.
region
class-attribute
instance-attribute
region: RcnRegion | None = field(default=None)
The geographical region.
The region's rules are used to interpret the contents of the RCN.
usage
class-attribute
instance-attribute
usage: RcnUsage | None = field(default=None)
Where the RCN can be circulated, in a geographical region or within a company.
value
instance-attribute
Raw unprocessed value.
May include leading zeros.
weight
class-attribute
instance-attribute
weight: Decimal | None = field(default=None)
A variable weight value extracted from the GTIN.
as_gtin_12
Format as a GTIN-12.
Source code in src/biip/gtin.py
| def as_gtin_12(self) -> str:
"""Format as a GTIN-12."""
return self._as_format(GtinFormat.GTIN_12)
|
as_gtin_13
Format as a GTIN-13.
Source code in src/biip/gtin.py
| def as_gtin_13(self) -> str:
"""Format as a GTIN-13."""
return self._as_format(GtinFormat.GTIN_13)
|
as_gtin_14
Format as a GTIN-14.
Source code in src/biip/gtin.py
| def as_gtin_14(self) -> str:
"""Format as a GTIN-14."""
return self._as_format(GtinFormat.GTIN_14)
|
as_gtin_8
Format as a GTIN-8.
Source code in src/biip/gtin.py
| def as_gtin_8(self) -> str:
"""Format as a GTIN-8."""
return self._as_format(GtinFormat.GTIN_8)
|
parse
classmethod
parse(
value: str, *, config: ParseConfig | None = None
) -> Gtin
Parse the given value into a Gtin
object.
Both GTIN-8, GTIN-12, GTIN-13, and GTIN-14 are supported.
The checksum is guaranteed to be valid if a GTIN object is returned.
Parameters:
-
value
(str
)
–
-
config
(ParseConfig | None
, default:
None
)
–
Configuration options for parsing.
Returns:
-
Gtin
–
GTIN data structure with the successfully extracted data.
Raises:
Source code in src/biip/gtin.py
| @classmethod
def parse(
cls,
value: str,
*,
config: ParseConfig | None = None,
) -> Gtin:
"""Parse the given value into a [`Gtin`][biip.gtin.Gtin] object.
Both GTIN-8, GTIN-12, GTIN-13, and GTIN-14 are supported.
The checksum is guaranteed to be valid if a GTIN object is returned.
Args:
value: The value to parse.
config: Configuration options for parsing.
Returns:
GTIN data structure with the successfully extracted data.
Raises:
ParseError: If the parsing fails.
"""
if config is None:
config = ParseConfig()
from biip.rcn import Rcn
value = value.strip()
if len(value) not in (8, 12, 13, 14):
msg = (
f"Failed to parse {value!r} as GTIN: "
f"Expected 8, 12, 13, or 14 digits, got {len(value)}."
)
raise ParseError(msg)
if not value.isdecimal():
msg = f"Failed to parse {value!r} as GTIN: Expected a numerical value."
raise ParseError(msg)
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: int | None = None
prefix_value = stripped_value
if gtin_format == GtinFormat.GTIN_14:
packaging_level = int(stripped_value[0])
prefix_value = stripped_value[1:]
elif gtin_format == GtinFormat.GTIN_12:
# Add a zero to convert U.P.C. Company Prefix to GS1 Company Prefix
prefix_value = stripped_value.zfill(13)
elif gtin_format == GtinFormat.GTIN_8:
prefix_value = stripped_value.zfill(12)
prefix = GS1Prefix.extract(prefix_value)
company_prefix = GS1CompanyPrefix.extract(prefix_value)
calculated_check_digit = gs1_standard_check_digit(payload)
if check_digit != calculated_check_digit:
msg = (
f"Invalid GTIN check digit for {value!r}: "
f"Expected {calculated_check_digit!r}, got {check_digit!r}."
)
raise ParseError(msg)
gtin_type: type[Gtin | Rcn]
if (
gtin_format <= GtinFormat.GTIN_13
and prefix is not None
and "Restricted Circulation Number" in prefix.usage
):
gtin_type = Rcn
else:
gtin_type = Gtin
result = gtin_type(
value=value,
format=gtin_format,
prefix=prefix,
company_prefix=company_prefix,
payload=payload,
check_digit=check_digit,
packaging_level=packaging_level,
)
if isinstance(result, Rcn):
result = result._with_usage() # noqa: SLF001
result = result._parsed_with_regional_rules( # noqa: SLF001
config=config
)
return result
|
without_variable_measure
without_variable_measure() -> Gtin
Create a new RCN where the variable measure is zeroed out.
This provides us with a number which still includes the item
reference, but does not vary with weight/price, and can thus be used
to lookup the relevant trade item in a database or similar.
This has no effect on RCNs intended for use within a company, as
the semantics of those numbers vary from company to company.
Returns:
-
Gtin
–
A RCN instance with zeros in the variable measure places.
Raises:
-
EncodeError
–
If the rules for variable measures in the region are unknown.
Source code in src/biip/rcn.py
| def without_variable_measure(self) -> Gtin:
"""Create a new RCN where the variable measure is zeroed out.
This provides us with a number which still includes the item
reference, but does not vary with weight/price, and can thus be used
to lookup the relevant trade item in a database or similar.
This has no effect on RCNs intended for use within a company, as
the semantics of those numbers vary from company to company.
Returns:
A RCN instance with zeros in the variable measure places.
Raises:
EncodeError: If the rules for variable measures in the region are unknown.
"""
if self.usage == RcnUsage.COMPANY:
# The value is an RCN, but it is intended for use within a company,
# so we can only interpret it as an opaque GTIN.
return self
if self.region is None:
msg = (
f"Cannot zero out the variable measure part of {self.value!r} as the "
f"RCN rules for the geographical region {self.region!r} are unknown."
)
raise EncodeError(msg)
strategy = _Strategy.get_for_rcn(self)
if strategy is None:
# This prefix has no rules for removing variable parts.
return self
return strategy.without_variable_measure(self)
|
RcnRegion
Bases: Enum
Enum of geographical regions with custom RCN rules.
The value of the enum is the lowercase ISO 3166-1 Alpha-2 code.
Source code in src/biip/rcn.py
| class RcnRegion(Enum):
"""Enum of geographical regions with custom RCN rules.
The value of the enum is the lowercase ISO 3166-1 Alpha-2 code.
"""
DENMARK = "dk"
"""Denmark"""
ESTONIA = "ee"
"""Estonia"""
FINLAND = "fi"
"""Finland"""
GERMANY = "de"
"""Germany"""
GREAT_BRITAIN = "gb"
"""Great Britain"""
LATVIA = "lv"
"""Latvia"""
LITHUANIA = "lt"
"""Lithuania"""
NORWAY = "no"
"""Norway"""
SWEDEN = "se"
"""Sweden"""
def __repr__(self) -> str:
"""Canonical string representation of format."""
return f"RcnRegion.{self.name}"
def get_currency_code(self) -> str | None:
"""Get the ISO-4217 currency code for the region."""
return {
RcnRegion.DENMARK: "DKK",
RcnRegion.GERMANY: "EUR",
RcnRegion.GREAT_BRITAIN: "GBP",
RcnRegion.NORWAY: "NOK",
RcnRegion.SWEDEN: "SEK",
}.get(self)
|
DENMARK
class-attribute
instance-attribute
ESTONIA
class-attribute
instance-attribute
FINLAND
class-attribute
instance-attribute
GERMANY
class-attribute
instance-attribute
GREAT_BRITAIN
class-attribute
instance-attribute
LATVIA
class-attribute
instance-attribute
LITHUANIA
class-attribute
instance-attribute
NORWAY
class-attribute
instance-attribute
SWEDEN
class-attribute
instance-attribute
get_currency_code
get_currency_code() -> str | None
Get the ISO-4217 currency code for the region.
Source code in src/biip/rcn.py
| def get_currency_code(self) -> str | None:
"""Get the ISO-4217 currency code for the region."""
return {
RcnRegion.DENMARK: "DKK",
RcnRegion.GERMANY: "EUR",
RcnRegion.GREAT_BRITAIN: "GBP",
RcnRegion.NORWAY: "NOK",
RcnRegion.SWEDEN: "SEK",
}.get(self)
|
RcnUsage
Bases: Enum
Enum of RCN usage restrictions.
Source code in src/biip/rcn.py
| class RcnUsage(Enum):
"""Enum of RCN usage restrictions."""
GEOGRAPHICAL = "geo"
"""Usage of RCN restricted to geopgraphical area."""
COMPANY = "company"
"""Usage of RCN restricted to internally in a company."""
def __repr__(self) -> str:
"""Canonical string representation of format."""
return f"RcnUsage.{self.name}"
|
COMPANY
class-attribute
instance-attribute
Usage of RCN restricted to internally in a company.
GEOGRAPHICAL
class-attribute
instance-attribute
Usage of RCN restricted to geopgraphical area.