PRD-001: Cartly ERP+POS SaaS — Phase 1: Technical Groundwork

|||> Status: APPROVED by QA + CPTO (2026-07-03) — Ready for Implementation
|||> Version: 0.8-CPTO
|> CTO Sign-off: 2026-07-03 — Technology Stack, Cloud Architecture, Data Model vollständig und genehmigt
|> CPTO Sign-off: 2026-07-03 — Full PRD reviewed and approved
|> Erstellt: 2026-07-03
|> Owner: Product Manager
|> Issue: PRD-001
|> QA Review: 2026-07-03 — 5 Critical Issues, 6 Partial Issues — PM-Fixes in v0.4 adressiert
|> Verbleibend: CI-003 (CTO: Tech Stack/Cloud/Data Model)


QA REVIEW (2026-07-03 v0.6) — CONDITIONS MET

Status: ✅ APPROVED (nach Bereinigung)

Das PRD v0.6 ist abnahmefähig. Alle vorherigen Critical Issues sind adressiert.

ID Section Letzter Stand
CI-001 Vision & Mission ✅ FIXED (v0.3)
CI-002 Pain Points ✅ FIXED (v0.3)
CI-003 Tech Stack / Cloud / Data Model ✅ CTO-APPROVED (v0.5)
CI-004 Core Workflows ✅ FIXED (v0.3)
CI-005 Attributes Priority ✅ FIXED (v0.4)

Verbleibende Hinweise (blockieren NICHT, aber beachten)

ID Issue Empfehlung
PI-001 2 Personas vorhanden, aber unvalidiert Vor Beta-Start mit echten Nutzern validieren
PI-002 Keine Gherkin Akzeptanzkriterien Vor Implementierung nachziehen (Issue an DEV)
PI-003 Store-spezifische Konfiguration partial Vor Phase-2 finalisieren
PI-004 API-Endpunkte nicht detailliert API-Spezifikation als separates Issue (Implementierung)
PI-005 Mitigation-Massnahmen ✅ FIXED
PI-006 Einige Offene Fragen noch "offen" MVP-Deadline + Beta-Kunden als prioritär klären

Bereinigung erfolgt (QA-Patch)

Verdict: ✅ APPROVED — PRD ist bereit für CPTO-Review und Implementierungsstart.


1. Produkt-Übersicht

Vision

Cartly befähigt kleine Fashion-Einzelhändler, ihre Filialen effizient zu führen — ohne Enterprise-Komplexität. In 5 Jahren ist Cartly die Standardlösung für 1–20-Mitarbeiter-Fashion-Businesses im DACH-Raum.

Mission

Kleine Retailer verdienen dieselbe Technologie wie große Ketten. Wir bauen eine Plattform, die einfach funktioniert: kein Consulting, keine Implementierungsprojekte, keine monatelange Einführung. Anmelden, verkaufen, wachsen.

Problem Statement

Aktuelle Situation:

Konsequenz:

Lösung

Cartly: Eine integrierte ERP+POS SaaS-Plattform speziell für Fashion Retailer.

Positioning Statement

Für kleinere Fashion-Einzelhändler, die eine einfache, cloud-basierte Lösung für Verkauf und Verwaltung suchen, ist Cartly eine All-in-One SaaS-Plattform, die POS und ERP vereint — ohne die Komplexität etablierter Enterprise-Systeme.


2. Zielgruppe

Primäre Zielgruppe

User Profile

|| User Type | Role | Hauptaufgaben |
|-----------|------|---------------|
|| Owner/Admin | Entscheidungsträger | Einrichtung, Reporting, Kostenkontrolle |
|| Store Manager | Tagesgeschäft | Verkauf, Bestellung, Inventur |
|| Sales Associate | Verkauf | Kassieren, Kundenberatung |

Persona: Sarah — Store Managerin (Primary User)

Feld Beschreibung
Name Sarah, 34
Rolle Store Managerin — führt den Laden allein, 2 Teilzeitkräfte
Tagesablauf Öffnet Laden → verkauft → macht Kasse → bestellt nach → macht Inventur
Tech-Skills WhatsApp, Instagram, Online-Banking. Kein ERP, kein POS-Training.
Pain Points "Ich verliere den Überblick, was noch da ist. Am liebsten würde ich abends in einer App sehen, was heute war."
Zitat "Ich will nicht stundenlang am PC sitzen. Das muss so einfach sein wie mein Handy."
Ziel mit Cartly 5 Minuten morgens, um zu sehen was los ist. schnell kassieren, schnell nachbestellen.

Persona: Marcus — Owner (Decision Maker)

Feld Beschreibung
Name Marcus, 48
Rolle Inhaber — 2 Filialen, überlegt eine dritte
Background BWL, hat bisher mit Excel und einer alten Kassensoftware gearbeitet
Pain Points "Ich sehe erst am Monatsende, wie es läuft. Und meine Mitarbeiterin muss alles zweimal eingeben."
Zitat "Ich will von überall sehen können, wie meine Läden laufen — ohne jemanden fragen zu müssen."
Ziel mit Cartly Echtzeit-Dashboard über alle Filialen. Kein Medienbruch mehr. Korrekte Zahlen auf Knopfdruck.

Pain Points

  1. [PAIN 1] Medienbruch POS ↔ Verwaltung — Verkäufe landen in der Kasse, der Owner muss dieselben Daten manuell in eine Buchhaltungssoftware übertragen. Zweifache Erfassung, doppelter Aufwand, Fehlerquellen.
  2. [PAIN 2] Kein Echtzeit-Überblick — Der Owner erfährt am Monatsende, wie der Laden lief — nicht früher. Keine zentrale Sicht auf Umsatz, Bestand oder Margin über alle Filialen.
  3. [PAIN 3] Fragmentierte Tools — Separate Kasse (oft stationär), separates Warenwirtschaftssystem, separates Reporting-Tool, ggf. noch eine Excel-Tabelle. Kein einheitliches Datenbild, keine durchgängige Customer Journey.

