> ## 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.

# Praxisbeispiele - Gutscheine

> Praxisbeispiele für Gutscheine in WEBSALE: Eingabeformular mit maximumCount-Check, Validierung, Einlösen im Checkout sowie typische Frontend-Snippets.

In diesem Abschnitt finden Sie Praxisbeispiele für die Verwendung von Gutscheinen im Checkout.

***

## Gutscheineingabe-Formular mit `maximumCount`-Check

Die Eingabe-Form wird nur angezeigt, solange weniger Gutscheine eingelöst sind als erlaubt. Sobald die Höchstgrenze erreicht ist, verschwindet das Formular automatisch.

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{{ var $cActionVoucherAdd = $wsActions.create("VoucherAdd") }}
{{ include "components/errorAlert.htm" with $cAction = $cActionVoucherAdd, $cViewEachField = true }}

{{ if len($wsVoucher.vouchers) < $wsVoucher.maximumCount }}
    <form method="post" action="{{= $wsViews.current.url() }}" data-ws-ajax-form>
        <input type="hidden" name="wsReplaceIds" value="wsBasketWrapper,wsBasketEntries,wsBasketOffcanvasContent">
        <input type="hidden" name="wsact"    value="{{= $cActionVoucherAdd.id }}">
        <input type="hidden" name="wscsrf"   value="{{= $cActionVoucherAdd.csrf }}">
        <input type="hidden" name="wstarget" value="{{= $wsViews.current.url() }}">

        <input type="text" name="id" value="" placeholder="Gutschein-Code eingeben">
        <button type="submit">Einlösen</button>
    </form>
{{ /if }}
```

<Info>
  Fehlermeldungen (z.B. "Mindestbestellwert nicht erreicht") werden über `components/errorAlert.htm` definiert und ausgegeben.
</Info>

***

## Liste eingelöster Gutscheine anzeigen

Pro Gutschein wird ein eigenes kleines Formular mit eindeutiger ID ausgegeben. Gültige Gutscheine erscheinen grün, ungültige rot.

```text theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{{ if $wsVoucher.vouchers }}
    <p>Eingelöste Gutscheine</p>

    {{ foreach $cVoucher in $wsVoucher.vouchers }}
        {{ var $cVoucherIsValid = $cVoucher.valid | ifNull(true) }}
        {{ var $cActionVoucherDelete = $wsActions.create("VoucherDelete") }}

        <form method="post"
              action="{{= $wsViews.viewUrl('basket.htm') }}"
              data-ws-ajax-form>
            <input type="hidden" name="wsReplaceIds" value="wsBasketWrapper,wsBasketEntries,wsBasketOffcanvasContent">
            <input type="hidden" name="id"       value="{{= $cVoucher.id }}">
            <input type="hidden" name="wsact"    value="{{= $cActionVoucherDelete.id }}">
            <input type="hidden" name="wscsrf"   value="{{= $cActionVoucherDelete.csrf }}">
            <input type="hidden" name="wstarget" value="{{= $wsViews.current.url() }}">

            <span>{{= $cVoucher.id }}</span>
            <button type="submit">Entfernen</button>
        </form>
    {{ /foreach }}
{{ /if }}
```

***

## Alle eingelösten Gutscheine im Warenkorb ausgeben

In diesem Beispiel werden alle eingelösten Gutscheine im Warenkorb ausgegeben. So sieht der Kunde transparent, welche Codes im System sind und welcher davon gerade greift. Nicht wirksame Gutscheine werden nicht in die Liste aufgenommen und werden über eine Fehlermeldung gekennzeichnet.

```text theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{{ var $cVoucherCount = 0 }}
{{ if $wsVoucher.vouchers }}
    {{ foreach $cVoucher in $wsVoucher.vouchers }}
        {{ $cVoucherCount = $cVoucherCount + 1 }}
        <tr>
            <td>
                <div>{{ if $cVoucherCount == 1 }}Gutschein{{ else }}Weiterer Gutschein{{ /if }}</div>
                <div>{{= $cVoucher.id }}</div>
            </td>
            <td>
                -{{= $cVoucher.value | currency }}
            </td>
        </tr>
    {{ /foreach }}
{{ /if }}
```

***

## Gutscheinfehler mit Grund und Gutschein-ID ausgeben

Statt eines allgemeinen Hinweises erhält der Kunde hier je Gutschein den konkreten Grund, warum der Gutschein nicht greift. Die Texte stammen aus [`checkout.voucherErrors`](/konfiguration/checkout-bestellablauf#checkout-vouchererrors-fehlertexte-zu-wirkungslosen-gutscheinen), der Fallback auf `code` greift nur, falls kein Text gepflegt ist.

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{{ if $wsCheckout.isOrderBlockedByIneffectiveVoucher }}
    <div class="alert alert-danger" role="alert">
        <strong>Gutscheine können nicht eingelöst werden:</strong>
        <ul>
            {{ foreach $cError in $wsCheckout.ineffectiveVoucherErrors }}
                {{ if $cError.details.voucherId }}
                    <li>
                        Gutschein <strong>{{= $cError.details.voucherId }}</strong>:
                        {{= $cError.text | ifNull($cError.code) }}
                    </li>
                {{ else }}
                    {{#
                        Steht die Berechnung des Mindestbestellwerts auf "sum", wird die Summe
                        aller Mindestbestellwerte geprüft. Dieser Fehler gehört zu keinem
                        einzelnen Gutschein und kommt daher ohne Gutschein-ID.
                    #}}
                    <li>{{= $cError.text | ifNull($cError.code) }}</li>
                {{ /if }}
            {{ /foreach }}
        </ul>
    </div>
{{ /if }}
```

