openapi: 3.1.0
info:
  title: Quotavo REST API
  version: 0.1.0-draft
  description: |
    HTTP-API für Quotavo — Cloud ERP für Angebote, Rechnungen und Kundenverwaltung.

    **Status:** In Vorbereitung. Die öffentliche API mit API-Schlüsseln wird schrittweise freigeschaltet.
    Die aktuelle Web-App nutzt eine session-basierte REST-API (Cookie + CSRF).

    OpenAPI-Datei: `/openapi.yaml` · Dokumentation: `/api-dokumentation`
  contact:
    name: Quotavo Support
    email: klemmer.robert.96@gmail.com
  license:
    name: Proprietary — Nutzung nur nach Vereinbarung

servers:
  - url: https://www.quotavo.de/api
    description: Produktion
  - url: http://localhost:3002/api
    description: Lokale Entwicklung (Spring)

tags:
  - name: Auth
    description: Registrierung, Anmeldung und Sitzung
  - name: Kunden
    description: Kundenstamm und Einsatzorte
  - name: Angebote
    description: Angebote erstellen und verwalten
  - name: Rechnungen
    description: Rechnungen und E-Rechnung-Export
  - name: Katalog
    description: Wiederverwendbare Leistungen und Materialien
  - name: Public
    description: Öffentliche Endpunkte ohne Anmeldung

paths:
  /auth/csrf:
    get:
      tags: [Auth]
      summary: CSRF-Token abrufen
      description: Liefert ein CSRF-Token für mutierende Requests der Web-App (Session-Cookie erforderlich).
      operationId: getCsrfToken
      responses:
        '200':
          description: CSRF-Token
          content:
            application/json:
              schema:
                type: object
                properties:
                  csrfToken:
                    type: string

  /auth/login:
    post:
      tags: [Auth]
      summary: Anmelden
      operationId: login
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [email, password]
              properties:
                email:
                  type: string
                  format: email
                password:
                  type: string
                  minLength: 8
      responses:
        '200':
          description: Angemeldet — Session-Cookie wird gesetzt
        '401':
          description: Ungültige Zugangsdaten

  /auth/me:
    get:
      tags: [Auth]
      summary: Aktuelle Sitzung
      operationId: getMe
      security:
        - sessionCookie: []
      responses:
        '200':
          description: Benutzer, Mandant und CSRF-Token
        '401':
          description: Nicht angemeldet

  /kunden:
    get:
      tags: [Kunden]
      summary: Kunden auflisten
      operationId: listKunden
      security:
        - sessionCookie: []
        - apiKey: []
      responses:
        '200':
          description: Liste der Kunden des Mandanten
    post:
      tags: [Kunden]
      summary: Kunde anlegen oder aktualisieren
      operationId: saveKunde
      security:
        - sessionCookie: []
        - apiKey: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Kunde'
      responses:
        '200':
          description: Gespeicherter Kunde

  /kunden/{id}:
    get:
      tags: [Kunden]
      summary: Kunde abrufen
      operationId: getKunde
      security:
        - sessionCookie: []
        - apiKey: []
      parameters:
        - $ref: '#/components/parameters/ResourceId'
      responses:
        '200':
          description: Kunde
        '404':
          description: Nicht gefunden
    delete:
      tags: [Kunden]
      summary: Kunde löschen
      operationId: deleteKunde
      security:
        - sessionCookie: []
        - apiKey: []
      parameters:
        - $ref: '#/components/parameters/ResourceId'
      responses:
        '204':
          description: Gelöscht

  /angebote:
    get:
      tags: [Angebote]
      summary: Angebote auflisten
      operationId: listAngebote
      security:
        - sessionCookie: []
        - apiKey: []
      responses:
        '200':
          description: Liste der Angebote
    post:
      tags: [Angebote]
      summary: Angebot speichern
      operationId: saveAngebot
      security:
        - sessionCookie: []
        - apiKey: []
      responses:
        '200':
          description: Gespeichertes Angebot

  /angebote/{id}/confirmation-link:
    post:
      tags: [Angebote]
      summary: Öffentlichen Bestätigungslink erzeugen
      operationId: createConfirmationLink
      security:
        - sessionCookie: []
        - apiKey: []
      parameters:
        - $ref: '#/components/parameters/ResourceId'
      responses:
        '200':
          description: Link-URL und Token

  /rechnungen:
    get:
      tags: [Rechnungen]
      summary: Rechnungen auflisten
      operationId: listRechnungen
      security:
        - sessionCookie: []
        - apiKey: []
      responses:
        '200':
          description: Liste der Rechnungen
    post:
      tags: [Rechnungen]
      summary: Rechnung speichern
      operationId: saveRechnung
      security:
        - sessionCookie: []
        - apiKey: []
      responses:
        '200':
          description: Gespeicherte Rechnung

  /rechnungen/export/zugferd:
    post:
      tags: [Rechnungen]
      summary: ZUGFeRD-PDF erzeugen
      operationId: exportZugferd
      security:
        - sessionCookie: []
        - apiKey: []
      responses:
        '200':
          description: PDF mit eingebetteter XML

  /rechnungen/export/xrechnung:
    post:
      tags: [Rechnungen]
      summary: XRechnung (CII-XML) exportieren
      operationId: exportXrechnung
      security:
        - sessionCookie: []
        - apiKey: []
      responses:
        '200':
          description: XML-Datei

  /rechnungen/export/datev:
    post:
      tags: [Rechnungen]
      summary: DATEV EXTF exportieren
      description: Ab Plus-Tarif.
      operationId: exportDatev
      security:
        - sessionCookie: []
        - apiKey: []
      responses:
        '200':
          description: CSV-Buchungsstapel

  /katalog-posten:
    get:
      tags: [Katalog]
      summary: Katalog-Posten auflisten
      operationId: listKatalogPosten
      security:
        - sessionCookie: []
        - apiKey: []
      responses:
        '200':
          description: Katalog-Einträge

  /public/angebote/{token}:
    get:
      tags: [Public]
      summary: Angebots-Bestätigungsseite (Metadaten)
      operationId: getPublicAngebot
      parameters:
        - name: token
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Öffentliche Angebotsinformationen

  /public/angebote/{token}/respond:
    post:
      tags: [Public]
      summary: Angebot bestätigen oder ablehnen
      operationId: respondPublicAngebot
      parameters:
        - name: token
          in: path
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                plz:
                  type: string
                action:
                  type: string
                  enum: [accept, reject]
      responses:
        '200':
          description: Antwort gespeichert

components:
  securitySchemes:
    sessionCookie:
      type: apiKey
      in: cookie
      name: angebot.sid
      description: Session-Cookie der Web-App (intern, Same-Origin).
    apiKey:
      type: apiKey
      in: header
      name: Authorization
      description: |
        **Geplant:** `Bearer qv_live_…` — Mandanten-API-Schlüssel.
        Noch nicht aktiv. Interesse? klemmer.robert.96@gmail.com

  parameters:
    ResourceId:
      name: id
      in: path
      required: true
      schema:
        type: string

  schemas:
    Kunde:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        email:
          type: string
          format: email
        strasse:
          type: string
        plz:
          type: string
        ort:
          type: string

    Error:
      type: object
      properties:
        error:
          type: string
