diff --git a/src/Controller/ScanController.php b/src/Controller/ScanController.php index bc088a46..750fccad 100644 --- a/src/Controller/ScanController.php +++ b/src/Controller/ScanController.php @@ -21,9 +21,14 @@ namespace App\Controller; +use App\Form\LabelSystem\ScanDialogType; use App\Services\LabelSystem\BarcodeParser; +use App\Services\LabelSystem\Barcodes\BarcodeNormalizer; use Doctrine\ORM\EntityNotFoundException; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\Form\FormError; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; /** @@ -33,10 +38,39 @@ use Symfony\Component\Routing\Annotation\Route; class ScanController extends AbstractController { protected $barcodeParser; + protected $barcodeNormalizer; - public function __construct(BarcodeParser $barcodeParser) + public function __construct(BarcodeParser $barcodeParser, BarcodeNormalizer $barcodeNormalizer) { $this->barcodeParser = $barcodeParser; + $this->barcodeNormalizer = $barcodeNormalizer; + } + + /** + * @Route("/", name="scan_dialog") + */ + public function dialog(Request $request): Response + { + $form = $this->createForm(ScanDialogType::class); + $form->handleRequest($request); + + if ($form->isSubmitted() && $form->isValid()) { + $input = $form['input']->getData(); + try { + [$type, $id] = $this->barcodeNormalizer->normalizeBarcodeContent($input); + try { + return $this->redirect($this->barcodeParser->getQRRedirectTarget($type, $id)); + } catch (EntityNotFoundException $exception) { + $this->addFlash('success', 'scan.qr_not_found'); + } + } catch (\InvalidArgumentException $exception) { + $this->addFlash('error', 'scan.format_unknown'); + } + } + + return $this->render('LabelSystem/Scanner/dialog.html.twig', [ + 'form' => $form->createView(), + ]); } /** @@ -44,7 +78,7 @@ class ScanController extends AbstractController * @param string $type * @param int $id */ - public function scanQRCode(string $type, int $id) + public function scanQRCode(string $type, int $id): Response { try { $this->addFlash('success', 'scan.qr_success'); diff --git a/src/Form/LabelSystem/ScanDialogType.php b/src/Form/LabelSystem/ScanDialogType.php new file mode 100644 index 00000000..cca128fb --- /dev/null +++ b/src/Form/LabelSystem/ScanDialogType.php @@ -0,0 +1,49 @@ +. + */ + +namespace App\Form\LabelSystem; + + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\Extension\Core\Type\SubmitType; +use Symfony\Component\Form\Extension\Core\Type\TextType; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\OptionsResolver\OptionsResolver; + +class ScanDialogType extends AbstractType +{ + public function buildForm(FormBuilderInterface $builder, array $options) + { + $builder->add('input', TextType::class, [ + 'attr' => [ + 'autofocus' => true, + ] + ]); + + $builder->add('submit', SubmitType::class, [ + + ]); + } + + public function configureOptions(OptionsResolver $resolver) + { + $resolver->setDefault('mapped', false); + } +} \ No newline at end of file diff --git a/src/Services/LabelSystem/BarcodeParser.php b/src/Services/LabelSystem/BarcodeParser.php index fd2a0b5d..ab8243c8 100644 --- a/src/Services/LabelSystem/BarcodeParser.php +++ b/src/Services/LabelSystem/BarcodeParser.php @@ -31,6 +31,7 @@ class BarcodeParser protected $urlGenerator; protected $em; + public function __construct(UrlGeneratorInterface $urlGenerator, EntityManagerInterface $entityManager) { $this->urlGenerator = $urlGenerator; @@ -58,6 +59,9 @@ class BarcodeParser return $this->urlGenerator->generate('app_part_show', ['id' => $lot->getPart()->getID()]); + case 'location': + return $this->urlGenerator->generate('part_list_store_location', ['id' => $id]); + default: throw new \InvalidArgumentException('Unknown $type: ' . $type); } diff --git a/src/Services/LabelSystem/Barcodes/BarcodeNormalizer.php b/src/Services/LabelSystem/Barcodes/BarcodeNormalizer.php new file mode 100644 index 00000000..8edd4e2d --- /dev/null +++ b/src/Services/LabelSystem/Barcodes/BarcodeNormalizer.php @@ -0,0 +1,75 @@ +. + */ + +namespace App\Services\LabelSystem\Barcodes; + + +class BarcodeNormalizer +{ + protected const PREFIX_TYPE_MAP = [ + 'L' => 'lot', + 'P' => 'part', + 'S' => 'location', + ]; + + /** + * Parses barcode content and normalizes it. + * Returns an array in the format ['part', 1]: First entry contains element type, second the ID of the element + * @param string $input + * @return array + */ + public function normalizeBarcodeContent(string $input): array + { + $input = trim($input); + $matches = []; + + //Some scanner output '-' as ß, so replace it (ß is never used, so we can replace it safely) + $input = str_replace('ß', '-', $input); + + //Extract parts from QR code's URL + if (preg_match('#^https?://.*/scan/(\w+)/(\d+)/?$#', $input, $matches)) { + return [$matches[1], (int) $matches[2]]; + } + + //New Code39 barcodes use L-000001 format + if (preg_match('#^(\w)-(\d{6,})$#', $input, $matches)) { + $prefix = $matches[1]; + $id = (int) $matches[2]; + + if (!isset(self::PREFIX_TYPE_MAP[$prefix])) { + throw new \InvalidArgumentException('Unknown prefix ' . $prefix); + } + return [self::PREFIX_TYPE_MAP[$prefix], $id]; + } + + //Legacy Part-DB location labels used $L00336 format + if (preg_match('#^\$L(\d{5,})$#', $input, $matches)) { + return ['location', (int) $matches[1]]; + } + + //Legacy Part-DB used EAN8 barcodes for part labels. Format 0000001(2) (note the optional 8th digit => checksum) + if (preg_match('#^(\d{7})\d?$#', $input, $matches)) { + return ['part', (int) $matches[1]]; + } + + + throw new \InvalidArgumentException('Unknown barcode format!'); + } +} \ No newline at end of file diff --git a/templates/LabelSystem/Scanner/dialog.html.twig b/templates/LabelSystem/Scanner/dialog.html.twig new file mode 100644 index 00000000..c077634a --- /dev/null +++ b/templates/LabelSystem/Scanner/dialog.html.twig @@ -0,0 +1,9 @@ +{% extends 'main_card.html.twig' %} + +{% block card_title %} {% trans %}label_scanner.title{% endtrans %}{% endblock %} + +{% block card_content %} + {{ form_start(form) }} + + {{ form_end(form) }} +{% endblock %} diff --git a/templates/_navbar.html.twig b/templates/_navbar.html.twig index 83d78e9b..9de4bf15 100644 --- a/templates/_navbar.html.twig +++ b/templates/_navbar.html.twig @@ -16,6 +16,12 @@