<Info>
  Prüfen Sie `details.voucherId` immer vor der Ausgabe. Die Gutschein-ID fehlt bewusst, wenn der Fehler aus der Summenprüfung der Mindestbestellwerte stammt. Welcher Fehler wann entsteht, steht unter [Wann welcher Fehler entsteht](/konfiguration/checkout-bestellablauf#wann-welcher-fehler-entsteht).
</Info>

***

## Kostenfreie Versandart für Gutschein-Warenkörbe

Enthält ein Warenkorb ausschließlich (Sofort-)Gutscheine, wird kein physischer Versand benötigt. Dafür lässt sich unter [`checkout.shippingMethod`](/konfiguration/checkout-bestellablauf#checkout-shippingmethod-versandarten) eine eigene, immer kostenfreie Versandart anlegen: Die Preisstaffel `basicCost` setzt die Kosten ab einer Zwischensumme von `0` auf `0`, und die Validierung [`shippingMethodValidation.productType`](/konfiguration/validierungs-und-prufservices) mit `rule: deny` sperrt die Versandart, sobald ein reguläres Produkt (Produkttyp `standard`) im Warenkorb liegt - sie ist also nur für reine Gutschein-Warenkörbe wählbar.

```json theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{
  "active": true,
  "basicCost": [
    {
      "cost": 0,
      "subtotal": 0
    }
  ],
  "description": "checkout.shippingMethod.INSTANT_VOUCHER.description",
  "group": null,
  "id": "INSTANT_VOUCHER",
  "image": "",
  "link": "",
  "name": "checkout.shippingMethod.INSTANT_VOUCHER.name",
  "orderText": "checkout.shippingMethod.INSTANT_VOUCHER.orderText",
  "taxable": true,
  "type": "standard",
  "validations": [
    {
      "options": {
        "rule": "deny",
        "ruleList": [
          "standard"
        ]
      },
      "service": "shippingMethodValidation.productType"
    }
  ],
  "weightCost": null
}
```

<Info>
  Der Wert `standard` in der `ruleList` ist der **Wert des Produkttyp-Feldes** der Produkte (das über `content.usedFields` als Produkttyp definierte Produktdatenfeld) - nicht zu verwechseln mit dem Parameter `type: "standard"` der Versandart selbst. Produkte, bei denen das Produkttyp-Feld nicht gesetzt ist, bestehen die Prüfung immer.

  `name`, `description` und `orderText` verweisen im Beispiel auf Textbausteine, sodass die Texte je Sprache über den Textbaustein-Dienst gepflegt werden können.
</Info>


## Related topics

- [checkout - Bestellablauf](/konfiguration/checkout-bestellablauf.md)
- [$wsVoucher - Gutscheine](/frontend/referenz/module/wsvoucher.md)
- [$wsWatchList - Merklisten](/frontend/referenz/module/wswatchlist.md)
- [$wsTestMode - Testmodus](/frontend/referenz/module/wstestmode.md)
- [$wsBasket - Warenkorb](/frontend/referenz/module/wsbasket.md)
