---
title: Kasse (POS)
description: Kassensystem für Veranstaltungen — Produkte, Stände, Kassierer, Verkauf am Terminal, Erstattungen, Kassensturz und Belegdruck.
navIcon: "🧾"
---

import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"
import { Icon } from "@/components/ui/icon"

Das Kassenmodul macht aus CrispyCMS ein vollwertiges Kassensystem für Veranstaltungen — Festivals, Vereinsfeste, Märkte. Es ist bewusst für den Einsatz auf einem Gelände ohne verlässliche Internetverbindung ausgelegt: Die Kassen sprechen mit einem Server im lokalen Netz, nicht mit der Cloud.

<Alert variant="destructive">
<Icon name="alert-circle" />
<AlertTitle>Die Installation allein macht Sie nicht kassenrechtlich konform</AlertTitle>
<AlertDescription>
Dieses Modul kann eine zertifizierte technische Sicherheitseinrichtung (TSE) anbinden und bereitet alle dafür nötigen Daten korrekt auf. Die Meldung Ihrer Kassen an das Finanzamt nach § 146a AO, die Verfahrensdokumentation und der DSFinV-K-Export für die Betriebsprüfung bleiben jedoch Ihre Aufgabe. Die Seite **Kasse → Kassensicherheit** listet auf, was noch offen ist.
</AlertDescription>
</Alert>

---

## In fünf Schritten zur ersten Kasse

1. **Veranstaltung anlegen und aktivieren** (*Kasse → Veranstaltungen*). Ohne aktive Veranstaltung kann keine Kasse verkaufen. Das **Standort-Kürzel** wird jeder Belegnummer vorangestellt und lässt sich nach dem ersten Beleg nicht mehr ändern.
2. **Steuersätze und Produkte pflegen** (*Kasse → Steuersätze*, *→ Produkte*). Preise werden immer **brutto** eingegeben.
3. **Stände anlegen** (*Kasse → Stände*) — je Theke, Grill oder Merch-Zelt einen.
4. **Kassierer anlegen** (*Kasse → Kassierer*). Jede Person bekommt automatisch eine vierstellige PIN. Diese Liste können Sie ausdrucken und beim Aufbau verteilen.
5. **Terminal öffnen** (*Kasse → Kasse öffnen*) — auf jedem Tablet einmalig Veranstaltung und Stand auswählen, danach mit PIN anmelden und die Schicht mit dem vorhandenen Wechselgeld öffnen.

### Zum Ausprobieren: Beispieldaten

Für eine Demo, eine Schulung oder eine frische Entwicklungsumgebung füllt ein Befehl die Kasse mit einem vollständigen, plausiblen Festival-Setup — Steuersätze, Zahlungsarten, Kategorien, rund zwanzig Produkte, eine aktive Veranstaltung, vier Stände mit passendem Sortiment und fünf Kassierer:

```bash
crisp crispy:pos:seed
crisp crispy:pos:seed --event="Sommerfest 2026" --site-code=SOF --wristbands="Armband-[1-500]"
```

Die PINs der angelegten Kassierer werden **einmalig** am Ende ausgegeben — an dieser Stelle kopieren, sie erscheinen später nirgends wieder.

Die Stände bekommen jeweils ein passendes Sortiment: Der Bierstand führt Getränke und Pfand, der Essensstand Speisen, Heißgetränke und Pfand, Merchandise nur Merchandise. Der Infopoint bleibt bewusst ohne Einschränkung — leeres Sortiment bedeutet *gesamtes Sortiment* — und ist der einzige Stand, an dem Armbänder aufgeladen werden dürfen.

Der Befehl ist wiederholbar: Vorhandenes wird erkannt und übersprungen, nichts wird überschrieben oder gelöscht. Einzige Ausnahme ist ein bereits vorhandener Stand **ohne** Sortiment — dort wird das Beispielsortiment nachgetragen. Ein Stand, dessen Sortiment jemand konfiguriert hat, bleibt unangetastet.

Zum Aufräumen gibt es die Gegenrichtung:

```bash
crisp crispy:pos:purge                   # löscht alle Kassendaten
crisp crispy:pos:purge --keep-catalogue  # behält Produkte, Kategorien, Steuersätze, Zahlungsarten
```

Der Befehl zeigt vorher an, wie viele Zeilen je Tabelle betroffen sind, und fragt nach (`--yes` überspringt die Rückfrage).

<Alert variant="destructive">
<Icon name="alert-circle" />
<AlertTitle>Beide Befehle laufen ausschließlich in einer Entwicklungsumgebung</AlertTitle>
<AlertDescription>
`crispy:pos:seed` und `crispy:pos:purge` verweigern den Dienst, sobald die Umgebung nicht `development` ist — auch auf Staging, denn Staging-Datenbanken werden häufig aus der Produktion zurückgespielt. Es gibt bewusst **keinen** Schalter, um das zu übergehen: Gebuchte Belege sind nach §146a AO unveränderbar, und ein Befehl, der sie löscht, darf in einer echten Installation nicht ausführbar sein.
</AlertDescription>
</Alert>

<Alert variant="destructive">
<Icon name="alert-circle" />
<AlertTitle>Beispieldaten gehören nicht in den Echtbetrieb</AlertTitle>
<AlertDescription>
Die angelegten Produkte und Kassierer sind Demodaten. Landen sie in einer echten Veranstaltung, stehen sie auf echten Belegen und in der TSE-Signatur — und Belege lassen sich nicht nachträglich löschen.
</AlertDescription>
</Alert>

---

## Mehrere Veranstaltungen gleichzeitig

Es können mehrere Veranstaltungen gleichzeitig aktiv sein. Jede Kasse wird beim ersten Start einer Veranstaltung und einem Stand zugeordnet und bucht danach ausschließlich dorthin. Läuft nur eine Veranstaltung, entfällt die Auswahl.

Entscheidend ist das **Standort-Kürzel**:

| Kürzel | Ergebnis |
|---|---|
| **Gleiches Kürzel** für beide Veranstaltungen | Eine gemeinsame, lückenlose Belegnummernfolge — passend für zwei Veranstaltungen am selben Ort. |
| **Unterschiedliche Kürzel** | Getrennte Belegnummernfolgen, je Standort eine. |

Bei der Standauswahl führt **Andere Veranstaltung wählen** zurück zur Veranstaltungsliste — nötig, wenn versehentlich die falsche gewählt wurde oder für die gewählte noch keine Stände angelegt sind. Läuft nur eine Veranstaltung, wird auch diese eine angezeigt und als *aktuell* markiert; die Liste beantwortet damit die Frage, auf welche Veranstaltung die Kasse gebucht ist. **Abbrechen** führt zurück, ohne etwas zu ändern.

Ist die gespeicherte Veranstaltung inzwischen abgeschlossen oder gelöscht, verwirft die Kasse sie beim nächsten Start von selbst und fragt neu. Über *Standzuordnung zurücksetzen* auf dem Anmeldebildschirm lässt sich ein Tablet vollständig neu zuordnen.

<Alert>
<Icon name="info" />
<AlertTitle>Die Kasse kommt ohne Browser-Navigation aus</AlertTitle>
<AlertDescription>
Sie ist dafür gedacht, als App (PWA) im Vollbild zu laufen — ohne Adressleiste und ohne Zurück-Schaltfläche. Jeder Bildschirm hat deshalb einen eigenen Weg zurück: Veranstaltungs- und Standauswahl, Anmeldung, Schichtbeginn und jeder Dialog. Wenn Ihnen irgendwo eine Rückkehrmöglichkeit fehlt, ist das ein Fehler und kein beabsichtigtes Verhalten.

