> ## Documentation Index
> Fetch the complete documentation index at: https://dokumentation.websale.de/llms.txt
> Use this file to discover all available pages before exploring further.

# Scan & Order

> Set up Scan & Order so customers can scan QR codes or barcodes via the search field camera to open products or add them straight to the basket.

With the help of this function, customers can call up products via QR code or barcode and add them to the basket. A scanner icon in the search field opens the camera in full-screen mode. If a URL is scanned, a direct redirection takes place. If, on the other hand, a barcode is scanned, the product with the scanned ID is placed directly into the basket.

***

## Modules

The following modules are relevant for the integration of Scan & Order:

* [\$wsViews](/en/frontend/referenz/module/wsviews) - current URL, destination pages, view URLs

***

## Frontend integration

### Dependencies

The scanner is based on the open-source library [html5-qrcode.](https://github.com/mebjas/html5-qrcode) The file `html5-qrcode.min.js` is stored in the `scripts/` directory and is only included via lazy load when the scanner is opened.

### QR button in the search field

The button is placed directly in front of the search field within the `ws-search-box` component and opens the scanner. Depending on the end device, it appears in the following locations:

* in the desktop search bar of the header
  <Frame>
    <img src="https://mintcdn.com/websaleag-44ee7ea6/GY7qcIHPANoLDMCt/images/image.png?fit=max&auto=format&n=GY7qcIHPANoLDMCt&q=85&s=67ae5509a5223f7555e187ee62551136" alt="Image" width="1903" height="76" data-path="images/image.png" />
  </Frame>
* in the mobile offcanvas navigation menu
  <Frame>
    <img src="https://mintcdn.com/websaleag-44ee7ea6/GY7qcIHPANoLDMCt/images/image-1.png?fit=max&auto=format&n=GY7qcIHPANoLDMCt&q=85&s=6065aae60be9c428ddaa27338cf1e243" alt="Image" width="395" height="111" data-path="images/image-1.png" />
  </Frame>

<br />The integration into the header is done via the file `components/layout/header.htm`:

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
<ws-search-box ...>
    <button type="button"
            class="btn btn-light border rounded-start"
            aria-label="%%QRCodeBtnTitle%%"
            data-bs-toggle="modal"
            data-bs-target="#wsQRCodeScanModal">
        <svg xmlns="http://www.w3.org/2000/svg" width="25" height="25" fill="currentColor" viewBox="0 0 16 16">
            <path d="M0 .5A.5.5 0 0 1 .5 0h3a.5.5 0 0 1 0 1H1v2.5a.5.5 0 0 1-1 0v-3Zm12 0a.5.5 0 0 1 .5-.5h3a.5.5 0 0 1 .5.5v3a.5.5 0 0 1-1 0V1h-2.5a.5.5 0 0 1-.5-.5ZM.5 12a.5.5 0 0 1 .5.5V15h2.5a.5.5 0 0 1 0 1h-3a.5.5 0 0 1-.5-.5v-3a.5.5 0 0 1 .5-.5Zm15 0a.5.5 0 0 1 .5.5v3a.5.5 0 0 1-.5.5h-3a.5.5 0 0 1 0-1H15v-2.5a.5.5 0 0 1 .5-.5ZM4 4h1v1H4V4Z"/>
            <path d="M7 2H2v5h5V2ZM3 3h3v3H3V3Zm2 8H4v1h1v-1Z"/>
            <path d="M7 9H2v5h5V9Zm-4 1h3v3H3v-3Zm8-6h1v1h-1V4Z"/>
            <path d="M9 2h5v5H9V2Zm1 1v3h3V3h-3ZM8 8v2h1v1H8v1h2v-2h1v2h1v-1h2v-1h-3V8H8Zm2 2H9V9h1v1Zm4 2h-1v1h-2v1h3v-2Zm-4 2v-1H8v1h2Z"/>
            <path d="M12 9h2V8h-2v1Z"/>
        </svg>
    </button>
    <input type="search" name="query" ...>
    ...
</ws-search-box>
```

<Info>
  The button must be placed identically both in the desktop header and in the mobile offcanvas menu. Using the attribute `aria-label=%QRCodeBtnTitle%`, the shop text snippets are used for accessibility.
</Info>

### Modal window

The full-screen modal is inserted once in the base layout. The area `#wsQRScanner` is responsible for displaying the camera preview from `HTML5-QR-Code`.

The integration is done via the file `layouts/default.htm`:

```text theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
<div id="wsQRCodeScanModal" class="modal" tabindex="-1">
    <div class="modal-dialog modal-fullscreen">
        <div class="modal-content">
            <div class="modal-header">
                <p class="h5 modal-title">
                    <svg xmlns="http://www.w3.org/2000/svg" width="35" height="35" fill="currentColor"
                         class="me-2 align-top" viewBox="0 0 16 16">
                        <path d="M0 .5A.5.5 0 0 1 .5 0h3a.5.5 0 0 1 0 1H1v2.5a.5.5 0 0 1-1 0v-3Zm12 0a.5.5 0 0 1 .5-.5h3a.5.5 0 0 1 .5.5v3a.5.5 0 0 1-1 0V1h-2.5a.5.5 0 0 1-.5-.5ZM.5 12a.5.5 0 0 1 .5.5V15h2.5a.5.5 0 0 1 0 1h-3a.5.5 0 0 1-.5-.5v-3a.5.5 0 0 1 .5-.5Zm15 0a.5.5 0 0 1 .5.5v3a.5.5 0 0 1-.5.5h-3a.5.5 0 0 1 0-1H15v-2.5a.5.5 0 0 1 .5-.5ZM4 4h1v1H4V4Z"/>
                        <path d="M7 2H2v5h5V2ZM3 3h3v3H3V3Zm2 8H4v1h1v-1Z"/>
                        <path d="M7 9H2v5h5V9Zm-4 1h3v3H3v-3Zm8-6h1v1h-1V4Z"/>
                        <path d="M9 2h5v5H9V2Zm1 1v3h3V3h-3ZM8 8v2h1v1H8v1h2v-2h1v2h1v-1h2v-1h-3V8H8Zm2 2H9V9h1v1Zm4 2h-1v1h-2v1h3v-2Zm-4 2v-1H8v1h2Z"/>
                        <path d="M12 9h2V8h-2v1Z"/>
                    </svg>
                    %%QrScan%%
                </p>
                <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="%%Close%%"></button>
            </div>
            <div class="modal-body position-relative">
                <div id="wsQRScanner" class="h-100"></div>
            </div>
        </div>
    </div>
</div>
```

### JavaScript constant

The path to the library is set as a JavaScript variable in the `{{ autoescape "js" }}` block of the layout file `layouts/default.htm`.

```javascript theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{{ autoescape "js" }}
    {{ var $cAddToBasketTarget = $wsViews.viewUrl("basket.htm") }}
    {{ var $cAddToBasketParams = { wscsrf: $wsActions.csrfToken, quantity: 1 } }}
    {{ var $cAddToBasketLink = $wsActions.url("BasketItemAdd", $cAddToBasketTarget, $cAddToBasketParams) }}
    <script>
        const wsQRCodeJS = "{{= static('scripts/html5-qrcode.min.js') }}";
        const wsAddToBasketLink = "{{= $cAddToBasketLink }}";
    </script>
{{ /autoescape }}
```

### Scanner logic

The following code block is to be inserted into the file `scripts/wsGlobal.js`. The flow is as follows:

1. When the scanner is opened for the first time, `html5-qrcode.min.js` is loaded.
2. If the library was already loaded, the scanner is started directly via `wsInitQRScan()`.
3. `html5-qrcode` starts the camera and shows the preview in the `#wsQRScanner` area.
4. After a successful scan, URL codes redirect directly to the scanned URL — barcodes place the product with the scanned ID directly into the basket.
5. When the window is closed, the camera is stopped.

```javascript theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
// ---------------------------------------------------------- QR code scanner - start
// const html5QrCode and function wsInitQRScan is in script html5-qrcode.min.js

function wsInitQRScan() {
    Html5Qrcode.getCameras().then(devices => {
        if (devices && devices.length) {
            // use this to start scanning
            html5QrCode.start(
                {
                    facingMode: "environment"
                },
                {
                    fps: 10,
                    qrbox: { width: 250, height: 250 }
                },
                (decodedText, decodedResult) => {
                    // do something when code is read
                    html5QrCode.stop().then((ignore) => {
                        // QR Code scanning is stopped
                        if (decodedText.startsWith("http")) {
                            // execute QR code if code is a URL
                            location.href = decodedText;
                        } else {
                            // execute WEBSALE search if Barcode
                            location.href = wsAddToBasketLink + "&productId=" + decodedText;
                        }
                    }).catch((err) => {
                        // Stop failed, handle it
                    });
                },
                (errorMessage) => {
                    // parse error, ignore it
                })
                .catch((err) => {
                    // Start failed, handle it
                }
            );
        }
    }).catch(err => {
        // access denied
    });
}

document.querySelector("#wsQRCodeScanModal").addEventListener("show.bs.modal", function(e) {
    var wsQRCodeScanScript = document.querySelector("#wsQRCodeScanScript");
    if (!wsQRCodeScanScript) {
        // lazyload script for better performance
        var script = document.createElement("script");
        script.setAttribute("id", "wsQRCodeScanScript");
        script.setAttribute("src", wsQRCodeJS);
        document.head.appendChild(script);
    } else {
        // execute function if script is already loaded
        wsInitQRScan();
    }
});

document.querySelector("#wsQRCodeScanModal").addEventListener("hidden.bs.modal", function(e) {
    html5QrCode.stop().then((ignore) => {
        // QR Code scanning is stopped
    }).catch((err) => {
        // Stop failed, handle it
    });
});

// ---------------------------------------------------------- QR code scanner - end
```

***

## Further links

* [html5-qrcode on GitHub](https://github.com/mebjas/html5-qrcode) - Documentation and configuration options of the scanner library