3. User Stories & Features

User Roles

Phase 1 Features (Technical Groundwork)

Must-Have

Should-Have

Nice-to-Have (Phase 2+)


4. Funktionale Anforderungen

Authentication & Authorization

ID Requirement Priority Notes
FA-001 Email/Password Login Must
FA-002 Password Reset Flow Must
FA-003 Session Management Must JWT-based
FA-004 Role-Based Access Control Must Admin/Manager/Sales

Platform Management

ID Requirement Priority Notes
PM-001 Company anlegen & bearbeiten Must
PM-002 Stores anlegen (1-n) Must
PM-003 User Management (CRUD) Must
PM-004 Rechte/Rollen zuweisen Must
PM-005 Stammdaten: Products Must
PM-006 Stammdaten: Categories Must
PM-007 Stammdaten: Attributes (Size, Color, Season) Must Fashion-spezifisch — ohne Größen/Farben kein Fashion-Sortiment möglich

5. Nicht-funktionale Anforderungen

Performance

Security

Scalability

Accessibility


6. Technische Anforderungen

6.1 Technology Stack (CPTO Decision)

Layer Technology Rationale
Frontend React 18 + TypeScript + Vite TypeScript从头强Typisierung, Vite = schnell Builds, exzellentes PWA-Ökosystem (Workbox)
UI-Komponenten Radix UI + Tailwind CSS Accessible Components, Customizable, Fashion-Retail Brand-Ready
State Management Zustand + TanStack Query Leichtgewichtig, TypeScript-nativ, Server State via TanStack
Backend Node.js + Fastify + TypeScript Schnellster HTTP-Layer (Benchmark-Sieger), TypeScript-nativ, Plugin-Ökosystem
ORM Prisma Type-safe, Auto-Migrations, Multi-Tenant via tenant_id,.excellente DX
Database PostgreSQL 16 (Supabase / Neon) Zuverlässig, JSONB für flexible Schemata, Row-Level Security für Multi-Tenant
Cache / Session Redis (Upstash) Serverless-kompatibel, niedrige Latenz,Pub/Sub für Real-time
Auth JWT (Access + Refresh) + HTTP-only Cookies Bewährt, keine Server-Side Session nötig, GDPR-konform
File Storage S3 (Backblaze B2 als Backup) Günstig, skalierbar, CDN-fertig
Search Meilisearch Open Source, schnell, typo-tolerant, Fashion-spezifisch (Facets für Größe/Farbe)
AI OpenRouter (einheitlicher Proxy) Provider-agnostisch, Kostenkontrolle, Single-Endpoint
Infrastructure Docker + Railway / Render (MVP) → EKS (Scale) Railway = schnell deployed, keine AWS-Komplexität für MVP; Migration zu Kubernetes bei Scale
Monitoring Sentry (Errors) + Highlight.io (Sessions) Error Tracking + Session Replay für schnellere Debugging
CI/CD GitHub Actions Integriert, kostenlos für Open Source-Repos, Actions-Ökosystem

Warum NICHT andere Optionen:

Cloud Architecture (CPTO Decision)

                          ┌─────────────────────────────────────────────────────────────┐
                          │                     AWS / Cloudflare                           │
                          │  CloudFront CDN ──► S3 (Static Assets) + Route 53            │
                          └─────────────────────────────────────────────────────────────┘
                                                    │
                                                    ▼
┌──────────────────┐      HTTPS/TLS      ┌─────────────────────────────────────────────┐
│   Browser (PWA)  │◄──────────────────► │          Application Load Balancer            │
│                  │                      └─────────────────────────────────────────────┘
└──────────────────┘                                │                    │
                                                    ▼                    ▼
                              ┌─────────────────────────────┐  ┌─────────────────────────┐
                              │   API Server (Fastify)       │  │  Web Server (Static)    │
                              │   Container: Node.js        │  │  Container: Nginx       │
                              │   - JWT Auth Middleware       │  │  - SPA serving           │
                              │   - Rate Limiting             │  │  - Asset caching          │
                              │   - Tenant Routing            │  └─────────────────────────┘
                              │   - AI Proxy (OpenRouter)     │
                              └─────────────────────────────┘
                                       │        │        │
                          ┌────────────┘        │        └────────────┐
                          ▼                     ▼                      ▼
           ┌──────────────────────┐  ┌────────────────┐  ┌────────────────────────┐
           │   PostgreSQL 16      │  │    Redis       │  │   S3 / Backblaze B2   │
           │   - Row-Level Sec.   │  │   - Sessions   │  │   - Product Images    │
           │   - tenant_id FKs    │  │   - Cache      │  │   - Exports           │
           │   - pgcrypto         │  │   - Pub/Sub    │  │   - Backups           │
           └──────────────────────┘  └────────────────┘  └────────────────────────┘
                          │
                          ▼
               ┌──────────────────────┐
               │   Meilisearch        │
               │   - Product Search   │
               │   - Typo-tolerant    │
               └──────────────────────┘

Multi-Tenant Strategie:

Infrastructure als Code:

Security:

API Strategy

Data Model (CPTO Decision)

┌─────────────────────────────────────────────────────────────────────────────┐
│                              CORE ENTITIES                                   │
└─────────────────────────────────────────────────────────────────────────────┘

