Skip to content
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

Fix no_checksum for EAN13 #224

Merged
merged 1 commit into from
Apr 8, 2024
Merged
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
47 changes: 27 additions & 20 deletions barcode/ean.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

:Provided barcodes: EAN-14, EAN-13, EAN-8, JAN
"""

from __future__ import annotations

__docformat__ = "restructuredtext en"
Expand Down Expand Up @@ -31,7 +32,7 @@
class EuropeanArticleNumber13(Barcode):
"""Initializes EAN13 object.

:param ean: The ean number as string.
:param ean: The ean number as string. If the value is too long, it is trimmed.
:param writer: The writer to render the barcode (default: SVGWriter).
:param no_checksum: Don't calculate the checksum. Use the provided input instead.
"""
Expand All @@ -43,21 +44,25 @@ class EuropeanArticleNumber13(Barcode):
def __init__(
self, ean: str, writer=None, no_checksum=False, guardbar=False
) -> None:
ean = ean[: self.digits]
if not ean.isdigit():
raise IllegalCharacterError("EAN code can only contain numbers.")
if len(ean) != self.digits:
if not ean[: self.digits].isdigit():
raise IllegalCharacterError(f"EAN code can only contain numbers {ean}.")

if len(ean) < self.digits:
raise NumberOfDigitsError(
f"EAN must have {self.digits} digits, not {len(ean)}."
f"EAN must have {self.digits} digits, received {len(ean)}."
)
self.ean = ean
# If no checksum

base = ean[: self.digits]
if no_checksum:
# Add a thirteen char if given in parameter,
# otherwise pad with zero
self.ean = f"{ean}{ean[self.digits] if len(ean) > self.digits else 0}"
# Use the thirteenth digit if given in parameter, otherwise pad with zero
if len(ean) > self.digits and ean[self.digits].isdigit():
last = int(ean[self.digits])
else:
last = 0
else:
self.ean = f"{ean}{self.calculate_checksum()}"
last = self.calculate_checksum(base)

self.ean = f"{base}{last}"

self.guardbar = guardbar
if guardbar:
Expand All @@ -76,13 +81,14 @@ def get_fullcode(self) -> str:
return self.ean[0] + " " + self.ean[1:7] + " " + self.ean[7:] + " >"
return self.ean

def calculate_checksum(self) -> int:
"""Calculates the checksum for EAN13-Code.
def calculate_checksum(self, value: str | None = None) -> int:
"""Calculates and returns the checksum for EAN13-Code.

:returns: The checksum for ``self.ean``.
Calculates the checksum for the supplied `value` (if any) or for this barcode's
internal ``self.ean`` property.
"""

ean_without_checksum = self.ean[: self.digits]
ean_without_checksum = value or self.ean[: self.digits]

evensum = sum(int(x) for x in ean_without_checksum[-2::-2])
oddsum = sum(int(x) for x in ean_without_checksum[-1::-2])
Expand Down Expand Up @@ -206,13 +212,14 @@ class EuropeanArticleNumber14(EuropeanArticleNumber13):
name = "EAN-14"
digits = 13

def calculate_checksum(self) -> int:
"""Calculates the checksum for EAN13-Code.
def calculate_checksum(self, value: str | None = None) -> int:
"""Calculates and returns the checksum for EAN14-Code.

:returns: The checksum for ``self.ean``.
Calculates the checksum for the supplied `value` (if any) or for this barcode's
internal ``self.ean`` property.
"""

ean_without_checksum = self.ean[: self.digits]
ean_without_checksum = value or self.ean[: self.digits]

evensum = sum(int(x) for x in ean_without_checksum[::2])
oddsum = sum(int(x) for x in ean_without_checksum[1::2])
Expand Down
32 changes: 32 additions & 0 deletions tests/test_ean.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
from __future__ import annotations

import sys

import pytest

from barcode.ean import EAN13


Expand All @@ -19,3 +23,31 @@ def test_ean_checksum_supplied_and_generated() -> None:
ean = EAN13("8421671433225") # input has 13 digits
assert ean.calculate_checksum() == 5
assert ean.ean == "8421671433225"


def test_ean_checksum_supplied_and_matching() -> None:
ean = EAN13("8421671433225", no_checksum=True) # input has 13 digits
assert ean.calculate_checksum() == 5
assert ean.ean == "8421671433225"


def test_ean_checksum_supplied_and_different() -> None:
ean = EAN13("8421671433229", no_checksum=True) # input has 13 digits
assert ean.calculate_checksum() == 5
assert ean.ean == "8421671433229"


def test_ean_checksum_generated_placeholder() -> None:
ean = EAN13("977114487500X") # input has 13 digits
assert ean.calculate_checksum() == 7
assert ean.ean == "9771144875007"


@pytest.mark.skipif(sys.platform == "win32", reason="no /dev/null")
def test_ean_checksum_supplied_placeholder() -> None:
ean = EAN13("977114487500X", no_checksum=True) # input has 13 digits
assert ean.calculate_checksum() == 7
assert ean.ean == "9771144875000"

with open("/dev/null", "wb") as f:
ean.write(f)
Loading