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

# BPO- (free fields at split item level)

> The tags for free input fields and checkboxes at split item level: output fields, read back entries and display the additional costs attached to them.

With the free fields at split item level, you offer the buyer input fields and checkboxes that do not belong to the product but to the order: gift wrapping, a preferred date, a greeting text. Additional costs can be attached to each of these fields.

Unlike the free input fields of the basket item (`BASKET-PR-AdditionalUserInput_1…20`), everything here is defined in the shop configuration `shop.config` in the `<BasketProductOptions>` section: which fields exist, where they appear, what they cost and how the input is validated. The tags with the prefix `BPO-` output this configuration in the template.

Guide: Free fields at split item level · Reference: Section `<BasketProductOptions>`

## The structure in the template

All tags in this group are nested inside each other, and the order of nesting is fixed.

```text theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{@BPO-Data}                      Schleife über die konfigurierten Felderblöcke
   {BPO-LevelOfUsage_XXX}        Weiche auf die eingestellte Verwendungsebene
      {@BPO-Input}               Schleife über die Felder dieses Blocks
      {@BPO-Costs}               Schleife über die Kostenberechnungen dieses Blocks
```

The range tag in the middle is the reason why the same code block works on three templates. The shop knows only one active level of usage, and `BPO-LevelOfUsage_XXX` ensures that the block only outputs something on the matching template.

> [!NOTE]
>
> Only one level of usage is ever active per shop. So you cannot offer a field per order and a field per basket item at the same time.

