# Requirements: Shopware 6 Address Validation Plugin

## 1. Projektueberblick

Fuer eine neue Shopware-6-Shopumgebung sollen zusaetzliche Feldpruefungen fuer B2B- und B2C-Shops umgesetzt werden.

Die Shopumgebung wird ueber eine individuelle Schnittstelle namens Syncline an das ERP-System SelectLine angebunden. Ziel ist es, bereits im Shop valide und ERP-kompatible Adressdaten sicherzustellen, ohne ein kostenpflichtiges Abo-Plugin einsetzen zu muessen.

Das Plugin soll pragmatisch, wartbar und update-sicher umgesetzt werden.

## 2. Zielsystem

- Shop-System: Shopware 6
- Zielversion: Shopware 6.7.7.1
- Plugin-Typ: Shopware 6 Plugin
- Einsatzort: `custom/plugins/`
- Pluginname: `SkywalkAddressValidation`
- Zielkanaele: B2B und B2C
- ERP-Anbindung: SelectLine ueber individuelle Schnittstelle Syncline

## 3. Ziel des Plugins

Das Plugin stellt zusaetzliche serverseitige Validierungen fuer Shopware-Adressfelder bereit.

Im ersten Umsetzungsschritt werden diese Business Cases abgedeckt:

- Begrenzung des Feldes `street` auf eine konfigurierbare maximale Zeichenanzahl.
- Validierung des Feldes `phoneNumber` nach internationalen Standards.

Die bestehende Shopware-Pflichtfeldpruefung bleibt fachliche Referenz, der Shopware-Core darf aber nicht veraendert werden.

## 4. Grundsaetzliche Anforderungen

### 4.1 Update-Sicherheit

- Keine Aenderungen am Shopware-Core.
- Umsetzung ueber Shopware-konforme Mechanismen.
- Nutzung von Symfony Services, Events, Subscriber oder Decorator-Ansatz, sofern passend.
- Nutzung von Shopware `SystemConfigService`.
- Fehlermeldungen ueber Snippets.
- Storefront-Template-Erweiterungen nur, wenn sie update-sicher sind.

### 4.2 Pragmatismus

Nicht gewuenscht sind:

- Externe SaaS-Abhaengigkeiten.
- Komplexe Regel-Engines.
- Admin-UI-Sonderlogik ausserhalb der Shopware-Plugin-Konfiguration.
- Overengineering.
- Core-Hacks.
- Eigene umfangreiche Telefonnummer-Regex-Logik.

### 4.3 Konfigurierbarkeit

Mindestens erforderlich:

- Aktivierung/Deaktivierung der Strassenlaengenpruefung.
- Maximale Zeichenanzahl fuer `street`.
- Aktivierung/Deaktivierung der Telefonnummernpruefung.
- Default-Region fuer Telefonnummernpruefung, z. B. `DE`.

## 5. Funktionale Anforderungen

### 5.1 Strassenpruefung

Zu pruefen ist das Shopware-Adressfeld `street`.

Regel:

- `street` darf eine maximal erlaubte Zeichenanzahl nicht ueberschreiten.
- Default: 35 Zeichen.
- Die maximale Zeichenanzahl muss ueber die Plugin-Einstellungen aenderbar sein.

Verhalten:

- Wenn die eingegebene Strasse laenger als der konfigurierte Wert ist, wird die Adresseingabe abgelehnt.
- Die Fehlermeldung nennt die konfigurierte Zeichenanzahl dynamisch.

Beispiel:

```text
Die Strasse darf maximal 35 Zeichen lang sein.
```

Betroffene Prozesse:

- Kundenregistrierung.
- Checkout-Rechnungsadresse.
- Checkout-Lieferadresse.
- Adressaenderung im Kundenkonto.
- Neue Adresse im Kundenkonto.
- Bearbeitung bestehender Kundenadressen.

Storefront UX:

- Optional, aber empfohlen: `street` erhaelt ein HTML-Attribut `maxlength`.
- Optional, aber empfohlen: Hinweis am Feld, z. B. `Maximal 35 Zeichen.`
- Die Frontend-Pruefung ersetzt niemals die serverseitige Pruefung.

Umsetzungsstand Phase 3:

- `street` erhaelt bei aktivierter Strassenvalidierung ein `maxlength`-Attribut aus der Plugin-Konfiguration.
- Unter dem Feld wird ein Hinweis mit der konfigurierten maximalen Zeichenanzahl angezeigt.

### 5.2 Telefonnummernpruefung

Zu pruefen ist das Shopware-Adressfeld `phoneNumber`.

Regel:

- Wenn eine Telefonnummer angegeben wird, wird sie nach internationalen Standards validiert.
- Die Umsetzung basiert auf `giggsey/libphonenumber-for-php`.
- Keine eigene komplexe Regex-Validierung verwenden.
- Telefonnummern muessen mit internationaler Laendervorwahl im Format `+...` eingegeben werden.
- Nationale Telefonnummern ohne Laendervorwahl, z. B. `015259802420` oder `0421 123456`, sind ungueltig.

Wichtig: Telefonnummern werden nur validiert, nicht normalisiert.

- Das Plugin darf den eingegebenen Telefonnummernwert nicht umformatieren.
- Das Plugin darf keine Laendervorwahl ergaenzen.
- Das Plugin darf keine Leerzeichen, Trennzeichen oder lokale Schreibweisen automatisch veraendern.
- Es findet keine automatische Speicherung einer E.164-, nationalen oder sonstigen normalisierten Telefonnummer statt.
- `libphonenumber` wird fuer Parsing und Validierung genutzt, nicht fuer eine dauerhafte Formatumwandlung.

Pflichtfeldverhalten:

- Das Plugin macht Telefonnummern nicht automatisch verpflichtend.
- Ein leeres Telefonnummernfeld ist erlaubt.
- Wenn eine Telefonnummer angegeben wird, muss sie valide sein.
- Falls Shopware oder eine andere Shop-Konfiguration das Feld bereits als Pflichtfeld definiert, wird diese Pflichtfeldlogik nicht ueberschrieben.

Internationale Nummern:

- Telefonnummern mit Laenderkennung werden unterstuetzt, z. B. `+49 421 123456`, `+43 1 234567`, `+41 44 1234567`.

Nationale Nummern:

- Nationale Telefonnummern ohne Laendervorwahl werden nicht akzeptiert.
- Die Default-Region dient nur als Fallback fuer Validierung und Hinweistext, wenn kein Land aus der Adresse ermittelt werden kann.
- Beispiel: `0421 123456` ist ungueltig; `+49 421 123456` ist gueltig.

Hinweis zur Laendervorwahl:

- Wenn das gewaehlte Adressland verfuegbar ist, soll im Fehlerhinweis die passende Laendervorwahl vorgeschlagen werden.
- Wenn kein Adressland verfuegbar ist, soll die konfigurierte Default-Region verwendet werden.
- Fallback-Beispiel: `+49`.
- In der Storefront wird zusaetzlich ein frueher Hinweis angezeigt, dass immer eine `+`-Laendervorwahl genutzt werden soll.

Fehlermeldung:

```text
Bitte geben Sie eine gueltige Telefonnummer ein.
```

Optionaler Hinweis:

```text
Bitte geben Sie eine gueltige Telefonnummer mit Laendervorwahl ein, z. B. +49 421 123456.
```

## 6. Plugin-Konfiguration

Die Plugin-Konfiguration wird ueber `src/Resources/config/config.xml` umgesetzt.

Erforderliche Konfigurationsfelder:

| Name | Typ | Default | Beschreibung |
| --- | --- | --- | --- |
| `enableStreetValidation` | Boolean | `true` | Aktiviert oder deaktiviert die Strassenlaengenpruefung. |
| `maxStreetLength` | Integer | `35` | Maximale erlaubte Zeichenanzahl fuer das Feld `street`. |
| `enablePhoneValidation` | Boolean | `true` | Aktiviert oder deaktiviert die Telefonnummernpruefung. |
| `defaultPhoneRegion` | Text oder Select | `DE` | Default-Laendercode fuer Validierungsfallback und Vorwahlhinweis. |
| `enablePostalCodeFormatHints` | Boolean | `true` | Aktiviert oder deaktiviert die laenderspezifischen PLZ-Format-Hinweise. |
| `postalCodeFormatRules` | Text/JSON | leer | Optionale Erweiterung oder Ueberschreibung der PLZ-Regeln als JSON-Objekt mit ISO-Laendercode, `format` und `regex`. |

Empfohlene Validierung der Konfigurationswerte:

- `maxStreetLength` darf nicht kleiner als 1 sein.
- Sinnvoller Bereich: 1 bis 255.
- `defaultPhoneRegion` sollte ein valider ISO-3166-1-Alpha-2-Laendercode sein.

### 6.1 PLZ-Format-Hinweise

Shopware 6.7.7.1 stellt eine eigene `CustomerZipCode`-Constraint bereit, deren Validator die PLZ-Regeln aus den Laender-Stammdaten (`defaultPostalCodePattern` bzw. `advancedPostalCodePattern`) liest und bei Verstoessen eine generische Meldung ausgibt. In `AddressValidationFactory` wird fuer `zipcode` zusaetzlich eine Laengenpruefung registriert.

Da die bestehende Shopware-Constraint nicht update-sicher nur mit einer dynamischen Format-Beispielmeldung ersetzt werden kann, nutzt das Plugin eine eigene kleine Symfony-Constraint fuer `zipcode`. Sie wird ueber dieselben Shopware Validation Events wie Street und Phone feldbezogen ergaenzt, aber nur wenn:

- `enablePostalCodeFormatHints` aktiv ist,
- das gewaehlte Land ermittelt werden kann,
- fuer den ISO-Laendercode eine Plugin-Regel vorhanden ist.

Initiale Default-Regeln:

| Land | Format | Regex |
| --- | --- | --- |
| US | `XY 12345` | `^[A-Z]{2} [0-9]{5}$` |
| CA | `XY A1A1A1` | `^[A-Z]{2} [A-Z][0-9][A-Z][0-9][A-Z][0-9]$` |
| AU | `XY 1234 oder XYZ 1234` | `^[A-Z]{2,3} [0-9]{4}$` |
| CZ | `123 45` | `^[0-9]{3} [0-9]{2}$` |

PLZ-Werte werden nicht normalisiert, umformatiert oder gespeichert veraendert. Nicht konfigurierte Laender erhalten keine zusaetzliche Plugin-PLZ-Meldung.

Das Feld `postalCodeFormatRules` ist eine pragmatische Admin-Erweiterung fuer weitere Laender. Beispiel:

```json
{"NL":{"format":"1234 AB","regex":"^[0-9]{4} [A-Z]{2}$"}}
```

Eine komfortablere Admin-Oberflaeche fuer Regel-Listen bleibt ein spaeterer Roadmap-Punkt.

## 7. Technische Anforderungen

### 7.1 Plugin-Struktur

Zielstruktur:

```text
custom/plugins/SkywalkAddressValidation/
├── composer.json
├── AGENTS.md
├── Requirements.md
├── roadmap.md
├── src/
│   ├── SkywalkAddressValidation.php
│   ├── Service/
│   │   └── AddressValidationService.php
│   ├── Subscriber/
│   │   └── AddressValidationSubscriber.php
│   └── Resources/
│       ├── config/
│       │   ├── config.xml
│       │   └── services.xml
│       ├── snippet/
│       │   ├── de_DE/
│       │   │   └── storefront.de-DE.json
│       │   └── en_GB/
│       │       └── storefront.en-GB.json
│       └── views/
│           └── storefront/
│               └── component/
│                   └── address/
│                       └── address-form.html.twig
└── tests/
    └── Service/
        └── AddressValidationServiceTest.php
```

Die genaue Template-Struktur ist anhand der vorhandenen Shopware-Version und Projektstruktur zu pruefen.

### 7.2 Services

Es soll ein zentraler Service fuer die fachliche Validierung erstellt werden: `AddressValidationService`.

Aufgaben:

- Konfiguration lesen.
- Strasse pruefen.
- Telefonnummer pruefen.
- Fehler strukturiert zurueckgeben.
- `libphonenumber` verwenden.
- Keine Storefront- oder Request-spezifische Logik enthalten.

Umsetzungsstand Phase 2:

- `AddressValidationService` liest die Plugin-Konfiguration ueber `SystemConfigService`.
- Strassen- und Telefonnummernpruefung sind in Methoden gekapselt und unabhaengig von Storefront-Templates.
- Validierungsergebnisse koennen strukturiert ueber kleine `Struct`-Klassen zurueckgegeben werden.
- Telefonnummern werden mit `giggsey/libphonenumber-for-php` geparst und validiert, aber nicht normalisiert oder gespeichert.
- Telefonnummern ohne `+`-Laendervorwahl werden abgelehnt.

### 7.3 Integration in Shopware

Der beste Integrationspunkt fuer Shopware 6.7.7.1 ist im Projekt zu pruefen.

Bevorzugt sind:

- Symfony Validation Constraints.
- Shopware Validation Events.
- Subscriber fuer adressbezogene Validierungsprozesse.
- Decorator fuer geeignete Address-Validation-Services, falls vorhanden und update-sicher.

Umsetzungsentscheidung Phase 2:

- Die Integration erfolgt ueber die Shopware-Validation-Events `framework.validation.address.create` und `framework.validation.address.update`.
- Ein `AddressValidationSubscriber` erweitert die bestehende Shopware-Adressvalidierung um eigene Symfony-Constraints.
- Die Constraints werden feldbezogen auf `street` und `phoneNumber` gesetzt.
- Sales-Channel-spezifische Konfiguration wird genutzt, wenn der aktuelle Request einen Sales Channel bereitstellt; ansonsten wird die globale Plugin-Konfiguration verwendet.

Nicht erlaubt:

- Aenderungen an Shopware-Core-Dateien.
- Direkte Manipulation interner Klassen ohne sauberen Extension Point.
- Validierung ausschliesslich per JavaScript.

### 7.4 Fehlerbehandlung

Validierungsfehler muessen so zurueckgegeben werden, dass Shopware sie im jeweiligen Formular korrekt anzeigen kann.

Fehler sollen feldbezogen sein:

- Fehler Strasse: `street`.
- Fehler Telefonnummer: `phoneNumber`.

Umsetzungsstand nach manuellem Test Phase 2:

- Shopware zeigt Storefront-Validation-Meldungen ueber `error.VIOLATION::...` Snippets an.
- Eigene Constraints muessen daher eigene Fehlercodes setzen und passende Snippet-Keys unter `error` bereitstellen.
- Fuer `street` wird `VIOLATION::SKYWALK_ADDRESS_VALIDATION_STREET_MAX_LENGTH` genutzt.
- Fuer `phoneNumber` wird `VIOLATION::SKYWALK_ADDRESS_VALIDATION_PHONE_INVALID` genutzt.
- Fuer `zipcode` wird `VIOLATION::SKYWALK_ADDRESS_VALIDATION_POSTAL_CODE_FORMAT_INVALID` genutzt, wenn fuer das gewaehlte Land eine Plugin-PLZ-Regel existiert.
- Die Default-Region `DE` dient nur als Fallback fuer Validierung und Vorwahlhinweis. Sie fuehrt nicht dazu, dass `+49` sichtbar ergaenzt oder gespeichert wird.
- Wenn `countryId` im Address-Validation-Event verfuegbar ist, wird die passende Vorwahl des gewaehlten Landes als Beispiel in die Fehlermeldung gegeben.

### 7.5 Mehrsprachigkeit

Mindestens folgende Sprachen:

- Deutsch.
- Englisch.

Erforderliche Snippets:

- `skywalk-address-validation.street.maxLength`
- `skywalk-address-validation.street.hint`
- `skywalk-address-validation.phone.invalid`
- `skywalk-address-validation.phone.hint`
- `skywalk-address-validation.postalCode.invalid`

Zusaetzliche Storefront-Fehler-Snippets:

- `error.VIOLATION::SKYWALK_ADDRESS_VALIDATION_STREET_MAX_LENGTH`
- `error.VIOLATION::SKYWALK_ADDRESS_VALIDATION_PHONE_INVALID`
- `error.VIOLATION::SKYWALK_ADDRESS_VALIDATION_POSTAL_CODE_FORMAT_INVALID`

## 8. Nicht-funktionale Anforderungen

### 8.1 Wartbarkeit

- Klare Klassennamen.
- Kleine Services.
- Keine unnoetige Abstraktion.
- Kommentierung nur dort, wo sie Mehrwert bietet.
- Business-Logik testbar halten.

### 8.2 Performance

- Die Validierung darf keine externen Netzwerkaufrufe ausfuehren.
- Die Telefonnummernpruefung muss lokal ueber die Library erfolgen.

### 8.3 Sicherheit und Datenschutz

- Eingaben nicht ungeprueft ausgeben.
- Fehlermeldungen ohne technische Details.
- Keine Speicherung zusaetzlicher personenbezogener Daten.
- Keine Uebertragung von Telefonnummern an externe Dienste.
- Telefonnummern werden durch das Plugin nicht normalisiert oder veraendert.

### 8.4 Kompatibilitaet

- Zielversion: Shopware 6.7.7.1.
- PHP-Version anhand der Shopware-Projektumgebung pruefen.
- Composer-Abhaengigkeiten kompatibel mit Shopware-Projekt pruefen.
- Phase 2 fuegt `giggsey/libphonenumber-for-php` als Composer-Abhaengigkeit hinzu.
- Da das Zielsystem Shopware 6.7 ist, aktiviert das Plugin `executeComposerCommands()`, damit Shopware die Composer-Abhaengigkeit beim Installieren/Aktualisieren aus der Plugin-`composer.json` nachziehen kann.

## 9. Tests

### 9.1 Unit Tests

Strasse:

- Strasse mit exakt 35 Zeichen ist gueltig.
- Strasse mit 36 Zeichen ist ungueltig.
- Konfigurierter Wert wird beruecksichtigt.
- Pruefung deaktiviert: keine Fehlermeldung.

Telefonnummer:

- Leeres Feld ist gueltig, wenn Telefonnummer nicht anderweitig verpflichtend ist.
- Internationale valide Nummer ist gueltig.
- Nationale Nummer ohne Laendervorwahl ist ungueltig.
- Ungueltige Nummer ist ungueltig.
- Pruefung deaktiviert: keine Fehlermeldung.
- Der urspruenglich eingegebene Telefonnummernwert bleibt unveraendert.
- Vorwahlbeispiel wird aus Region ermittelt.

PLZ:

- US gueltig: `NY 12345`.
- US ungueltig: `12345`.
- CA gueltig: `ON K1A0B1`.
- CA ungueltig: `K1A0B1`.
- AU gueltig mit zwei Buchstaben: `NS 1234`.
- AU gueltig mit drei Buchstaben: `NSW 1234`.
- CZ gueltig: `123 45`.
- CZ ungueltig: `12345`.
- Nicht konfiguriertes Land: keine zusaetzliche PLZ-Fehlermeldung.
- Deaktivierte PLZ-Hinweislogik: keine zusaetzliche PLZ-Fehlermeldung.
- PLZ-Werte bleiben unveraendert.

Umsetzungsstand Phase 4:

- `tests/Service/AddressValidationServiceTest.php` enthaelt fokussierte Unit Tests fuer Strassenlaenge, internationale Telefonnummern, nationale Nummern ohne Laendervorwahl und Vorwahlbeispiele.
- `phpunit.xml.dist` ist vorhanden.
- `docs/test-plan.md` dokumentiert Unit-, manuelle und Regressionstests.

### 9.2 Manuelle Tests

Mindestens zu testen in der Storefront:

- Registrierung B2C.
- Registrierung B2B, sofern vorhanden.
- Checkout als Gast.
- Checkout als eingeloggter Kunde.
- Neue Rechnungsadresse.
- Neue Lieferadresse.
- Adresse im Kundenkonto aendern.
- Unterschiedliche Sales Channels, sofern vorhanden.

### 9.3 Regressionstests

Zu pruefen:

- Standard-Shopware-E-Mail-Validierung funktioniert weiterhin.
- Pflichtfeldlogik von Shopware bleibt unveraendert.
- Bestehende Adressen werden nicht automatisch veraendert.
- Syncline-Export wird nicht direkt veraendert.
- Keine Auswirkungen auf Produkt-, Warenkorb- oder Zahlungslogik.

## 9.4 Rollout

- Rollout erfolgt schrittweise ueber Testshop, Staging und Produktion.
- Vor Produktion muss ein Backup von Dateien und Datenbank vorhanden sein.
- Nach Installation/Aktualisierung sind Cache, Storefront und Logs zu pruefen.
- Rollback erfolgt ueber Deaktivierung oder Wiedereinspielen der vorherigen stabilen ZIP-Version.

Umsetzungsstand Phase 5:

- `docs/rollout.md` dokumentiert Testshop-, Staging-, Produktions- und Rollback-Schritte.

## 10. Codex-Arbeitsweise

Codex arbeitet phasenweise und liefert nach jeder Phase eine kurze Zusammenfassung.

Phase 1: Analyse

- Shopware-Projektstruktur pruefen.
- Vorhandene Plugin-Struktur pruefen.
- Verfuegbare Testbefehle pruefen.
- PHP- und Composer-Versionen pruefen.
- Relevante Shopware-Validierungspunkte pruefen.
- Bestehende Storefront-Overrides pruefen.
- Bestehende Address- oder Checkout-Customizations pruefen.

Ergebnis:

- Kurze technische Einschaetzung.
- Empfohlener Integrationspunkt.
- Erkannte Risiken.

Phase 2: Plugin-Skeleton

- Plugin-Verzeichnis.
- `composer.json`.
- Hauptklasse.
- `services.xml`.
- `config.xml`.
- Basisklassen.

Ergebnis:

- Plugin installierbar.
- Plugin in Shopware sichtbar.
- Konfiguration sichtbar.

Phase 3: Validierungslogik

- `AddressValidationService`.
- Strassenpruefung.
- Telefonnummernpruefung.
- Nutzung von `giggsey/libphonenumber-for-php`.
- Konfigurationszugriff.

Ergebnis:

- Validierungslogik unabhaengig testbar.

Phase 4: Shopware-Integration

- Validierung in passende Shopware-Prozesse integrieren.

Ergebnis:

- Fehler werden in Formularen korrekt angezeigt.
- Registrierung, Checkout und Account-Adressen sind abgedeckt.

Phase 5: Storefront UX

- Optional `maxlength` fuer Strasse.
- Optional Hilfetexte.
- Snippet-Nutzung.

Ergebnis:

- Bessere Nutzerfuehrung.
- Serverseitige Pruefung bleibt massgeblich.

Phase 6: Tests

- Unit Tests.
- Ggf. Integrationstests, falls Projektstruktur vorhanden.
- Testdokumentation.

Ergebnis:

- Tests ausfuehrbar.
- Kernfaelle abgedeckt.

Phase 7: Review und Haertung

- Coding Standards.
- Fehlerfaelle.
- Konfigurationsdefaults.
- Edge Cases.
- Shopware-Kompatibilitaet.
- Keine Core-Aenderungen.

Ergebnis:

- Finaler Reviewbericht.
- Liste offener Punkte.
- Deployment-Hinweise.

## 11. Definition of Done

Das Plugin gilt als fertig, wenn:

- Es unter Shopware 6.7.7.1 installierbar und aktivierbar ist.
- Die Plugin-Konfiguration im Admin sichtbar ist.
- Die maximale Strassenlaenge konfigurierbar ist.
- Der Defaultwert fuer die Strassenlaenge 35 Zeichen ist.
- `street` serverseitig validiert wird.
- `phoneNumber` serverseitig mit `libphonenumber` validiert wird.
- `phoneNumber` nur mit internationaler `+`-Laendervorwahl akzeptiert wird.
- `zipcode` fuer konfigurierte Laender feldbezogene Format-Hinweise mit konkretem Beispiel erhaelt.
- PLZ-Werte durch das Plugin nicht normalisiert, umformatiert oder automatisch veraendert werden.
- Telefonnummer optional bleibt, sofern nicht anderweitig verpflichtend.
- Telefonnummern durch das Plugin nicht normalisiert, umformatiert oder automatisch veraendert werden.
- Fehlermeldungen in Deutsch und Englisch vorhanden sind.
- Registrierung, Checkout und Kundenkonto geprueft sind.
- Unit Tests fuer die Kernlogik vorhanden sind.
