Skip to content

Commit 06eb1a7

Browse files
committed
Fix no_checksum for EAN13
The `ean` variable was shadowed early, so a custom 13th digit was lost when ignoring checksums. Fixes: #43
1 parent 4c0c3a4 commit 06eb1a7

2 files changed

Lines changed: 59 additions & 20 deletions

File tree

barcode/ean.py

Lines changed: 27 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
33
:Provided barcodes: EAN-14, EAN-13, EAN-8, JAN
44
"""
5+
56
from __future__ import annotations
67

78
__docformat__ = "restructuredtext en"
@@ -31,7 +32,7 @@
3132
class EuropeanArticleNumber13(Barcode):
3233
"""Initializes EAN13 object.
3334
34-
:param ean: The ean number as string.
35+
:param ean: The ean number as string. If the value is too long, it is trimmed.
3536
:param writer: The writer to render the barcode (default: SVGWriter).
3637
:param no_checksum: Don't calculate the checksum. Use the provided input instead.
3738
"""
@@ -43,21 +44,25 @@ class EuropeanArticleNumber13(Barcode):
4344
def __init__(
4445
self, ean: str, writer=None, no_checksum=False, guardbar=False
4546
) -> None:
46-
ean = ean[: self.digits]
47-
if not ean.isdigit():
48-
raise IllegalCharacterError("EAN code can only contain numbers.")
49-
if len(ean) != self.digits:
47+
if not ean[: self.digits].isdigit():
48+
raise IllegalCharacterError(f"EAN code can only contain numbers {ean}.")
49+
50+
if len(ean) < self.digits:
5051
raise NumberOfDigitsError(
51-
f"EAN must have {self.digits} digits, not {len(ean)}."
52+
f"EAN must have {self.digits} digits, received {len(ean)}."
5253
)
53-
self.ean = ean
54-
# If no checksum
54+
55+
base = ean[: self.digits]
5556
if no_checksum:
56-
# Add a thirteen char if given in parameter,
57-
# otherwise pad with zero
58-
self.ean = f"{ean}{ean[self.digits] if len(ean) > self.digits else 0}"
57+
# Use the thirteenth digit if given in parameter, otherwise pad with zero
58+
if len(ean) > self.digits and ean[self.digits].isdigit():
59+
last = int(ean[self.digits])
60+
else:
61+
last = 0
5962
else:
60-
self.ean = f"{ean}{self.calculate_checksum()}"
63+
last = self.calculate_checksum(base)
64+
65+
self.ean = f"{base}{last}"
6166

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

79-
def calculate_checksum(self) -> int:
80-
"""Calculates the checksum for EAN13-Code.
84+
def calculate_checksum(self, value: str | None = None) -> int:
85+
"""Calculates and returns the checksum for EAN13-Code.
8186
82-
:returns: The checksum for ``self.ean``.
87+
Calculates the checksum for the supplied `value` (if any) or for this barcode's
88+
internal ``self.ean`` property.
8389
"""
8490

85-
ean_without_checksum = self.ean[: self.digits]
91+
ean_without_checksum = value or self.ean[: self.digits]
8692

8793
evensum = sum(int(x) for x in ean_without_checksum[-2::-2])
8894
oddsum = sum(int(x) for x in ean_without_checksum[-1::-2])
@@ -206,13 +212,14 @@ class EuropeanArticleNumber14(EuropeanArticleNumber13):
206212
name = "EAN-14"
207213
digits = 13
208214

209-
def calculate_checksum(self) -> int:
210-
"""Calculates the checksum for EAN13-Code.
215+
def calculate_checksum(self, value: str | None = None) -> int:
216+
"""Calculates and returns the checksum for EAN14-Code.
211217
212-
:returns: The checksum for ``self.ean``.
218+
Calculates the checksum for the supplied `value` (if any) or for this barcode's
219+
internal ``self.ean`` property.
213220
"""
214221

215-
ean_without_checksum = self.ean[: self.digits]
222+
ean_without_checksum = value or self.ean[: self.digits]
216223

217224
evensum = sum(int(x) for x in ean_without_checksum[::2])
218225
oddsum = sum(int(x) for x in ean_without_checksum[1::2])

tests/test_ean.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
from __future__ import annotations
22

3+
import sys
4+
5+
import pytest
6+
37
from barcode.ean import EAN13
48

59

@@ -19,3 +23,31 @@ def test_ean_checksum_supplied_and_generated() -> None:
1923
ean = EAN13("8421671433225") # input has 13 digits
2024
assert ean.calculate_checksum() == 5
2125
assert ean.ean == "8421671433225"
26+
27+
28+
def test_ean_checksum_supplied_and_matching() -> None:
29+
ean = EAN13("8421671433225", no_checksum=True) # input has 13 digits
30+
assert ean.calculate_checksum() == 5
31+
assert ean.ean == "8421671433225"
32+
33+
34+
def test_ean_checksum_supplied_and_different() -> None:
35+
ean = EAN13("8421671433229", no_checksum=True) # input has 13 digits
36+
assert ean.calculate_checksum() == 5
37+
assert ean.ean == "8421671433229"
38+
39+
40+
def test_ean_checksum_generated_placeholder() -> None:
41+
ean = EAN13("977114487500X") # input has 13 digits
42+
assert ean.calculate_checksum() == 7
43+
assert ean.ean == "9771144875007"
44+
45+
46+
@pytest.mark.skipif(sys.platform == "win32", reason="no /dev/null")
47+
def test_ean_checksum_supplied_placeholder() -> None:
48+
ean = EAN13("977114487500X", no_checksum=True) # input has 13 digits
49+
assert ean.calculate_checksum() == 7
50+
assert ean.ean == "9771144875000"
51+
52+
with open("/dev/null", "wb") as f:
53+
ean.write(f)

0 commit comments

Comments
 (0)