Part-DB-server/src/Controller/ScanController.php

231 lines
10 KiB
PHP
Raw Normal View History

<?php
2022-11-29 21:21:26 +01:00
/*
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 2022 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/>.
*/
2020-05-10 21:39:31 +02:00
declare(strict_types=1);
/**
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
2022-11-29 22:28:53 +01:00
* Copyright (C) 2019 - 2022 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\Controller;
2020-04-26 21:26:11 +02:00
use App\Form\LabelSystem\ScanDialogType;
use App\Services\LabelSystem\BarcodeScanner\BarcodeRedirector;
use App\Services\LabelSystem\BarcodeScanner\BarcodeScanHelper;
use App\Services\LabelSystem\BarcodeScanner\BarcodeSourceType;
use App\Services\LabelSystem\BarcodeScanner\LocalBarcodeScanResult;
use App\Services\LabelSystem\BarcodeScanner\LCSCBarcodeScanResult;
use App\Services\LabelSystem\BarcodeScanner\EIGP114BarcodeScanResult;
use Doctrine\ORM\EntityNotFoundException;
2022-08-14 19:32:53 +02:00
use InvalidArgumentException;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
2020-04-26 21:26:11 +02:00
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Attribute\MapQueryParameter;
use Symfony\Component\Routing\Attribute\Route;
use App\Services\InfoProviderSystem\PartInfoRetriever;
use App\Services\InfoProviderSystem\ProviderRegistry;
2024-06-22 00:31:43 +02:00
/**
* @see \App\Tests\Controller\ScanControllerTest
*/
2023-05-28 01:21:05 +02:00
#[Route(path: '/scan')]
class ScanController extends AbstractController
{
public function __construct(
protected BarcodeRedirector $barcodeParser,
protected BarcodeScanHelper $barcodeNormalizer,
private readonly ProviderRegistry $providerRegistry,
private readonly PartInfoRetriever $infoRetriever,
) {}
2020-04-26 21:26:11 +02:00
2023-07-02 17:46:09 +02:00
#[Route(path: '', name: 'scan_dialog')]
public function dialog(Request $request, #[MapQueryParameter] ?string $input = null): Response
2020-04-26 21:26:11 +02:00
{
$this->denyAccessUnlessGranted('@tools.label_scanner');
2020-04-26 21:26:11 +02:00
$form = $this->createForm(ScanDialogType::class);
$form->handleRequest($request);
$mode = null;
if ($input === null && $form->isSubmitted() && $form->isValid()) {
2020-04-26 21:26:11 +02:00
$input = $form['input']->getData();
$mode = $form['mode']->getData();
}
2020-05-10 21:39:31 +02:00
$infoModeData = null;
$createUrl = null;
if ($input !== null) {
2020-04-26 21:26:11 +02:00
try {
$scan_result = $this->barcodeNormalizer->scanBarcodeContent($input, $mode ?? null);
//Perform a redirect if the info mode is not enabled
if (!$form['info_mode']->getData()) {
try {
// redirect user to part page
return $this->redirect($this->barcodeParser->getRedirectURL($scan_result));
} catch (EntityNotFoundException) {
// 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) {
// Dont 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
$infoModeData = $scan_result->getDecodedForInfoMode();
2020-04-26 21:26:11 +02:00
}
} catch (InvalidArgumentException) {
2020-04-26 21:26:11 +02:00
$this->addFlash('error', 'scan.format_unknown');
}
}
2023-05-28 01:21:05 +02:00
return $this->render('label_system/scanner/scanner.html.twig', [
'form' => $form,
'infoModeData' => $infoModeData,
'createUrl' => $createUrl,
2020-04-26 21:26:11 +02:00
]);
}
/**
2020-05-10 21:39:31 +02:00
* The route definition for this action is done in routes.yaml, as it does not use the _locale prefix as the other routes.
*/
2020-04-26 21:26:11 +02:00
public function scanQRCode(string $type, int $id): Response
{
$type = strtolower($type);
try {
$this->addFlash('success', 'scan.qr_success');
2020-05-10 21:39:31 +02:00
if (!isset(BarcodeScanHelper::QR_TYPE_MAP[$type])) {
throw new InvalidArgumentException('Unknown type: '.$type);
}
//Construct the scan result manually, as we don't have a barcode here
$scan_result = new LocalBarcodeScanResult(
target_type: BarcodeScanHelper::QR_TYPE_MAP[$type],
target_id: $id,
//The routes are only used on the internal generated QR codes
source_type: BarcodeSourceType::INTERNAL
);
return $this->redirect($this->barcodeParser->getRedirectURL($scan_result));
} catch (EntityNotFoundException) {
$this->addFlash('success', 'scan.qr_not_found');
2020-05-10 21:39:31 +02:00
return $this->redirectToRoute('homepage');
}
}
2020-05-10 21:39:31 +02:00
}