Shopware Preisberechnung für eine spezifische Kundengruppe im Frontend durchführen

Dieses Tutorial zeigt Dir, wie Du neben dem Normalpreis eines Produkts auch den Preis für eine dedizierte Kundengruppe anzeigen kannst. Ich löse dieses Problem über den ListProductService und einen künstlich erzeugten ShopContext.

Shopware verwendet seit Version 5.2 (dieses Tutorial wurde mit 5.2.20 umgesetzt) den PriceCalculationService (engine/Shopware/Bundle/StoreFrontBundle/Service/Core/PriceCalculationService) des StoreFrontBundles, um die Produktpreise zu ermitteln. Eine Ausnahme davon stellt zum Beispiel noch die Warenkorbberechnung (sBaket) dar, die hier Ihr eigenes Süppchen kocht. Diese Ausnahmen werden in den kommenden Major-Releases allerdings nach und nach verschwinden und der PriceCalculationService systemweit Verwendung finden.

Die Problemstellung (Beispiel)

Der Shopbetreiber möchte auf der Produktdetailseite zu jedem Produkt folgenden Informationstext anzeigen: „Registrieren Sie sich jetzt als Fachhändler und zahlen Sie für dieses Produkt nur € 5,00 statt € 6,00!„, sofern der Kunde noch nicht der Kundengruppe „Händler“ angehört.

Die Intention liegt auf der Hand: Die Kunden – die offenbar großteils Fachhändler sind – sollen dazu bewegt werden, sich als ein solcher (mit Gewerbeschein) zu registrieren um diese Daten beispielsweise in der Vermarktung zu nützen. Der Shopbetreiber führt dazu die beiden Kundengruppen Endkunde (Key „EK“) und Händler (Key „H“) im System, zu denen die jeweiligen Preise beim Produkt hinterlegt sind. In unserem Beispiel sind das für Mitglieder der Kundengruppe Endkunde € 6,00 und für Mitglieder der Kundengruppe Händler € 5,00.

Die Ausgangslage in Shopware

Shopware bietet mit dem PriceCalculationService (shopware_storefront.price_calculation_service) einen wunderbaren Dienst an, um die Preise von Produkten zu berechnen. Dieser Service lässt sich sehr einfach dekorieren und somit um andere Berechnungsweisen ergänzen.

Der PriceCalculationService wird unter anderem auch vom ListProductService (shopware_storefront.list_product_service) genützt, um die Preise beim Auslesen von Produktdaten zu ermitteln. Dieser Service bietet eine get() Methode, um ein Produkt anhand seiner Produktnummer und eines ShopContexts zu ermitteln. Und genau diesen Service in Verbindung mit einem (manipuliertem) ShopContext nütze ich in diesem Beispiel, um den Preis für eine andere Kundengruppe zu erhalten.

Mein Lösungsansatz

Der neue Text auf der Produktdetailseite wird als Widget (ESI-Tag) implementiert, um den HTTP Cache zu umgehen. Eine Umsetzung ohne Widget über Event-Subscriber ist natürlich auch möglich, in meinem Fall aber nicht zielführend, da ich wie oben beschrieben auch die Preise dynamisch berechne und somit auf das Umgehen des Caches angewiesen bin.

Der nachfolgende Sourcecode soll die Idee für diese Problemlösung skizzieren und ist nicht als Copy-Paste Manual gedacht. Grundlegende und umfangreiche Informationen zur Pluginentwicklung, Themeentwicklung, Services und Events findest Du auf developers.shopware.com.

Aufruf des Widget-Controllers

Um den Widget Controller aufzurufen, überlade ich einen Block des Produktdetail-Templates.

{extends file="parent:frontend/detail/index.tpl"}

{* Product - Base information *}
{block name='frontend_detail_index_buy_container_base_info'}

    {$smarty.block.parent}
    {action module=widgets controller=retailer_info action=detailInfoWidget number=$sArticle.ordernumber defaultPrice=$sArticle.price}

{/block}

Der Widget-Controller

Der Widget-Controller im Plugin (Controllers/Widgets/RetailerInfo.php) nimmt den ESI Request entgegen, ermittelt den neuen Preis und gibt ihn an die View, die im nächsten Punkt gezeigt wird, weiter.

Um den Preis für eine andere Kundengruppe zu berechnen, hole ich mir zuerst die entsprechende Entity (hier mit dem Key „H“ versehen). Aus dieser Entity baue ich anschließend ein CustomerGroupStruct, dass der ShopContext – den ich „manipulieren“ möchte – erwartet.

Im zweiten Schritt erstelle ich mir einen neuen ShopContext ($targetShopContext) auf Basis des bestehenden Contexts ($sourceShopContext). Der einzige Parameter den ich dabei verändere ist die currentCustomerGroup. Diese wird vom PriceCalculationService herangezogen, um den Preis zu berechnen. Da diese Property protected ist, kann ich den bestehenden Kontext nicht einfach kopieren und verändern.