Aus demselben Grund trägt **jede Schaltfläche eine sichtbare Beschriftung** und es gibt keine Sprechblasen beim Überfahren mit der Maus: An einem Tablet gibt es keinen Mauszeiger, mit dem man etwas „überfahren" könnte. Wo eine Erklärung zu lang für eine Beschriftung ist — etwa beim TSE-Hinweis — lässt sie sich antippen. Auf schmalen Geräten im Hochformat entfallen die Beschriftungen zugunsten des Produktrasters; die Symbole bleiben.
</AlertDescription>
</Alert>

In der Verwaltung zeigen alle veranstaltungsbezogenen Seiten — Stände, Kassierer, Schichten, Transaktionen, Berichte, Armbänder, Aufladungen, Drucker und Kassensicherheit — oben eine **Auswahl der Veranstaltung**, sobald mehr als eine existiert. Sie bestimmt, worauf sich Listen, Filter und Exporte dieser Seite beziehen. Bei nur einer Veranstaltung entfällt die Auswahl.

---

## Kassierer auf Stände beschränken

Unter *Kasse → Kassierer* lässt sich pro Person festlegen, an welchen Ständen sie sich anmelden darf. Ohne Auswahl gilt: **alle Stände**.

Meldet sich eine nicht freigegebene Person an, wird die Anmeldung mit einem entsprechenden Hinweis abgelehnt. Ein solcher Versuch zählt **nicht** als Fehlversuch für die PIN-Sperre.

### Kassierer deaktivieren und wieder aktivieren

In der Liste unter *Kasse → Kassierer* schaltet dieselbe Schaltfläche in beide Richtungen: **Deaktivieren** stoppt die Anmeldung sofort, **Aktivieren** stellt sie wieder her. Wer am Freitag nach Hause geht, steht am Samstag wieder hinter der Theke.

Kassierer werden nie gelöscht, sondern nur deaktiviert — bereits gebuchte Verkäufe bleiben dieser Person zugeordnet, und ein gelöschter Datensatz würde diese Zuordnung zerreißen.

Die **PIN bleibt beim Deaktivieren erhalten** und gilt nach dem Aktivieren unverändert weiter. Eine Kollision ist dabei ausgeschlossen: PINs sind innerhalb einer Veranstaltung über aktive *und* deaktivierte Kassierer hinweg eindeutig, sodass die freigewordene PIN zwischenzeitlich an niemanden vergeben werden konnte. Wenn die PIN inzwischen jemand gesehen haben könnte, erzeugt *Neue PIN* eine frische.

### Wann die PIN erneut verlangt wird

Ein Tablet steht selten still und wird selten von einer einzigen Person bedient. Die Anmeldung endet deshalb in zwei Fällen von selbst:

- **Beim Neuladen der Seite.** Immer und ohne Ausnahme. Wer die Kasse neu lädt — oder ein Gerät in die Hand nimmt, das jemand anders offen gelassen hat — muss sich anmelden, bevor der nächste Verkauf auf einen Namen gebucht wird.
- **Nach Inaktivität.** Voreingestellt sind 15 Minuten, einstellbar unter *Einstellungen → Kasse* (`Automatische Abmeldung nach`). Ein Zähler in der Kopfzeile zeigt die verbleibende Zeit und färbt sich in der letzten Minute rot. Jede Bedienung — Tippen, Scannen, Tastatur — setzt ihn zurück. Der Wert `0` schaltet die automatische Abmeldung ab.

Der **Warenkorb bleibt in beiden Fällen erhalten** und steht nach der nächsten Anmeldung wieder bereit. Ein halb erfasster Vorgang geht also nicht verloren, nur weil jemand ein Fass wechseln musste.

---

## Der Verkaufsvorgang

Das Terminal ist eine Vollbildansicht ohne die übrige Verwaltungsoberfläche. Links das farbige Produktraster, rechts der Warenkorb.

- **Produkt antippen** legt es in den Warenkorb; nochmaliges Antippen erhöht die Menge.
- **Scannen** funktioniert ohne vorherigen Klick in ein Feld — der Scanner wird an seiner Tippgeschwindigkeit erkannt. Ein Mensch kann so schnell nicht tippen, deshalb löst normales Tippen niemals versehentlich einen Scan aus. Ein Scan wird gegen Produkte, Coupons **und** Armbänder zugleich geprüft; passt er auf genau einen Eintrag, wird dieser sofort angewendet.
- **Suchen (F3)** findet Produkte über Name, Artikelnummer oder Barcode.
- **Parken (F4)** legt einen Warenkorb beiseite, wenn jemand sein Portemonnaie holen geht. Geparkte Vorgänge sind an allen Kassen desselben Standes abrufbar.
- **Coupon (F6)** löst einen Rabattcode ein — siehe *Rabatte & Coupons*.
- **Guthaben (F7)** fragt ein Armband oder einen Coupon rein lesend ab — siehe *Guthaben abfragen*.
- **Bar** öffnet den Ziffernblock mit Schnellwahltasten; das Rückgeld erscheint groß.
- **Armband** öffnet ein Eingabefeld. Leser und Tastatur sind dort gleichzeitig aktiv: Ein funktionierender Leser füllt das Feld selbst, andernfalls tragen Sie die Nummer von Hand ein und bestätigen mit Enter.

<Alert>
<Icon name="info" />
<AlertTitle>Mehrdeutige Codes fragt die Kasse nach</AlertTitle>
<AlertDescription>
Passt ein gescannter Code auf **mehr als einen** Eintrag — etwa ein Barcode, der zugleich ein Coupon-Code ist — erscheint eine Auswahl mit allen Treffern samt Art, Bezeichnung und Zustand. Die Kasse rät nicht: Nur wer den Artikel in der Hand hält, kann die Frage beantworten.

Wurde der Leser dagegen bewusst für einen bestimmten Zweck geöffnet — Zahlung mit Armband, Aufladen, Guthabenabfrage — gilt diese Absicht. Dort führt ein Scan immer genau das aus, wonach der Bildschirm gefragt hat.
</AlertDescription>
</Alert>

<Alert>
<Icon name="info" />
<AlertTitle>Der Warenkorb übersteht einen Neustart</AlertTitle>
<AlertDescription>
Stürzt das Tablet ab oder wird die Seite versehentlich neu geladen, ist der halbfertige Warenkorb noch da. Nach dem Schließen des Browsers ist er verworfen — ein Warenkorb von gestern wäre mit veralteten Preisen gefährlicher als hilfreich.
</AlertDescription>
</Alert>

---

## Änderungen während des Betriebs

Preise, neue Produkte, Zahlungsarten, das Sortiment eines Standes oder eine Einstellung lassen sich mitten in der Veranstaltung ändern. Laufende Kassen holen sich die Änderung **von selbst**, ohne dass jemand ein Tablet neu laden muss — was ohnehin eine erneute PIN-Eingabe bedeuten würde.

Die Kasse fragt dazu etwa alle 45 Sekunden eine einzige Versionsnummer ab und lädt den Katalog nur dann neu, wenn sich diese geändert hat. Nach jedem abgeschlossenen Verkauf wird zusätzlich geprüft. Das hält die Last auch bei zwanzig Tablets in einem Gelände-WLAN vernachlässigbar.

Automatisch übernommen werden:

- Produkte: Preise, Namen, Farben, neue und entfernte Artikel
- Kategorien und Zahlungsarten
- Das Sortiment des jeweiligen Standes
- Währung, Rasterspalten, Ton, Armband- und Freipreis-Schalter

Nicht automatisch übernommen wird die **Zeit bis zur automatischen Abmeldung** — sie würde sonst den laufenden Zähler unter der Kassiererin neu starten. Sie greift ab der nächsten Anmeldung.

<Alert>
<Icon name="info" />
<AlertTitle>Ein offener Warenkorb behält seine Preise</AlertTitle>
<AlertDescription>
Ändert sich der Preis eines Artikels, der bereits im Warenkorb liegt, bleibt die angezeigte Summe stehen — sie soll sich nicht verändern, während die Kundschaft daraufschaut. Die betroffene Zeile wird markiert und ein Hinweis eingeblendet. **Gebucht wird immer der aktuelle Preis**, denn die Preise kommen beim Abschluss ohnehin aus dem Katalog und nie aus dem Browser. Wer den alten Preis noch gewähren will, storniert die Zeile und erfasst sie neu.
</AlertDescription>
</Alert>

Bestandsänderungen aus Verkäufen lösen bewusst **keine** Aktualisierung aus: Bestand ändert sich bei jedem einzelnen Verkauf, und zwanzig Tablets nach jedem Bier neu laden zu lassen wäre genau die Last, die dieses Verfahren vermeidet. Der eigene Bestand wird nach jedem eigenen Verkauf ohnehin frisch geholt.

---

## Rabatte & Coupons

Die Kasse kennt bewusst **keinen frei eingebbaren Rabatt**. Rabatt entsteht ausschließlich durch einen Coupon, den die Kundschaft vorlegt — das ist die übliche Kontrolle gegen Schwund, weil ein Rabattfeld an der Theke von niemandem geprüft werden kann.

### Aktion und Codes

Zwei Ebenen, unter *Kasse → Coupons*:

- Eine **Aktion** trägt die Regeln: wie viel Rabatt, worauf, wie oft, bis wann.
- Ein **Code** gehört zu einer Aktion. Eine gedruckte Charge sind tausende Codes zu einer Regel.

Die Trennung erlaubt es, eine Regel nach dem Druck noch zu korrigieren, ohne die Bons neu zu drucken.

Codes werden als Serie erzeugt — dieselbe Klammer-Schreibweise wie bei den Armbändern, etwa `SOMMER-[0001-2000]`. Ein einzelner Code ohne Klammern (`HELFER20`) ist ebenso möglich. Codes sind **aktionsübergreifend eindeutig**, weil ein Scan nur den Code liefert und dieser genau einen Coupon treffen muss. Die Groß-/Kleinschreibung spielt beim Einlösen keine Rolle.

### Was eine Aktion festlegt

| Einstellung | Bedeutung |
|---|---|
| **Rabattart** | Prozentual oder Festbetrag. |
| **Gilt für** | Kategorien und/oder einzelne Produkte. Beides leer = gesamtes Sortiment. Kategorien und Produkte werden **addiert**, nicht geschnitten. |
| **Einlösung** | *Einzelcode* (jeder Code einmal — gedruckte Bons) oder *Sammelcode* (ein Code mehrfach, wahlweise mit Obergrenze). |
| **Überhang** | Nur bei Festbetrag: deckeln und verfallen lassen, Restwert auf dem Coupon behalten, oder die Einlösung ablehnen. |
| **Mindestumsatz** | Gemessen an den **passenden** Artikeln, nicht am ganzen Warenkorb — „10 € Mindestumsatz auf Merchandise" lässt sich nicht durch Bier erfüllen. |
| **Kombinierbar** | Ob mehrere Coupons auf einen Bon dürfen. |
| **Gültigkeitszeitraum** | Optional, sonst unbegrenzt. |
| **Veranstaltung** | Leer = gilt überall. Sonst nur bei der gewählten Veranstaltung. |

### An der Kasse

Ein Coupon wird einfach **gescannt** — passt der Code zu keinem Produkt, sucht die Kasse ihn als Coupon. Für einen unleserlichen Bon oder einen Code wie `HELFER20` gibt es die Schaltfläche **Coupon** (F6).

Jeder Coupon steht als eigene Zeile im Warenkorb und lässt sich einzeln wieder entfernen. Ändert sich der Warenkorb, wird jeder Coupon neu bewertet: fällt der letzte passende Artikel weg, verschwindet der Coupon mit einem Hinweis.

Werden mehrere Coupons kombiniert, wirken sie **nacheinander**: zwei Mal 50 % auf denselben Artikel ergeben 75 % Nachlass, nicht 100 %. Der Rabatt kann den Warenkorb nie unter null drücken.

<Alert>
<Icon name="info" />
<AlertTitle>Der Rabatt wird auf die betroffenen Zeilen verteilt</AlertTitle>
<AlertDescription>
Ein Coupon auf „alle Getränke" reduziert nur die Getränkezeilen — anteilig und auf den Cent genau. Das ist nötig, damit die **Umsatzsteuer je Satz** stimmt: ein Rabatt auf 19 %-Ware darf die 7 %-Position nicht mit vermindern. Nicht betroffene Zeilen bleiben unverändert.
</AlertDescription>
</Alert>

### Wenn der Rabatt alles abdeckt

Ein Coupon über 100 % — etwa ein Mitarbeiterrabatt — bringt die Summe auf 0,00 €. Statt der Zahlungsarten erscheint dann eine einzelne Schaltfläche **Ohne Zahlung abschließen**.

Der Vorgang wird ganz normal gebucht: Ware hat den Stand verlassen, der Bestand wird abgebucht, der Beleg wird erstellt und signiert. Er trägt **keine Zahlungszeile**, denn es hat kein Geld den Besitzer gewechselt — eine Zeile über 0,00 € würde im Z-Bericht eine Zahlung vortäuschen, die es nie gab. Auf dem Bon steht stattdessen *„Keine Zahlung — vollständig durch Rabatt gedeckt"*.

### Auf dem Beleg und bei Erstattungen

Jeder Coupon steht mit Name, Code und Betrag **einzeln** auf dem Bon und in der Belegansicht — bei einer Reklamation geht es immer um einen bestimmten Coupon. Die Angaben sind auf dem Beleg fest gespeichert und bleiben auch dann lesbar, wenn die Aktion später gelöscht wird.

Bei einer **vollständigen** Erstattung wird der Coupon wieder freigegeben und ist erneut einlösbar. Bei einer Teilerstattung nicht: die Kundschaft behält Ware, die der Rabatt mit bezahlt hat.

<Alert variant="destructive">
<Icon name="alert-circle" />
<AlertTitle>„Restwert bleibt auf dem Coupon" ist kein Rabatt mehr</AlertTitle>
<AlertDescription>
Ein Coupon, der Guthaben über den Einkauf hinaus behält, verhält sich wie ein **Gutschein** — umsatzsteuerlich ist das etwas anderes als eine Preisminderung. Diese Einstellung vor dem Einsatz mit Ihrer Steuerberatung klären. Die beiden anderen Überhang-Varianten sind unproblematisch.
</AlertDescription>
</Alert>

---

## Erstattungen

Erstattungen werden nicht am Terminal, sondern unter *Kasse → Transaktionen* auf dem betreffenden Beleg ausgelöst und brauchen die Berechtigung `pos.refund` — bewusst getrennt von „darf verkaufen".

