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

# Materials: BestPrice order module

> Complete sample template for the ordering function of the BestPrice order module, with the parameters of the price tier chart and all E-BP tags.

With the BestPrice order module, the price of a product drops the more customers pre-order it. This page contains a complete sample template for the ordering process and describes the parameters of the price tier chart.

Adapt the template to your shop design. All labels in it are sample texts.

Guide: BestPrice order module · Reference: E-BP tags

## Template requirements

The chart is a separate JavaScript module that needs two things in the template: a `canvas` element and two `script` blocks.

### The canvas element

The plugin is written in the Solid.js framework and draws the chart into a `canvas` element. You need to provide this element in the template.

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
<div id="canvasContainer" style="height: 250px">
  <canvas id="bulk"></canvas>
</div>
```

The module calculates the height and width of the `canvas` element itself, overwriting the values in the markup. The calculation is based on the style of the surrounding element. So give the container a height, otherwise the chart stays invisible.

### The two script blocks

The first block loads the module, the second configures and starts it.

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
<script src="/$WS/ws_sysdata/js/ws-best_price_modul_1.0.0.js"></script>
```

Reference: WEBSALE JavaScript library

## The sample template

The template covers all three steps of the ordering process and additionally shows a summary of all of the customer's reservations.

> [accordion] Sample template for the ordering process
>
> ```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
> <html>
> <head>
>   <!--Head Elements-->
> </head>
>
> <body>
>
> {WS-Include}
>
> <script src="/$WS/ws_sysdata/js/ws-best_price_modul_1.0.0.js"></script>
> <script type="text/javascript">
>
>
> function ws_AJAXloadProductStart(){}
> function ws_AJAXloadProductError(){}
> function ws_AJAXloadProductResponseSuccess(){}
> function ws_AJAXloadProductResponseError(){}
> function reloadinfos(value) {
> 	~WS-DisableAmpersandLinkEncoding~
> 	ws_AJAXloadTemplate("~PR-Link($PR-ProdIndex$)~&amount=" +value, '~WS-Charset~', ws_AJAXloadProductStart, ws_AJAXloadProductError, 'ws_AJAXloadProductResponseSuccess()', 'ws_AJAXloadProductResponseError()'); 
> 	~WS-EnableAmpersandLinkEncoding~
> 	return true;
> }
> </script>
>
> <h2>Bestpricemodul</h2>
>
> <div>Startet: ~E-BP_Begin~</div>
> <div>Endet: ~E-BP_End~</div>
> <div  style="margin-top: 50px">
>
>
> <!-- Prüfung auf diverse Situationen -->
> {E-BP_LOGGED-IN}<div>Ich bin gerade angemeldet</div>{/E-BP_LOGGED-IN}
> {!E-BP_LOGGED-IN}<div>Ich bin gerade <strong>NICHT</strong> angemeldet</div>{/!E-BP_LOGGED-IN}
> {E-BP_HAS-ADDRESS}<div>Ich habe eine Rechnungsadresse</div>{/E-BP_HAS-ADDRESS}
> {E-BP_HAS-DELIVERY-ADDRESS}<div>Ich habe eine Lieferadresse</div>{/E-BP_HAS-DELIVERY-ADDRESS}
> {E-BP_ACTIVE}<div>Diese Kampagne ist aktiv</div>{/E-BP_ACTIVE}
> {E-BP_BEFORE-BEGIN}<div>Diese Kampagne ist noch nicht aktiv</div>{/E-BP_BEFORE-BEGIN}
> {E-BP_FINISHED}<div>Diese Kampagne ist schon beendet</div>{/E-BP_FINISHED}
>
>
> {E-BP_Error}<div style="color:red">~E-BP_Error~</div>{/E-BP_Error}
>
> </div>
>
> <!-- Anzeige des Diagramms -->
> <div id="canvasContainer"  style="height: 250px">
> <canvas id="bulk"></canvas>
> </div>
>
> <script>
>     function runCanvas() {
>       var canvasElement = document.getElementById('bulk');
>       var newCanvas = newBulkBestPrice(canvasElement,{
>          barType:'horizontal',
>          progressWidth:20,
>          progressLineWidth:2,
>          progressLineColor:'black',
>          progressFillColor:'#6dfa36',
>          fontType:'normal 7pt Helvetica',
>          dataBar: [
>              ~DC-Counter1_reset~
>              {@E-BP_PriceList}
>
>              ~DC-Counter1_inc~
>              {!DC-Counter1(1)},
>              {/!DC-Counter1(1)}
>              {
>                 text:'~E-BP_PriceList-Price~ €',
>                 countStand: ~E-BP_PriceList-Amount~
>              }
>              {/@E-BP_PriceList}
>
>          ],
>          totalMount: ~E-BP_TotalAmount~
>       });
>     }    
>     // run on page load
>     runCanvas();
>     // run on resize of the window
>     window.addEventListener('resize',runCanvas);
> </script>
>
> <!-- Schritt 1: Auswahl der Variante und Menge -->
> {E-BP_CAMPAIGN}
> <h3>Kampagne - ~E-BP_BasicPrice_format~</h3>
> <form action="~FORM-Template(ws_product.htm)~" method="Post" name="product">
> <div>Aktueller Preis: {E-BP_CurrentPrice}~E-BP_CurrentPrice_Format~{/E-BP_CurrentPrice}{!E-BP_CurrentPrice}~PR-OrgPrice~ ~WS-CurrencySymbol~ {/!E-BP_CurrentPrice}</div>
> <div>Aktuell schon bestellte Produkte: ~E-BP_TotalAmount_Format~</div>
>
> <!-- Anzeige der Preisstaffel -->
> <div  style="margin-top: 50px">
> <div>Preisstaffel:</div>
> <div style="padding-left: 50px">
> 	{@E-BP_PriceList}
> 	<div>~E-BP_PriceList-Amount~ Stück: ~E-BP_PriceList-Price_Format~ ~WS-CurrencySymbol~</div>
> 	{/@E-BP_PriceList}
> </div>
> </div>
>
>
> <input type="hidden" name="wsbp_act" value="show_summary">
> <input type="hidden" name="pi" value="~PR-ProdIndex~">
> <div  style="margin-top: 50px">
>
>
>
> <input type="text" name="amount" value="{E-BP_Amount}~E-BP_Amount~{/E-BP_Amount}" onblur="reloadinfos(this.value)"/>
>
> {E-BP_ACTIVE}
> <input type="submit" value="Bestellen">
> {/E-BP_ACTIVE}
> </div>
>
> </form>
> {/E-BP_CAMPAIGN}
>
>
> <!-- Schritt 2: Auswahl der Lieferadresse und Bestätigung der Bestellung-->
> {E-BP_SUMMARY}
> <h3>Zusammenfassung</h3>
> <form action="~FORM-Template(ws_product.htm)~" method="Post" name="product">
> <!-- Wichtige Hiddenfelder -->
> <!-- Aktion  -->
> <input type="hidden" name="wsbp_act" value="preorder">
> <!-- Produktindex -->
> <input type="hidden" name="pi" value="~PR-ProdIndex~">
> <!-- Anzahl, die Bestellt werden soll -->
> <input type="hidden" name="amount" value="~E-BP_Amount~" />
> <!-- Eine OrderID um Doppelbestellungen zu vermeiden -->
> <input type="hidden" name="orderid" value="~E-BP_OrderId~">
>
>
> <!-- Anzeige der Rechnungsadresse -->
>
> <div style="margin-top: 50px">
> <div>
> <input type="radio" name="use_delivery_address" value="no" {!E-BP_HAS-DELIVERY-ADDRESS}checked{/!E-BP_HAS-DELIVERY-ADDRESS}> Liefere an Rechnungsadresse:
>
> </div>
> <!-- Anzeige von Informationen der Rechnungsadresse -->
> {E-BP_BillingAddress}
> <div>
> 	Vorname:
> 	~E-BP_Address-Firstname~
> </div>
> <div>
> 	Nachname :
> 	~E-BP_Address-Lastname~
> </div>
> {/E-BP_BillingAddress}
> </div>
>
> <!-- Anzeige von Informationen der Lieferadresse -->
> {E-BP_HAS-DELIVERY-ADDRESS}
> <div>
> <div>
> <input type="radio" name="use_delivery_address" value="yes" checked> Liefere an Lieferadresse:
>
> </div>
> {E-BP_DeliveryAddress}
> <div>
> 	Vorname:
> 	~E-BP_Address-Firstname~
> </div>
> <div>
> 	Nachname :
> 	~E-BP_Address-Lastname~
> </div>
> {/E-BP_DeliveryAddress}
> </div>
> {/E-BP_HAS-DELIVERY-ADDRESS}
>
>
> <div  style="margin-top: 50px">
> <div>
> Gewählte Menge: ~E-BP_Amount~
> </div>
>
> <!-- Pflicht: ohne das Feld confirm führt das Modul die Bestellung nicht aus -->
> <input type="hidden" name="confirm" value="yes">
> <input type="submit" value="Bestellen">
> </div>
>
> </form>
>
>
>
> {/E-BP_SUMMARY}
>
>
> <!-- Schritt 3: Bestellbestätigung -->
> {E-BP_CONFIRM}
> <h3>Bestätigung</h3>
>
>
> <p>Danke für die Bestellung!</p>
> <div>Aktueller Preis: {E-BP_CurrentPrice}~E-BP_CurrentPrice_format~{/E-BP_CurrentPrice}{!E-BP_CurrentPrice}~PR-OrgPrice~ ~WS-CurrencySymbol~ {/!E-BP_CurrentPrice}</div>
> <div>Aktuell schon bestellte Produkte: ~E-BP_TotalAmount~</div>
> <div>Ihre Bestellmenge: ~E-BP_Amount~</div>
> <p style="background-color:#00FF00">
> 	Sobald die Kampagne abgelaufen ist bekommen Sie eine E-Mail bezüglich der finalen Bestellung.
> 	Sie können auch in ihrem Kundenkonto alle laufenden Kampagnen, zu denen Sie Vorbestellungen getätigt haben, noch einmal ansehen.
> </p>
>
>
> {/E-BP_CONFIRM}
>
> <!-- Zusammenfassung aller Reservierungen von aktiven Kampagnen des Kunden -->
> <div id="zusammenfassung_bestprice">
> {ST-Ajax}<WS-Ajax-zusammenfassung_bestprice>{/ST-Ajax}  
> <table style="border-spacing: 10px;border-collapse: separate;margin-top: 50px;">
> <tr>
> 	<td>Listenpreis</td>
> 	<td>Bestellpreis</td>
> 	<td>Ersparnis</td>
> 	<td>Gesamtersparnis</td>
> </tr>
> <tr>
> 	<td>~E-BP_BasicPrice_format~</td>
> 	<td>~E-BP_CurrentPrice_format~</td>
> 	<td>~E-BP_CurrentSaving_format~</td>
> 	<td>~E-BP_TotalSaving_format~</td>
> </tr>
> <tr>
> 	<td> </td>
> 	<td> </td>
> 	<td>Preis:</td>
> 	<td>~E-BP_TotalPrice_format~</td>
> </tr>
> </table>
>
>
> <div style="margin-top: 50px;">
>
> <div style="font-weight:bold">Reservierungen</div>
>
> <table style="border-spacing: 10px;border-collapse: separate;">
> <tr style="font-weight: bold">
> 	<td>Produkt</td>
> 	<td>Menge</td>
> 	<td>Aktueller Stückpreis</td>
> 	<td>Aktueller Gesamtpreis</td>
> 	<td>Aktuelle Ersparnis (pro Stück)</td>
> 	<td>Aktuelle Gesamtersparnis</td>
> </tr>
>
> {@E-BP_Orders}
>
> {NO_PARSING}{PR-AdvData({/NO_PARSING}~E-BP_ProdIndex~{NO_PARSING})}{/NO_PARSING}
> <tr>
> 	<td>~E-BP_Name~</td>
> 	<td>~E-BP_Amount~</td>
> 	<td>
> 		{E-BP_CurrentPrice}~E-BP_CurrentPrice_format~{/E-BP_CurrentPrice}{!E-BP_CurrentPrice}~PR-OrgPrice~{/!E-BP_CurrentPrice}~WS-CurrencySymbol~
> 	</td>
> 	<td>~E-BP_TotalPrice_format~ ~WS-CurrencySymbol~</td>
> 	<td>~E-BP_CurrentSaving_format~ ~WS-CurrencySymbol~ </td>
> 	<td>~E-BP_TotalSaving_format~ ~WS-CurrencySymbol~</td>
> </tr>
> {NO_PARSING}{/PR-AdvData({/NO_PARSING}~E-BP_ProdIndex~{NO_PARSING})}{/NO_PARSING}
> {/@E-BP_Orders}
> </table>
>
> <div style="padding: 10px; padding-bottom: 0px; border-top: 1px solid black">
> 	Gesamtpreis aller Reservierungen: ~E-BP_CompletePrice_format~ ~WS-CurrencySymbol~</div>
> <div style="padding: 10px">
> 	Gesamtersparnis aller Reservierungen: ~E-BP_CompleteSaving_format~ ~WS-CurrencySymbol~
> </div>
>
>
> <div style="padding: 10px; padding-bottom: 0px; border-top: 1px solid black">
> 	Gesamtpreis aller Reservierungen + aktueller Reserviervorgang: ~E-BP_NewCompletePrice_format~ ~WS-CurrencySymbol~
> </div>
> <div style="padding: 10px">
> 	Gesamtersparnis aller Reservierungen + aktueller Reserviervorgang: ~E-BP_NewCompleteSaving_format~ ~WS-CurrencySymbol~
> </div>
>
> </div>
>
> {ST-Ajax}</WS-Ajax-zusammenfassung_bestprice>{/ST-Ajax}
> </div>
>
> {/WS-Include}
> </body>
> </html>
> ```

