Promotions and Discounts feature integration
Edit on GitHubTo start using the Discount Promotion feature, you have to do some configuration in your Zed application.
Prerequisites
- First make sure you have the latest
DiscountPromotion
module. Usecthecomposer require spryker/discount-promotion
command to install it. - You also need at least
"spryker/discount": "^4.5.0"
for the discount module.
- Run
vendor/bin/console transfer:generate
to generate the latest transfer object. - Run
vendor/bin/console propel:diff
to generate migration file for the database. Inspect this new file and check if onlyspy_discount_promotion
has been created there. - Run
vendor/bin/console propel:migrate
to migrate the latest generate migration file. - Run
vendor/bin/console propel:model:build
to generate new propel Entities and Query classes.
Enabling Discount Promotions
To enable Discount promotions, you have to add a number of plugins to the Discount
module so that DiscountPromotion
can extend it.
Below there is the example of the DiscountDependencyProvider
class.
<?php
namespace Pyz\Zed\Discount;
use Spryker\Zed\Discount\DiscountDependencyProvider as SprykerDiscountDependencyProvider;
use Spryker\Zed\DiscountPromotion\Communication\Plugin\Discount\DiscountFilterPromotionDiscountsPlugin;
use Spryker\Zed\DiscountPromotion\Communication\Plugin\Discount\DiscountPromotionCalculationFormExpanderPlugin;
use Spryker\Zed\DiscountPromotion\Communication\Plugin\Discount\DiscountPromotionCollectorStrategyPlugin;
use Spryker\Zed\DiscountPromotion\Communication\Plugin\Discount\DiscountPromotionConfigurationExpanderPlugin;
use Spryker\Zed\DiscountPromotion\Communication\Plugin\Discount\DiscountPromotionPostSavePlugin;
use Spryker\Zed\DiscountPromotion\Communication\Plugin\Discount\DiscountPromotionPostUpdatePlugin;
class DiscountDependencyProvider extends SprykerDiscountDependencyProvider
{
/**
* @return array
*/
protected function getDiscountableItemFilterPlugins()
{
return [
new DiscountFilterPromotionDiscountsPlugin(), //Filter out discountable items which have promotionItem = trye
];
}
/**
* @return \Spryker\Zed\Discount\Dependency\Plugin\CollectorStrategyPluginInterface[]
*/
protected function getCollectorStrategyPlugins()
{
return [
new DiscountPromotionCollectorStrategyPlugin(), //specialized collector strategy for promotion discounts
];
}
/**
* @return \Spryker\Zed\Discount\Dependency\Plugin\DiscountPostSavePluginInterface[]
*/
protected function getDiscountPostSavePlugins()
{
return [
new DiscountPromotionPostSavePlugin(), //Save promotion discount
];
}
/**
* @return \Spryker\Zed\Discount\Dependency\Plugin\DiscountPostUpdatePluginInterface[]
*/
protected function getDiscountPostUpdatePlugins()
{
return [
new DiscountPromotionPostUpdatePlugin(),//Update promotion discount
];
}
/**
* @return \Spryker\Zed\Discount\Dependency\Plugin\DiscountConfigurationExpanderPluginInterface[]
*/
protected function getDiscountConfigurationExpanderPlugins()
{
return [
new DiscountPromotionConfigurationExpanderPlugin(), //Expand DiscountConfigurationTransfer with Promotion discount data.
];
}
/**
* This plugin allows to expand DiscountConfigurationTransfer when using
*
* @return \Spryker\Zed\Discount\Dependency\Plugin\Form\DiscountFormExpanderPluginInterface[]
*/
protected function getDiscountFormExpanderPlugins()
{
return [
new DiscountPromotionCalculationFormExpanderPlugin(), //Expand Discount form type with new promotion discount. Adds new form fields.
];
}
/**
* @return \Spryker\Zed\Discount\Dependency\Plugin\Form\DiscountFormDataProviderExpanderPluginInterface[]
*/
protected function getDiscountFormDataProviderExpanderPlugins()
{
return [
new DiscountPromotionCalculationFormDataExpanderPlugin(), // Expand Discount form with additional data
];
}
/**
* @return \Spryker\Zed\Discount\Dependency\Plugin\DiscountViewBlockProviderPluginInterface[]
*/
protected function getDiscountViewTemplateProviderPlugins()
{
return [
new DiscountPromotionViewBlockProviderPlugin(), //Provide additional content to discount view page
];
}
/**
* @return \Spryker\Zed\Discount\Dependency\Plugin\DiscountViewBlockProviderPluginInterface[]
*/
protected function getDiscountApplicableFilterPlugins()
{
return [
new DiscountPromotionFilterApplicableItemsPlugin(), //Filter promotion items from decision rule
];
}
}
The new calculator plugin must be registered in CalculationDependencyProvider
:
<?php
namespace Pyz\Zed\Calculation;
use Spryker\Zed\DiscountPromotion\Communication\Plugin\Calculation\RemovePromotionItemsCalculatorPlugin;
class CalculationDependencyProvider extends SprykerCalculationDependencyProvider
{
protected function getQuoteCalculatorPluginStack(Container $container)
{
return [
new RemoveTotalsCalculatorPlugin(),
new RemoveAllCalculatedDiscountsCalculatorPlugin(),
new RemovePromotionItemsCalculatorPlugin(), //Removes promotion items from quote
...//other plugins
];
}
}
The new Cart expander plugin must be registered in:
<?php
namespace Pyz\Zed\Cart;
class CartDependencyProvider extends SprykerCartDependencyProvider
{
/**
* @param \Spryker\Zed\Kernel\Container $container
*
* @return \Spryker\Zed\Cart\Dependency\ItemExpanderPluginInterface[]
*/
protected function getExpanderPlugins(Container $container)
{
return [
... //other plugins
new CartGroupPromotionItems(), //expand group key with promo item identifier
];
}
Usage in Yves
To be able to see promotion products, you have to change how cart items are rendered in Yves:
- Take the
Pyz\Yves\DiscountPromotion
module from demoshop and place it somewhere in your project. - Change the
CartOperationHandler::add
method to include promotion item flag. Like$itemTransfer->setIsPromotion((bool)$this->request->request->get('isPromo'));
- Inject
ProductPromotionMapperPlugin
to Cart Module:
<?php
namespace Pyz\Yves\Cart;
use Spryker\Yves\DiscountPromotion\Plugin\ProductPromotionMapperPlugin;
class CartDependencyProvider extends AbstractBundleDependencyProvider
{
const PLUGIN_PROMOTION_PRODUCT_MAPPER = 'PLUGIN_PROMOTION_PRODUCT_MAPPER';
/**
* @param \Spryker\Yves\Kernel\Container $container
*
* @return \Spryker\Yves\Kernel\Container
*/
protected function providePlugins(Container $container)
{
...//other plugins
$container[self::PLUGIN_PROMOTION_PRODUCT_MAPPER] = function () {
return new ProductPromotionMapperPlugin();
};
}
}
- Add the
getProductPromotionMapperPlugin
method to theDiscountFactory
provider.
<?php
namespace Pyz\Yves\Cart;
class CartFactory extends AbstractFactory
{
/**
* @return \Spryker\Yves\DiscountPromotion\Dependency\PromotionProductMapperPluginInterface
*/
public function getProductPromotionMapperPlugin()
{
return $this->getProvidedDependency(CartDependencyProvider::PLUGIN_PROMOTION_PRODUCT_MAPPER);
}
}
- Add call to plugin in
CartController
.
<?php
namespace Pyz\Yves\Cart\Controller;
/**
* @method \Spryker\Client\Cart\CartClientInterface getClient()
* @method \Pyz\Yves\Cart\CartFactory getFactory()
*/
class CartController extends AbstractController
{
/**
* @param array|null $selectedAttributes
*
* @return array
*/
public function indexAction(array $selectedAttributes = null)
{
$promotionStorageProducts = $this->getFactory()
->getProductPromotionMapperPlugin()
->mapPromotionItemsFromProductStorage(
$quoteTransfer,
$this->getRequest()
);
$this->viewResponse([
//other data
'promotionStorageProducts' => $promotionStorageProducts,
]);
}
Change twig templates to render promotion products. Since we’ve changed how quantity is rendered for promotion products, some cart templates in our demoshop were reorganized.
Firstly, make sure a promotion item twig template is called in Pyz/Yves/Cart/Theme/default/cart/index.twig
. This usually should be placed after cart items as in the example below:
{% for cartItem in cartItems %}
{% if cartItem.bundleProduct is defined %}
{% include '@cart/cart/parts/cart-item.twig' with {
cartItem: cartItem.bundleProduct,
bundleItems: cartItem.bundleItems
} %}
{% else %}
{% include '@cart/cart/parts/cart-item.twig' %}
{% endif %}
{% endfor %} //existing code
{% include '@DiscountPromotion/discount-promotion/item-list.twig' %} //new include
Pyz/Yves/Cart/Theme/default/cart/parts/cart-item.twig
was also heavily modified to work with promotion products (please check our demoshop version), as the cart page can be different per project.
The key points that were changed: the “Add to cart” button extracted to Pyz/Yves/Cart/Theme/default/cart/parts/cart-add-to-cart.twig
, item price information extracted to Pyz/Yves/Cart/Theme/default/cart/parts/cart-item-prices.twig
, cart product variants extracted to Pyz/Yves/Cart/Theme/default/cart/parts/cart-product-variants.twig
.
Below there is the demoshop Pyz/Yves/Cart/Theme/default/cart/parts/cart-item.twig
file for reference.
<div class="callout cart-item"><div class="row">
{% include '@Cart/cart/parts/cart-images.twig' %}
<div class="small-9 large-expand columns"><ul class="no-bullet">
{# General data #}
<li class="lead">{{ cartItem.name }}</li><li class="__secondary"><small>{{ 'cart.item.sku' | trans }} {{ cartItem.sku }}</small></li>
{% if bundleItems is defined %}
{# Product Bundles #}
<li><strong>{{ 'cart.item.bundle.description' | trans }}</strong><ul>
{% for bundleItem in bundleItems %}
<li>{{ bundleItem.quantity }} x {{ bundleItem.name }} </li>
{% endfor %}
</ul></li>
{% else %}
{% include '@Cart/cart/parts/cart-product-variants.twig' %}
{% endif %}
</ul></div>
{% include '@Cart/cart/parts/cart-item-prices.twig' %}
{% include '@Cart/cart/parts/cart-add-to-cart.twig' %}
</div></div>
Make sure CartOperationHandler
sets ID of idDiscountPromotion
.
public function add($sku, $quantity, $optionValueUsageIds = [])
{
$itemTransfer = new ItemTransfer();
$itemTransfer->setSku($sku);
$itemTransfer->setQuantity($quantity);
$itemTransfer->setIdDiscountPromotion($this->getIdDiscountPromotion()); //new setter
$this->addProductOptions($optionValueUsageIds, $itemTransfer);
$quoteTransfer = $this->cartClient->addItem($itemTransfer);
$this->cartClient->storeQuote($quoteTransfer);
}
protected function getIdDiscountPromotion()
{
return (int)$this->request->request->get('idDiscountPromotion');
}
When using promotion discount with voucher code, you will get the error message that voucher is not correct. It’s because voucher code is a product offered as promotion and not yet added to cart.
You have to modify \Pyz\Yves\Discount\Handler\VoucherHandler::addFlashMessages
to handle discounts with promotions.
Add the following condition:
namespace Pyz\Yves\Discount\Handler;
class VoucherHandler extends BaseHandler implements VoucherHandlerInterface
{
/**
* @param \Generated\Shared\Transfer\QuoteTransfer $quoteTransfer
* @param string $voucherCode
*
* @return void
*/
protected function addFlashMessages($quoteTransfer, $voucherCode)
{
//---new code
if ($this->isVoucherFromPromotionDiscount($quoteTransfer, $voucherCode)) {
return;
}
//-----
if ($this->isVoucherCodeApplied($quoteTransfer, $voucherCode)) {
$this->setFlashMessagesFromLastZedRequest($this->calculationClient);
return;
}
$this->flashMessenger->addErrorMessage('cart.voucher.apply.failed');
}
/**
* @param \Generated\Shared\Transfer\QuoteTransfer $quoteTransfer
* @param string $voucherCode
*
* @return bool
*/
protected function isVoucherFromPromotionDiscount(QuoteTransfer $quoteTransfer, $voucherCode)
{
foreach ($quoteTransfer->getUsedNotAppliedVoucherCodes() as $voucherCodeUsed) {
if ($voucherCodeUsed === $voucherCode) {
return true;
}
}
return false;
}
}
After this you should be able to use the new discounts with promotion.
Thank you!
For submitting the form