## The complete example

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

      {@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="fehler">~BPO-Input_error~</span>
         {/BPO-Input_error}
      {/@BPO-Input}

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

   {/BPO-LevelOfUsage_BasketPosition}
{/@BPO-Data}

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

> [!WARNING]
>
> Without the `BT-BPO_Refresh` button, the entries are lost. It writes the values back to the basket. The only exception is the product detail view: there, the fields are inside `~FORM-Product~` and are transferred to the basket together with the product.

Reference: BT-BPO\_Refresh

## The level of usage

Which of the three levels applies is defined by the `LevelOfUsage` parameter in the `<BasketProductOptions>` section. Each level has its own range tag and its own place in the template.

| Range tag | The fields apply | Belongs on |
| - | - | - |
| `BPO-LevelOfUsage_Once` | once for the entire order | basket page (`ws_basket.htm`), outside `{BASKET-PR-Data}` |
| `BPO-LevelOfUsage_BasketPosition` | per basket item | product detail view (`ws_product.htm`) inside `~FORM-Product~`, or basket page inside `{@BASKET-PR-Data}` |
| `BPO-LevelOfUsage_BasketPositionAddress` | per delivery address of a basket item | order overview page (`ws_place_order.htm`), inside `{@BASKET-PR-D-MultiData}` and `{BASKET-PR-D-SingleData}` |

### BPO-UseOnce

Encloses the entire block for the `Once` level. It sits one level above `BPO-Data` and is the only tag in this group that stands outside the loop.

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{BPO-UseOnce}
   {@BPO-Data}
      {BPO-LevelOfUsage_Once}
         <!-- Felder und Kosten -->
      {/BPO-LevelOfUsage_Once}
   {/@BPO-Data}
{/BPO-UseOnce}
```

> [!NOTE]
>
> At first glance, the two tags seem redundant. However, `BPO-UseOnce` belongs to the basket page and keeps the block out of the item loop, while `BPO-LevelOfUsage_Once` checks the setting of the individual field block. For the other two levels, `BPO-UseOnce` is omitted.

### BPO-LevelOfUsage\_BasketPositionAddress

Applies when the fields apply per delivery address within a basket item. This requires the split order function, that is, splitting an item across multiple delivery addresses.

> [!WARNING]
>
> The block must appear twice on the order overview page: once in `{@BASKET-PR-D-MultiData}` and once in `{BASKET-PR-D-SingleData}`. Even with split order active, the buyer can choose only one delivery address for an item, and then the second range applies.

Guide: Splitting a basket item and sending it to multiple delivery addresses · Reference: BASKET-PR-D-MultiData, BASKET-PR-D-SingleData

## The loop over the field blocks

### BPO-Data

Loop over the field blocks configured in `<BasketProductOptions>`. In each iteration, the fields and cost calculations of one block are available.

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{@BPO-Data}
   {BPO-LevelOfUsage_XXX}
      <!-- Felder und Kosten dieses Blocks -->
   {/BPO-LevelOfUsage_XXX}
{/@BPO-Data}
```

> [!NOTE]
>
> All other tags in this group except `BPO-UseOnce` and `BPO-AllCosts_Sum` are only permitted inside this loop. Outside it, they return nothing because they lack the reference to the field block.

### BPO-ID

Outputs the identifier of the field block, that is, the value of `BasketProductOptionID` from the shop configuration.

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{@BPO-Data}
   <div data-bpo="~BPO-ID~">
      ...
   </div>
{/@BPO-Data}
```

> [!NOTE]
>
> The identifier has no meaning for the buyer. It is useful as a hook for CSS and JavaScript when several field blocks should look different.

## The input fields

### BPO-Input

Loop over the fields of the block. As a single tag, it outputs the number of configured fields.

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

   {@BPO-Input}
      <!-- ein einzelnes Feld -->
   {/@BPO-Input}
{/@BPO-Data}
```

### BPO-Input\_Type\_Input and BPO-Input\_Type\_Checkbox

The two range tags distinguish the field type. `BPO-Input_Type_Input` applies to the text input field, `BPO-Input_Type_Checkbox` to the checkbox.

```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}
```

> [!WARNING]
>
> The second, hidden field for the checkbox is not a typo. An unchecked checkbox submits nothing at all, so the shop could not distinguish between "deselected" and "form never submitted". The field with the suffix `_hidden` is always set and reports that this checkbox was in the form at all. Without this field, a checkbox that has been checked once cannot be unchecked again.

### BPO-Input\_ID and BPO-Input\_input

`BPO-Input_ID` outputs the identifier of the field from the shop configuration, `BPO-Input_input` the technical field name for the `name` attribute.

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{@BPO-Input}
   <label for="~BPO-Input_ID~">Ihr Wunsch</label>
   <input type="text" id="~BPO-Input_ID~" name="~BPO-Input_input~">
{/@BPO-Input}
```

> [!WARNING]
>
> The `name` attribute must come from `BPO-Input_input`. A name you assign yourself is not recognized on submission, and the entry is lost.

### BPO-Input\_value

Returns what the buyer entered. For the text input field, this is the text; for the checkbox, the value `on`. As a range tag, it applies as soon as the field is filled in or checked.

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

   {!BPO-Input_value}
      Noch keine Angabe gemacht.
   {/!BPO-Input_value}
{/@BPO-Input}
```

> [!NOTE]
>
> On the order overview page, output without an input field makes sense so that the buyer sees their entries once more before submitting. For this, `BPO-Data`, `BPO-Input` and `BPO-Input_value` without the `LevelOfUsage` enclosure are sufficient.

### BPO-Input\_error

Outputs the error message stored in the shop configuration for the input validation of this field. As a range tag, it only applies if the validation actually failed.

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{@BPO-Input}
   <input type="text" name="~BPO-Input_input~">

   {BPO-Input_error}
      <span class="fehler">~BPO-Input_error~</span>
   {/BPO-Input_error}
{/@BPO-Input}
```

> [!NOTE]
>
> The message comes from the `<GenericInputChecks>` section, linked via the field's `+UseGenericInputCheck` parameter. It belongs directly next to the field and not at the top of the page, otherwise the buyer will look at the wrong one when there are several fields.

Reference: Section `<GenericInputChecks>`

### BPO-Input\_costs

Outputs the additional costs attached to this one field.

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{@BPO-Input}
   <input type="checkbox" name="~BPO-Input_input~">
   Geschenkverpackung
   {BPO-Input_costs}(~BPO-Input_costs~ ~WS-CurrencySymbol~){/BPO-Input_costs}
{/@BPO-Input}
```

> [!NOTE]
>
> This is the output at the field, not the billing. The amount actually charged is provided by `BPO-Costs_Sum`, and the total across all field blocks by `BPO-AllCosts_Sum`.

## The additional costs

Costs are not attached to the field but to the field block. A block can have several cost calculations, and each calculation has its own conditions. That is why there is a second loop alongside the field loop.

### BPO-Costs

Loop over the cost calculations of the block. As a single tag, it outputs their number.

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{@BPO-Data}
   {@BPO-Costs}
      {BPO-Costs_Valid}
         {BPO-Costs_Sum}
            {BPO-Costs_Name}~BPO-Costs_Name~: {/BPO-Costs_Name}
            ~BPO-Costs_Sum~ ~WS-CurrencySymbol~
         {/BPO-Costs_Sum}
      {/BPO-Costs_Valid}
   {/@BPO-Costs}
{/@BPO-Data}
```

### BPO-Costs\_Valid and BPO-Costs\_Sum

`BPO-Costs_Valid` applies when the configured conditions of this cost calculation are met. `BPO-Costs_Sum` outputs the calculated amount and applies as a range tag when an amount is incurred.

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{@BPO-Costs}
   {BPO-Costs_Sum}
      Aufpreis: ~BPO-Costs_Sum~ ~WS-CurrencySymbol~
   {/BPO-Costs_Sum}

   {!BPO-Costs_Sum}
      keine zusätzlichen Kosten
   {/!BPO-Costs_Sum}
{/@BPO-Costs}
```

> [!WARNING]
>
> The costs are incurred as soon as the conditions of the calculation are met, not only when the buyer fills in the field. The only exception is a condition that checks exactly that (`Proof = BasketProductOptionInputID` with `Type = filled`). A cost calculation without a condition therefore always applies, even if the field is empty.

> [!NOTE]
>
> Only display the costs inside `{BPO-Costs_Valid}`. The loop runs over all configured calculations, including those whose conditions do not currently apply.

### BPO-Costs\_ID, BPO-Costs\_Name and BPO-Costs\_Value

| Tag | Returns |
| - | - |
| `BPO-Costs_ID` | the unique identifier of the cost calculation |
| `BPO-Costs_Name` | the name assigned in the configuration |
| `BPO-Costs_Value` | the configured calculation value, that is, the amount or the percentage |

> [!WARNING]
>
> `BPO-Costs_Value` is the value from the configuration, not the result. For `percentualSum`, it contains the percentage, and for `multiplyWithQuantity`, the amount per piece. What the buyer pays is in `BPO-Costs_Sum`.

### The type of calculation

Three range tags indicate which rule is used for the calculation. Exactly one of them always applies.

| Range tag | Meaning |
| - | - |
| `BPO-Costs_Type_singleFixCost` | fixed amount, once per (split) item |
| `BPO-Costs_Type_percentualSum` | percentage of the goods value of the (split) item or of the basket |
| `BPO-Costs_Type_multiplyWithQuantity` | amount per piece, multiplied by the quantity of the (split) item |

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{@BPO-Costs}
   {BPO-Costs_Type_singleFixCost}
      Pauschale von ~BPO-Costs_Value~ ~WS-CurrencySymbol~
   {/BPO-Costs_Type_singleFixCost}

   {BPO-Costs_Type_percentualSum}
      ~BPO-Costs_Value~ % des Warenwerts
   {/BPO-Costs_Type_percentualSum}

   {BPO-Costs_Type_multiplyWithQuantity}
      ~BPO-Costs_Value~ ~WS-CurrencySymbol~ je Stück
   {/BPO-Costs_Type_multiplyWithQuantity}
{/@BPO-Costs}
```

> [!NOTE]
>
> The three tags are mainly useful during setup to see which rule applies. For the buyer, the final amount from `BPO-Costs_Sum` is usually sufficient.

### BPO-AllCosts\_Sum

Outputs the sum of all additional costs across all field blocks. As a range tag, it applies if any costs are incurred at all.

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

> [!NOTE]
>
> This tag stands outside the `BPO-Data` loop and belongs in the invoice footer next to shipping and payment. It is the only value in this group that visibly explains the order total.

## The tags at a glance

| Tag | Single tag | Range tag | Negative range tag | Loop tag |
| - | - | - | - | - |
| `BPO-UseOnce` | – | yes | yes | – |
| `BPO-Data` | yes | yes | yes | yes |
| `BPO-LevelOfUsage_Once` | – | yes | yes | – |
| `BPO-LevelOfUsage_BasketPosition` | – | yes | yes | – |
| `BPO-LevelOfUsage_BasketPositionAddress` | – | yes | yes | – |
| `BPO-ID` | yes | – | – | – |
| `BPO-Input` | yes | yes | yes | yes |
| `BPO-Input_ID` | yes | – | – | – |
| `BPO-Input_Type_Input` | – | yes | yes | – |
| `BPO-Input_Type_Checkbox` | – | yes | yes | – |
| `BPO-Input_input` | yes | yes | yes | – |
| `BPO-Input_value` | yes | yes | yes | – |
| `BPO-Input_error` | yes | yes | yes | – |
| `BPO-Input_costs` | yes | yes | yes | – |
| `BPO-Costs` | yes | yes | yes | yes |
| `BPO-Costs_ID` | yes | – | – | – |
| `BPO-Costs_Name` | yes | yes | yes | – |
| `BPO-Costs_Value` | yes | yes | yes | – |
| `BPO-Costs_Type_singleFixCost` | – | yes | yes | – |
| `BPO-Costs_Type_percentualSum` | – | yes | yes | – |
| `BPO-Costs_Type_multiplyWithQuantity` | – | yes | yes | – |
| `BPO-Costs_Sum` | yes | yes | yes | – |
| `BPO-Costs_Valid` | – | yes | yes | – |
| `BPO-AllCosts_Sum` | yes | yes | yes | – |

The tags are permitted on the product detail view (`ws_product.htm`), the basket page (`ws_basket.htm`) and the order overview page (`ws_place_order.htm`), with these exceptions:

| Tag | Only permitted on |
| - | - |
| `BPO-UseOnce` | basket page |
| `BPO-LevelOfUsage_Once` | basket page |
| `BPO-LevelOfUsage_BasketPosition` | basket page, product detail view |
| `BPO-LevelOfUsage_BasketPositionAddress` | order overview page |
| `BPO-AllCosts_Sum` | basket page, order overview page |

In addition, all tags except `BPO-UseOnce` and `BPO-AllCosts_Sum` are only available inside the `BPO-Data` loop.

## Related topics

* Guide: Free fields at split item level
* Reference: Section `<BasketProductOptions>`
* Reference: Section `<GenericInputChecks>`
* Reference: BT-BPO\_Refresh
* Reference: Items in the basket


## Related topics

- [Buttons on the product and basket page](https://dokumentation.websale.de/en/v8s/referenz/tags/bt/produkt-und-warenkorb.md)
- [Shopping basket and order process](https://dokumentation.websale.de/en/v8s/referenz/konfigurationen/shopkonfiguration-shop-config/warenkorb-und-bestellprozess.md)
- [Addresses and input checks](https://dokumentation.websale.de/en/v8s/referenz/konfigurationen/shopkonfiguration-shop-config/adressen-und-eingabepruefung.md)
- [Freely positioned product menu](https://dokumentation.websale.de/en/v8s/navigation/frei-positioniertes-produktmenue.md)
- [TopRank HTML pages](https://dokumentation.websale.de/en/v8s/referenz/tags/toprank-html-seiten.md)


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