> ## 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 - Online-Bestellschein

> Online-Bestellschein für die Direktbestellung umsetzen: Produkte per Produktnummer erfassen, in den Warenkorb übernehmen und Eingaben über die Session halten.

Bei der Direktbestellung gibt der Käufer die Produktnummern der gewünschten Produkte in einen Bestellschein ein. Anschließend kann er alle erfassten Positionen mit einem Klick in den Warenkorb übernehmen. Voraussetzung hierfür ist, dass dem Kunden die entsprechenden Produktnummern vorliegen, beispielsweise anhand eines Katalogs.

Dafür ist nichts weiter erforderlich. Zusätzliche Skripte sind nicht nötig.

## Eingaben der Direktbestellung während einer Session halten

Der Shop speichert die bei einer Direktbestellung geprüften Produkte, die noch nicht in den Warenkorb gelegt wurden, in der Session. Im Cookie steht nur der Verweis auf die Session. Wenn `saveCountInSession` unter `checkout.directOrder` aktiviert ist, merkt sich der Shop dort auch die aktuelle Zahl der Eingabezeilen.

Wenn das Formular zur Direktbestellung später erneut aufgerufen wird, werden die gespeicherten Produktdaten automatisch in die entsprechenden Eingabefelder übernommen. So entfällt die erneute Eingabe von Produktnummer und Menge, wenn der Shopbesucher seine Auswahl noch nicht in den Warenkorb gelegt hat, sondern zunächst andere Seiten des Shops besucht hat.

## Aktionen

Folgende Aktionen stehen zur Verfügung:

### DirectOrderAdd

Diese Aktion prüft die Eingabe einer Zeile und trägt das Produkt in die Bestellschein-Liste ein. In den Warenkorb legt sie nichts, dafür gibt es einen eigenen Schritt, siehe [Positionen in den Warenkorb übernehmen](#positionen-in-den-warenkorb-übernehmen).

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

Diese Aktion entfernt eine Zeile aus der Bestellschein-Liste. Der Warenkorb bleibt davon unberührt.

```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 der Direktbestellung

### Template erstellen

Erstellen Sie im Verzeichnis `views/modules` eine neue Datei `directOrder.htm`.

In dieser Datei werden mehrere Reihen für die jeweiligen Bestellpositionen erzeugt.

```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>Direktbestellung</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>Bitte Artikelnummer eingeben</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="Art.-Nr.">
													<label>Art.-Nr.</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">Löschen</button>
											</div>
										</div>
										<label>Menge</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 }}
```

### Fehlermeldungen

Die Fehlermeldungen können entweder vor oder innerhalb des Form-Elements platziert werden. Die Aktion `DirectOrderAdd` meldet nicht alle Fehler am selben Feld:

* Fehler zur Menge stehen unter `errorsByField.quantity`.
* Fehler zur Artikelnummer und zum Produkt stehen unter `errorsByField.id`.
* Fehler der Artikelnummern-Felder aus `itemNumberFields` stehen unter dem Namen des jeweiligen Eingabefeldes. Das betrifft `itemNumberCheckFailed` und `missingId` bei einem leeren Pflichtfeld.

In diesem Beispiel werden Fehlermeldungen angezeigt, wenn die Menge eines Artikels falsch eingegeben wird.

```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">
      Es sind Fehler aufgetreten:
      <ul>
        {{ foreach $err in $cActionDirectOrderAdd.errorsByField.quantity }}
          <li>{{ if $err.text }}{{= $err.text }}{{ else }}{{= $err.code }}{{ /if }}</li>
        {{ /foreach }}
      </ul>
    </div>
  {{ /if }}
{{ /foreach }}
```

In diesem Beispiel werden Fehlermeldungen beim Hinzufügen eines Artikels angezeigt. Die zweite Schleife gibt zusätzlich die Fehler der Artikelnummern-Felder aus. Die Typprüfung ist nötig, weil `itemNumberFields` auch Trennzeichen enthält und diese keinen `name` haben.

```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">
      Es sind Fehler aufgetreten:
      <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>
  Die Ausgabe je Artikelnummern-Feld ergänzt die Ausgabe von `errorsByField.id`, sie ersetzt diese nicht. Wenn sich die zusammengesetzte Artikelnummer keinem Produkt zuordnen lässt, meldet die Aktion `invalidId` am Feld `id`. Das gilt auch dann, wenn die Zeile gar kein Eingabefeld `id` enthält.
</Warning>

### Positionen in den Warenkorb übernehmen

Ein eigenes Formular legt die erfassten Positionen mit der Aktion `BasketItemAdd` in den Warenkorb. Dabei wird pro Position ein verstecktes Feld mit dem Namen `multiProducts.<Produktnummer>.quantity` übermittelt. Bei aktiver [Werbemittelkennzeichnung](/frontend/funktionsubersicht/werbemittelkennzeichnung) wird zusätzlich das Feld `multiProducts.<Produktnummer>.insert` übermittelt. Ohne diesen Schritt entsteht nie eine Bestellung.

```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 }}>In den Warenkorb</button>
</form>
```

Mehr dazu steht in der Modulreferenz unter [Positionen in den Warenkorb übernehmen](/frontend/referenz/module/wsdirectorder#positionen-in-den-warenkorb-übernehmen).

### Seite im Footer verlinken

Sie können in jedem Template den Link zur Direktbestellung integrieren. Dieser lautet:

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


## Related topics

- [$wsDirectOrder - Direktbestellung](/frontend/referenz/module/wsdirectorder.md)
- [Validierungs- und Prüfservices](/konfiguration/validierungs-und-prufservices.md)
- [checkout - Bestellablauf](/konfiguration/checkout-bestellablauf.md)
- [Praxisbeispiele - Gutscheine](/frontend/praxisbeispiele/gutscheine.md)
- [Praxisbeispiele - Testmodus](/frontend/praxisbeispiele/praxisbeispiele-testmodus.md)


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