Dabei gilt:

- Es entsteht **immer ein neuer Beleg** mit eigener Nummer. Der Originalbeleg bleibt unverändert.
- Es kann **positionsweise** und teilweise erstattet werden.
- Die Erstattung geht auf die **ursprünglichen Zahlungsarten** zurück. Zahlungen per Armband werden immer dem Armband gutgeschrieben — sonst wäre das Armband ein Weg, Guthaben in Bargeld zu tauschen.
- Ein **Grund ist Pflicht**.

---

## Einlagen, Entnahmen und Manager-Freigaben

Bargeld bewegt sich auch außerhalb von Verkäufen: Wechselgeld wird geholt, Überschüsse werden zwischendurch abgeschöpft. Solche Bewegungen erfasst die Kasse als **Einlage** (Geld rein) oder **Entnahme** (Geld raus). Beide fließen in den erwarteten Kassenbestand ein — genau deshalb müssen sie erfasst werden, sonst taucht die Differenz beim Kassensturz auf und niemand weiß mehr warum.

An der Kasse führt die Schaltfläche **Manager** dorthin. Betrag und **Grund sind Pflicht**: eine Bewegung ohne Begründung ist beim späteren Lesen des Z-Berichts nicht von einem Fehlbetrag zu unterscheiden.

### Manager

Ein Kassierer kann unter *Kasse → Kassierer* zum **Manager** gemacht werden. Manager geben mit ihrer **ganz normalen Kassierer-PIN** Aktionen frei, die ein Kassierer nicht allein durchführen darf:

| Aktion | Warum freigabepflichtig |
|---|---|
| **Entnahme** | Der klassische Weg, auf dem Bargeld ohne Gegenbuchung verschwindet. |
| **Einlage** | Spiegelbildlich: eine erfundene Einlage kaschiert einen Fehlbetrag, bevor der Kassensturz ihn findet. |
| **Erstattung** | Geld geht über die Theke zurück. |
| **Schublade öffnen** | Die Hardware protokolliert davon nichts — die Freigabe *ist* der Nachweis. |

**Jeder** Manager der Veranstaltung kann an **jeder** Kasse freigeben, auch an Ständen, für die er selbst nicht freigeschaltet ist. Das ist Absicht: Eine Schichtleitung läuft über das Gelände, und eine Freigabe, die den richtigen Stand voraussetzt, findet um 1 Uhr nachts nicht statt — die Entnahme bleibt dann unerfasst, also genau das, was verhindert werden soll.

### Das Manager-Panel

Ein Tippen auf **Manager** öffnet dasselbe Zahlenfeld wie die Kassierer-Anmeldung. Nach korrekter PIN erscheint ein Panel mit den freigabepflichtigen Funktionen:

| Eintrag | Wirkung |
|---|---|
| **Einlage** | Geld in die Schublade, mit Betrag und Grund. |
| **Entnahme** | Geld heraus, mit Betrag und Grund. |
| **Schublade öffnen** | Öffnet die Lade ohne Verkauf. |
| **X-Bericht** | Zwischenstand der laufenden Schicht — Umsatz nach Zahlart und **Soll-Bargeld**. Ändert nichts. |

Oben im Panel stehen der **Name** der freigebenden Person und ein **Countdown**. Läuft er ab, schließt sich das Panel von selbst und die nächste Aktion verlangt die PIN erneut. Die Standardzeit beträgt drei Minuten und lässt sich über `Pos_Manager_Unlock_Seconds` ändern.

Die Freigabe ist an **diese eine Kasse** gebunden und endet außerdem, sobald das Panel geschlossen, die Kassiererin abgemeldet oder das Tablet in den Hintergrund gelegt wird.

<Alert>
<Icon name="info" />
<AlertTitle>Warum eine PIN und nicht einfach eine Berechtigung</AlertTitle>
<AlertDescription>
Das Tablet meldet sich mit einem **gemeinsamen** CMS-Konto an. Jede Berechtigung dieses Kontos — auch `pos.refund` oder `pos.open_drawer` — gilt damit für alle vierzig Helfer gleichermaßen, die im Lauf des Wochenendes daran stehen. Sie schränken das Gerät ein, nicht die Person.

Die Kassierer-PIN sagte bisher nur, *wer* verkauft. Die Manager-PIN sagt, *wer unterschrieben hat* — und wird zusammen mit Kassierer, Stand, Betrag und Grund protokolliert.

Jede einzelne Aktion im Panel schreibt weiterhin **ihren eigenen** Protokolleintrag mit dem Namen des Managers. Die kurze Freigabe ändert nur, wie oft die PIN getippt wird, nicht was festgehalten wird. Vor jeder einzelnen Aktion eine PIN zu verlangen wäre sicherer auf dem Papier — in der Praxis tippt eine Schichtleitung sie dann bei drei Kassen ein Dutzend Mal vor wartendem Publikum, und spätestens dann ist sie keine PIN mehr.
</AlertDescription>
</Alert>

<Alert>
<Icon name="info" />
<AlertTitle>Warum das Soll-Bargeld hier sichtbar ist</AlertTitle>
<AlertDescription>
Beim Kassensturz wird der erwartete Betrag bewusst **nicht** angezeigt — wer die Zielzahl kennt, zählt auf sie hin. Im X-Bericht steht sie trotzdem: Eine Schichtleitung, die entscheiden soll, ob Geld abgeschöpft werden muss, braucht sie. Der Unterschied ist nicht die Zahl, sondern wer sie in welchem Moment sieht.
</AlertDescription>
</Alert>

Ist für eine Veranstaltung **niemand** als Manager hinterlegt, bleibt die Schaltfläche *Manager* an der Kasse ausgeblendet; Erstattungen und das Öffnen der Schublade funktionieren dann wie bisher über die CMS-Berechtigung. Eine bestehende Installation ändert ihr Verhalten also erst, wenn der erste Manager benannt wird.

Fehlversuche bei der Manager-PIN werden **getrennt** von der Kassierer-PIN gezählt — sonst könnte eine Kassiererin, die sich bei der eigenen Anmeldung vertippt, jede Freigabe an diesem Tablet blockieren.

---

## Pfand

Becherpfand ist auf deutschen Festivals nahezu Standard und war bis zuletzt die größte offene Stelle in der Abrechnung: Der Pfandbetrag ist echtes Geld, das die Kasse verlässt und wieder betritt, ohne dass das System davon wusste. Jeder Stand, der Pfand nahm, rechnete es am Ende von Hand nach.

Einschalten unter *Einstellungen → Kasse → Pfand*, verwalten unter *Kasse → Verwalten → Pfand*.

### Pfandartikel

Ein **Pfandartikel** ist der Gegenstand, auf den Pfand erhoben wird — Becher, Schüssel, Glas, Kiste. Er ist bewusst **kein Produkt**:

| Ein Produkt … | Ein Pfandartikel … |
|---|---|
| kann rabattiert und couponiert werden | nie — es wird zum vollen Wert zurückgenommen |
| hat einen Bestand | hat keinen |
| ist Umsatz | ist verwahrtes Geld des Gastes |
| lässt sich einzeln verkaufen | hängt immer an etwas anderem |

Dass Pfand nicht rabattierbar ist, steckt damit in der Datenstruktur und nicht in einer Regel, die sich sechs Stellen im Code merken müssten.

### Zuordnung zu Produkten

Unter *Produkte → Bearbeiten → Pfand* wird ein Pfandartikel zugeordnet, zusammen mit der Zahl der **Pfandeinheiten je verkaufter Einheit**. Fast immer 1 — beim Kasten Bier aber 21: zwanzig Flaschen und der Kasten. Wer das auf 1 stehen lässt, zahlt am Ende einen Bruchteil dessen zurück, was kassiert wurde.

Beim Verkauf entsteht die Pfandposition automatisch, direkt unter dem auslösenden Artikel:

```
Bier 0,5 l          4,00
Pfand Becher        2,00
─────────────────────────
Summe               6,00
```

Direkt darunter, nicht am Bonende gesammelt: Wer „Bier 4,00 / Pfand 2,00" liest, versteht die 6,00 € — dieselben zwei Zeilen acht Positionen auseinander beginnen eine Diskussion.

<Alert>
<Icon name="info" />
<AlertTitle>Der Steuersatz kommt vom Produkt</AlertTitle>
<AlertDescription>
Pfand übernimmt bei der Ausgabe den Steuersatz des Artikels, zu dem es gehört. Dasselbe Becherpfand erscheint deshalb am Bierstand mit 19 % und bei der Suppe mit 7 % — das ist so gewollt.

Bei der **anonymen Rückgabe** gibt es kein Produkt zum Nachschlagen: Da stehen sechs Becher auf der Theke und niemand weiß, was daraus getrunken wurde. Dafür trägt der Pfandartikel einen eigenen Steuersatz, der ausschließlich für Rückgaben gilt.
</AlertDescription>
</Alert>

### Rückgabe an der Kasse

Schaltfläche **Pfand** an der Kasse. Menge pro Artikel hoch- oder runterzählen, *Auszahlen* — fertig. **Kein Beleg nötig.**

Das ist eine bewusste Entscheidung. Die Rückgabe an den Originalbon zu knüpfen wäre wasserdicht und in der Praxis unbenutzbar: Der Bon liegt im Müll oder wurde nach §148 AO nie gedruckt. Eine Regel, die niemand befolgen kann, ist keine Kontrolle.

Begrenzt wird die Rückgabe stattdessen durch ihren **Rahmen**:

- Nur hinterlegte Pfandartikel, nur zum hinterlegten Wert. Es gibt keine Möglichkeit, „gib dem Gast 50 €" auszudrücken — nur „sechs Becher".
- Der Betrag wird immer serverseitig gerechnet, nie vom Tablet übernommen.
- Oberhalb von `Pos_Deposit_Return_Max_Cents` (Standard 50 €, rund hundert Becher) muss ein Manager freigeben.
- Jede Rückgabe erzeugt einen eigenen Beleg mit eigener Belegnummer und erscheint im Transaktionsjournal als *Pfandrückgabe*.

Die Auszahlung geht **immer bar** aus der Schublade. Auf Karte ginge eine Buchung, die sich gegen keinen Verkauf abstimmen lässt; auf ein Armband würde aus Bechern ein Weg, Guthaben aufzuladen.

<Alert variant="destructive">
<Icon name="alert-circle" />
<AlertTitle>Den Pfandwert zu ändern ist eine echte Entscheidung</AlertTitle>
<AlertDescription>
Becher, die gestern zu 2,00 € ausgegeben wurden, kommen morgen zum **neuen** Wert zurück — die anonyme Rückgabe kann den alten nicht nachschlagen. Erhöhen kostet bei jedem umlaufenden Becher die Differenz; senken benachteiligt jeden Gast, der noch einen hält. Die Oberfläche weist beim Speichern darauf hin.
</AlertDescription>
</Alert>

### Pfand im Umlauf

Unter *Kasse → Berichte* und auf der Pfandseite steht, wie viel Pfand ausgegeben, wie viel zurückgenommen wurde und wie viel noch unterwegs ist. Der Wert der **Rückstellung** ist der Betrag, der in den Kassen bleiben muss, um alles zurücknehmen zu können — dieselbe Art Verbindlichkeit wie offenes Armband-Guthaben.

Ein negativer Wert ist kein Fehler: Becher aus einer früheren Veranstaltung kommen hier zurück.

<Alert>
<Icon name="info" />
<AlertTitle>Pfand abschalten strandet niemanden</AlertTitle>
<AlertDescription>
Ein **deaktivierter** Pfandartikel wird beim Verkauf nicht mehr berechnet, kann aber weiterhin zurückgenommen werden. Sonst stünden alle Gäste mit unbezahlbaren Bechern da. **Gelöscht** werden kann ein Artikel nur, solange dafür nie Pfand ausgegeben wurde.
</AlertDescription>
</Alert>

---

## Kassiererwechsel (Übergabe)

Schaltfläche **Übergabe** an der Kasse. Die **neue** Person tippt ihre **eigene** PIN — nicht die alte Person, die die Kasse jemandem zuweist. Das ist der ganze Grund, warum die Übergabe vertrauenswürdig ist: Niemand kann den Namen einer anderen Person über die nächsten drei Stunden Umsatz schreiben.

**Die Schublade wird dabei nicht gezählt.** Die Schicht gehört der *Kasse*, nicht der Person — deshalb auch der eindeutige Index pro Terminal. Eine Festivalbar wechselt alle paar Stunden das Personal; ein Kassensturz bei jedem Wechsel hieße vier Zählungen pro Nacht und Kasse, mit Schlange durch jede einzelne — oder, weit wahrscheinlicher, dass alle unter dem Namen weiterarbeiten, der um 18 Uhr angemeldet hat.

Der Warenkorb bleibt erhalten: Eine Übergabe mitten in einer Bestellung ist an einer vollen Bar der Normalfall.

<Alert>
<Icon name="info" />
<AlertTitle>Was die Übergabe leistet — und was nicht</AlertTitle>
<AlertDescription>
Erhalten bleibt die **Zuordnung**: In `pos_shift_cashiers` steht, wer zwischen welchen Zeitpunkten an der Kasse war, und jede Transaktion trägt ihren Kassierer.

Nicht erhalten bleibt die Zurechenbarkeit des **Bargelds**: Nach zwei Übergaben lässt sich ein Fehlbetrag um 2 Uhr nicht mehr ehrlich einer von drei Personen zuschreiben. Zuordnen lässt er sich einem Zeitfenster — von dort grenzt das Transaktionsprotokoll weiter ein.
</AlertDescription>
</Alert>

Jedes Ende einer Kassiererzeit wird mit **Grund** festgehalten, weil die vier Fälle nicht dasselbe sind:

| Grund | Bedeutung |
|---|---|
| **Übergabe** | Eine namentlich bekannte Person hat übernommen. Das Geld war nie unbeaufsichtigt. |
| **Abmeldung** | Jemand hat sich bewusst abgemeldet. |
| **Zeitüberschreitung** | Das Tablet hat sich selbst gesperrt. Niemand hat etwas entschieden, die Schublade stand da. |
| **Kassensturz** | Die Schicht wurde geschlossen, das Geld ist gezählt. |

Der Unterschied zwischen *Abmeldung* und *Zeitüberschreitung* ist die erste Frage, die bei einem Fehlbetrag gestellt wird — deshalb werden sie getrennt festgehalten statt beide als „nicht mehr angemeldet".

---

## Inventur

Der Bestand wird bei jedem Verkauf heruntergezählt, aber nichts hat ihn je mit dem Regal verglichen. Bruch, Personalgetränke, ein nie eingebuchtes Fass, eine Kiste, die verschwunden ist — nichts davon läuft über die Kasse, und jedes davon entfernt die Zahl weiter von der Wirklichkeit. Ein Bestand, den nie jemand geprüft hat, ist einer, dem stillschweigend niemand mehr traut.

*Kasse → Inventur*, Berechtigung `pos.manage_stocktakes`.

### Ablauf

1. **Starten.** Bereich wählen (ein Stand oder die ganze Veranstaltung) und entscheiden, ob der Bestand am Ende korrigiert werden soll.
2. **Zählen.** Jede Zeile wird sofort gespeichert, sobald das Feld verlassen wird. Die Liste kann jederzeit unterbrochen und später fortgesetzt werden. <kbd>Enter</kbd> springt zur nächsten Zeile.
3. **Abschließen.** Erst danach erscheinen Soll, Differenz und Wert.

Gezählt werden nur Produkte mit aktivierter **Bestandsführung**. Deaktivierte Produkte sind bewusst dabei — im Regal stehen sie trotzdem, und sie wegzulassen ist genau der Weg, auf dem Bestand aus den Büchern verschwindet, ohne aufzuhören zu existieren.

<Alert>
<Icon name="info" />
<AlertTitle>Der Soll-Bestand wird beim Start eingefroren</AlertTitle>
<AlertDescription>
Nicht beim Abschluss. Eine Zählung dauert eine Stunde und die Bar verkauft weiter; würde man um 01:00 Gezähltes gegen den Bestand um 02:00 halten, entstünde eine Differenz aus ganz normalem Betrieb — und ausgerechnet die Leute, die gezählt haben, müssten sie erklären.

Die Kehrseite gehört dazu: Alles, was **während** der Zählung noch verkauft wird, erscheint hinterher als Fehlmenge. Deshalb gehört eine Inventur ans Ende des Betriebs.
</AlertDescription>
</Alert>

<Alert>
<Icon name="info" />
<AlertTitle>Auch hier wird blind gezählt</AlertTitle>
<AlertDescription>
Solange die Inventur läuft, steht der Soll-Bestand nirgends auf dem Bildschirm — dieselbe Überlegung wie beim Kassensturz: Wer die Zielzahl kennt, zählt auf sie hin.

Ein **leeres** Feld heißt „noch nicht gezählt" und ist ausdrücklich **nicht** null Stück. Nicht gezählte Positionen fließen in keine Differenz ein. Würde man beides gleichsetzen, wäre jede noch unbearbeitete Zeile eine Fehlmenge und die Auswertung erst mit der allerletzten Eingabe brauchbar.
</AlertDescription>
</Alert>

### Ergebnis

Fehlmenge und Überbestand werden **getrennt** ausgewiesen, nicht nur saldiert. 200 € zu wenig beim Schnaps und 200 € zu viel beim Bier ergeben netto null und sind trotzdem keine saubere Inventur: Das eine ist Schwund, das andere ein Buchungsfehler.

Bewertet wird zum **Verkaufspreis**, nicht zum Einkaufspreis — einen solchen kennt dieses System nicht, und einen zu erfinden machte die Zahl auf eine Weise falsch, die niemand sehen könnte. So beantwortet sie „welcher Umsatz ist uns entgangen" und überzeichnet den reinen Warenverlust.

Wurde beim Start **Bestand korrigieren** gewählt, wird der Bestand beim Abschluss auf die gezählten Mengen gesetzt — nicht um die Differenz verschoben. Nicht gezählte Zeilen bleiben unberührt.

<Alert variant="destructive">
<Icon name="alert-circle" />
<AlertTitle>Eine abgeschlossene Inventur lässt sich nicht wieder öffnen</AlertTitle>
<AlertDescription>
Und auch nicht löschen. Genau das wäre der Weg, eine unangenehme Differenz verschwinden zu lassen, nachdem sie jemand gesehen hat — und das zu verhindern ist der Zweck der Übung. Eine falsche Zählung wird durch eine neue beantwortet, nicht durch eine Korrektur.

Abbrechen ist möglich, solange die Inventur läuft; die bereits gezählten Zeilen bleiben dann erhalten, fließen aber in keine Auswertung ein.
</AlertDescription>
</Alert>

Die Zählliste lässt sich als CSV oder XLSX herunterladen — auch leer, zum Ausdrucken und Abhaken auf Papier.

---

## Auswertung

Unter *Kasse → Berichte*, unterhalb der X-/Z-Berichte. Alle Zahlen stammen direkt aus dem Transaktionsjournal; es gibt keine zwischengespeicherten Summen, die davon abweichen könnten. Optional auf einen Stand einschränkbar.

| Auswertung | Beantwortet |
|---|---|
| **Umsatz nach Uhrzeit** | Wann muss die Bar dreifach besetzt sein und wann kann jemand nach Hause? |
| **Renner & Penner** | Was bestellen wir nächstes Jahr mehr, was weniger? |
| **Stände im Vergleich** | Umsatz, Bonzahl, Ø Bon und Anteil je Stand. |
| **Kassierer** | Umsatz, Ø Bon, Erstattungen, Kassendifferenz je Person. |

Die Stundenkurve zeigt **alle 24 Stunden**, auch die leeren. Eine Kurve, die nur aus Stunden mit Umsatz besteht, schließt ihre eigenen Lücken — und die toten Stunden sind das, was bei der Schichtplanung interessiert.

Renner werden **nach Umsatz und nach Menge getrennt** ausgewiesen. Die beiden widersprechen sich fast immer: Der billigste Artikel führt fast immer die Menge an und fast nie den Umsatz. Für eine Bestellliste braucht man das eine, für die Karte das andere.

<Alert>
<Icon name="alert-circle" />
<AlertTitle>Die Kassiererauswertung ist keine Rangliste</AlertTitle>
<AlertDescription>
Wer die vollen Stunden am stärksten besuchten Stand gearbeitet hat, steht dort oben — unabhängig davon, wie gut die Person gearbeitet hat. Die Zahl misst vor allem den Dienstplan.

Aussagekräftig sind **Ø Bon** und die Zahl der Erstattungen: Fällt jemand dort deutlich aus dem Rahmen, lohnt die Nachfrage. Die **Kassendifferenz** wird der Person zugeordnet, die die Schicht *eröffnet* hat — nach einer Übergabe gehört die Schublade mehreren Leuten, und das ist eine Näherung, keine Zuweisung.
</AlertDescription>
</Alert>

Produkte, die sich **überhaupt nicht** verkauft haben, erscheinen nicht unter den Pennern: Zu ihnen existieren keine Positionen. Diese Frage beantwortet die Produktliste.

---

## Kassensturz

Der Abschluss lässt sich **an der Kasse selbst** durchführen (Schaltfläche *Kassensturz*, sofern die Berechtigung `pos.manage_shifts` vorliegt) oder in der Verwaltung unter *Kasse → Schichten*. Der Weg über die Kasse ist der übliche: Dort steht die Person, die die Schublade in der Hand hat.

<Alert>
<Icon name="info" />
<AlertTitle>Blind gezählt — der Sollbestand erscheint erst danach</AlertTitle>
<AlertDescription>
Der erwartete Kassenbestand wird **vor** dem Zählen weder angezeigt noch vorbelegt, und er steht auch nicht in der Schichtenliste, solange die Schicht offen ist. Erst nach dem Eintragen des tatsächlich gezählten Betrags erscheinen Sollbestand und Differenz.