## The three steps of the ordering process

The module has three page states. You query which one currently applies with the three section tags of the same name. The form field `wsbp_act` advances to the next step.

| Step | Section tag | `wsbp_act` in the form | Purpose |
| - | - | - | - |
| 1 | `E-BP_CAMPAIGN` | `show_summary` | choose quantity |
| 2 | `E-BP_SUMMARY` | `preorder` | choose delivery address and confirm |
| 3 | `E-BP_CONFIRM` | not sent | show confirmation |

The form fields the module evaluates:

| Field | Meaning |
| - | - |
| `wsbp_act` | the desired action, see the table above |
| `pi` | the product index of the campaign product |
| `amount` | the pre-ordered quantity |
| `orderid` | an ID from `~E-BP_OrderId~` that prevents duplicate orders |
| `use_delivery_address` | `yes` delivers to the delivery address, `no` to the billing address |
| `confirm` | must be sent along in step 2, otherwise the module doesn't execute the order |

> [!NOTE]
>
> The quantity input in the first step has an `onblur` attached that reloads the page via `ws_AJAXloadTemplate`. Only this makes the chart and tier price update while the customer changes the quantity.
>
> The two tags `~WS-DisableAmpersandLinkEncoding~` and `~WS-EnableAmpersandLinkEncoding~` turn ampersand encoding in the link off and back on. Without them, the `&` in the appended `amount` parameter becomes `&amp;` and the call fails.

## Configuring the price tier chart

You pass the parameters as an object to `newBulkBestPrice`. Parameters with an asterisk are required, all others are optional.

| Name | Type | Belongs to | Description |
| - | - | - | - |
| `barType` | `horizontal` or `vertical` | root | Orientation of the chart bar. Default: `vertical` |
| `progressWidth` | Number | root | Width of the chart bar |
| `progressLineWidth` | Number | root | Line width of the chart bar. Default: 1 |
| `progressLineColor` | Color as name, hex, or RGB | root | Line color of the chart bar. Default: `black` |
| `progressFillColor` | Color as name, hex, or RGB | root | Background color of the chart bar. Default: `#007bff` |
| `fontType` | Style, size, and font | root | Default: `normal 7pt Helvetica` |
| `totalMount`\* | Number | root | current number of products already ordered |
| `dataBar`\* | Array | root | one or more objects, see below |
| `text`\* | String | `dataBar[Obj]` | the text in the chart |
| `countStand` | Number | `dataBar[Obj]` | position and display of the quantity with the corresponding price |

> [!WARNING]
>
> The previous documentation listed `ProgressLineWidth`, `ProgressLineColor`, and `TotalMount` with a capital first letter, while the example uses `progressLineWidth`, `progressLineColor`, and `totalMount`. JavaScript object keys are case-sensitive, so the names here have been aligned with the working example.
>
> The name `totalMount` is a typo in the module. It must still be written exactly like that.

