mirror of
https://github.com/Part-DB/Part-DB-server.git
synced 2026-01-20 17:19:34 +00:00
added redirection to part page on successful scan of lcsc, digikey, and mouser barcodes. added create part button if part does not exist in database
This commit is contained in:
parent
4325f9e0e6
commit
f3cd32cc91
2 changed files with 110 additions and 6 deletions
|
|
@ -46,6 +46,8 @@ use App\Services\LabelSystem\BarcodeScanner\BarcodeRedirector;
|
||||||
use App\Services\LabelSystem\BarcodeScanner\BarcodeScanHelper;
|
use App\Services\LabelSystem\BarcodeScanner\BarcodeScanHelper;
|
||||||
use App\Services\LabelSystem\BarcodeScanner\BarcodeSourceType;
|
use App\Services\LabelSystem\BarcodeScanner\BarcodeSourceType;
|
||||||
use App\Services\LabelSystem\BarcodeScanner\LocalBarcodeScanResult;
|
use App\Services\LabelSystem\BarcodeScanner\LocalBarcodeScanResult;
|
||||||
|
use App\Services\LabelSystem\BarcodeScanner\LCSCBarcodeScanResult;
|
||||||
|
use App\Services\LabelSystem\BarcodeScanner\EIGP114BarcodeScanResult;
|
||||||
use Doctrine\ORM\EntityNotFoundException;
|
use Doctrine\ORM\EntityNotFoundException;
|
||||||
use InvalidArgumentException;
|
use InvalidArgumentException;
|
||||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
|
|
@ -53,6 +55,8 @@ use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use Symfony\Component\HttpKernel\Attribute\MapQueryParameter;
|
use Symfony\Component\HttpKernel\Attribute\MapQueryParameter;
|
||||||
use Symfony\Component\Routing\Attribute\Route;
|
use Symfony\Component\Routing\Attribute\Route;
|
||||||
|
use App\Services\InfoProviderSystem\PartInfoRetriever;
|
||||||
|
use App\Services\InfoProviderSystem\ProviderRegistry;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see \App\Tests\Controller\ScanControllerTest
|
* @see \App\Tests\Controller\ScanControllerTest
|
||||||
|
|
@ -60,9 +64,12 @@ use Symfony\Component\Routing\Attribute\Route;
|
||||||
#[Route(path: '/scan')]
|
#[Route(path: '/scan')]
|
||||||
class ScanController extends AbstractController
|
class ScanController extends AbstractController
|
||||||
{
|
{
|
||||||
public function __construct(protected BarcodeRedirector $barcodeParser, protected BarcodeScanHelper $barcodeNormalizer)
|
public function __construct(
|
||||||
{
|
protected BarcodeRedirector $barcodeParser,
|
||||||
}
|
protected BarcodeScanHelper $barcodeNormalizer,
|
||||||
|
private readonly ProviderRegistry $providerRegistry,
|
||||||
|
private readonly PartInfoRetriever $infoRetriever,
|
||||||
|
) {}
|
||||||
|
|
||||||
#[Route(path: '', name: 'scan_dialog')]
|
#[Route(path: '', name: 'scan_dialog')]
|
||||||
public function dialog(Request $request, #[MapQueryParameter] ?string $input = null): Response
|
public function dialog(Request $request, #[MapQueryParameter] ?string $input = null): Response
|
||||||
|
|
@ -72,26 +79,113 @@ class ScanController extends AbstractController
|
||||||
$form = $this->createForm(ScanDialogType::class);
|
$form = $this->createForm(ScanDialogType::class);
|
||||||
$form->handleRequest($request);
|
$form->handleRequest($request);
|
||||||
|
|
||||||
|
$mode = null;
|
||||||
if ($input === null && $form->isSubmitted() && $form->isValid()) {
|
if ($input === null && $form->isSubmitted() && $form->isValid()) {
|
||||||
$input = $form['input']->getData();
|
$input = $form['input']->getData();
|
||||||
$mode = $form['mode']->getData();
|
$mode = $form['mode']->getData();
|
||||||
}
|
}
|
||||||
|
|
||||||
$infoModeData = null;
|
$infoModeData = null;
|
||||||
|
$createUrl = null;
|
||||||
|
|
||||||
if ($input !== null) {
|
if ($input !== null) {
|
||||||
try {
|
try {
|
||||||
$scan_result = $this->barcodeNormalizer->scanBarcodeContent($input, $mode ?? null);
|
$scan_result = $this->barcodeNormalizer->scanBarcodeContent($input, $mode ?? null);
|
||||||
|
|
||||||
//Perform a redirect if the info mode is not enabled
|
//Perform a redirect if the info mode is not enabled
|
||||||
if (!$form['info_mode']->getData()) {
|
if (!$form['info_mode']->getData()) {
|
||||||
try {
|
try {
|
||||||
|
// redirect user to part page
|
||||||
return $this->redirect($this->barcodeParser->getRedirectURL($scan_result));
|
return $this->redirect($this->barcodeParser->getRedirectURL($scan_result));
|
||||||
} catch (EntityNotFoundException) {
|
} catch (EntityNotFoundException) {
|
||||||
$this->addFlash('success', 'scan.qr_not_found');
|
// Fallback: show decoded info like info-mode as part does not exist
|
||||||
|
$infoModeData = $scan_result->getDecodedForInfoMode();
|
||||||
|
|
||||||
|
$locale = $request->getLocale();
|
||||||
|
|
||||||
|
// If it's an LCSC scan, offer "create part" link
|
||||||
|
if ($scan_result instanceof LCSCBarcodeScanResult) {
|
||||||
|
$lcscCode = $scan_result->getPC();
|
||||||
|
|
||||||
|
if (is_string($lcscCode) && $lcscCode !== '') {
|
||||||
|
// Prefer generating a relative URL; browser will use current host
|
||||||
|
$createUrl = "/{$locale}/part/from_info_provider/lcsc/{$lcscCode}/create";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If EIGP114 (Mouser / Digi-Key), offer "create part" link
|
||||||
|
if ($scan_result instanceof EIGP114BarcodeScanResult) {
|
||||||
|
// Use guessed vendor and supplierPartNumber.
|
||||||
|
$vendor = $scan_result->guessBarcodeVendor();
|
||||||
|
|
||||||
|
if ($vendor === 'mouser' && is_string($scan_result->supplierPartNumber)
|
||||||
|
&& $scan_result->supplierPartNumber !== '') {
|
||||||
|
|
||||||
|
try {
|
||||||
|
$mouserProvider = $this->providerRegistry->getProviderByKey('mouser');
|
||||||
|
|
||||||
|
if (!$mouserProvider->isActive()) {
|
||||||
|
$this->addFlash('warning', 'Mouser provider is disabled / not configured.');
|
||||||
|
} else {
|
||||||
|
// Search Mouser using the MPN
|
||||||
|
$dtos = $this->infoRetriever->searchByKeyword(
|
||||||
|
keyword: $scan_result->supplierPartNumber,
|
||||||
|
providers: [$mouserProvider]
|
||||||
|
);
|
||||||
|
|
||||||
|
// If there are results, provider_id is MouserPartNumber (per MouserProvider.php)
|
||||||
|
$best = $dtos[0] ?? null;
|
||||||
|
|
||||||
|
if ($best !== null && is_string($best->provider_id) && $best->provider_id !== '') {
|
||||||
|
$createUrl = '/'
|
||||||
|
. rawurlencode($locale)
|
||||||
|
. '/part/from_info_provider/mouser/'
|
||||||
|
. rawurlencode($best->provider_id)
|
||||||
|
. '/create';
|
||||||
|
} else {
|
||||||
|
$this->addFlash('warning', 'No Mouser match found for this MPN.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (\InvalidArgumentException $e) {
|
||||||
|
// provider key not found in registry
|
||||||
|
$this->addFlash('warning', 'Mouser provider is not installed/enabled.');
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
// Don’t break scanning UX if provider lookup fails
|
||||||
|
$this->addFlash('warning', 'Mouser lookup failed: ' . $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Digikey can keep using customerPartNumber if present (it is in their barcode)
|
||||||
|
if ($vendor === 'digikey') {
|
||||||
|
|
||||||
|
try {
|
||||||
|
$provider = $this->providerRegistry->getProviderByKey('digikey');
|
||||||
|
|
||||||
|
if (!$provider->isActive()) {
|
||||||
|
$this->addFlash('warning', 'Digi-Key provider is disabled / not configured (API key missing).');
|
||||||
|
} else {
|
||||||
|
$id = $scan_result->customerPartNumber ?: $scan_result->supplierPartNumber;
|
||||||
|
|
||||||
|
if (is_string($id) && $id !== '') {
|
||||||
|
$createUrl = '/'
|
||||||
|
. rawurlencode($locale)
|
||||||
|
. '/part/from_info_provider/digikey/'
|
||||||
|
. rawurlencode($id)
|
||||||
|
. '/create';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (\InvalidArgumentException $e) {
|
||||||
|
$this->addFlash('warning', 'Digi-Key provider is not installed/enabled');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($createUrl === null) {
|
||||||
|
$this->addFlash('warning', 'scan.qr_not_found');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else { //Otherwise retrieve infoModeData
|
} else { //Otherwise retrieve infoModeData
|
||||||
$infoModeData = $scan_result->getDecodedForInfoMode();
|
$infoModeData = $scan_result->getDecodedForInfoMode();
|
||||||
|
|
||||||
}
|
}
|
||||||
} catch (InvalidArgumentException) {
|
} catch (InvalidArgumentException) {
|
||||||
$this->addFlash('error', 'scan.format_unknown');
|
$this->addFlash('error', 'scan.format_unknown');
|
||||||
|
|
@ -101,6 +195,7 @@ class ScanController extends AbstractController
|
||||||
return $this->render('label_system/scanner/scanner.html.twig', [
|
return $this->render('label_system/scanner/scanner.html.twig', [
|
||||||
'form' => $form,
|
'form' => $form,
|
||||||
'infoModeData' => $infoModeData,
|
'infoModeData' => $infoModeData,
|
||||||
|
'createUrl' => $createUrl,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,16 @@
|
||||||
|
|
||||||
{% if infoModeData %}
|
{% if infoModeData %}
|
||||||
<hr>
|
<hr>
|
||||||
<h4>{% trans %}label_scanner.decoded_info.title{% endtrans %}</h4>
|
<div class="d-flex align-items-center mb-2">
|
||||||
|
<h4 class="mb-0">{% trans %}label_scanner.decoded_info.title{% endtrans %}</h4>
|
||||||
|
|
||||||
|
{% if createUrl %}
|
||||||
|
<a class="btn btn-primary ms-2" href="{{ createUrl }}"
|
||||||
|
target="_blank" title="{% trans %}part.create.btn{% endtrans %}">
|
||||||
|
<i class="fa-solid fa-plus-square"></i>
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
<table class="table table-striped table-hover table-bordered table-sm">
|
<table class="table table-striped table-hover table-bordered table-sm">
|
||||||
<tbody>
|
<tbody>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue