import { type TransportOptions } from '../client/transport.js'; import { type ReceiptCompany } from '../models/receipt-company.js'; import type { PosSettings } from '../pos/settings.js'; /** * Verbindungsangaben ohne Anmeldung — alles, was [TransportOptions] ausser der * Anmeldung fuehrt. Alle Felder sind wahlfrei: ohne Angabe gelten der Kassenweg * der Produktion ([POS_BASE_URL]), das Zeitlimit der Produktion und das * globale `fetch`. Die Web-Kasse gibt `posBaseUrl: '/api/v3'` (gleicher Ursprung). */ export type RegisterDeviceConnection = Omit; /** Ausweis eines gekoppelten Geraets — Nachweis der beiden Folgeaufrufe. */ export interface RegisterDeviceCredentials { /** Kunde, unter dem das Geraet haengt (aus der Kopplung). */ ownerUid: string; /** Dieses Geraet (aus der Kopplung). */ deviceId: string; /** Geheimnis dieses Geraets; das Backend liefert es genau einmal aus. */ deviceSecret: string; } /** Was das Geraet ueber sich sagt -- fuer das Panel (welches Geraet ist das?). Alles optional. */ export interface RegisterClientInfo { userAgent?: string; platform?: string; language?: string; /** IANA-Zeitzone, z. B. Europe/Vienna. */ tz?: string; screen?: { w: number; h: number; }; /** * Welcher Build der Anwendung gerade laeuft (frei, z. B. "0.6.46+3120"). * Bei einer Stoerungsmeldung die entscheidende Angabe: ohne sie laesst sich * ein alter Build im Browser-Cache weder belegen noch ausschliessen -- und * genau der steckt oft hinter "bei mir geht es aber". Das Backend kuerzt auf * 80 Zeichen (register-geraetedaten.js, clientAusParams). */ app?: string; } /** Standort aus der Browser-Ortung (freiwillig); Grundlage der Standortsperre. */ export interface RegisterGeo { lat: number; lng: number; /** Genauigkeit in Metern. */ acc?: number; } /** Angaben, die Kopplung und Login zusaetzlich mitschicken duerfen. */ export interface RegisterDeviceInfo { client?: RegisterClientInfo; geo?: RegisterGeo | null; } export interface PairRegisterDeviceOptions extends RegisterDeviceConnection, RegisterDeviceInfo { /** * Die Kasse ist schon auf einem anderen Geraet geoeffnet (BELEGT-Rueckfrage): * `true` uebernimmt sie hierher und widerruft das alte Geraet. */ takeover?: boolean; /** * Achtstelliger Kopplungs-Code aus dem Panel. Er ist 15 Minuten gueltig, * genau **einmal** verwendbar und gilt fuer eine bestimmte Kasse. * * Gross-/Kleinschreibung und Leerzeichen an den Raendern sind gleichgueltig: * das Backend beschneidet und schreibt gross. Dieses Paket prueft das Format * **nicht** — es kennt das Alphabet nicht und wuerde eine spaetere Erweiterung * ausschliessen. */ code: string; /** Bezeichnung dieses Geraets in der Geraeteliste; ohne Angabe "Kasse". */ label?: string; } /** * Ergebnis der Kopplung: der vollstaendige Ausweis dieses Geraets. * * **Speicherformat der App bleibt, wie es ist** (Nachtrag §9, B2): wer den * Ausweis ablegt, bildet `companyName`/`cashregisterLabel` auf seine * bisherigen Felder ab (Web-Kasse: `betrieb`/`kasse`). Die Aufrufe dieses * Pakets lesen aus einem abgelegten Satz nur `ownerUid`, `deviceId`, * `deviceSecret` (und `cashregisterId` beim Login); weitere Felder eines alten * Satzes stoeren nicht. */ export interface PairedRegisterDevice extends RegisterDeviceCredentials { /** Kasse, an die die Kopplung dieses Geraet gebunden hat. */ cashregisterId: string; /** Firmenname des Betriebs; Anzeige, kann leer sein. */ companyName: string; /** Bezeichnung der Kasse (ohne Bezeichnung die Kassen-ID); Anzeige, kann leer sein. */ cashregisterLabel: string; /** Die Kopplung gilt der Test-Umgebung des Betriebs (`kr_test_`-Konto). */ testEnvironment: boolean; } /** * Art eines Kassen-Benutzers: eine Person oder ein Geraet (Sammelkonto). Ein * kuenftiger, hier noch unbekannter Wert kommt unveraendert durch, statt still * zu "person" zu werden — beim Lesen ist dieses Paket tolerant. */ export type RegisterUserKind = 'person' | 'device' | (string & {}); /** Ein Kassen-Benutzer, wie ihn der Anmeldebildschirm zeigt. */ export interface RegisterUserSummary { id: string; /** Anzeigename; kann leer sein. */ name: string; kind: RegisterUserKind; /** * Die PIN dieses Benutzers wurde noch nicht unter der aktuellen Regel des * Betriebs gesetzt: die Kasse zeigt ihm das Freifeld statt der Kaestchen. * Das Backend sendet das Feld nur, wenn es zutrifft; gelesen ist es immer da. */ pinPolicyOutdated: boolean; } /** PIN-Regel des Betriebs: daraus baut die Kasse Kaestchen und Tastatur. */ export interface RegisterPinPolicy { /** Feste Stellenzahl (Backend: 3 bis 6). */ length: number; /** `digits` (nur 0-9) oder `alphanumeric` (0-9 plus Kopplungs-Alphabet). */ charset: 'digits' | 'alphanumeric' | (string & {}); } /** Anmeldemodus des Geraets: Benutzerauswahl oder nur PIN. */ export type RegisterLoginMode = 'select_user' | 'pin'; /** Antwort von [listRegisterUsersForDevice]: Benutzer, Regel, Modus. */ export interface RegisterDeviceUsers { /** Im Modus `pin` bewusst leer — Namen haben am nur-PIN-Geraet nichts verloren. */ users: RegisterUserSummary[]; /** * Kassen-Einstellungen (betriebsweit + Geraet), gemischt mit den Standardwerten. * Ohne `vouchers`: die Antwort des Servers fuehrt den Block nicht, er kommt * mit `getPosSettings`; ein Vorgabe-Block waere erfunden. */ settings: Omit; /** Belegkopf-Daten des Betriebs (Name, Anschrift, UID, Fusszeilen), ohne Geheimnisse. */ receiptHeader: ReceiptCompany | null; /** * `null`, wenn das Backend (noch) keine Regel nennt — dann zeigt die Kasse * das Freifeld, statt Kaestchen mit einer erratenen Stellenzahl. */ policy: RegisterPinPolicy | null; loginMode: RegisterLoginMode; /** Der Betrieb verlangt die Ortung beim Login (Standortsperre an). */ locationLock: boolean; /** * Darf die gebundene Kasse ueberhaupt Belege erstellen? `ready: false` * nennt den Grund als Menschentext (ausser Betrieb, keine Signaturkarte, * Startbeleg fehlt): die Kasse sperrt das Kassieren VOR dem ersten Beleg * statt an createReceipt zu scheitern. Fehlt das Feld, gilt bereit. * `stockLocationId`: Lager-Standort der Kasse, fehlt = Standard-Standort. */ cashregister?: { ready: boolean; reason?: string | null; stockLocationId?: string; }; /** Das Geraet haengt an der Test-Umgebung des Betriebs. */ testEnvironment: boolean; } /** * Rechte eines Kassen-Benutzers (Backend: `perms`, Vorlagen `PERMS_KASSIER` * und `PERMS_CHEF`). Der Inhaber kann einzelne Rechte abweichend setzen, * deshalb sind weitere Schluessel zugelassen. * * **Ein fehlendes Recht gilt als nicht erteilt.** Die Oberflaeche soll im * Zweifel weniger anbieten, nicht mehr; die tatsaechliche Grenze zieht ohnehin * das Backend. */ /** Reichweite eines Rechts: keine | nur eigene Belege | alle. */ export type RegisterScope = 'none' | 'own' | 'all'; export interface RegisterUserPerms { /** Belege ausstellen. */ sell: boolean; /** Belege stornieren (Schalter, gespiegelt aus cancelScope !== 'none'). */ cancel: boolean; /** Artikelstamm bearbeiten. */ articles: boolean; /** Beleglayout/Kassen-Einstellungen bearbeiten (Chef). */ layout: boolean; /** Berichte ansehen. */ reports: boolean; /** Eine belegte Kasse uebernehmen (nur Kassen-Chef). */ takeover: boolean; /** Storno-Reichweite; fehlt bei Altbestand (dann zaehlt `cancel`: true = all). */ cancelScope?: RegisterScope; /** Belege ansehen; fehlt bei Altbestand (= all). */ receiptsScope?: RegisterScope; /** Kassenlade ohne Verkauf oeffnen. */ drawer?: boolean; /** Rabatt geben. */ discount?: boolean; /** Trinkgeld anderen zuweisen. */ tipAssign?: boolean; /** * Lager: Standorte und Mengen sehen (listMyStockLocations, listMyStock). * Fehlt das Recht, gilt es als erteilt – anders als alle anderen * (siehe [stockViewOf]); ein ausdrueckliches `false` sperrt. */ stockView?: boolean; /** Lager: Einkaufswerte sehen (`values` in listMyStock); gibt nur der Inhaber frei. */ stockCosts?: boolean; /** Lager: Wareneingang, Umbuchen. */ stockMove?: boolean; /** Lager: Abgang, Zustand, Gegenbuchung. */ stockLoss?: boolean; /** Inventur: zaehlen. */ stocktakeCount?: boolean; /** Inventur: abschliessen. */ stocktakeClose?: boolean; /** Standort der Kasse waehlen (setMyCashregisterStockLocation). */ stockLocation?: boolean; /** Gutschein verkaufen (fehlt bei altem Server: gilt wie beim Kassier, siehe [voucherPermsOf]). */ voucherSell?: boolean; /** Gutschein einloesen (fehlt bei altem Server: gilt wie beim Kassier). */ voucherRedeem?: boolean; /** Gutscheine verwalten, z. B. Etikett nachdrucken (fehlt: verweigert). */ voucherManage?: boolean; [weiteresRecht: string]: boolean | RegisterScope | undefined; } /** * Alle Rechte-Schluessel, die dieses Paket kennt — die Zwillinge pruefen * dagegen, und ueber `fixtures/surface.json` steht die Liste im Vertrag. * * `satisfies` schliesst die eine Richtung: ein Name, den [RegisterUserPerms] * nicht fuehrt (Tippfehler, umbenanntes Recht), kommt hier nicht durch. * Die Gegenrichtung schliesst [_RechteVollstaendig] direkt darunter. */ export declare const REGISTER_PERMS: readonly ["sell", "cancel", "articles", "layout", "reports", "takeover", "cancelScope", "receiptsScope", "drawer", "discount", "tipAssign", "stockView", "stockCosts", "stockMove", "stockLoss", "stocktakeCount", "stocktakeClose", "stockLocation", "voucherSell", "voucherRedeem", "voucherManage"]; /** Reichweite lesen, mit der Migration des Backends (register-auth.js). */ export declare function cancelScopeOf(perms: RegisterUserPerms | null | undefined): RegisterScope; export declare function receiptsScopeOf(perms: RegisterUserPerms | null | undefined): RegisterScope; /** * Darf der Benutzer Standorte und Mengen sehen? Wie im Backend (lager-core): * ohne Schluessel gilt `stockView` als erteilt, ein ausdrueckliches `false` * sperrt; alle anderen Lager-Rechte gelten ohne Schluessel als verweigert. */ export declare function stockViewOf(perms: RegisterUserPerms | null | undefined): boolean; /** Die drei Gutschein-Rechte, aufgeloest (kein Schluessel fehlt mehr). */ export interface VoucherPerms { voucherSell: boolean; voucherRedeem: boolean; voucherManage: boolean; } /** * Gutschein-Rechte lesen. Das Backend loest sie bei der Anmeldung auf; ein * alter Server (und `/api`-Antworten) kennt sie nicht. Dann gilt je fehlendem * Schluessel die Vorgabe des Kassiers: verkaufen und einloesen ja, verwalten * nein. Ein ausdrueckliches `false` sperrt; was kein Wahrheitswert ist, * erteilt nichts. */ export declare function voucherPermsOf(perms: Partial | null | undefined): VoucherPerms; /** Der angemeldete Kassen-Benutzer. */ export interface RegisterUser { id: string; name: string; perms: RegisterUserPerms; } /** Ergebnis der PIN-Anmeldung. */ export interface RegisterUserSession { /** * Custom-Token des Anmeldediensts. Damit meldet der Verbraucher sich dort an * und bekommt das ID-Token, das `registerUserAuth` braucht; dieses Paket * kennt den Anmeldedienst nicht. */ customToken: string; /** Laufende Sitzung — Kopfzeile `register-session` jedes weiteren Aufrufs. */ sessionId: string; /** * Ablauf der Sitzung in Millisekunden seit 1970 (das Backend rechnet hier mit * `Date.now()`, nicht mit Wiener Wanduhrzeit). Die Sitzung lebt 90 Sekunden * und will alle 30 Sekunden erneuert werden. */ expiresAt: number; user: RegisterUser; } export interface ListRegisterUsersForDeviceOptions extends RegisterDeviceConnection, RegisterDeviceCredentials { } export interface RegisterUserLoginOptions extends RegisterDeviceConnection, RegisterDeviceCredentials, RegisterDeviceInfo { /** Kassen-Benutzer aus [listRegisterUsersForDevice]. */ userId: string; /** * PIN dieses Benutzers. Das Format prueft dieses Paket **nicht**: eine zu * strenge Pruefung im Client sperrte Benutzer aus, deren PIN das Panel anders * gesetzt hat. */ pin: string; /** Kasse, an der die Sitzung eroeffnet wird. */ cashregisterId: string; /** * Eine belegte Kasse uebernehmen. Nur mit dem Recht `takeover` (Kassen-Chef); * die aelteste laufende Sitzung wird dabei verdraengt. Ohne das Recht bleibt * es bei der Abweisung `cashregister_in_use` (Daten `deviceLabel`, * `takeoverAllowed: false`, siehe [registerErrorDetails]). */ takeover?: boolean; /** * Welche Sitzung soll weichen? Kennung aus [listRegisterSessionsForDevice]. * Ohne Angabe verdraengt das Backend die aelteste -- so wie bisher. * * Eine Kennung, die nicht mehr laeuft, wird ABGEWIESEN statt still durch die * aelteste ersetzt: Wer waehlt, bekommt seine Wahl oder eine Fehlermeldung, * nie einen Unbeteiligten. */ takeoverSessionId?: string; } export interface RegisterPinLoginOptions extends RegisterDeviceConnection, RegisterDeviceCredentials, RegisterDeviceInfo { /** * Die PIN allein — sie identifiziert UND authentifiziert (Geraete-Modus * `pin`; das Backend haelt PINs je Betrieb eindeutig). Das Format prueft * dieses Paket **nicht** — dieselbe Zurueckhaltung wie bei * [RegisterUserLoginOptions.pin]. */ pin: string; /** Kasse, an der die Sitzung eroeffnet wird. */ cashregisterId: string; /** Wie [RegisterUserLoginOptions.takeover]. */ takeover?: boolean; /** Wie [RegisterUserLoginOptions.takeoverSessionId]. */ takeoverSessionId?: string; } /** * Geraet koppeln: der Kopplungs-Code wird gegen den dauerhaften Ausweis dieses * Geraets getauscht. **Ohne Anmeldung** — der Code ist der Nachweis. * * Er wird dabei verbraucht, auch wenn der Aufrufer das Ergebnis verliert: eine * unvollstaendige Antwort ist deshalb ein Fehler und kein halbes Geraet (siehe * [PairedRegisterDevice]). */ export declare function pairRegisterDevice(options: PairRegisterDeviceOptions): Promise; /** * Kassen-Benutzer dieses Betriebs auflisten — die Auswahl des * Anmeldebildschirms. **Ohne Anmeldung**; der Ausweis des Geraets ist der * Nachweis. * * Die Antwort traegt ausschliesslich Kennung, Name und Art: keine Rechte, keine * Kassen, keine Hashes. Gesperrte Benutzer fehlen bereits in der Antwort. */ /** * „Geraet entkoppeln“ an der Kasse selbst: das Geraet weist sich mit seinem * Geheimnis aus und sperrt sich im Backend — damit sieht das Panel den * Widerruf und keine Sitzung laeuft nach. Idempotent: ein schon gesperrtes * Geraet meldet Erfolg. Danach das Geraet lokal vergessen. */ export declare function unpairRegisterDevice(options: ListRegisterUsersForDeviceOptions): Promise; /** Eine laufende Sitzung an einer Kasse -- Zeile in der Auswahl. */ export interface RegisterSession { id: string; deviceId: string | null; /** * Name des Geraets; `null`, wenn es keinen hat (unter `/v3` sendet das * Backend dann `null` statt des frueheren Ersatzes „Kasse"). Die Oberflaeche * waehlt ihren eigenen Ersatztext. */ deviceLabel: string | null; startedAt: number | null; expiresAt: number | null; /** Laeuft diese Sitzung auf DIESEM Geraet? */ own: boolean; /** * Wer angemeldet ist. Fehlt an einem nur-PIN-Geraet: Dort zeigt schon die * Benutzerliste keine Namen, und was dort nicht steht, darf hier nicht * durch die Hintertuer kommen. `undefined` heisst „wird nicht genannt", * nicht „hat keinen Namen". */ userName?: string; } /** Was eine Kasse gerade haelt -- Lizenzzahl und laufende Sitzungen. */ export interface RegisterSessionOverview { /** Wie viele Sitzungen gleichzeitig laufen duerfen. */ licenses: number; /** Aelteste zuerst: oben steht, was das Backend ohne Wahl verdraengen wuerde. */ sessions: RegisterSession[]; } export interface ListRegisterSessionsForDeviceOptions extends RegisterDeviceConnection, RegisterDeviceCredentials { } /** * Welche Sitzungen haelt diese Kasse gerade? * * Fuer den Anmeldebildschirm: Sind alle Lizenzplaetze belegt, muss eine * weichen -- und der Kassier soll sehen, WELCHE, statt dass das Backend still * die aelteste nimmt. Die Wahl geht dann als * [RegisterUserLoginOptions.takeoverSessionId] mit. * * Ausgewiesen wird sich ueber das Geraete-Geheimnis, wie bei * [listRegisterUsersForDevice]: An diesem Punkt gibt es weder Sitzung noch * Token. Die Kasse bestimmt das Backend aus dem Geraet, nicht aus dem Aufruf. */ export declare function listRegisterSessionsForDevice(options: ListRegisterSessionsForDeviceOptions): Promise; export declare function listRegisterUsersForDevice(options: ListRegisterUsersForDeviceOptions): Promise; /** * Kassen-Benutzer per PIN anmelden. **Ohne Anmeldung** — dieser Aufruf * erzeugt sie: das Custom Token der Antwort wird zum ID-Token, die `sessionId` * zur Kopfzeile `register-session`. Beides zusammen ergibt `registerUserAuth`. * * Das Backend prueft in dieser Reihenfolge: Geraet, Benutzer, Sperre, PIN, * Kassenzuweisung, Kopplungsbindung, Lizenzplatz. Fehlversuche zaehlen und * sperren gestaffelt (ab dem fuenften 30 Sekunden, ab dem neunten 15 Minuten). */ export declare function registerUserLogin(options: RegisterUserLoginOptions): Promise; /** * Kassen-Benutzer allein mit der PIN anmelden (Geraete-Modus `pin`). **Ohne * Anmeldung** — wie [registerUserLogin], nur ohne Benutzerauswahl: das Backend * ermittelt den Benutzer ueber die betriebsweit eindeutige PIN. Fehlversuche * zaehlen und sperren dort am **Geraet**, nicht an einem Benutzer. */ export declare function registerPinLogin(options: RegisterPinLoginOptions): Promise;