> [!NOTE]
>
> In the `text` field, only the HTML entity for the euro sign can be substituted. For any other currency symbol, use the Unicode value, `€` in the example.

### The counter in the dataBar array

The array needs commas between the entries, but none before the first one. The example solves this with a counter:

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
~DC-Counter1_reset~
{@E-BP_PriceList}
   ~DC-Counter1_inc~
   {!DC-Counter1(1)},{/!DC-Counter1(1)}
   {
      text:'~E-BP_PriceList-Price~ €',
      countStand: ~E-BP_PriceList-Amount~
   }
{/@E-BP_PriceList}
```

`DC-Counter1_inc` increments on every iteration, and the negated block `{!DC-Counter1(1)}` inserts the comma everywhere except in the first iteration. You can use the same pattern for any other JSON structure in the template.

### The NO\_PARSING block in the reservation list

The reservation list contains a construct that looks confusing at first glance:

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{NO_PARSING}{PR-AdvData({/NO_PARSING}~E-BP_ProdIndex~{NO_PARSING})}{/NO_PARSING}
```

`PR-AdvData` expects a product index as a parameter, which here only comes from `~E-BP_ProdIndex~` at runtime. Without `NO_PARSING`, the parser would evaluate the half-finished tag immediately and fail. The `NO_PARSING` brackets protect the two halves so that only the assembled expression gets parsed.

