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>
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.phpvier 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.phpergä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:diffausführen- Generierte Migration prüfen — erwartet: 4
ADD COLUMN ... nullableaufapp.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.phperstellen:
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.
EbayInventoryApiClientumgetLocations()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:EbayAccountApiClientregistrieren (gleiche Argumente wieEbayInventoryApiClient):
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.phperstellen:
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
EbayAdapterbekommtArticleTypePlatformConfigRepositoryInterfaceinjiziertpublishListing()lädt die Config überfindByArticleTypeAndPlatformType($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';ingetCategoryId()entfernen (Methode fällt weg, Config übernimmt) - Bestehende Unit-Tests für
EbayAdapteranpassen
7. EasyAdmin CRUD — EbayArticleTypePlatformConfigCrudController
src/Infrastructure/Http/Controller/Admin/EbayArticleTypePlatformConfigCrudController.phperstellen- Entity:
ArticleTypePlatformConfig createIndexQueryBuilder()überschreiben → filtert aufplatform.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>— ruftEbayPolicyProviderauf, gibtnullzurück bei leerer Liste oder Exception- Wenn
null:TextFieldstattChoiceField+ einmalige Flash-Warnung"eBay API nicht erreichbar — ID manuell eingeben" services.yaml—EbayPolicyProviderin 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.yamlergänzen ArticleTypePlatformConfigaus 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.localsetzen oderEbayPolicyProvidermocken - 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