┌──────────────┐       ┌──────────────┐       ┌──────────────┐
│   Company    │──────►│    Store     │──────►│   User       │
│──────────────│  1:n  │──────────────│  1:n  │──────────────│
│ id (PK)      │       │ id (PK)      │       │ id (PK)       │
│ name         │       │ company_id(FK)│       │ store_id (FK) │
│ settings     │       │ name         │       │ email         │
│ theme        │       │ address      │       │ password_hash│
│ tier         │       │ timezone     │       │ role         │
│ created_at   │       │ currency     │       │ created_at   │
└──────────────┘       │ is_active    │       └──────────────┘
                       └──────────────┘                │
                                                       │ n:1
                                                       ▼
┌──────────────┐       ┌──────────────┐       ┌──────────────┐
│   Category   │◄──────│   Product    │──────►│   SKU        │
│──────────────│  1:n  │──────────────│  1:n  │──────────────│
│ id (PK)      │       │ id (PK)      │       │ id (PK)      │
│ company_id(FK)│       │ company_id(FK)│       │ product_id(FK)│
│ parent_id(FK) │       │ category_id │       │ sku_code     │
│ name         │       │ name         │       │ price        │
│ slug         │       │ description  │       │ cost_price   │
│ sort_order   │       │ images[]     │       │ stock_qty    │
│ is_active    │       │ attributes{} │       │ size         │
└──────────────┘       │ is_active    │       │ color        │
                       └──────────────┘       │ barcode      │
                                              └──────────────┘
                                                       │
                                                       │ 1:n
                                                       ▼
                                              ┌──────────────┐
                                              │   SaleItem   │
                                              │──────────────│
                                              │ id (PK)      │
                                              │ sale_id (FK) │
                                              │ sku_id (FK)  │
                                              │ qty          │
                                              │ unit_price   │
                                              │ discount     │
                                              └──────────────┘

┌──────────────┐       ┌──────────────┐
│    Sale      │──────►│   Customer   │
│──────────────│  n:1  │──────────────│
│ id (PK)      │       │ id (PK)      │
│ store_id(FK) │       │ company_id   │
│ user_id(FK)  │       │ email        │
│ customer_id  │       │ name         │
│ total        │       │ phone        │
│ tax          │       │ created_at   │
│ payment_method│      │ preferences{}│
│ status       │       └──────────────┘
│ created_at   │
└──────────────┘

Key Design Decisions:


6.1 Technische Leitlinien (Architectural Principles)

Diese Leitlinien sind für ALLE Entwicklungsarbeit bindend:

6.1.1 Code-Qualität & Best Practices

6.1.2 Third-Party & Open Source

6.1.3 Programming Languages

6.1.4 PWA (Progressive Web App)

6.1.5 Custom Themes & CI

6.1.6 Versionierte API

6.1.7 Update & Deployment Strategie

6.1.8 Legal & Compliance


6.2 Pricing-Modell

6.2.1 Tiers: Good / Better / Best

Feature Good Better Best
API Calls/Monat 100 1.000 10.000+
Stores 1 3 unlimited
User 2 10 unlimited
Reports Basic Advanced Full
Support Email Email + Chat Dedicated
Custom Theme
Custom Domain
Beta Features
SLA 99% 99.5% 99.9%

6.2.2 Feature Flags

6.2.3 Beispiel Feature-Flag Struktur

features:
  - id: advanced_analytics
    tier: better
    limits:
      api_calls_per_month: 1000
      max_users: 10
    beta: false
  - id: pos_offline_mode
    tier: best
    limits:
      api_calls_per_month: 10000
    beta: true

7. User Flows

Onboarding Flow

Sign Up → Email Verification → Company Setup → First Store → Invite Team → Dashboard

Core Workflows

WF-1: Kassieren (Point of Sale)

1. Sales Associate meldet sich am Tablet/Web-App an
2. Produkt aus Katalog wählen (Suchbegriff, Barcode-Scan, Kategorie-Browse)
3. Warenkorb wird geführt — Positionen können entfernt/angepasst werden
4. Zahlung erfassen (Bar, Kartenzahlung, Split-Payment)
5. Beleg wird erzeugt (digital + Druck)
6. Bestand wird in Echtzeit reduziert
7. Transaktion wird im Backend gebucht

WF-2: Produkt anlegen (Stammdaten)

1. Admin/Manager öffnet Produktverwaltung
2. Produktdetails erfassen: Name, SKU, Preis, Kategorie, Attribute (Größe/Farbe/Saison)
3. Bestand initial setzen (pro Filiale)
4. Produkt wird im Katalog sichtbar
5. optional: Produktfoto hochladen

WF-3: Bestellung aufgeben (Store → Lager/Zentrale)

1. Manager öffnet Bestellmaske
2. Produkte + Mengen auswählen (Bestand prüfen)
3. Bestellung absenden → an Zentrallager/Supplier
4. Wareneingang wird im System gebucht
5. Bestand wird aktualisiert

WF-4: Reporting

1. Owner/Manager öffnet Dashboard
2. Zeitraum wählen (Tag, Woche, Monat,自定义)
3. Kennzahlen sehen: Umsatz, Anzahl Transaktionen, Ø Warenkorbwert, Top-Produkte

8. Priorisierung

MoSCoW (Phase 1)

Priority Features
Must Auth, RBAC, Company Setup, Store Setup, User Management, Product Stammdaten
Should Dashboard, Basic Reporting, Category Management
Could Notifications, User Preferences
Won't (Phase 1) CRM, Supplier Management, Advanced Analytics

6.3 AI Features

6.3.1 KI-Integration

6.3.2 Mögliche AI-Anwendungsfälle (Phase 2+)

6.3.3 AI Technical Requirements


8. Risiken & Offene Fragen

Annahmen (zu validieren)

Technische Risiken & Mitigation

|| ID | Risiko | Wahrscheinlichkeit | Impact | Mitigation |
|----|-------|-------------------|--------|------------|
| TR-001 | Multi-Tenant Performance — viele Kunden auf Shared Infrastructure | Mittel | Hoch | PostgreSQL Row-Level Security + Connection Pooling (PgBouncer); Performance-Tests mit repräsentativen Lastprofilen |
| TR-002 | Payment Integration Komplexität | Hoch | Hoch | Stripe als Primary Payment Processor (einfache API, gute Doku); Andere Provider erst in späteren Phasen |
| TR-003 | TSE-Integration (Deutschland) | Mittel | Hoch | Cloud-basierte TSE (fiskaltray.cloud);kein lokales Hardware-Dependency;_frühzeitige Einbindung eines Steuerberaters |
| TR-004 | Beta-Kunden finden sich nicht | Mittel | Hoch | Chief of Staff.startet Outreach in Netzwerk; Early-Adopter-Anreiz (3 Monate gratis) |
| TR-005 | Scope Creep durch Kundenfeedback | Hoch | Mittel | MoSCoW-Priorisierung enforces; keine Features außerhalb Phase 1 ohne CPTO-Entscheidung |

Offene Fragen mit Owner & Deadline

Frage Owner Status Deadline
MVP-Deadline? Chief of Staff / CPTO offen 2026-07-10
Erste Beta-Kunden? Chief of Staff offen 2026-07-15
Payment-Integration MUST-HAVE? CPTO offen 2026-07-10
Accounting-Integration MUST-HAVE? CPTO offen 2026-07-10
CI/CD Toolchain gewählt? CTO offen 2026-07-15
Hosting-Provider gewählt? CTO offen 2026-07-15

9. Glossary

Term Definition
POS Point of Sale — Kassensystem
ERP Enterprise Resource Planning — Unternehmenssoftware
Stammdaten Master Data — zentrale Referenzdaten (Products, Customers, etc.)
RBAC Role-Based Access Control
Multi-Tenant Eine Instanz bedient mehrere Kunden


10. Gherkin Akzeptanzkriterien (QA-001)

Diese Szenarien definieren die Abnahmebedingungen für Phase-1 Must-Have Features.
Jedes Szenario folgt dem Given-When-Then Muster und wird als automatisierter Test implementiert.


10.1 Authentication & Authorization (FA-001 – FA-004)

AC-AUTH-001: Benutzer-Registrierung (Happy Path)

Feature: Benutzer-Registrierung

  Scenario: Erfolgreiche Registrierung eines neuen Benutzers
    Given der Benutzer sich nicht im System befindet
    And die Company "Fashion Store GmbH" existiert
    When der Benutzer sich mit Email "sarah@example.com" und Passwort "SecurePass123!" registriert
    Then wird ein neues User-Konto erstellt
    And die Email-Adresse "sarah@example.com" ist verifiziert
    And der User hat die Rolle "Admin" innerhalb der Company
    And der Benutzer erhält eine JWT mit Gültigkeit von maximal 15 Minuten

AC-AUTH-002: Registrierung mit ungültiger Email

  Scenario: Registrierung mit invalider Email-Adresse schlägt fehl
    Given der Benutzer sich nicht im System befindet
    When der Benutzer sich mit Email "invalid-email" und Passwort "SecurePass123!" registriert
    Then wird eine Fehlermeldung zurückgegeben: "Ungültige Email-Adresse"
    And kein User-Konto wird erstellt

AC-AUTH-003: Registrierung mit weakem Passwort

  Scenario: Registrierung mit zu kurzem Passwort schlägt fehl
    Given der Benutzer sich nicht im System befindet
    When der Benutzer sich mit Email "sarah@example.com" und Passwort "123" registriert
    Then wird eine Fehlermeldung zurückgegeben: "Passwort muss mindestens 8 Zeichen haben"
    And kein User-Konto wird erstellt

AC-AUTH-004: Doppelte Email-Registrierung

  Scenario: Bereits registrierte Email kann nicht erneut registriert werden
    Given der Benutzer mit Email "sarah@example.com" existiert bereits
    When der Benutzer sich mit Email "sarah@example.com" und Passwort "SecurePass123!" registriert
    Then wird eine Fehlermeldung zurückgegeben: "Email-Adresse bereits registriert"
    And kein zweites User-Konto wird erstellt

AC-AUTH-005: Erfolgreicher Login

Feature: Benutzer-Login

  Scenario: Erfolgreicher Login mit gültigen Credentials
    Given der User "sarah@example.com" existiert mit Passwort-Hash
    And der User ist nicht aktuell eingeloggt
    When der Benutzer sich mit Email "sarah@example.com" und Passwort "SecurePass123!" einloggt
    Then wird ein Access-Token (JWT) zurückgegeben
    And ein Refresh-Token wird als HTTP-only Cookie gesetzt
    And die Session ist 24 Stunden gültig

AC-AUTH-006: Login mit falschem Passwort

  Scenario: Login mit falschem Passwort scheitert
    Given der User "sarah@example.com" existiert mit Passwort-Hash
    When der Benutzer sich mit Email "sarah@example.com" und Passwort "WrongPassword!" einloggt
    Then wird HTTP 401 zurückgegeben
    And die Fehlermeldung lautet: "Ungültige Anmeldedaten"
    And kein Access-Token wird ausgestellt

AC-AUTH-007: Login mit nicht-existierender Email

  Scenario: Login mit unbekannter Email scheitert
    Given kein User mit Email "unknown@example.com" existiert
    When der Benutzer sich mit Email "unknown@example.com" und Passwort "AnyPass123!" einloggt
    Then wird HTTP 401 zurückgegeben
    And die Fehlermeldung lautet: "Ungültige Anmeldedaten"

AC-AUTH-008: Password Reset Flow

  Scenario: Password Reset Email wird gesendet
    Given der User "sarah@example.com" existiert
    When der Benutzer einen Password-Reset für "sarah@example.com" anfordert
    Then wird eine Email mit Reset-Link an "sarah@example.com" gesendet
    And der Reset-Token ist 1 Stunde gültig
    And der Reset-Token kann nur einmal verwendet werden

AC-AUTH-009: Password Reset mit ungültigem Token

  Scenario: Password Reset mit abgelaufenem Token scheitert
    Given ein abgelaufener Reset-Token existiert für "sarah@example.com"
    When der Benutzer einen neues Passwort "NewSecure123!" mit dem abgelaufenen Token setzt
    Then wird HTTP 400 zurückgegeben
    And die Fehlermeldung lautet: "Reset-Token ist abgelaufen oder ungültig"

AC-AUTH-010: JWT Access Token läuft ab

  Scenario: API-Zugriff mit abgelaufenem Access-Token wird abgelehnt
    Given der Benutzer hat einen abgelaufenen Access-Token
    When der Benutzer eine API-Anfrage mit diesem Token stellt
    Then wird HTTP 401 zurückgegeben
    And die Fehlermeldung lautet: "Token abgelaufen"

AC-AUTH-011: Refresh Token Rotation

  Scenario: Refresh Token wird bei Login ausgetauscht (Rotation)
    Given der Benutzer hat einen gültigen Refresh-Token
    When der Benutzer sich mit Email und Passwort einloggt
    Then wird ein neuer Refresh-Token ausgestellt
    And der alte Refresh-Token wird invalidiert
    And der alte Refresh-Token kann nicht mehr verwendet werden

10.2 Role-Based Access Control (RBAC) (FA-004)

AC-RBAC-001: Admin kann alle Ressourcen sehen

Feature: RBAC — Admin Zugriff

  Scenario: Admin sieht alle Stores der Company
    Given der User "admin@store.com" hat die Rolle "Admin"
    And die Company hat 3 Stores: "Store Berlin", "Store Hamburg", "Store München"
    When der Admin eine GET-Anfrage auf "/api/v1/stores" stellt
    Then werden alle 3 Stores zurückgegeben

AC-RBAC-002: Admin kann neue User anlegen

  Scenario: Admin kann einen neuen User erstellen
    Given der User "admin@store.com" hat die Rolle "Admin"
    When der Admin einen POST auf "/api/v1/users" mit Email "newuser@store.com" stellt
    Then wird HTTP 201 zurückgegeben
    And der neue User "newuser@store.com" existiert

AC-RBAC-003: Manager kann nur eigenen Store sehen

Feature: RBAC — Manager Zugriff

  Scenario: Manager sieht nur seinen zugewiesenen Store
    Given der User "manager@store.com" hat die Rolle "Manager"
    And der Manager ist dem Store "Store Berlin" zugewiesen
    And die Company hat weitere Stores: "Store Hamburg", "Store München"
    When der Manager eine GET-Anfrage auf "/api/v1/stores" stellt
    Then wird nur "Store Berlin" zurückgegeben
    And "Store Hamburg" und "Store München" sind nicht in der Antwort

AC-RBAC-004: Manager kann keine User anderer Stores verwalten

  Scenario: Manager kann keine User außerhalb seines Stores anlegen
    Given der User "manager@store.com" hat die Rolle "Manager"
    And der Manager ist dem Store "Store Berlin" zugewiesen
    When der Manager einen POST auf "/api/v1/users" mit Email "other@store.com" und Store "Store Hamburg" stellt
    Then wird HTTP 403 zurückgegeben
    And die Fehlermeldung lautet: "Zugriff verweigert: Store nicht zugewiesen"

AC-RBAC-005: Sales Associate kann keine Admin-Endpoints aufrufen

Feature: RBAC — Sales Zugriff

  Scenario: Sales Associate wird am User-Management Endpunkt blockiert
    Given der User "sales@store.com" hat die Rolle "Sales"
    When der Sales Associate eine GET-Anfrage auf "/api/v1/users" stellt
    Then wird HTTP 403 zurückgegeben
    And die Fehlermeldung lautet: "Unzureichende Berechtigungen"

AC-RBAC-006: Sales Associate kann Verkauf starten

  Scenario: Sales Associate kann einen Verkauf abschließen
    Given der User "sales@store.com" hat die Rolle "Sales"
    And der Sales Associate ist dem Store "Store Berlin" zugewiesen
    And Produkt "T-Shirt Basic" mit SKU "TS-001" existiert im Store
    And Bestand von SKU "TS-001" ist 10
    When der Sales Associate einen POST auf "/api/v1/sales" mit SKU "TS-001" und Menge 2 stellt
    Then wird HTTP 201 zurückgegeben
    And der Verkauf wird in der Datenbank gespeichert
    And Bestand von SKU "TS-001" wird auf 8 reduziert

AC-RBAC-007: Unautorisierter Zugriff auf fremde Company-Daten

  Scenario: User kann nicht auf Daten einer anderen Company zugreifen
    Given User "user@companyA.com" gehört zu Company "CompanyA"
    And User ist nicht Mitglied von Company "CompanyB"
    When der User "user@companyA.com" eine GET-Anfrage auf "/api/v1/companies/CompanyB/stores" stellt
    Then wird HTTP 403 zurückgegeben
    And die Fehlermeldung lautet: "Zugriff verweigert"

AC-RBAC-008: Role-Upgrade ohne Berechtigung verhindert

  Scenario: User kann eigene Rolle nicht selbst ändern
    Given der User "sales@store.com" hat die Rolle "Sales"
    When der Sales Associate einen PUT auf "/api/v1/users/me" mit neuer Rolle "Admin" stellt
    Then wird HTTP 403 zurückgegeben
    And die Rolle bleibt "Sales"

10.3 Company & Store Setup (PM-001, PM-002)

AC-SETUP-001: Company erfolgreich anlegen

Feature: Company-Verwaltung

  Scenario: Admin legt neue Company an
    Given der User "owner@newstore.com" ist authentifiziert
    When der User eine POST-Anfrage auf "/api/v1/companies" mit folgendem Body stellt:
      """
      {
        "name": "Fashion Store GmbH",
        "tier": "good",
        "timezone": "Europe/Berlin",
        "currency": "EUR"
      }
      """
    Then wird HTTP 201 zurückgegeben
    And die Company "Fashion Store GmbH" existiert in der Datenbank
    And der User "owner@newstore.com" ist Admin dieser Company

AC-SETUP-002: Company-Name bereits vorhanden

  Scenario: Doppelter Company-Name wird abgelehnt
    Given die Company "Fashion Store GmbH" existiert bereits
    When ein User eine POST-Anfrage auf "/api/v1/companies" mit Name "Fashion Store GmbH" stellt
    Then wird HTTP 409 zurückgegeben
    And die Fehlermeldung lautet: "Company-Name bereits vergeben"

AC-SETUP-003: Store zu Company hinzufügen

Feature: Store-Verwaltung

  Scenario: Admin legt neuen Store an
    Given die Company "Fashion Store GmbH" existiert
    And der User "admin@store.com" ist Admin der Company
    When der Admin eine POST-Anfrage auf "/api/v1/companies/{companyId}/stores" mit folgendem Body stellt:
      """
      {
        "name": "Store Berlin",
        "address": "Alexanderplatz 1, 10178 Berlin",
        "timezone": "Europe/Berlin",
        "currency": "EUR"
      }
      """
    Then wird HTTP 201 zurückgegeben
    And der Store "Store Berlin" existiert
    And der Store ist der Company "Fashion Store GmbH" zugeordnet

AC-SETUP-004: Store-Name im Scope der Company eindeutig

  Scenario: Store-Name muss innerhalb der Company eindeutig sein
    Given die Company "Fashion Store GmbH" hat bereits einen Store "Store Berlin"
    When der Admin einen neuen Store "Store Berlin" für dieselbe Company anlegt
    Then wird HTTP 409 zurückgegeben
    And die Fehlermeldung lautet: "Store-Name bereits vorhanden"

AC-SETUP-005: Company-Theme konfigurieren

  Scenario: Admin kann Company-Theme setzen
    Given die Company "Fashion Store GmbH" existiert
    And der User "admin@store.com" ist Admin der Company
    When der Admin eine PATCH-Anfrage auf "/api/v1/companies/{companyId}" mit folgendem Body stellt:
      """
      {
        "theme": {
          "primaryColor": "#1A73E8",
          "secondaryColor": "#F8F9FA",
          "accentColor": "#FF6D00",
          "fontFamily": "Inter"
        }
      }
      """
    Then wird HTTP 200 zurückgegeben
    And das Theme ist in der Company gespeichert

10.4 User Management (PM-003, PM-004)

AC-USER-001: Admin kann neuen User einladen

Feature: User Management

  Scenario: Admin lädt neuen User per Email ein
    Given der User "admin@store.com" hat die Rolle "Admin"
    When der Admin eine POST-Anfrage auf "/api/v1/users/invite" mit Email "newuser@store.com" und Rolle "Manager" stellt
    Then wird HTTP 201 zurückgegeben
    And eine Einladungs-Email wird an "newuser@store.com" gesendet
    And der User hat initial die Rolle "Manager"

AC-USER-002: Admin kann User-Rolle ändern

  Scenario: Admin ändert User-Rolle von Sales zu Manager
    Given der User "sales@store.com" hat die Rolle "Sales"
    And der User "admin@store.com" hat die Rolle "Admin"
    When der Admin eine PATCH-Anfrage auf "/api/v1/users/{salesUserId}" mit neuer Rolle "Manager" stellt
    Then wird HTTP 200 zurückgegeben
    And der User "sales@store.com" hat jetzt die Rolle "Manager"

AC-USER-003: Admin kann User deaktivieren (Soft Delete)

  Scenario: Admin deaktiviert User-Konto
    Given der User "oldemployee@store.com" existiert mit Status "active"
    And der User "admin@store.com" hat die Rolle "Admin"
    When der Admin eine DELETE-Anfrage auf "/api/v1/users/{oldUserId}" stellt
    Then wird HTTP 200 zurückgegeben
    And der User hat Status "inactive"
    And der User existiert weiterhin in der Datenbank (Soft Delete)
    And der User kann sich nicht mehr einloggen

AC-USER-004: User-Liste zeigt nur aktive User

  Scenario: Admin sieht nur aktive User in der User-Liste
    Given die Company hat 5 aktive und 2 inaktive User
    When der Admin eine GET-Anfrage auf "/api/v1/users" stellt
    Then werden nur die 5 aktiven User zurückgegeben
    And inaktive User sind nicht in der Liste

10.5 Stammdaten: Products & Categories (PM-005, PM-006, PM-007)

AC-PROD-001: Produkt anlegen

Feature: Produkt-Stammdaten

  Scenario: Admin legt neues Produkt an
    Given die Company "Fashion Store GmbH" existiert
    And die Kategorie "T-Shirts" existiert
    And der User "admin@store.com" hat die Rolle "Admin"
    When der Admin eine POST-Anfrage auf "/api/v1/products" mit folgendem Body stellt:
      """
      {
        "name": "Basic T-Shirt",
        "sku": "TS-BASIC-001",
        "categoryId": "{categoryId}",
        "price": 29.99,
        "costPrice": 12.00,
        "attributes": {
          "size": ["S", "M", "L", "XL"],
          "color": ["Schwarz", "Weiß"],
          "season": "Sommer 2026"
        }
      }
      """
    Then wird HTTP 201 zurückgegeben
    And das Produkt "Basic T-Shirt" existiert mit SKU "TS-BASIC-001"

AC-PROD-002: Produkt mit bereits existierender SKU

  Scenario: Produkt mit duplicater SKU wird abgelehnt
    Given das Produkt mit SKU "TS-BASIC-001" existiert bereits
    When der Admin ein neues Produkt mit SKU "TS-BASIC-001" anlegt
    Then wird HTTP 409 zurückgegeben
    And die Fehlermeldung lautet: "SKU bereits vorhanden"

AC-PROD-003: Produkt mit fehlenden Pflichtfeldern

  Scenario: Produkt ohne Pflichtfelder wird abgelehnt
    Given der User "admin@store.com" hat die Rolle "Admin"
    When der Admin eine POST-Anfrage auf "/api/v1/products" mit unvollständigen Daten stellt:
      """
      {
        "name": "Basic T-Shirt"
      }
      """
    Then wird HTTP 400 zurückgegeben
    And die Fehlermeldung listet die fehlenden Pflichtfelder auf

AC-PROD-004: Produkt-Bestand pro Store pflegen

  Scenario: Bestand eines Produkts pro Store setzen
    Given das Produkt "Basic T-Shirt" SKU "TS-BASIC-001" existiert
    And der Store "Store Berlin" existiert
    When der Manager eine POST-Anfrage auf "/api/v1/stores/{storeId}/inventory" mit SKU "TS-BASIC-001" und Menge 50 stellt
    Then wird HTTP 200 zurückgegeben
    And der Bestand für Store "Store Berlin" beträgt 50

AC-PROD-005: Bestand wird bei Verkauf reduziert

  Scenario: Bestand sinkt nach Verkauf
    Given das Produkt "Basic T-Shirt" SKU "TS-BASIC-001" hat Bestand 10 im Store "Store Berlin"
    When ein Verkauf mit 3 Einheiten von SKU "TS-BASIC-001" abgeschlossen wird
    Then wird der Bestand auf 7 reduziert
    And ein Inventory-Log-Eintrag wird erstellt

AC-PROD-006: Kategorie anlegen (Hierarchisch)

Feature: Kategorien

  Scenario: Admin legt Hauptkategorie und Unterkategorie an
    Given der User "admin@store.com" hat die Rolle "Admin"
    When der Admin eine POST-Anfrage auf "/api/v1/categories" mit Name "Bekleidung" und parentId "null" stellt
    Then wird HTTP 201 zurückgegeben
    And die Kategorie "Bekleidung" existiert
    When der Admin eine POST-Anfrage auf "/api/v1/categories" mit Name "T-Shirts" und parentId "{parentCategoryId}" stellt
    Then wird HTTP 201 zurückgegeben
    And die Kategorie "T-Shirts" ist eine Unterkategorie von "Bekleidung"

AC-PROD-007: Produkte nach Kategorie filtern

  Scenario: Produktliste zeigt nur Produkte der ausgewählten Kategorie
    Given es gibt 10 Produkte in Kategorie "T-Shirts"
    And es gibt 5 Produkte in Kategorie "Hosen"
    When eine GET-Anfrage auf "/api/v1/products?categoryId={tshirtCategoryId}" gestellt wird
    Then werden genau 10 Produkte zurückgegeben
    And alle gehören zur Kategorie "T-Shirts"

AC-PROD-008: Fashion-Attribute (Size, Color, Season)

  Scenario: Produkt mit Fashion-spezifischen Attributen anlegen
    Given der User "admin@store.com" hat die Rolle "Admin"
    When der Admin eine POST-Anfrage auf "/api/v1/products" mit Attributen stellt:
      """
      {
        "name": "Sommerkleid",
        "sku": "SK-001",
        "attributes": {
          "size": ["XS", "S", "M", "L", "XL"],
          "color": ["Rot", "Blau", "Grün"],
          "season": "Sommer 2026",
          "material": "Baumwolle"
        }
      }
      """
    Then wird HTTP 201 zurückgegeben
    And das Produkt hat alle Attribute gespeichert

10.6 Multi-Store Support (Grundarchitektur)

AC-MULTI-001: User kann mehreren Stores zugeordnet werden

Feature: Multi-Store Support

  Scenario: Manager ist für mehrere Stores verantwortlich
    Given die Company hat 3 Stores: "Store A", "Store B", "Store C"
    And der User "manager@store.com" hat die Rolle "Manager"
    When der Admin den Manager den Stores "Store A" und "Store B" zuordnet
    Then wird HTTP 200 zurückgegeben
    And der Manager kann auf "Store A" und "Store B" zugreifen
    And der Manager kann nicht auf "Store C" zugreifen

AC-MULTI-002: Bestand ist store-spezifisch

  Scenario: Gleiches Produkt hat unterschiedlichen Bestand pro Store
    Given das Produkt "Basic T-Shirt" SKU "TS-BASIC-001" existiert
    And Store "Store Berlin" hat Bestand 50
    And Store "Store Hamburg" hat Bestand 30
    When eine GET-Anfrage auf "/api/v1/stores/{berlinStoreId}/inventory/TS-BASIC-001" gestellt wird
    Then wird Bestand 50 zurückgegeben
    When eine GET-Anfrage auf "/api/v1/stores/{hamburgStoreId}/inventory/TS-BASIC-001" gestellt wird
    Then wird Bestand 30 zurückgegeben

