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

# Freie Eingabefelder im Warenkorb auf Positionsebene

> Wie Sie in WEBSALE V8s je Warenkorbposition freie Textfelder anbieten und wie Sie mit BasketProductOptions Eingabefelder und Checkboxen mit Zusatzkosten und Prüfungen einrichten.

Manche Angaben gehören nicht zum Produkt, sondern zur Bestellung dieses Produkts: der gewünschte Liefertermin für genau diese Position, eine Kostenstelle, ein Hinweis für die Kommissionierung. Solche Angaben kann der Käufer nicht bei der Produktauswahl machen, sondern erst im Warenkorb.

WEBSALE V8s bietet dafür zwei Wege, die sich im Aufwand deutlich unterscheiden:

| Weg | Wofür |
| - | - |
| **Freie Textfelder** je Warenkorbposition | einfache Texteingaben ohne Preiswirkung, in wenigen Minuten eingerichtet |
| **BasketProductOptions** | Eingabefelder und Checkboxen mit Zusatzkosten, Bedingungen und Eingabeprüfung, auch je Lieferadresse bei Split-Order |

Diese Seite beschreibt beide, beginnend mit dem einfacheren.

Geht es Ihnen um Texteingaben zum Produkt selbst, beispielsweise eine Beflockung, gehören diese auf die Produktdetailansicht und nicht in den Warenkorb.

## Teil 1: Freie Textfelder je Warenkorbposition

Je Position stehen zwanzig Felder zur Verfügung, jedes mit bis zu 4048 Zeichen. Der Käufer kann sie im Bestellablauf jederzeit ändern.

### Schritt 1: Freischaltung

Eine Freischaltung durch WEBSALE ist nicht erforderlich.

### Schritt 2: Zeichen aus den Eingaben entfernen

Optional, aber empfehlenswert. Tragen Sie alle Zeichen ein, die der Shop aus den Eingaben entfernen soll.

```ini theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
<BasketEntries>
   AdditionalInput-RemoveSigns = "*|~%${};"<>§·@ ()/-"
</BasketEntries>
```

Referenz: [Abschnitt BasketEntries](https://dokumentation.websale.de/v8s/referenz/konfigurationen/shopkonfiguration-shop-config/warenkorb-und-bestellprozess.md#basketentries)

### Schritt 3: Eingabefelder auf der Warenkorbseite einbauen

Die Felder gehören auf `ws_basket.htm` innerhalb der Positionsschleife. Zu jedem Feld gehören zwei Tags: eines liefert den Feldnamen, das andere den bereits gespeicherten Wert.

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{BASKET-PR-Data}
   {@BASKET-PR-Data}

      <label for="delivWish">Wunschtermin für diese Position</label>
      <input type="text" id="delivWish"
         name="~BASKET-PR-AdditionalUserInput_input_1~"
         value="~BASKET-PR-AdditionalUserInput_1~">

   {/@BASKET-PR-Data}
{/BASKET-PR-Data}

{BT-AdditionalUserInput}
   <input type="submit" name="~BT-AdditionalUserInput~" value="Angaben speichern">
{/BT-AdditionalUserInput}
```

Der Feldname enthält die Nummer des Feldes **und** die Nummer der Warenkorbposition, damit die Eingaben auseinandergehalten werden. Sie müssen dafür nichts tun, das Tag erledigt es.

Die Schaltfläche zum Speichern gehört in dasselbe Formular wie die Felder, aber nur einmal, nicht in die Schleife. Andernfalls steht sie hinter jeder Position, obwohl sie ohnehin alle Felder auf einmal speichert.

### Schritt 4: Inhalte auf anderen Seiten ausgeben

Die Werte stehen auf allen Seiten zur Verfügung, die Warenkorbdaten enthalten, beispielsweise auf der Bestellübersicht (`ws_place_order.htm`). Dort brauchen Sie kein Eingabefeld mehr, sondern nur die Ausgabe.

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{BASKET-PR-Data}
   {@BASKET-PR-Data}
      {BASKET-PR-AdditionalUserInput_1}
         Wunschtermin: ~BASKET-PR-AdditionalUserInput_1~
      {/BASKET-PR-AdditionalUserInput_1}
   {/@BASKET-PR-Data}
{/BASKET-PR-Data}
```

Die Klammerung verhindert, dass bei Positionen ohne Eingabe eine leere Beschriftung stehen bleibt.

## Teil 2: BasketProductOptions

Der zweite Weg kann deutlich mehr und kostet entsprechend mehr Einrichtung. Sie definieren Eingabefelder und Checkboxen in der `shop.config`, hängen optional Zusatzkosten daran, machen ihr Erscheinen von Bedingungen abhängig und lassen die Eingaben prüfen.

### Wo die Felder erscheinen

Das entscheidet der Parameter `LevelOfUsage`.

| Wert | Bedeutung |
| - | - |
| `UseOnce` | einmal pro Bestellung |
| `UseForEachBasketPosition` | einmal je Warenkorbposition |
| `UseForEachUsedDeliveryAddress` | einmal je Lieferadresse einer aufgeteilten Position (Split-Order) |

> [!WARNING]
>
> Es kann immer nur eine dieser Varianten gleichzeitig im Shop aktiv sein. Legen Sie sich also vorher fest, statt zwei Varianten nebeneinander auszuprobieren.

### Schritt 1: Freischaltung

Eine Freischaltung durch WEBSALE ist nicht erforderlich.

### Schritt 2: Felder und Kosten konfigurieren

Das folgende Beispiel definiert je Warenkorbposition ein Eingabefeld und eine Checkbox. Ein ausgefülltes Eingabefeld kostet 5 Euro, eine angehakte Checkbox 2 Euro.

```ini theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
<BasketProductOptions>
   BasketProductOptionsVATType  = dynamic
   BasketProductOptionsVATIndex =

   <+BasketProductOption>
      LevelOfUsage          = UseForEachBasketPosition   # UseOnce | UseForEachBasketPosition | UseForEachUsedDeliveryAddress
      Test                  = yes
      BasketProductOptionID = BPO-ID-1

      <Inputs>
         <+Input>
            Type                  = Input
            ID                    = BPO-Input-ID-1
            Name                  = gravur
            +UseGenericInputCheck =
         </+Input>
         <+Input>
            Type                  = Checkbox
            ID                    = BPO-Input-ID-2
            Name                  = eilig
            +UseGenericInputCheck =
         </+Input>
      </Inputs>

      <Costs>
         <+Cost>
            ID    = BPO-Cost-ID-1
            Name  = Gravur
            Type  = singleFixCost   # singleFixCost | percentualSum | multiplyWithQuantity
            Value = 5.00
            <+ConditionGroup>
               <+Condition>
                  Proof = BasketProductOptionInputID
                  Type  = filled
                  Value = BPO-Input-ID-1
               </+Condition>
            </+ConditionGroup>
         </+Cost>
         <+Cost>
            ID    = BPO-Cost-ID-2
            Name  = Eilzuschlag
            Type  = singleFixCost
            Value = 2.00
            <+ConditionGroup>
               <+Condition>
                  Proof = BasketProductOptionInputID
                  Type  = filled
                  Value = BPO-Input-ID-2
               </+Condition>
            </+ConditionGroup>
         </+Cost>
      </Costs>
   </+BasketProductOption>
</BasketProductOptions>
```

> [!WARNING]
>
> Füllen Sie `Name` bei jedem `<+Input>` aus. Der Shop bildet den Namen des HTML-Eingabefeldes aus diesem Wert, nicht aus der `ID`. Bleibt `Name` leer, erhält das Feld keinen brauchbaren Namen und die Eingabe geht verloren.

Ohne Bedingung fallen die Kosten immer an, auch bei leerem Feld oder nicht angehakter Checkbox. Erst die Bedingung `Proof = BasketProductOptionInputID` mit `Type = filled` koppelt die Kosten an die tatsächliche Eingabe. Im Beispiel oben ist das bei beiden Kostensätzen so gemacht.

Referenz: [Abschnitt BasketProductOptions](https://dokumentation.websale.de/v8s/referenz/konfigurationen/shopkonfiguration-shop-config/warenkorb-und-bestellprozess.md#basketproductoptions)

### Schritt 3: Sichtbarkeit an Bedingungen knüpfen

Optional. Sollen die Felder nur bei bestimmten Produkten, ab einem Bestellwert oder nur bei Split-Order erscheinen, ergänzen Sie innerhalb von `<+BasketProductOption>` einen Abschnitt `<Requirements>`.

```ini theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
<+BasketProductOption>
   <Requirements>
      <+RequirementGroup>
         <+Requirement>
            Proof      = ProductNumber
            Type       = inlist
            Value      = A-12345,B-4321,C-56789,D-3434
            Identifier =
         </+Requirement>
      </+RequirementGroup>
   </Requirements>
</+BasketProductOption>
```

Referenz: [Abschnitt Requirements](https://dokumentation.websale.de/v8s/referenz/konfigurationen/shopkonfiguration-shop-config/warenkorb-und-bestellprozess.md#requirements)

### Schritt 4: Eingaben prüfen

Ebenfalls optional. Eine Prüfung definieren Sie einmal in `<GenericInputChecks>` und weisen sie über ihre ID im Parameter `+UseGenericInputCheck` des jeweiligen `<+Input>` zu.

```ini theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
<GenericInputChecks>
   <GenericInputCheckDefaults>
      RemoveSigns = "*|~%${};"<>§·@ ()/-"
   </GenericInputCheckDefaults>
   <+GenericInputCheck>
      ID           = check-mindestens-ein-zeichen
      RemoveSigns  =
      Type         = minlen
      ErrorMessage = Bitte geben Sie mindestens ein Zeichen ein.
      Signs        =
      RegEx        =
      Len          = 1
      List         =
   </+GenericInputCheck>
</GenericInputChecks>
```

Referenz: [Abschnitt GenericInputChecks](https://dokumentation.websale.de/v8s/referenz/konfigurationen/shopkonfiguration-shop-config/adressen-und-eingabepruefung.md#genericinputchecks)

### Schritt 5: Fehlermeldung hinterlegen

```ini theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
<NotifyMessages-Txt>
   BPOInputCheckError = Bitte prüfen Sie Ihre Angaben in den markierten Feldern.
</NotifyMessages-Txt>
```

Referenz: [Abschnitt NotifyMessages-Txt](https://dokumentation.websale.de/v8s/referenz/konfigurationen/shopkonfiguration-shop-config/texte-und-meldungen.md#notifymessages-txt)

### Schritt 6: Der Feldblock

Dieser Block ist in allen Integrationsvarianten derselbe. Er durchläuft die Felder, unterscheidet Eingabefeld und Checkbox, zeigt eine Fehlermeldung je Feld und listet die zugehörigen Kosten auf.

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{@BPO-Input}

   {BPO-Input_Type_Input}
      <input type="text" name="~BPO-Input_input~"
         {BPO-Input_value}value="~BPO-Input_value~"{/BPO-Input_value}>
   {/BPO-Input_Type_Input}

   {BPO-Input_Type_Checkbox}
      <input type="checkbox" name="~BPO-Input_input~"
         {BPO-Input_value}checked{/BPO-Input_value}>
      <input type="checkbox" name="~BPO-Input_input~_hidden" style="display:none" checked>
   {/BPO-Input_Type_Checkbox}

   {BPO-Input_error}
      <span class="error">~BPO-Input_error~</span>
   {/BPO-Input_error}

{/@BPO-Input}

{@BPO-Costs}
   {BPO-Costs_Name}~BPO-Costs_Name~{/BPO-Costs_Name}
   {BPO-Costs_Sum}~BPO-Costs_Sum~ ~WS-CurrencySymbol~{/BPO-Costs_Sum}
{/@BPO-Costs}
```

Das zweite, unsichtbare Feld hinter der Checkbox ist kein Versehen. Ein nicht angehaktes Kontrollkästchen sendet gar nichts, der Shop könnte „nicht angehakt" und „Feld gar nicht vorhanden" sonst nicht unterscheiden. Das mitgesendete Feld auf `_hidden` sagt ihm, dass die Checkbox angezeigt wurde.

Zu den Kosten gibt es weitere Tags, falls Sie mehr ausgeben wollen als Name und Summe:

| Tag | Ausgabe |
| - | - |
| `BPO-Costs_ID` | technische ID des Kostensatzes |
| `BPO-Costs_Value` | konfigurierter Wert |
| `BPO-Costs_Type_singleFixCost` | Klammerung: einmaliger Festbetrag |
| `BPO-Costs_Type_percentualSum` | Klammerung: prozentual vom Wert der Position |
| `BPO-Costs_Type_multiplyWithQuantity` | Klammerung: mit der Menge multipliziert |
| `BPO-Costs_Valid` | Klammerung: Kosten fallen tatsächlich an |

`BPO-Costs` lässt sich mit einer Kosten-ID als Parameter einschränken, beispielsweise `{BPO-Costs(BPO-Cost-ID-1)}`. Damit greifen Sie einen bestimmten Kostensatz heraus, statt alle zu durchlaufen. Als Einzeltag gibt `~BPO-Costs~` die Anzahl der Kostensätze aus.

### Schritt 7: Den Block an der richtigen Stelle einbauen

Wo der Block hingehört, hängt von `LevelOfUsage` ab. Die Klammerung mit `BPO-LevelOfUsage_…` muss dabei zum gewählten Wert passen.

#### Einmal pro Bestellung (UseOnce)

Auf der Warenkorbseite, **außerhalb** der Positionsschleife.

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{BPO-UseOnce}
   {@BPO-Data}
      {BPO-LevelOfUsage_Once}
         ... Feldblock aus Schritt 6 ...
      {/BPO-LevelOfUsage_Once}
   {/@BPO-Data}
{/BPO-UseOnce}

<input type="submit" name="~BT-BPO_Refresh~" value="Angaben speichern">
```

#### Je Warenkorbposition, auf der Produktdetailansicht

Innerhalb des Produktformulars. Eine Schaltfläche zum Speichern brauchen Sie hier nicht, denn alle Eingaben innerhalb von `FORM-Product` wandern beim Ablegen automatisch in den Warenkorb.

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
<form action="~FORM-Product~" method="post" id="productform">
   {@BPO-Data}
      {BPO-LevelOfUsage_BasketPosition}
         ... Feldblock aus Schritt 6 ...
      {/BPO-LevelOfUsage_BasketPosition}
   {/@BPO-Data}
</form>
```

#### Je Warenkorbposition, auf der Warenkorbseite

Innerhalb der Positionsschleife.

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{BASKET-PR-Data}
   {@BASKET-PR-Data}
      {@BPO-Data}
         {BPO-LevelOfUsage_BasketPosition}
            ... Feldblock aus Schritt 6 ...
         {/BPO-LevelOfUsage_BasketPosition}
      {/@BPO-Data}
   {/@BASKET-PR-Data}
{/BASKET-PR-Data}

<input type="submit" name="~BT-BPO_Refresh~" value="Angaben speichern">
```

#### Je Lieferadresse bei Split-Order

Auf der Bestellübersichtsseite. Diese Variante setzt voraus, dass die Aufteilung einer Warenkorbposition auf mehrere Lieferadressen aktiv ist. Die Felder erscheinen, sobald der Käufer auf der Adressseite „An mehrere Adressen liefern" gewählt hat.

Der Feldblock gehört hier an **zwei** Stellen, weil die Bestellübersicht aufgeteilte und nicht aufgeteilte Positionen in getrennten Bereichen ausgibt.

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{@BASKET-PR-D-MultiData}
   {@BPO-Data}
      {BPO-LevelOfUsage_BasketPositionAddress}
         ... Feldblock aus Schritt 6 ...
      {/BPO-LevelOfUsage_BasketPositionAddress}
   {/@BPO-Data}
{/@BASKET-PR-D-MultiData}

{BASKET-PR-D-SingleData}
   {@BPO-Data}
      {BPO-LevelOfUsage_BasketPositionAddress}
         ... Feldblock aus Schritt 6 ...
      {/BPO-LevelOfUsage_BasketPositionAddress}
   {/@BPO-Data}
{/BASKET-PR-D-SingleData}

<input type="submit" name="~BT-BPO_Refresh~" value="Angaben speichern">
```

### Schritt 8: Gesamtkosten ausgeben

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
Aufpreis für Ihre Zusatzangaben:
{BPO-AllCosts_Sum}~BPO-AllCosts_Sum~ ~WS-CurrencySymbol~{/BPO-AllCosts_Sum}
{!BPO-AllCosts_Sum}keine{/!BPO-AllCosts_Sum}
```

### Schritt 9: Eingegebene Werte ausgeben

Für die reine Anzeige, beispielsweise auf der Bestellübersicht, reicht der Wert ohne Eingabefeld. Ob der Block inner- oder außerhalb der Positionsschleife steht, richtet sich nach `LevelOfUsage`.

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{@BPO-Data}
   {@BPO-Input}
      ~BPO-Input_value~
   {/@BPO-Input}
{/@BPO-Data}
```

### Schritt 10: Testmodus beenden

Solange `Test = yes` gesetzt ist, läuft die Funktion im Testbetrieb. Nach erfolgreichem Test stellen Sie den Parameter im Abschnitt `<+BasketProductOption>` auf `no`.

## Die BPO-Tags im Überblick

| Tag | Verwendung |
| - | - |
| `BPO-Data` | Schleife über die konfigurierten Optionen |
| `BPO-ID` | Bezeichner der aktuellen Option |
| `BPO-UseOnce` | Klammerung für die Variante „einmal pro Bestellung" |
| `BPO-LevelOfUsage_Once` | Klammerung, greift bei `UseOnce` |
| `BPO-LevelOfUsage_BasketPosition` | Klammerung, greift bei `UseForEachBasketPosition` |
| `BPO-LevelOfUsage_BasketPositionAddress` | Klammerung, greift bei `UseForEachUsedDeliveryAddress` |
| `BPO-Input` | Schleife über die Felder |
| `BPO-Input_ID` | ID des Feldes aus der Konfiguration |
| `BPO-Input_input` | Name des HTML-Feldes |
| `BPO-Input_value` | eingegebener Wert, auch als Klammerung |
| `BPO-Input_error` | Fehlermeldung zu diesem Feld |
| `BPO-Input_costs` | Kosten, die an diesem Feld hängen |
| `BPO-Input_Type_Input` | Klammerung: Textfeld |
| `BPO-Input_Type_Checkbox` | Klammerung: Kontrollkästchen |
| `BPO-Costs` | Schleife über die Kostensätze, mit ID als Parameter einschränkbar |
| `BPO-AllCosts_Sum` | Summe aller Zusatzkosten |
| [`BT-BPO_Refresh`](https://dokumentation.websale.de/v8s/referenz/tags/bt/produkt-und-warenkorb#schaltflachen-die-anderswo-beschrieben-sind) | Schaltfläche zum Speichern der Eingaben |

## Verwandte Themen

* Wegweiser: [Texteingabefelder zum Produkt, für Eingaben auf der Produktdetailansicht](https://dokumentation.websale.de/v8s/produkte/texteingabefelder-zum-produkt.md)
* Wegweiser: [Warenkorb anzeigen und löschen](https://dokumentation.websale.de/v8s/warenkorb/warenkorb-anzeigen-loeschen.md)
* Wegweiser: Eine Warenkorbposition aufteilen und an mehrere Lieferadressen senden (Split-Order)
* Wegweiser: [Freie Produktfelder im Warenkorb anzeigen, für Angaben aus dem Produktstamm](https://dokumentation.websale.de/v8s/warenkorb/freie-produktfelder-im-warenkorb-anzeigen.md)


## Related topics

- [Schaltflächen auf Produkt- und Warenkorbseite](https://dokumentation.websale.de/v8s/referenz/tags/bt/produkt-und-warenkorb.md)
- [BPO- (Freie Felder auf Split-Positions-Ebene)](https://dokumentation.websale.de/v8s/referenz/tags/bpo.md)
- [Texteingabefelder zum Produkt](https://dokumentation.websale.de/v8s/produkte/texteingabefelder-zum-produkt.md)
- [Warenkorb und Bestellprozess](https://dokumentation.websale.de/v8s/referenz/konfigurationen/shopkonfiguration-shop-config/warenkorb-und-bestellprozess.md)
- [Gutscheine im Warenkorb](https://dokumentation.websale.de/v8s/referenz/tags/basket/gutscheine-im-warenkorb.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.