Das ist der Sinn der Sache: Wer den Zielwert vorher sieht, tippt ihn ab, statt zu zählen — und eine Zählung, die keine Differenz zeigen kann, ist keine Zählung. Ein leeres Feld wird deshalb auch nicht als „0,00" gewertet, sondern zurückgewiesen.
</AlertDescription>
</Alert>

Unter *Kasse → Schichten* schließen Sie eine Schicht ab. Das System zeigt den erwarteten Kassenbestand:

> Wechselgeld + Bareinnahmen + Einlagen − Entnahmen

Sie tragen den **tatsächlich gezählten** Betrag ein; die Differenz wird vorzeichenrichtig gespeichert und **nicht korrigiert**. Eine Fehldifferenz ist eine Tatsache über den Abend und soll auch als solche sichtbar bleiben.

<Alert>
<Icon name="info" />
<AlertTitle>Schichten gehören zur Kasse, nicht zur Person</AlertTitle>
<AlertDescription>
Die Geldschublade hängt physisch an der Kasse. Wechselt mitten am Abend die Person, ist das ein Kassiererwechsel — kein Kassensturz. Wer wann bedient hat, steht trotzdem im Z-Bericht.
</AlertDescription>
</Alert>

Entnehmen Sie Geld für Wechselzwecke, erfassen Sie das als **Entnahme** — sonst stimmt die Abrechnung am Ende nicht und die Differenz landet zu Unrecht beim Kassierer.

---

## Armbänder (RFID)

Optional, standardmäßig deaktiviert. Ein Armband trägt ein Guthaben, das an allen Ständen ausgegeben werden kann.

<Alert variant="destructive">
<Icon name="alert-circle" />
<AlertTitle>Eine Aufladung ist kein Verkauf</AlertTitle>
<AlertDescription>
Armbänder sind als **Mehrzweckgutscheine** umgesetzt: Die Umsatzsteuer entsteht erst beim Einlösen gegen Ware, nicht beim Aufladen. Eine Aufladung erzeugt deshalb bewusst keinen Beleg und keine TSE-Signatur. Nicht verbrauchtes Guthaben ist eine **Verbindlichkeit**, kein Umsatz, und muss zum Veranstaltungsende ausgezahlt oder ausdrücklich verfallen werden. Wenn Ihre Armbänder ausschließlich Waren eines einzigen Steuersatzes kaufen können, wäre die Einordnung eine andere — halten Sie in dem Fall vor dem Einsatz Rücksprache mit Ihrer Steuerberatung.
</AlertDescription>
</Alert>

### Aufladen an der Kasse

Aufladen ist pro Stand freizugeben: In *Kasse → Stände* aktivieren Sie beim jeweiligen Stand **Armband-Aufladung erlaubt**. Nur dann erscheint an dessen Kassen die Schaltfläche **Armband aufladen**; ein Stand ohne Freigabe kann kein Guthaben laden, auch wenn die Kassiererin dort angemeldet ist.

An der Kasse wird das Armband gescannt oder die Nummer von Hand eingetragen, danach werden Betrag und Zahlungsart erfasst. Die Zahlungsart ist Pflicht — ohne sie stimmt die Kassenabrechnung nicht:

- **Barzahlung** wird zusätzlich als **Einlage** auf die laufende Schicht gebucht. Das Geld liegt tatsächlich in der Schublade, es gibt dazu aber keinen Verkauf. Ohne diese Buchung wäre jede Schublade beim Kassensturz genau um den aufgeladenen Betrag im Plus.
- **Kartenzahlung** erzeugt keine Kassenbewegung, da nichts in die Schublade gelangt.

Eine offene Schicht ist Voraussetzung. Aufladungen aus der Verwaltung (*Kasse → Armbänder*) gehören zu keiner Schublade und erzeugen deshalb keine Kassenbewegung.

### Wo Aufladungen erscheinen

Nicht unter *Kasse → Transaktionen*. Diese Liste enthält ausschließlich signierte Belege — jeder Eintrag hat Belegnummer, Umsatzsteuer und TSE-Signatur. Eine Aufladung hat nichts davon, weil beim Aufladen eines Mehrzweckgutscheins keine Umsatzsteuer entsteht. Aufgeladenes Guthaben ist eine Verbindlichkeit; zum Umsatz wird es erst beim Verkauf, der dann ganz normal in den Transaktionen steht.

Aufladungen stehen stattdessen unter *Kasse → Aufladungen*: gefiltert nach Zeitraum, Stand, Zahlungsart und Armband, mit Summen je Zahlungsart und Export (Berechtigung `pos.export_transactions`).

Bei der Abstimmung hilft die Unterscheidung:

| Zahlungsart der Aufladung | Wo sie auftaucht |
|---|---|
| **Bar** | *Kasse → Aufladungen* **und** als Einlage im Z-Bericht der Schicht — das Geld liegt in der Schublade. |
| **Karte** | Nur unter *Kasse → Aufladungen*. Es gelangt nichts in die Schublade, deshalb gibt es keine Kassenbewegung. |
| **Aus der Verwaltung** | Nur unter *Kasse → Aufladungen*, gekennzeichnet als „Verwaltung". Gehört zu keiner Schublade. |

### Serie anlegen

Armbänder kommen aus der Druckerei als durchnummerierte Charge. Statt sie einzeln zu erfassen, legt **Serie anlegen** auf der Armbänder-Seite eine ganze Charge aus einem Muster an. Die Schreibweise ist die von NetBox:

| Muster | Ergebnis |
|---|---|
| `Armband-[1-1000]` | `Armband-1` bis `Armband-1000` |
| `Armband-[0001-1000]` | Führende Nullen bleiben erhalten: `Armband-0001` … |
| `VIP-[1,4,9]` | Nur `VIP-1`, `VIP-4`, `VIP-9` |
| `Tor-[a-d]` | `Tor-a` bis `Tor-d` |
| `[A-C]-[1-50]` | Alle Kombinationen — hier 150 Stück |
| `Armband-[1-3,7,10-11]` | Bereiche und Einzelwerte gemischt |

Ob führende Nullen erhalten bleiben, entscheidet der **Anfangswert**: `[001-100]` ergibt dreistellige Nummern, `[1-100]` einstellige. Das ist wichtig, weil `Armband-007` und `Armband-7` für einen Menschen dasselbe Armband sind, für die Datenbank aber zwei verschiedene.

Bereits vorhandene Kennungen werden **übersprungen**, nicht überschrieben — eine zweite Kiste derselben Charge lässt sich also mit demselben Muster nachtragen. Pro Vorgang sind höchstens 100.000 Armbänder erlaubt.

Auf der Kommandozeile geht dasselbe, was sich für große Chargen am Vorabend anbietet:

```bash
crisp crispy:pos:wristbands --pattern="Armband-[0001-2000]" --dry-run
crisp crispy:pos:wristbands --pattern="Armband-[0001-2000]" --balance=1000
```

`--dry-run` zeigt nur Anzahl und Bereich an. Ohne `--event` wird die aktive Veranstaltung verwendet.

### Guthaben abfragen

„Wie viel ist da noch drauf?" ist die häufigste Frage an einem Stand mit Armbändern. Die Schaltfläche **Guthaben** an der Kasse (oder F7) beantwortet sie: Armband an den Leser halten oder Nummer eintippen — das Guthaben erscheint groß, darunter Status und die letzten Bewegungen.

Die Abfrage ist **rein lesend**. Kein Warenkorb, keine Buchung, nichts, was sich versehentlich auslösen ließe — deshalb kann das Fenster offen bleiben, während mehrere Gäste nacheinander fragen. Nach jeder Antwort ist der Leser sofort wieder bereit.