AC-MULTI-003: Verkauf ist store-zugeordnet

  Scenario: Verkauf wird immer dem aktuellen Store zugeordnet
    Given der User "sales@store.com" ist Sales im Store "Store Berlin"
    When der Sales Associate einen Verkauf abschließt
    Then wird der Verkauf Store "Store Berlin" zugeordnet
    And der Verkauf ist nicht in anderen Stores sichtbar

AC-MULTI-004: User eines Stores kann nicht auf anderen Store zugreifen

  Scenario: Manager kann keine Verkäufe eines anderen Stores sehen
    Given der User "manager@store.com" ist nur Manager für Store "Store Berlin"
    When der Manager eine GET-Anfrage auf "/api/v1/stores/{otherStoreId}/sales" stellt
    Then wird HTTP 403 zurückgegeben

10.7 Dashboard (Basic)

AC-DASH-001: Dashboard zeigt aggregierte Kennzahlen

Feature: Basic Dashboard

  Scenario: Admin sieht Dashboard mit Kennzahlen
    Given die Company hat Verkäufe der letzten 7 Tage
    And die Company hat 3 Stores
    When der User eine GET-Anfrage auf "/api/v1/dashboard" stellt
    Then werden folgende Kennzahlen zurückgegeben:
      | Kennzahl | Typ |
      | totalRevenueToday | number |
      | totalRevenueWeek | number |
      | transactionCountToday | number |
      | transactionCountWeek | number |
      | topProducts | array |
    And die Daten sind für alle Stores der Company aggregiert (Admin)

AC-DASH-002: Manager sieht nur eigene Store-Daten

  Scenario: Manager sieht Dashboard nur für seinen Store
    Given der User "manager@store.com" hat die Rolle "Manager"
    And der Manager ist nur für Store "Store Berlin" zuständig
    When der Manager eine GET-Anfrage auf "/api/v1/dashboard" stellt
    Then werden nur Daten für "Store Berlin" angezeigt
    And keine Daten anderer Stores sind sichtbar

10.8 Edge Cases & Error Handling

AC-EDGE-001: Gleichzeitiger Zugriff auf letzte verfügbare Bestandseinheit

Feature: Edge Case — Bestand-Konkurrenz

  Scenario: Zwei parallele Verkäufe des letzten Produkts
    Given das Produkt SKU "LIMITED-001" hat nur noch Bestand 1
    And zwei Sales Associates starten gleichzeitig einen Verkauf über je 1 Einheit
    When beide Verkäufe nahezu gleichzeitig abgeschlossen werden
    Then erhält genau einer der Verkäufe HTTP 201
    And der andere erhält HTTP 409 mit "Unzureichender Bestand"
    And der Bestand ist nie negativ

AC-EDGE-002: Leere Company (keine Stores)

  Scenario: Dashboard bei Company ohne Stores
    Given die Company hat keine Stores
    When der Admin eine GET-Anfrage auf "/api/v1/dashboard" stellt
    Then wird HTTP 200 zurückgegeben
    And alle Kennzahlen sind 0 oder leer
    And eine Info-Meldung wird zurückgegeben: "Noch keine Stores angelegt"

AC-EDGE-003: Produkt ohne Bestand

  Scenario: Verkauf mit 0-Bestand wird abgelehnt
    Given das Produkt SKU "OUT-OF-STOCK" hat Bestand 0
    When ein Sales Associate einen Verkauf mit SKU "OUT-OF-STOCK" versucht
    Then wird HTTP 409 zurückgegeben
    And die Fehlermeldung lautet: "Produkt nicht verfügbar"

AC-EDGE-004: Ungültige UUID in URL

  Scenario: API gibt 400 bei ungültiger UUID zurück
    Given ein User ist authentifiziert
    When eine GET-Anfrage auf "/api/v1/products/invalid-uuid" gestellt wird
    Then wird HTTP 400 zurückgegeben
    And die Fehlermeldung lautet: "Ungültige ID: muss eine gültige UUID sein"

AC-EDGE-005: Mehrere Rollen-Zuweisungen (Edge)

  Scenario: User kann nicht zwei Rollen gleichzeitig haben
    Given der User "ambiguous@store.com" hat die Rolle "Sales"
    When ein Admin versucht, dem User zusätzlich die Rolle "Manager" zuzuweisen
    Then wird HTTP 400 zurückgegeben
    And die Fehlermeldung lautet: "User kann nur eine Rolle haben"

CPTO Review Notes (2026-07-03)

Verdict: ✅ APPROVED — Ready for Implementation

Action Items aus CPTO-Review:


Changelog

Version Datum Änderung Autor
0.9 2026-07-03 QA-001: Gherkin Akzeptanzkriterien für Phase-1 Must-Have Features ergänzt DEV
0.8 2026-07-03 PM submitted for CPTO-Review Product Manager
0.7 2026-07-03 QA APPROVED: QA-Review-Block aktualisiert, Checkbox-Todos entfernt, APPROVED fuer CPTO-Review QA
0.6 2026-07-03 FINALIZED: Status-Update. PRD produktionsreif für CPTO-Review. Product Manager
0.5 2026-07-03 CTO-APPROVED: Tech Stack, Cloud Architecture, Data Model signiert CTO
0.4 2026-07-03 PM adressiert alle 5 Critical Issues aus QA-Review Product Manager
0.3 2026-07-03 Vision & Mission ergänzt; Pain Points 1-3 ausgearbeitet; Core Workflows WF-1 bis WF-4 dokumentiert Product Manager
0.2 2026-07-03 Technische Leitlinien, Pricing-Modell, AI Features, Feature Flags ergänzt Product Manager
0.1 2026-07-03 Initiale Version — Grundstruktur, Zielgruppe, MoSCoW-Priorisierung Product Manager

Dieses PRD wird kontinuierlich aktualisiert. Jede Änderung muss als Comment im Issue dokumentiert werden.