## Other E-BP tags

The example uses only some of the available tags. The following don't appear in it and are added here.

| Tag | Usage |
| - | - |
| `E-BP_Info` | section and single tag for notice messages, structured like `E-BP_Error` |
| `E-BP_OrderDate` | time of the pre-order, with an optional format parameter |
| `E-BP_ProdNumber` | the product number of the campaign product |
| `E-BP_CampaignID` | the internal ID of the campaign |
| `E-BP_Warranty(…)` | section that checks whether one of the passed warranties applies |

In addition, `~E-BP_Begin~` and `~E-BP_End~` accept a format parameter, for example `~E-BP_Begin(%d.%m.%Y)~`. Without a parameter, they output the default format.

### The address fields

Within `E-BP_BillingAddress` and `E-BP_DeliveryAddress`, the example shows only first and last names. The complete address is available. This list is added here.

| Tag | Field |
| - | - |
| `E-BP_Address-Company` | Company |
| `E-BP_Address-Salutation` | Salutation |
| `E-BP_Address-SalutationCode` | Salutation as code |
| `E-BP_Address-Title` | Title |
| `E-BP_Address-TitleCode` | Title as code |
| `E-BP_Address-FirstName` | First name |
| `E-BP_Address-LastName` | Last name |
| `E-BP_Address-Street1` | Street |
| `E-BP_Address-Street2` | second street line |
| `E-BP_Address-Zip` | Postal code |
| `E-BP_Address-City` | City |
| `E-BP_Address-State` | State |
| `E-BP_Address-Country` | Country |
| `E-BP_Address-CountryISO` | Country as ISO code |
| `E-BP_Address-PostOfficeBox` | PO box |
| `E-BP_Address-PostCenter` | Post office branch |
| `E-BP_Address-Department` | Department |
| `E-BP_Address-Phone` | Phone |
| `E-BP_Address-BusinessPhone` | Business phone |
| `E-BP_Address-MobilePhone` | Mobile phone |
| `E-BP_Address-Fax` | Fax |
| `E-BP_Address-BusinessFax` | Business fax |
| `E-BP_Address-e-mail` | Email address |
| `E-BP_Address-DateOfBirth` | Date of birth |
| `E-BP_Address-TaxID` | VAT identification number |
| `E-BP_Address-Suffix1` to `E-BP_Address-Suffix10` | the ten additional fields |

