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

# Practical examples - Online order form

> Implement an online order form for direct ordering: enter products by product number, add them to the basket and keep entries across the session.

With direct ordering, the buyer enters the product numbers of the desired products in an order form. They can then add all entered items to the basket with a single click. The prerequisite is that the customer has the corresponding product numbers, for example from a catalog.

Nothing else is required for this. No additional scripts are needed.

## Keep direct order entries during a session

The shop stores the products checked during a direct order that have not yet been added to the basket in the session. The cookie only contains the reference to the session. If `saveCountInSession` is enabled under `checkout.directOrder`, the shop also remembers the current number of input lines there.

If the direct order form is called up again later, the stored product data is automatically filled into the corresponding input fields. This avoids having to re-enter the product number and quantity if the shop visitor has not yet added their selection to the basket but has visited other pages of the shop first.

## Actions

The following actions are available:

### DirectOrderAdd

This action checks the entry of a line and adds the product to the order form list. It does not add anything to the basket. There is a separate step for that, see [Add items to the basket](#add-items-to-the-basket).

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{{ foreach $cProduct in range(0, $wsDirectOrder.currentLines - 1) }}
  {{ var $cActionDirectOrderAdd = $wsActions.create("DirectOrderAdd", tag=string($cProduct)) }}
{{ /foreach }}
```

### DirectOrderDelete

This action removes a line from the order form list. The basket is not affected.

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{{ foreach $cProduct in range(0, $wsDirectOrder.currentLines - 1) }}
  {{ var $cActionDirectOrderDelete = $wsActions.create("DirectOrderDelete", tag=string($cProduct)) }}
{{ /foreach }}
```

## Integration of the direct order

### Create template

Create a new file `directOrder.htm` in the `views/modules` directory.

In this file, several rows are generated for the respective order items.

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{{ extends "layouts/layout.htm" }}

{{ block content_main }}
	<div id="wsBasketWrapper">
		<h1>Direct order</h1>
		<table class="table">
			<tbody id="wsDirectOrderTableBody">
				{{ foreach $cProduct in range(0, $wsDirectOrder.currentLines - 1) }}
					{{ var $cActionDirectOrderAdd = $wsActions.create("DirectOrderAdd", tag=string($cProduct)) }}
					{{ var $cActionDirectOrderDelete = $wsActions.create("DirectOrderDelete", tag=string($cProduct)) }}
					<tr class="wsDirectOrderRow">
						<td class="wsTableCellMin">
							<svg role="img" xmlns="http://www.w3.org/2000/svg" width="80" height="80" viewBox="0 0 512 512"><g><path d="M379.453,123.562c0,0,7.232,270.164,17.48,336.787c-164.692,52.798-282.127,0-282.127,0c10.247-66.623,19.001-339.639,19.001-339.639" fill="none" stroke="#474747" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="15"/><path d="M131.033,193.628l-30.599,64.168c-46.962-7.004-88.077-48.676-88.077-48.676l99.61-164.692l81.112-19.349l10.59,29.084c25.215,35.473,78.085,34.885,102.505-1.141l8.858-27.944l82.999,19.349l99.61,164.692c0,0-41.115,41.672-88.077,48.676l-27.741-61.85" fill="none" stroke="#474747" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="15"/></g></svg>
						</td>
						<td>
							<div class="row">
								<div class="col-12 col-md">
									<form method="POST" action="{{= $wsViews.current.url() }}">
										<input type="hidden" name="wscsrf" value="{{= $cActionDirectOrderAdd.csrf }}">
										<input type="hidden" name="wstarget" value="{{= $wsViews.current.url() }}">

										<p>Please enter the item number</p>
										<div class="row">
											<div class="col col-sm-auto">
												<div class="form-floating">
													<input type="text" name="id" value="{{= $wsDirectOrder.items[$cProduct].id | ifNull('') }}" class="wsDirectOrderInputNumber form-control" placeholder="Item no.">
													<label>Item no.</label>
												</div>
											</div>
											<div class="col-auto">
												<button type="submit" name="wsact" value="{{= $cActionDirectOrderAdd.id }}" class="btn btn-primary"><svg role="img" xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" viewBox="0 0 16 16" class="align-text-bottom"><path d="M11.742 10.344a6.5 6.5 0 1 0-1.397 1.398h-.001c.03.04.062.078.098.115l3.85 3.85a1 1 0 0 0 1.415-1.414l-3.85-3.85a1.007 1.007 0 0 0-.115-.1zM12 6.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0z"></path></svg></button>
												<button type="submit" name="wsact" value="{{= $cActionDirectOrderDelete.id }}" class="btn btn-secondary">Delete</button>
											</div>
										</div>
										<label>Quantity</label>
										<input type="text" name="quantity" value="{{= $wsDirectOrder.items[$cProduct].quantity | ifNull('') }}">
										{{ foreach $item in $wsConfig.directOrder.itemNumberFields }}
											<div class="itemNumberFields"> 
												{{ if $item.type == "field" }}
													<input type="text" name="{{= $item.name }}">
												{{ elseif $item.type == "separator" }}
													{{= $item.sign }}
												{{ /if }}
											</div>
										{{ /foreach }}
									</form>
								</div>
							</div>
						</td>
					</tr>
				{{ /foreach }}
			</tbody>
		</table>
	</div>
{{ /block }}
```

### Error messages

The error messages can be placed either before or inside the form element. The `DirectOrderAdd` action does not report all errors on the same field:

* Errors relating to the quantity are found under `errorsByField.quantity`.
* Errors relating to the item number and the product are found under `errorsByField.id`.
* Errors of the item number fields from `itemNumberFields` are found under the name of the respective input field. This applies to `itemNumberCheckFailed` and to `missingId` for an empty required field.

In this example, error messages are displayed if the quantity of an item is entered incorrectly.

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{{ foreach $cProduct in range(0, $wsDirectOrder.currentLines - 1) }}
  {{ var $cActionDirectOrderAdd = $wsActions.create("DirectOrderAdd", tag=string($cProduct)) }}
  ...
  {{ if $cActionDirectOrderAdd.errorsByField.quantity }}
    <div class="alert alert-danger">
      Errors occurred:
      <ul>
        {{ foreach $err in $cActionDirectOrderAdd.errorsByField.quantity }}
          <li>{{ if $err.text }}{{= $err.text }}{{ else }}{{= $err.code }}{{ /if }}</li>
        {{ /foreach }}
      </ul>
    </div>
  {{ /if }}
{{ /foreach }}
```

In this example, error messages are displayed when adding an item. The second loop additionally outputs the errors of the item number fields. The type check is necessary because `itemNumberFields` also contains separators, and these have no `name`.

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{{ foreach $cProduct in range(0, $wsDirectOrder.currentLines - 1) }}
  {{ var $cActionDirectOrderAdd = $wsActions.create("DirectOrderAdd", tag=string($cProduct)) }}
  ...
  {{ if $cActionDirectOrderAdd.errorsByField.id }}
    <div class="alert alert-danger">
      Errors occurred:
      <ul>
        {{ foreach $err in $cActionDirectOrderAdd.errorsByField.id }}
          <li>{{ if $err.text }}{{= $err.text }}{{ else }}{{= $err.code }}{{ /if }}</li>
        {{ /foreach }}
      </ul>
    </div>
  {{ /if }}

  {{ foreach $field in $wsConfig.directOrder.itemNumberFields }}
    {{ if $field.type == "field" and $cActionDirectOrderAdd.errorsByField[$field.name] }}
      <div class="alert alert-danger">
        {{ foreach $err in $cActionDirectOrderAdd.errorsByField[$field.name] }}
          {{ if $err.text }}{{= $err.text }}{{ else }}{{= $err.code }}{{ /if }}
        {{ /foreach }}
      </div>
    {{ /if }}
  {{ /foreach }}
{{ /foreach }}
```

<Warning>
  The output per item number field supplements the output of `errorsByField.id`; it does not replace it. If the combined item number cannot be assigned to a product, the action reports `invalidId` on the `id` field. This also applies if the line does not contain an `id` input field at all.
</Warning>

### Add items to the basket

A separate form adds the entered items to the basket using the `BasketItemAdd` action. For each item, a hidden field named `multiProducts.<product number>.quantity` is submitted. If [insert codes](/en/frontend/funktionsubersicht/werbemittelkennzeichnung) are active, the field `multiProducts.<product number>.insert` is submitted as well. Without this step, an order is never created.

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{{ var $cActionBasketItemAdd = $wsActions.create("BasketItemAdd", tag="directorder") }}
<form method="post" action="{{= $wsViews.current.url() }}">
  <input type="hidden" name="wsact" value="{{= $cActionBasketItemAdd.id }}">
  <input type="hidden" name="wscsrf" value="{{= $cActionBasketItemAdd.csrf }}">
  <input type="hidden" name="wstarget" value="{{= $wsViews.viewUrl('basket.htm') }}">
  {{ foreach $item in $wsDirectOrder.items }}
    <input type="hidden" name="multiProducts.{{= $item.id }}.quantity" value="{{= $item.quantity }}">
    {{ if $item.insert }}
      <input type="hidden" name="multiProducts.{{= $item.id }}.insert" value="{{= $item.insert }}">
    {{ /if }}
  {{ /foreach }}
  <button type="submit"{{ if not $wsDirectOrder.items }} disabled{{ /if }}>Add to basket</button>
</form>
```

For more information, see [Transfer items to the basket](/en/frontend/referenz/module/wsdirectorder#transfer-items-to-the-basket) in the module reference.

### Link the page in the footer

You can integrate the link to the direct order in any template. It is:

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
<a href="{{= $wsViews.viewUrl('modules/directOrder.htm') }}">Direct order</a>
```


## Related topics

- [checkout - Order flow](/en/konfiguration/checkout-bestellablauf.md)
- [Insert codes](/en/frontend/funktionsubersicht/werbemittelkennzeichnung.md)
- [Practical examples - Vouchers](/en/frontend/praxisbeispiele/gutscheine.md)
- [$wsDirectOrder - Direct order](/en/frontend/referenz/module/wsdirectorder.md)
- [Practical examples - Linking with the payment provider](/en/frontend/praxisbeispiele/verknuepfung-zahlungsanbieter.md)


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