From 0cc7fbd7e000e3874fc3ed2583c550eb2f002a02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Kutn=C3=BD?= Date: Wed, 17 Jul 2024 20:27:49 +0200 Subject: [PATCH 1/4] Added failing test of ITF14 checksum --- tests/ChecksumBarcodeTest.php | 37 +++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 tests/ChecksumBarcodeTest.php diff --git a/tests/ChecksumBarcodeTest.php b/tests/ChecksumBarcodeTest.php new file mode 100644 index 0000000..2bbf0db --- /dev/null +++ b/tests/ChecksumBarcodeTest.php @@ -0,0 +1,37 @@ + TypeEan13::class, 'barcodes' => [ + '081231723897' => '0812317238973', + '004900000463' => '0049000004632', + ]], + ['type' => TypeITF14::class, 'barcodes' => [ + '0001234560001' => '00012345600012', + '0540014128876' => '05400141288766', + ]], + ]; + + public function testAllSupportedBarcodeTypes() + { + foreach ($this::$supportedBarcodes as $barcodeTestSet) { + $barcodeType = $this->getBarcodeType($barcodeTestSet['type']); + + foreach ($barcodeTestSet['barcodes'] as $testBarcode => $expectedBarcode) { + $this->assertEquals($expectedBarcode, $barcodeType->getBarcodeData($testBarcode)->getBarcode()); + } + } + } + + + private function getBarcodeType(string $typeClass): TypeInterface + { + return new $typeClass; + } +} From acaf80faf87a183ce285d276ea443ad5ecee6aaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Kutn=C3=BD?= Date: Wed, 17 Jul 2024 20:31:21 +0200 Subject: [PATCH 2/4] Fixed checksum calculation for ITF14 barcode type source: How to calculate a check digit manually https://www.gs1.org/services/how-calculate-check-digit-manually --- src/Types/TypeITF14.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Types/TypeITF14.php b/src/Types/TypeITF14.php index ff395d9..12d6054 100644 --- a/src/Types/TypeITF14.php +++ b/src/Types/TypeITF14.php @@ -73,8 +73,8 @@ private function getChecksum(string $code): string $total = 0; for ($charIndex = 0; $charIndex <= (strlen($code) - 1); $charIndex++) { - $integerOfChar = intval($code . substr($charIndex, 1)); - $total += $integerOfChar * (($charIndex === 0 || $charIndex % 2 === 0) ? 3 : 1); + $integerOfChar = intval($code[$charIndex]); + $total += $integerOfChar * ($charIndex % 2 === 0 ? 3 : 1); } $checksum = 10 - ($total % 10); From 5e76e1c79a44c4391dc9c7f0c25747498c5e4958 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Kutn=C3=BD?= Date: Wed, 17 Jul 2024 22:41:16 +0200 Subject: [PATCH 3/4] Add failint test for invalid ITF14 checksum --- tests/ChecksumBarcodeTest.php | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/tests/ChecksumBarcodeTest.php b/tests/ChecksumBarcodeTest.php index 2bbf0db..eff35ab 100644 --- a/tests/ChecksumBarcodeTest.php +++ b/tests/ChecksumBarcodeTest.php @@ -1,13 +1,15 @@ TypeEan13::class, 'barcodes' => [ '081231723897' => '0812317238973', '004900000463' => '0049000004632', @@ -17,14 +19,36 @@ class ChecksumBarcodeTest extends TestCase '0540014128876' => '05400141288766', ]], ]; + private const INVALID_BARCODES = [ + ['type' => TypeEan13::class, 'barcodes' => ['0812317238972', '0049000004633']], + ['type' => TypeITF14::class, 'barcodes' => ['00012345600016', '05400141288762']], + ]; - public function testAllSupportedBarcodeTypes() + public function testAllValidBarcodeTypes() { - foreach ($this::$supportedBarcodes as $barcodeTestSet) { + foreach (self::VALID_BARCODES as $barcodeTestSet) { $barcodeType = $this->getBarcodeType($barcodeTestSet['type']); - foreach ($barcodeTestSet['barcodes'] as $testBarcode => $expectedBarcode) { - $this->assertEquals($expectedBarcode, $barcodeType->getBarcodeData($testBarcode)->getBarcode()); + foreach ($barcodeTestSet['barcodes'] as $testBarcode => $validBarcode) { + $this->assertEquals($validBarcode, $barcodeType->getBarcodeData($testBarcode)->getBarcode()); + } + } + } + + public function testAllInvalidBarcodeTypes() + { + foreach (self::INVALID_BARCODES as $barcodeTestSet) { + $barcodeType = $this->getBarcodeType($barcodeTestSet['type']); + + foreach ($barcodeTestSet['barcodes'] as $invalidBarcode) { + try { + $barcodeType->getBarcodeData($invalidBarcode)->getBarcode(); + } catch (BarcodeException $exception) { + $this->assertInstanceOf(InvalidCheckDigitException::class, $exception); + continue; + } + + $this->assertTrue(false, sprintf('Exception was not thrown for barcode "%s".', $invalidBarcode)); } } } From a1397928ccd111d01105b3517abcbb6611bc90fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Kutn=C3=BD?= Date: Wed, 17 Jul 2024 22:58:36 +0200 Subject: [PATCH 4/4] ITF14 throws InvalidCheckDigitException when checksum is invalid --- src/Types/TypeITF14.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Types/TypeITF14.php b/src/Types/TypeITF14.php index 12d6054..d2f87bb 100644 --- a/src/Types/TypeITF14.php +++ b/src/Types/TypeITF14.php @@ -5,6 +5,7 @@ use Picqer\Barcode\Barcode; use Picqer\Barcode\BarcodeBar; use Picqer\Barcode\Exceptions\InvalidCharacterException; +use Picqer\Barcode\Exceptions\InvalidCheckDigitException; use Picqer\Barcode\Exceptions\InvalidLengthException; class TypeITF14 implements TypeInterface @@ -12,6 +13,7 @@ class TypeITF14 implements TypeInterface /** * @throws InvalidLengthException * @throws InvalidCharacterException + * @throws InvalidCheckDigitException */ public function getBarcodeData(string $code): Barcode { @@ -35,6 +37,10 @@ public function getBarcodeData(string $code): Barcode if (strlen($code) === 13) { $code .= $this->getChecksum($code); + } elseif (substr($code, -1) !== $this->getChecksum(substr($code, 0, -1))) { + // If length of given barcode is same as final length, barcode is including checksum + // Make sure that checksum is the same as we calculated + throw new InvalidCheckDigitException(); } $barcode = new Barcode($code);