Im selben Feld lässt sich auch ein **Coupon-Code** prüfen. Statt eines Guthabens erscheinen dann Wert, Aktion, Geltungsbereich, Ablaufdatum und die Anzahl der verbleibenden Einlösungen.

Anders als beim Bezahlen wird ein **gesperrtes Armband hier angezeigt statt abgewiesen** — die Sperre ist ja gerade die Antwort auf „warum funktioniert mein Armband nicht?".

### Armbänder verwalten

Die Übersicht unter *Kasse → Armbänder* lädt seitenweise und zeigt nie alle Armbänder auf einmal — bei mehreren tausend Bändern wäre die Seite sonst genau dann unbenutzbar, wenn sie gebraucht wird.

Das Suchfeld ist beim Öffnen bereits aktiv: Ein Armband an den Leser zu halten trägt die Kennung ein und startet die Suche. Von Hand lässt sich ebenso nach einem Teil der Kennung oder der Bezeichnung suchen. Zusätzlich filtert die Statusauswahl auf aktive, gesperrte oder ausgezahlte Bänder.

Das **offene Guthaben** oben links bezieht sich immer auf die gesamte Veranstaltung, nicht auf die gerade gefilterte Auswahl — es ist die Verbindlichkeit zum Veranstaltungsende.

---

## Belegdruck

Drucker werden unter *Kasse → Drucker* eingerichtet und einem Stand oder einer einzelnen Kasse zugeordnet.

| Protokoll | Wann |
|---|---|
| **ESC/POS (Port 9100)** | Funktioniert mit fast jedem Thermodrucker. Meldet **keinen** Papierstatus zurück. |
| **Epson ePOS-Print** | Bevorzugen, wenn die Hardware es kann — meldet Papier, Deckel und Offline-Zustand. |
| **Star CloudPRNT** | Der Drucker holt seine Aufträge selbst ab. |

Ein Druckfehler bricht **nie** einen Verkauf ab. Der Beleg wandert in die Warteschlange, der Kassierer bekommt einen Hinweis und bedient weiter; ein Hintergrundauftrag druckt nach, sobald der Drucker wieder erreichbar ist.

Die Geldschublade hängt am Bondrucker und wird über einen Impuls geöffnet — ohne erreichbaren Drucker öffnet sich also auch die Schublade nicht.

<Alert>
<Icon name="info" />
<AlertTitle>Testdruck gehört in den Aufbau</AlertTitle>
<AlertDescription>
Der Testdruck enthält absichtlich Umlaute. Eine falsch eingestellte Codepage fällt so beim Aufbau auf und nicht beim ersten echten Beleg.
</AlertDescription>
</Alert>

---

## Berechtigungen

| Berechtigung | Erlaubt |
|---|---|
| `pos.use` | Kasse bedienen und verkaufen |
| `pos.refund` | Stornieren und erstatten |
| `pos.discount` | Reserviert für frei eingebbare Rabatte — die Kasse bietet bewusst keine an, siehe *Rabatte & Coupons* |
| `pos.open_drawer` | Schublade außerhalb eines Verkaufs öffnen |
| `pos.read_transactions` | Transaktionen einsehen |
| `pos.export_transactions` | Transaktionen exportieren |
| `pos.manage_shifts` | Schichten schließen, Kassensturz |
| `pos.view_reports` | X-/Z-Berichte |
| `pos.manage_products` | Produkte, Kategorien, Steuersätze |
| `pos.manage_payment_methods` | Zahlungsarten |
| `pos.manage_events` | Veranstaltungen |
| `pos.manage_stands` | Stände und Kassen |
| `pos.manage_cashiers` | Kassierer und PINs |
| `pos.manage_wristbands` | Armbänder, Aufladungen |
| `pos.manage_coupons` | Rabattaktionen & Coupons |
| `pos.manage_deposits` | Pfandartikel verwalten |
| `pos.manage_stocktakes` | Inventuren durchführen |
| `pos.manage_printers` | Drucker |

Verkaufen, Geld zurückgeben und die Schublade öffnen sind bewusst drei verschiedene Berechtigungen — diese Trennung ist die verbreitetste Maßnahme gegen Schwund im Einzelhandel.

`pos.manage_stocktakes` ist aus demselben Grund von `pos.manage_products` getrennt, obwohl es bei einer Inventur „um Produkte geht": Zählen ist eine Aufgabe für die Person, die den Stand abschließt — die Preise des ganzen Katalogs ändern zu dürfen ist etwas, das man dabei nicht nebenbei erwerben sollte.

<Alert variant="destructive">
<Icon name="alert-circle" />
<AlertTitle>Die Kassierer-PIN ist kein Passwort</AlertTitle>
<AlertDescription>
Die PIN sagt, **wer** an der Kasse steht — sie ersetzt nicht die Anmeldung. Der eigentliche Zugangsschutz ist der CMS-Login des Tablets. Verwenden Sie eine Kassen-PIN daher niemals als Passwort für irgendetwas anderes.
</AlertDescription>
</Alert>

---

## Kommandozeile

```bash
# X-/Z-Bericht einer Schicht anzeigen
crisp crispy:pos:report --shift=12

# Druckwarteschlange ansehen, verarbeiten, Drucker prüfen
crisp crispy:pos:print-queue
crisp crispy:pos:print-queue --drain
crisp crispy:pos:print-queue --status
crisp crispy:pos:print-queue --test=3

# Armbänder aus einem Muster anlegen (--dry-run zeigt nur die Vorschau)
crisp crispy:pos:wristbands --pattern="Armband-[0001-2000]" --dry-run
crisp crispy:pos:wristbands --pattern="Armband-[0001-2000]" --balance=1000

# Beispieldaten für Demo und Entwicklung (nur Entwicklungsumgebung)
crisp crispy:pos:seed
crisp crispy:pos:seed --event="Sommerfest 2026" --site-code=SOF --wristbands="Armband-[1-500]"

# Alle Kassendaten löschen (nur Entwicklungsumgebung)
crisp crispy:pos:purge
crisp crispy:pos:purge --keep-catalogue
```

---

## Vor der Veranstaltung

Die Seite **Kasse → Kassensicherheit** prüft automatisch: aktive Veranstaltung, TSE konfiguriert und erreichbar, TSE-Zeit gültig, Drucker eingerichtet, Kassen registriert.

<Alert variant="destructive">
<Icon name="alert-circle" />
<AlertTitle>Die TSE-Zeit ist die häufigste Panne am ersten Tag</AlertTitle>
<AlertDescription>
Eine TSE signiert nicht ohne gültige Uhrzeit. Auf einem abgeschotteten Gelände gibt es keine Zeitsynchronisierung über das Internet — Sie brauchen eine lokale Zeitquelle. Schalten Sie die TSE **am Vortag** ein, nicht zwanzig Minuten vor Türöffnung.
</AlertDescription>
</Alert>

## Nach der Veranstaltung

1. Alle Schichten mit Kassensturz abschließen
2. Offenes Armband-Guthaben auszahlen oder verfallen lassen
3. **DSFinV-K- und TSE-Export aus der TSE-Middleware ziehen** — die Hardware verlässt danach den Standort
4. Veranstaltung abschließen

Der Transaktionsexport unter *Transaktionen* ist eine betriebswirtschaftliche Auswertung für die Buchhaltung. Er ersetzt den DSFinV-K-Export für die Betriebsprüfung **nicht**.