## Pitfalls

Three things stand out when you rebuild the example. All three are added here because they aren't in the previous documentation.

> [!WARNING]
>
> **`E-BP_CompletePrice` doesn't work as a section.** The section check is stored in the module under the misspelled name `complteprice`. As single tags, `~E-BP_CompletePrice~` and `~E-BP_CompletePrice_format~` work normally; only the block `{E-BP_CompletePrice}…{/E-BP_CompletePrice}` has no effect. Instead, check `{E-BP_Orders}` to see whether any reservations exist.

> [!NOTE]
>
> `E-BP_NewCompletePrice` and `E-BP_NewCompleteSaving` exist only as single tags, not as sections. They return the sum of all previous reservations plus the current process.
>
> `E-BP_BillingAddress` and `E-BP_DeliveryAddress` are pure switches to the respective address record. They are always true, and a negated block `{!E-BP_BillingAddress}` has no effect. To check whether an address exists at all, use `E-BP_HAS-ADDRESS` or `E-BP_HAS-DELIVERY-ADDRESS`.

> [!NOTE]
>
> The module doesn't care about the capitalization of tags. `~E-BP_CurrentPrice_Format~` and `~E-BP_CurrentPrice_format~` are the same tag, which is why the inconsistent spelling in the example has no consequences.

## Campaign states

Three section tags query the status of the campaign. They are mutually exclusive.

| Tag | Applies when |
| - | - |
| `E-BP_BEFORE-BEGIN` | the campaign has been created but hasn't started yet |
| `E-BP_ACTIVE` | the campaign is running. Only then does the module accept pre-orders |
| `E-BP_FINISHED` | the campaign has ended or has already been converted into orders |

In the example, `E-BP_ACTIVE` wraps the order button. Keep it that way, otherwise after the campaign ends the customer sees a button that leads nowhere.

> [!NOTE]
>
> Compared with the previous documentation, fourteen places have been corrected: the three parameter names `progressLineWidth`, `progressLineColor`, and `totalMount` in the configuration table, two typos in sample texts, an unclosed `p` element, a superfluous table end tag, two `p` elements that contained block elements, the `orderid` field that was a visible input field, two unformatted price tags in the display, a comment that described a checkbox where there is a hidden field, and the English subheading "Summary".


## Related topics

- [E-BP- (BestPrice order)](https://dokumentation.websale.de/en/v8s/referenz/tags/e-bp.md)
- [Prices, taxes, and currency](https://dokumentation.websale.de/en/v8s/referenz/konfigurationen/shopkonfiguration-shop-config/preise-steuern-und-waehrung.md)
- [Emails](https://dokumentation.websale.de/en/v8s/referenz/konfigurationen/shopkonfiguration-shop-config/e-mails.md)
- [Materials: FINDOLOGIC files](https://dokumentation.websale.de/en/v8s/materialien/materialien-findologic-dateien.md)
- [Materials: subscription module](https://dokumentation.websale.de/en/v8s/materialien/materialien-abo-modul.md)


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