Zuletzt hole ich mir über den ListProductService mit dem neuen ShopContext und der Artikelnummer das Produkt und darüber wiederum den aktuellen Preis für die Kundengruppe „Händler“.

use Shopware\Models\Customer\Customer;

class Shopware_Controllers_Widgets_RetailerInfo extends Enlight_Controller_Action
{

    /**
     * Shows information on the way to next level
     */
    public function detailInfoWidgetAction() {

        $customer = null;
        $userId = $this->container->get('session')->get('sUserId');
        $number = $this->Request()->getParam('number');
        $defaultPrice = $this->Request()->getParam('defaultPrice');

        /** @var \Shopware\Bundle\StoreFrontBundle\Struct\ShopContextInterface $sourceContext */
        $sourceContext = $this->container->get('shopware_storefront.context_service')->getShopContext();
        
        // Skip widget if customer is already in retailer group
        if ('H' == sourceContext->getCurrentCustomerGroup()->getKey()) return true;

        /** @var \Shopware\Bundle\StoreFrontBundle\Service\ListProductServiceInterface $listProductService */
        $listProductService = $this->container->get('shopware_storefront.list_product_service');

        /** @var \Shopware\Models\Customer\Group $customerGroup */
        $customerGroup = $this->getModelManager()->getRepository(\Shopware\Models\Customer\Group::class)->findOneBy(array('key' => 'H'));

        /** @var Shopware\Bundle\StoreFrontBundle\Struct\Customer\Group $customerGroupStruct */
        $customerGroupStruct = new Shopware\Bundle\StoreFrontBundle\Struct\Customer\Group();
        $customerGroupStruct->setKey($customerGroup->getKey());
        $customerGroupStruct->setId($customerGroup->getId());
        $customerGroupStruct->setName($customerGroup->getName());
        $customerGroupStruct->setDisplayGrossPrices($customerGroup->getTax());
        $customerGroupStruct->setInsertedGrossPrices($customerGroup->getTaxInput());
        $customerGroupStruct->setMinimumOrderValue($customerGroup->getMinimumOrder());
        $customerGroupStruct->setPercentageDiscount($customerGroup->getDiscount());
        $customerGroupStruct->setSurcharge($customerGroup->getMinimumOrderSurcharge());
        $customerGroupStruct->setUseDiscount($customerGroup->getMode());

        $targetShopContext = new \Shopware\Bundle\StoreFrontBundle\Struct\ShopContext(
            $sourceContext->getBaseUrl(),
            $sourceContext->getShop(),
            $sourceContext->getCurrency(),
            $customerGroupStruct,
            $sourceContext->getFallbackCustomerGroup(),
            $sourceContext->getTaxRules(),
            $sourceContext->getPriceGroups(),
            $sourceContext->getArea(),
            $sourceContext->getCountry(),
            $sourceContext->getState()
        );

        /** @var \Shopware\Bundle\StoreFrontBundle\Struct\ListProduct $product */
		$product = $listProductService->get($number, $targetShopContext);

        $this->view->assign('retailerPrice',$product->getCheapestPrice()->getCalculatedPrice());
        $this->view->assign('defaultPrice',$defaultPrice);

    }

}

Anzeigen des Info-Textes – Widget View

Die letzte Übung ist nun das Anzeigen des Info-Textes auf der Produktdetailseite. Dafür müssen wir für das Widget noch eine View anlegen (custom/plugins/PluginName/Resources/views/retailer_info/widgets/retailer_info/detail_info_widget.tpl), die dann automatisch zurückgegeben und an der Stelle des {action} Tags platziert wird.

{if !is_null($retailerPrice)}
    <p>Registrieren Sie sich jetzt als Fachhändler und zahlen Sie für dieses Produkt nur {$retailerPrice|currency} statt {$defaultPrice|currency}!</p>
{/if}
Zusammenfassung
Shopware Preisberechnung für eine spezifische Kundengruppe im Frontend durchführen
Titel
Shopware Preisberechnung für eine spezifische Kundengruppe im Frontend durchführen
Beschreibung
Dieses Tutorial zeigt Dir, wie Du neben dem Normalpreis eines Produkts auch den Preis für eine dedizierte Kundengruppe anzeigen kannst. Ich löse dieses Problem über den ListProductService und einen künstlich erzeugten ShopContext.
Autor
Publisher
synonymous.rocks
Logo

1 Kommentar

In diesem Beitrag kannst Du Kommentare verfassen.


  • Danke für deinen Beitrag.
    Ich stand gerade heute ebenfalls vor dem Problem einen ShopContext für einen Shop und eine Kundengruppe im backend / in einem cronjob zur Verfügung zu stellen. Vielleicht hilft dieser snippet ja jemandem, der danach sucht: https://dpaste.de/v3MW
    Viele Grüße

    Eike Warneke 1 Jahr ago Antworten


Kommentar verfassen

Folge mir auf Twitter

Hol Dir kostenlos Tipps und Tricks zu Shopware, E-Commerce und andere Open-Source Produkte.

Folge @synonymousrocks