mirror of
https://github.com/Part-DB/Part-DB-server.git
synced 2025-06-20 17:15:51 +02:00
Added capability to scan Digikey barcodes and open the local part part page based on the result (#811)
* added capability to scan digikey barcodes and open the local part page based on the digikey part number or manufacturer part number * had replaced one too many doublequotes * Generalized interpretation of format06 barcodes, added ids for mouser * Renamed vendor_barcode to user_barcode in entities * Added a own class to parse EIGP114 barcodes * Added tests to EIGP114Barcode parser * Refactored code * Changed BarcodeRedirector to support the new Barcode EIGP114BarcodeScanResult class * Added possibility to just show all information contained in a barcode * Dont require trailer for EIGP114 barcodes, as digikey does not seem to put them onto their barcodes * Fixed inspection issues --------- Co-authored-by: jona <a@b.c> Co-authored-by: Jan Böhmer <mail@jan-boehmer.de>
This commit is contained in:
parent
9c99217dee
commit
9e85b70c17
20 changed files with 868 additions and 177 deletions
|
@ -148,7 +148,7 @@ class PartMergerTest extends KernelTestCase
|
|||
public function testMergeOfPartLots(): void
|
||||
{
|
||||
$lot1 = (new PartLot())->setAmount(2)->setNeedsRefill(true);
|
||||
$lot2 = (new PartLot())->setInstockUnknown(true)->setVendorBarcode('test');
|
||||
$lot2 = (new PartLot())->setInstockUnknown(true)->setUserBarcode('test');
|
||||
$lot3 = (new PartLot())->setDescription('lot3')->setAmount(3);
|
||||
$lot4 = (new PartLot())->setDescription('lot4')->setComment('comment');
|
||||
|
||||
|
|
|
@ -39,12 +39,12 @@ declare(strict_types=1);
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace App\Tests\Services\LabelSystem\Barcodes;
|
||||
namespace App\Tests\Services\LabelSystem\BarcodeScanner;
|
||||
|
||||
use App\Entity\LabelSystem\LabelSupportedElement;
|
||||
use App\Services\LabelSystem\Barcodes\BarcodeRedirector;
|
||||
use App\Services\LabelSystem\Barcodes\BarcodeScanResult;
|
||||
use App\Services\LabelSystem\Barcodes\BarcodeSourceType;
|
||||
use App\Services\LabelSystem\BarcodeScanner\BarcodeRedirector;
|
||||
use App\Services\LabelSystem\BarcodeScanner\BarcodeSourceType;
|
||||
use App\Services\LabelSystem\BarcodeScanner\LocalBarcodeScanResult;
|
||||
use Doctrine\ORM\EntityNotFoundException;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||
|
||||
|
@ -60,17 +60,17 @@ final class BarcodeRedirectorTest extends KernelTestCase
|
|||
|
||||
public static function urlDataProvider(): \Iterator
|
||||
{
|
||||
yield [new BarcodeScanResult(LabelSupportedElement::PART, 1, BarcodeSourceType::INTERNAL), '/en/part/1'];
|
||||
yield [new LocalBarcodeScanResult(LabelSupportedElement::PART, 1, BarcodeSourceType::INTERNAL), '/en/part/1'];
|
||||
//Part lot redirects to Part info page (Part lot 1 is associated with part 3)
|
||||
yield [new BarcodeScanResult(LabelSupportedElement::PART_LOT, 1, BarcodeSourceType::INTERNAL), '/en/part/3'];
|
||||
yield [new BarcodeScanResult(LabelSupportedElement::STORELOCATION, 1, BarcodeSourceType::INTERNAL), '/en/store_location/1/parts'];
|
||||
yield [new LocalBarcodeScanResult(LabelSupportedElement::PART_LOT, 1, BarcodeSourceType::INTERNAL), '/en/part/3'];
|
||||
yield [new LocalBarcodeScanResult(LabelSupportedElement::STORELOCATION, 1, BarcodeSourceType::INTERNAL), '/en/store_location/1/parts'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider urlDataProvider
|
||||
* @group DB
|
||||
*/
|
||||
public function testGetRedirectURL(BarcodeScanResult $scanResult, string $url): void
|
||||
public function testGetRedirectURL(LocalBarcodeScanResult $scanResult, string $url): void
|
||||
{
|
||||
$this->assertSame($url, $this->service->getRedirectURL($scanResult));
|
||||
}
|
||||
|
@ -79,7 +79,7 @@ final class BarcodeRedirectorTest extends KernelTestCase
|
|||
{
|
||||
$this->expectException(EntityNotFoundException::class);
|
||||
//If we encounter an invalid lot, we must throw an exception
|
||||
$this->service->getRedirectURL(new BarcodeScanResult(LabelSupportedElement::PART_LOT,
|
||||
$this->service->getRedirectURL(new LocalBarcodeScanResult(LabelSupportedElement::PART_LOT,
|
||||
12_345_678, BarcodeSourceType::INTERNAL));
|
||||
}
|
||||
}
|
|
@ -39,13 +39,12 @@ declare(strict_types=1);
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace App\Tests\Services\LabelSystem\Barcodes;
|
||||
namespace App\Tests\Services\LabelSystem\BarcodeScanner;
|
||||
|
||||
use App\Entity\LabelSystem\LabelSupportedElement;
|
||||
use App\Services\LabelSystem\Barcodes\BarcodeScanHelper;
|
||||
use App\Services\LabelSystem\Barcodes\BarcodeScanResult;
|
||||
use App\Services\LabelSystem\Barcodes\BarcodeSourceType;
|
||||
use Com\Tecnick\Barcode\Barcode;
|
||||
use App\Services\LabelSystem\BarcodeScanner\BarcodeScanHelper;
|
||||
use App\Services\LabelSystem\BarcodeScanner\BarcodeSourceType;
|
||||
use App\Services\LabelSystem\BarcodeScanner\LocalBarcodeScanResult;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
||||
|
||||
class BarcodeScanHelperTest extends WebTestCase
|
||||
|
@ -61,55 +60,55 @@ class BarcodeScanHelperTest extends WebTestCase
|
|||
public static function dataProvider(): \Iterator
|
||||
{
|
||||
//QR URL content:
|
||||
yield [new BarcodeScanResult(LabelSupportedElement::PART_LOT, 1, BarcodeSourceType::INTERNAL),
|
||||
yield [new LocalBarcodeScanResult(LabelSupportedElement::PART_LOT, 1, BarcodeSourceType::INTERNAL),
|
||||
'https://localhost:8000/scan/lot/1'];
|
||||
yield [new BarcodeScanResult(LabelSupportedElement::PART, 123, BarcodeSourceType::INTERNAL),
|
||||
yield [new LocalBarcodeScanResult(LabelSupportedElement::PART, 123, BarcodeSourceType::INTERNAL),
|
||||
'https://localhost:8000/scan/part/123'];
|
||||
yield [new BarcodeScanResult(LabelSupportedElement::STORELOCATION, 4, BarcodeSourceType::INTERNAL),
|
||||
yield [new LocalBarcodeScanResult(LabelSupportedElement::STORELOCATION, 4, BarcodeSourceType::INTERNAL),
|
||||
'http://foo.bar/part-db/scan/location/4'];
|
||||
|
||||
//Current Code39 format:
|
||||
yield [new BarcodeScanResult(LabelSupportedElement::PART_LOT, 10, BarcodeSourceType::INTERNAL),
|
||||
yield [new LocalBarcodeScanResult(LabelSupportedElement::PART_LOT, 10, BarcodeSourceType::INTERNAL),
|
||||
'L0010'];
|
||||
yield [new BarcodeScanResult(LabelSupportedElement::PART_LOT, 123, BarcodeSourceType::INTERNAL),
|
||||
yield [new LocalBarcodeScanResult(LabelSupportedElement::PART_LOT, 123, BarcodeSourceType::INTERNAL),
|
||||
'L0123'];
|
||||
yield [new BarcodeScanResult(LabelSupportedElement::PART_LOT, 123456, BarcodeSourceType::INTERNAL),
|
||||
yield [new LocalBarcodeScanResult(LabelSupportedElement::PART_LOT, 123456, BarcodeSourceType::INTERNAL),
|
||||
'L123456'];
|
||||
yield [new BarcodeScanResult(LabelSupportedElement::PART, 2, BarcodeSourceType::INTERNAL),
|
||||
yield [new LocalBarcodeScanResult(LabelSupportedElement::PART, 2, BarcodeSourceType::INTERNAL),
|
||||
'P0002'];
|
||||
|
||||
//Development phase Code39 barcodes:
|
||||
yield [new BarcodeScanResult(LabelSupportedElement::PART_LOT, 10, BarcodeSourceType::INTERNAL),
|
||||
yield [new LocalBarcodeScanResult(LabelSupportedElement::PART_LOT, 10, BarcodeSourceType::INTERNAL),
|
||||
'L-000010'];
|
||||
yield [new BarcodeScanResult(LabelSupportedElement::PART_LOT, 10, BarcodeSourceType::INTERNAL),
|
||||
yield [new LocalBarcodeScanResult(LabelSupportedElement::PART_LOT, 10, BarcodeSourceType::INTERNAL),
|
||||
'Lß000010'];
|
||||
yield [new BarcodeScanResult(LabelSupportedElement::PART, 123, BarcodeSourceType::INTERNAL),
|
||||
yield [new LocalBarcodeScanResult(LabelSupportedElement::PART, 123, BarcodeSourceType::INTERNAL),
|
||||
'P-000123'];
|
||||
yield [new BarcodeScanResult(LabelSupportedElement::STORELOCATION, 123, BarcodeSourceType::INTERNAL),
|
||||
yield [new LocalBarcodeScanResult(LabelSupportedElement::STORELOCATION, 123, BarcodeSourceType::INTERNAL),
|
||||
'S-000123'];
|
||||
yield [new BarcodeScanResult(LabelSupportedElement::PART_LOT, 12_345_678, BarcodeSourceType::INTERNAL),
|
||||
yield [new LocalBarcodeScanResult(LabelSupportedElement::PART_LOT, 12_345_678, BarcodeSourceType::INTERNAL),
|
||||
'L-12345678'];
|
||||
|
||||
//Legacy storelocation format
|
||||
yield [new BarcodeScanResult(LabelSupportedElement::STORELOCATION, 336, BarcodeSourceType::INTERNAL),
|
||||
yield [new LocalBarcodeScanResult(LabelSupportedElement::STORELOCATION, 336, BarcodeSourceType::INTERNAL),
|
||||
'$L00336'];
|
||||
yield [new BarcodeScanResult(LabelSupportedElement::STORELOCATION, 12_345_678, BarcodeSourceType::INTERNAL),
|
||||
yield [new LocalBarcodeScanResult(LabelSupportedElement::STORELOCATION, 12_345_678, BarcodeSourceType::INTERNAL),
|
||||
'$L12345678'];
|
||||
|
||||
//Legacy Part format
|
||||
yield [new BarcodeScanResult(LabelSupportedElement::PART, 123, BarcodeSourceType::INTERNAL),
|
||||
yield [new LocalBarcodeScanResult(LabelSupportedElement::PART, 123, BarcodeSourceType::INTERNAL),
|
||||
'0000123'];
|
||||
yield [new BarcodeScanResult(LabelSupportedElement::PART, 123, BarcodeSourceType::INTERNAL),
|
||||
yield [new LocalBarcodeScanResult(LabelSupportedElement::PART, 123, BarcodeSourceType::INTERNAL),
|
||||
'00001236'];
|
||||
yield [new BarcodeScanResult(LabelSupportedElement::PART, 1_234_567, BarcodeSourceType::INTERNAL),
|
||||
yield [new LocalBarcodeScanResult(LabelSupportedElement::PART, 1_234_567, BarcodeSourceType::INTERNAL),
|
||||
'12345678'];
|
||||
|
||||
//Test IPN barcode
|
||||
yield [new BarcodeScanResult(LabelSupportedElement::PART, 2, BarcodeSourceType::IPN),
|
||||
yield [new LocalBarcodeScanResult(LabelSupportedElement::PART, 2, BarcodeSourceType::IPN),
|
||||
'IPN123'];
|
||||
|
||||
//Test vendor barcode
|
||||
yield [new BarcodeScanResult(LabelSupportedElement::PART_LOT, 2,BarcodeSourceType::VENDOR),
|
||||
yield [new LocalBarcodeScanResult(LabelSupportedElement::PART_LOT, 2,BarcodeSourceType::USER_DEFINED),
|
||||
'lot2_vendor_barcode'];
|
||||
}
|
||||
|
||||
|
@ -131,7 +130,7 @@ class BarcodeScanHelperTest extends WebTestCase
|
|||
/**
|
||||
* @dataProvider dataProvider
|
||||
*/
|
||||
public function testNormalizeBarcodeContent(BarcodeScanResult $expected, string $input): void
|
||||
public function testNormalizeBarcodeContent(LocalBarcodeScanResult $expected, string $input): void
|
||||
{
|
||||
$this->assertEquals($expected, $this->service->scanBarcodeContent($input));
|
||||
}
|
|
@ -0,0 +1,154 @@
|
|||
<?php
|
||||
/*
|
||||
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
|
||||
*
|
||||
* Copyright (C) 2019 - 2025 Jan Böhmer (https://github.com/jbtronics)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published
|
||||
* by the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace App\Tests\Services\LabelSystem\BarcodeScanner;
|
||||
|
||||
use App\Services\LabelSystem\BarcodeScanner\EIGP114BarcodeScanResult;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class EIGP114BarcodeScanResultTest extends TestCase
|
||||
{
|
||||
|
||||
public function testGuessBarcodeVendor(): void
|
||||
{
|
||||
//Generic barcode:
|
||||
|
||||
$barcode = new EIGP114BarcodeScanResult([
|
||||
'P' => '596-777A1-ND',
|
||||
'1P' => 'XAF4444',
|
||||
'Q' => '3',
|
||||
'10D' => '1452',
|
||||
'1T' => 'BF1103',
|
||||
'4L' => 'US',
|
||||
]);
|
||||
|
||||
$this->assertNull($barcode->guessBarcodeVendor());
|
||||
|
||||
//Digikey barcode:
|
||||
$barcode = new EIGP114BarcodeScanResult([
|
||||
'P' => '596-777A1-ND',
|
||||
'1P' => 'XAF4444',
|
||||
'Q' => '3',
|
||||
'10D' => '1452',
|
||||
'1T' => 'BF1103',
|
||||
'4L' => 'US',
|
||||
'13Z' => 'Digi-Key',
|
||||
]);
|
||||
$this->assertEquals('digikey', $barcode->guessBarcodeVendor());
|
||||
|
||||
//Mouser barcode:
|
||||
$barcode = new EIGP114BarcodeScanResult([
|
||||
'P' => '596-777A1-ND',
|
||||
'1P' => 'XAF4444',
|
||||
'Q' => '3',
|
||||
'10D' => '1452',
|
||||
'1T' => 'BF1103',
|
||||
'4L' => 'US',
|
||||
'1V' => 'Mouser',
|
||||
]);
|
||||
|
||||
$this->assertEquals('mouser', $barcode->guessBarcodeVendor());
|
||||
|
||||
//Farnell barcode:
|
||||
$barcode = new EIGP114BarcodeScanResult([
|
||||
'P' => '596-777A1-ND',
|
||||
'1P' => 'XAF4444',
|
||||
'Q' => '3',
|
||||
'10D' => '1452',
|
||||
'1T' => 'BF1103',
|
||||
'4L' => 'US',
|
||||
'3P' => 'Farnell',
|
||||
]);
|
||||
|
||||
$this->assertEquals('element14', $barcode->guessBarcodeVendor());
|
||||
}
|
||||
|
||||
public function testIsFormat06Code(): void
|
||||
{
|
||||
$this->assertFalse(EIGP114BarcodeScanResult::isFormat06Code(''));
|
||||
$this->assertFalse(EIGP114BarcodeScanResult::isFormat06Code('test'));
|
||||
$this->assertFalse(EIGP114BarcodeScanResult::isFormat06Code('12232435ew4rf'));
|
||||
|
||||
//Valid code (with trailer)
|
||||
$this->assertTrue(EIGP114BarcodeScanResult::isFormat06Code("[)>\x1E06\x1DP596-777A1-ND\x1D1PXAF4444\x1DQ3\x1D10D1452\x1D1TBF1103\x1D4LUS\x1E\x04"));
|
||||
|
||||
//Valid code (digikey, without trailer)
|
||||
$this->assertTrue(EIGP114BarcodeScanResult::isFormat06Code("[)>\x1e06\x1dPQ1045-ND\x1d1P364019-01\x1d30PQ1045-ND\x1dK12432 TRAVIS FOSS P\x1d1K85732873\x1d10K103332956\x1d9D231013\x1d1TQJ13P\x1d11K1\x1d4LTW\x1dQ3\x1d11ZPICK\x1d12Z7360988\x1d13Z999999\x1d20Z0000000000000000000000000000000000000000000000000000000000000000000000000000000000000"));
|
||||
}
|
||||
|
||||
public function testParseFormat06CodeInvalid(): void
|
||||
{
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
EIGP114BarcodeScanResult::parseFormat06Code('');
|
||||
}
|
||||
|
||||
public function testParseFormat06Code(): void
|
||||
{
|
||||
$barcode = EIGP114BarcodeScanResult::parseFormat06Code("[)>\x1E06\x1DP596-777A1-ND\x1D1PXAF4444\x1DQ3\x1D10D1452\x1D1TBF1103\x1D4LUS\x1E\x04");
|
||||
$this->assertEquals([
|
||||
'P' => '596-777A1-ND',
|
||||
'1P' => 'XAF4444',
|
||||
'Q' => '3',
|
||||
'10D' => '1452',
|
||||
'1T' => 'BF1103',
|
||||
'4L' => 'US',
|
||||
], $barcode->data);
|
||||
}
|
||||
|
||||
public function testDataParsing(): void
|
||||
{
|
||||
$barcode = new EIGP114BarcodeScanResult([
|
||||
'P' => '596-777A1-ND',
|
||||
'1P' => 'XAF4444',
|
||||
'Q' => '3',
|
||||
'10D' => '1452',
|
||||
'1T' => 'BF1103',
|
||||
'4L' => 'US',
|
||||
]);
|
||||
|
||||
$this->assertEquals('596-777A1-ND', $barcode->customerPartNumber);
|
||||
$this->assertEquals('XAF4444', $barcode->supplierPartNumber);
|
||||
$this->assertEquals(3, $barcode->quantity);
|
||||
$this->assertEquals('1452', $barcode->alternativeDateCode);
|
||||
$this->assertEquals('BF1103', $barcode->lotCode);
|
||||
$this->assertEquals('US', $barcode->countryOfOrigin);
|
||||
}
|
||||
|
||||
public function testDigikeyParsing(): void
|
||||
{
|
||||
$barcode = EIGP114BarcodeScanResult::parseFormat06Code("[)>\x1e06\x1dPQ1045-ND\x1d1P364019-01\x1d30PQ1045-ND\x1dK12432 TRAVIS FOSS P\x1d1K85732873\x1d10K103332956\x1d9D231013\x1d1TQJ13P\x1d11K1\x1d4LTW\x1dQ3\x1d11ZPICK\x1d12Z7360988\x1d13Z999999\x1d20Z0000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
|
||||
|
||||
$this->assertEquals('digikey', $barcode->guessBarcodeVendor());
|
||||
|
||||
$this->assertEquals('Q1045-ND', $barcode->customerPartNumber);
|
||||
$this->assertEquals('364019-01', $barcode->supplierPartNumber);
|
||||
$this->assertEquals(3, $barcode->quantity);
|
||||
$this->assertEquals('231013', $barcode->dateCode);
|
||||
$this->assertEquals('QJ13P', $barcode->lotCode);
|
||||
$this->assertEquals('TW', $barcode->countryOfOrigin);
|
||||
$this->assertEquals('Q1045-ND', $barcode->digikeyPartNumber);
|
||||
$this->assertEquals('85732873', $barcode->digikeySalesOrderNumber);
|
||||
$this->assertEquals('103332956', $barcode->digikeyInvoiceNumber);
|
||||
$this->assertEquals('PICK', $barcode->digikeyLabelType);
|
||||
$this->assertEquals('7360988', $barcode->digikeyPartID);
|
||||
$this->assertEquals('999999', $barcode->digikeyNA);
|
||||
$this->assertEquals('0000000000000000000000000000000000000000000000000000000000000000000000000000000000000', $barcode->digikeyPadding);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue