SalesRepresentative 1.3.0 Help

Entwickler-Übersicht

Diese Seite richtet sich an Plugin-Entwickler, die das SalesRepresentative-Plugin erweitern, seine Store-API-Routes verstehen oder die Caching- bzw. Kontext-Logik anpassen wollen.

Datenmodell

customer (Shopware) ├── leoparden_sales_representative (1:1, Vertreterprofil + media) │ └── leoparden_sales_representative_affiliate_code (1:N, Codes des Vertreters) ├── leoparden_sales_representative_customers (Junction: customer ↔ affiliate_code) └── leoparden_sales_representative_order_extra (pro Warenkorb-token: Adressen + AffiliateCode)
  • leoparden_sales_representative — Profil (Adresse, Kontakt, media), 1:1 zum Kunden über customer_id.

  • …_affiliate_code — AffiliateCodes je Vertreter.

  • …_customers — Junction mit zusammengesetztem PK (customer_id + leoparden_sales_representative_affiliate_code_id); realisiert die n:m-Beziehung Kunde ↔ AffiliateCode.

  • …_order_extra — token-bezogener Datensatz mit Liefer-/Rechnungsadresse und aktivem AffiliateCode für „Als Kunde bestellen".

DAL-Definitionen unter src/Content/SalesRepresentative/ und …/Aggregate/<Name>/. Repository-IDs: leoparden_sales_representative.repository, …_affiliate_code.repository, …_customers.repository, …_order_extra.repository.

Entity-Extensions

src/Content/Extension/ hängt Assoziationen an Core-Entitäten (shopware.entity.extension):

Extension

Ziel

Assoziation

CustomerExtension

customer

…_addresses (Adressen mit Vertreterfeld), … (Vertreterprofil), …_order_extra, …_affiliate_code (m:n über Junction)

OrderExtension

order

Order-Extra-Bezug

ProductExtension

product

Vertreter-Extension (Custom-Products-Integration)

CustomerAddressExtension

customer_address

Vertreterzuordnung am Adress-Custom-Field

SalesChannelContextSubscriber

Das Herzstück zur Laufzeit. Auf SalesChannelContextCreatedEvent hängt der Subscriber drei Extensions an den eingeloggten Kunden:

  1. Vertreterprofil (leoparden_sales_representative) — via SalesRepresentativeRoute,

  2. aktiver AffiliateCode — via SalesRepresentativeCustomersRoute (jüngster Code des Kunden),

  3. Order-Extra (Prio -500) — sucht/erzeugt den token-gebundenen Datensatz, trägt Default-Adressen und den AffiliateCode nach und hängt ihn als Extension an.

Den „effektiven" Kunden löst der Subscriber durchgängig über getExtension('leoparden_current_company')?->getCompany() ?? $customer auf — ist ein Kunde Teil einer Firma, gilt die Firma als Vertreter/Kunde. Dieses Idiom wiederholt sich an mehreren Stellen und ist beim Erweitern beizubehalten.

Store-API-Routes

Jede Ressource folgt dem Paar Abstract<Name>Route → <Name>Route (Repository-Suche; getDecorated() wirft DecorationPatternException). Je Route zwei Events unter Content/Events/<Name>/: …CriteriaEvent (Kriterien vor der Suche) und …LoadedEvent (Ergebnis nachbearbeiten).

Route

Pfad

_entity

SalesRepresentativeRoute

/store-api/sales-representative

leoparden_sales_representative

SalesRepresentativeCustomersRoute

/store-api/sales-representative-customers

…_customers

SalesRepresentativeAffiliateCodeRoute

/store-api/sales-representative-affiliate-code

…_affiliate_code

SalesRepresentativeOrderExtraRoute

/store-api/sales-representative-customer-orders

…_order_extra

SalesRepresentativeAdditionalAddressesRoute

/store-api/sales-representative-additional-addresses

customer_address

CustomerSalesRepresentativeRoute

/store-api/customer-sales-representative

(Attach-Route, nicht gecacht)

Caching (6.7-Modell)

Seit dem 6.7-Caching-Rework gibt es keine Cached*Route-Decorators und keine Cache-Key-/Tags-Events mehr. Stattdessen:

  • Jede cachende Route trägt am #[Route]-Attribut defaults: ['_entity' => '<entity>', '_httpCache' => true]. Shopware cacht die Response und invalidiert sie automatisch, sobald die eigene _entity geschrieben wird.

  • Services/CacheInvalidator (ein readonly-Subscriber auf EntityWrittenContainerEvent) deckt nur die entitätsübergreifenden Fälle ab, die das _entity-Auto-Handling nicht sieht — z. B. invalidiert ein customer-Write die kundengebundenen Vertreter-Routes. Invalidiert wird über den _entity-Namen als Tag (CacheInvalidator::invalidate([...])).

Flow-Event: CustomerInviteEvent

CustomerInviteEvent (Core/Framework/Event/) ist ein Flow-Action-Event, registriert über getActionEventClasses() der Plugin-Klasse sowie Core/Flow/Storer/CustomerInviteStorer und Core/Flow/Subscriber/BusinessEventCollectorSubscriber. Es trägt Kunde, Vertreter und Accept-URL und treibt die Einladungs-Mail (sales_representative_mail).

Subscriber

Subscriber

Aufgabe

SalesChannelContextSubscriber

Hängt Vertreter/AffiliateCode/Order-Extra an den Kunden (s. o.)

AffiliateTrackingSubscriber

Wertet den AffiliateCode-URL-Parameter aus (Erst-Zuordnung/Wechsel je nach Config)

CustomerSavedSubscriber

Pflegt Zuordnungen beim Speichern des Kunden

OrderStateSubscriber

Überträgt im Checkout gewählte Adressen/Status auf die Order

AddressSubscriber

Adress-Dropdown im Checkout

CriteriaSubscriber

Ergänzt Kriterien für die Vertreter-Routes

CacheInvalidator

Entitätsübergreifende HTTP-Cache-Invalidierung

BusinessTransactionsSubscriber/BusinessCustomProductsSubscriber

Integration in Gläserner Auftrag/BusinessCustomProducts

Storefront-Layer

Controller/SalesRepresentativeController.php bündelt die Storefront-Routes unter /SalesRepresentative/* (Profil-Index, Kundenliste, Kunden-/Bestelldetail, Bild-Tausch, Einladung/Annahme, ChangeOrderContext, Adress-Modal). Die Content/Page/*PageLoader bauen die Page-Structs aus den Store-API-Routes.

Die TS-Plugins in src/Resources/app/storefront/src/ erweitern @leoparden/plugin-tools-storefront-Basisklassen (AbstractStoreApi.Plugin, AbstractEntityListing.Plugin) und liefern den Request-Body über einen get body()-Getter. Registrierung in main.ts, Bindung über data-sales-representative-*-Attribute.

Adress-Modal (6.7)

addressBook() rendert seit 6.7 das address-manager-Modal (@Storefront/storefront/component/address/address-manager-modal.html.twig, Vorbild: Core-AddressController::addressManager()) und übergibt zusätzlich redirectTo/redirectParameters. Das alte address-editor-modal wurde in 6.7 entfernt.

CMS-Elemente

Unter src/Resources/views/storefront/element/:

  • Vertreter-eigen: …-sales-representative-customer, …-customers, …-customer-orders

  • Integration: …-transactions, …-processes, …-articles-on-order, …-recently-delivered, …-custom-products

Administration

Resources/app/administration/src/main.ts registriert das Modul leoparden-sales-representative (Navigation unter sw-customer, Entität leoparden_sales_representative, Suchtyp via searchTypeService) und überschreibt sw-customer-detail; per routeMiddleware wird der sales-representative-Tab in die Kundendetail-Route injiziert. Das Bundle wird in 6.7 über Vite gebaut (keine plugin-eigene Webpack-Config).

Testing

  • phpunit.xml bootet den Kernel über TestBootstrap.php; die Testsuite deckt src/Test rekursiv ab — Unit- und Integration-Tests laufen im selben Run.

  • src/Test/Unit/ — kernel-/DB-freie Unit-Tests (PHPUnit\Framework\TestCase, #[CoversClass]), z. B. CacheInvalidatorTest, der über EntityWrittenContainerEvent::createWithWrittenEvents()/…createWithDeletedEvents() die invalidierten Tags prüft.

  • src/Test/*.php — Integration-Tests via SalesRepresentativeTestBehaviour (IntegrationTestBehaviour + SalesChannelApiTestBehaviour).

  • Tests laufen aus dem Plugin-Verzeichnis mit gesetztem SHOPWARE_BUILD_DIR; composer test räumt Cache, fixt Code-Style und startet PHPUnit.

Erweiterungspunkte

  1. Kriterien/Ergebnisse anpassen — auf die …CriteriaEvent/…LoadedEvent der jeweiligen Store-API-Route subscriben.

  2. Cache-Invalidierung — bei neuen entitätsübergreifenden Abhängigkeiten den CacheInvalidator erweitern (Tag = _entity-Name der Ziel-Route).

  3. Einladungs-Mail — Flow Builder auf CustomerInviteEvent oder eigener EventSubscriber.

Last modified: 10 August 2026