SuperSeller3000/docs/superpowers/plans/2026-05-19-07-ebay-admin-policies.md
Simon Kuehn 31c5116a1b docs: add CLAUDE.md and Plan 7 (eBay admin navigation + business policies)
Plan 7 covers: ArticleTypePlatformConfig policy fields, EbayAccountApiClient,
EbayPolicyProvider with live dropdown choices, EbayAdapter reading real config,
and per-adapter admin navigation section.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 06:25:14 +00:00

10 KiB

SuperSeller3000 — Plan 7: eBay Admin-Navigation & Business Policies

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Jeder Channel-Adapter bekommt einen eigenen Navigationsbereich im Admin-Panel. Für eBay werden alle Pflichtfelder für ein vollständiges Listing abgebildet: Die vier Business Policies (Fulfillment, Payment, Return, Merchant Location) werden direkt aus dem eBay-Account abgerufen und als Dropdown im Admin angeboten — keine manuelle ID-Eingabe.

Auslöser: publishListing() scheitert ohne listingPolicies + merchantLocationKey im Offer-Body. Außerdem ist getCategoryId() derzeit hardcoded auf '177' — der Adapter liest ArticleTypePlatformConfig noch gar nicht.


Was ein vollständiges eBay-Listing braucht

Feld Quelle Status
Titel, Beschreibung Article.ebayTitle/ebayDescription
Kategorie-ID ArticleTypePlatformConfig.categoryId ⚠️ vorhanden, aber Adapter liest es nicht
Condition, Fotos Article
Item Specifics (Aspects) ArticleTypeEbayMapping
Preis, Bestand Article.listingPrice/stock
Fulfillment Policy ID eBay Account → ArticleTypePlatformConfig fehlt
Payment Policy ID eBay Account → ArticleTypePlatformConfig fehlt
Return Policy ID eBay Account → ArticleTypePlatformConfig fehlt
Merchant Location Key eBay Account → ArticleTypePlatformConfig fehlt

Business Policies werden einmalig im eBay Verkäuferkonto definiert und danach per ID referenziert. Der Admin ruft die Liste live ab und zeigt sie als Dropdown.


Schritte

1. ArticleTypePlatformConfig — 4 neue Felder

  • In src/Domain/Channel/ArticleTypePlatformConfig.php vier nullable String-Felder ergänzen:
#[ORM\Column(type: 'string', length: 100, nullable: true)]
private ?string $fulfillmentPolicyId = null;

#[ORM\Column(type: 'string', length: 100, nullable: true)]
private ?string $paymentPolicyId = null;

#[ORM\Column(type: 'string', length: 100, nullable: true)]
private ?string $returnPolicyId = null;

#[ORM\Column(type: 'string', length: 100, nullable: true)]
private ?string $merchantLocationKey = null;
  • Getter + Setter für alle vier Felder

2. ArticleTypePlatformConfigRepositoryInterface — neue Query-Methode

  • In src/Domain/Channel/Repository/ArticleTypePlatformConfigRepositoryInterface.php ergänzen:
public function findByArticleTypeAndPlatformType(ArticleType $articleType, string $platformType): ?ArticleTypePlatformConfig;
  • Implementierung in DoctrineArticleTypePlatformConfigRepository:
public function findByArticleTypeAndPlatformType(ArticleType $articleType, string $platformType): ?ArticleTypePlatformConfig
{
    return $this->em->createQuery('
        SELECT c FROM App\Domain\Channel\ArticleTypePlatformConfig c
        JOIN c.platform p
        WHERE c.articleType = :articleType AND p.type = :platformType
    ')
        ->setParameter('articleType', $articleType)
        ->setParameter('platformType', $platformType)
        ->getOneOrNullResult();
}

3. Migration

  • docker compose exec app php bin/console doctrine:migrations:diff ausführen
  • Generierte Migration prüfen — erwartet: 4 ADD COLUMN ... nullable auf app.article_type_platform_configs
  • Dateipfad-Konvention: migrations/Version2026MMDD000001.php

4. EbayAccountApiClient (neu)

Neuer Client für die eBay Account API (/sell/account/v1). Gleiche Auth-Struktur wie EbayInventoryApiClient.

  • src/Infrastructure/Channel/Ebay/EbayAccountApiClient.php erstellen:
final class EbayAccountApiClient
{
    private const ACCOUNT_BASE = '/sell/account/v1';

    public function __construct(
        private readonly HttpClientInterface $httpClient,
        private readonly EbayOAuthClient $oauthClient,
        private readonly string $apiBaseUrl,
        private readonly string $marketplaceId,
    ) {}

    /** @return list<array{fulfillmentPolicyId: string, name: string}> */
    public function getFulfillmentPolicies(): array { ... }

    /** @return list<array{paymentPolicyId: string, name: string}> */
    public function getPaymentPolicies(): array { ... }

    /** @return list<array{returnPolicyId: string, name: string}> */
    public function getReturnPolicies(): array { ... }
}

Alle drei rufen GET {BASE}/{resource}?marketplace_id={marketplaceId} auf.
Response-Key: fulfillmentPolicies / paymentPolicies / returnPolicies.

  • EbayInventoryApiClient um getLocations() erweitern:
/** @return list<array{merchantLocationKey: string, name: string}> */
public function getLocations(): array
{
    // GET /sell/inventory/v1/location
    $data = $this->request('GET', self::INVENTORY_BASE.'/location', []);
    return $data['locations'] ?? [];
}
  • services.yaml: EbayAccountApiClient registrieren (gleiche Argumente wie EbayInventoryApiClient):
App\Infrastructure\Channel\Ebay\EbayAccountApiClient:
    arguments:
        $apiBaseUrl: '%env(EBAY_API_BASE_URL)%'
        $marketplaceId: '%env(EBAY_MARKETPLACE_ID)%'

5. EbayPolicyProvider (neu)

Service, der Policy-Listen für EasyAdmin-Formulare aufbereitet und cached.

  • src/Infrastructure/Channel/Ebay/EbayPolicyProvider.php erstellen:
final class EbayPolicyProvider
{
    public function __construct(
        private readonly EbayAccountApiClient $accountClient,
        private readonly EbayInventoryApiClient $inventoryClient,
        private readonly CacheInterface $cache,
    ) {}

    /** @return array<string, string>  Label => ID */
    public function getFulfillmentChoices(): array { ... }

    /** @return array<string, string> */
    public function getPaymentChoices(): array { ... }

    /** @return array<string, string> */
    public function getReturnChoices(): array { ... }

    /** @return array<string, string>  Label => merchantLocationKey */
    public function getLocationChoices(): array { ... }
}
  • Label-Format: "Policyname (ID)" → Value: die ID
  • Cache-Key pro Policy-Typ, TTL 300 s (cache.app)
  • Bei Exception (API nicht erreichbar, fehlende Credentials): leeres Array zurückgeben — der CRUD-Controller behandelt das

6. EbayAdapter — Config wirklich lesen, Policies übergeben

  • EbayAdapter bekommt ArticleTypePlatformConfigRepositoryInterface injiziert
  • publishListing() lädt die Config über findByArticleTypeAndPlatformType($article->getArticleType(), 'ebay')
  • Exception werfen wenn kein Config-Eintrag existiert: "No eBay platform config for ArticleType {name}"
  • createOffer() body erweitern:
'categoryId' => $config->getCategoryId(),
'listingPolicies' => array_filter([                       // array_filter entfernt null-Werte
    'fulfillmentPolicyId' => $config->getFulfillmentPolicyId(),
    'paymentPolicyId'     => $config->getPaymentPolicyId(),
    'returnPolicyId'      => $config->getReturnPolicyId(),
]),
...($config->getMerchantLocationKey() !== null ? [
    'merchantLocationKey' => $config->getMerchantLocationKey(),
] : []),
  • Hardcoded return '177'; in getCategoryId() entfernen (Methode fällt weg, Config übernimmt)
  • Bestehende Unit-Tests für EbayAdapter anpassen

7. EasyAdmin CRUD — EbayArticleTypePlatformConfigCrudController

  • src/Infrastructure/Http/Controller/Admin/EbayArticleTypePlatformConfigCrudController.php erstellen
  • Entity: ArticleTypePlatformConfig
  • createIndexQueryBuilder() überschreiben → filtert auf platform.type = 'ebay'

configureFields():

public function configureFields(string $pageName): iterable
{
    yield AssociationField::new('articleType', 'Artikel-Typ');
    yield TextField::new('categoryId', 'eBay Kategorie-ID');

    $choices = $this->tryGetChoices('fulfillment');   // siehe unten
    yield $choices !== null
        ? ChoiceField::new('fulfillmentPolicyId', 'Versand-Policy')->setChoices($choices)
        : TextField::new('fulfillmentPolicyId', 'Versand-Policy (ID)');

    // analog für payment, return, merchantLocationKey
}
  • tryGetChoices(string $type): ?array<string,string> — ruft EbayPolicyProvider auf, gibt null zurück bei leerer Liste oder Exception
  • Wenn null: TextField statt ChoiceField + einmalige Flash-Warnung "eBay API nicht erreichbar — ID manuell eingeben"
  • services.yamlEbayPolicyProvider in den Controller injizieren

8. Navigation — eBay-Bereich in DashboardController

  • In configureMenuItems() einen eBay-Submenü-Block einfügen:
yield MenuItem::subMenu('eBay', 'fa fa-store')->setSubItems([
    MenuItem::linkTo(EbayArticleTypePlatformConfigCrudController::class, 'Kategorie & Policies', 'fa fa-sliders'),
    MenuItem::linkToRoute('eBay Aspect Import', 'fa fa-download', 'admin_ebay_aspect_import'),
]);
  • Übersetzungsschlüssel in messages.de.yaml / messages.en.yaml ergänzen
  • ArticleTypePlatformConfig aus dem allgemeinen Bereich entfernen, falls er dort noch auftaucht (war bisher nicht in der Nav)

Nicht in diesem Plan

  • Weitere Adapter-Sektionen (Amazon, Kaufland) — Struktur ist vorbereitet, wird angelegt wenn die Adapter existieren
  • Sandbox-Policies abrufen — die Account-API ist auf dem echten eBay-Account. Für Tests: Policy-IDs aus Sandbox-Account manuell in .env.local setzen oder EbayPolicyProvider mocken
  • Kategorie-ID als Typeahead (statt Text-Input) — ist ein separates Thema

Dateien die geändert werden

src/Domain/Channel/ArticleTypePlatformConfig.php          ← 4 neue Felder
src/Domain/Channel/Repository/ArticleTypePlatformConfigRepositoryInterface.php  ← neue Methode
src/Infrastructure/Persistence/Repository/DoctrineArticleTypePlatformConfigRepository.php  ← Implementierung
src/Infrastructure/Channel/Ebay/EbayAccountApiClient.php  ← NEU
src/Infrastructure/Channel/Ebay/EbayInventoryApiClient.php  ← getLocations()
src/Infrastructure/Channel/Ebay/EbayPolicyProvider.php    ← NEU
src/Infrastructure/Channel/Ebay/EbayAdapter.php           ← Config lesen, Policies übergeben
src/Infrastructure/Http/Controller/Admin/EbayArticleTypePlatformConfigCrudController.php  ← NEU
src/Infrastructure/Http/Controller/Admin/DashboardController.php  ← Navigation
config/services.yaml                                      ← neue Services
migrations/Version2026...php                              ← NEU