<?php declare(strict_types=1);
namespace Nimbits\NimbitsPriceOnRequestNext\Service;
use Nimbits\NimbitsPriceOnRequestNext\Setting\Service\SettingService;
use Shopware\Core\Checkout\Cart\Delivery\Struct\DeliveryInformation;
use Shopware\Core\Checkout\Cart\Delivery\Struct\DeliveryTime;
use Shopware\Core\Checkout\Cart\Exception\InvalidPayloadException;
use Shopware\Core\Checkout\Cart\Exception\InvalidQuantityException;
use Shopware\Core\Checkout\Cart\LineItem\LineItem;
use Shopware\Core\Checkout\Cart\Price\Struct\CalculatedPrice;
use Shopware\Core\Checkout\Cart\Rule\LineItemScope;
use Shopware\Core\Content\Product\ProductEntity;
use Shopware\Core\Content\Product\SalesChannel\SalesChannelProductEntity;
use Shopware\Core\Framework\Struct\Struct;
use Shopware\Core\Framework\Struct\StructCollection;
use Shopware\Core\Content\Product\Cart\ProductLineItemFactory;
use Shopware\Core\Checkout\Cart\SalesChannel\CartService;
use Shopware\Core\Checkout\Cart\RuleLoader;
use Shopware\Core\Checkout\Cart\CartRuleLoader;
use Shopware\Core\Checkout\Cart\AbstractRuleLoader;
use Shopware\Core\Checkout\Cart\CachedRuleLoader;
use Shopware\Core\Framework\Rule\Rule;
use Shopware\Core\Framework\Uuid\Uuid;
use Shopware\Core\System\SalesChannel\SalesChannelContext;
class PriceOnRequestService
{
/** @var SettingService $settingsService */
private SettingService $settingsService;
private CartService $cartService;
private CartRuleLoader $cartRuleLoader;
private CachedRuleLoader $cachedRuleLoader;
private ProductLineItemFactory $productLineItemFactory;
/**
* Creates a new instance of the service.
*
* @param SettingService $settingsService
*/
public function __construct(
SettingService $settingsService,
CartService $cartService,
CartRuleLoader $cartRuleLoader,
CachedRuleLoader $cachedRuleLoader,
ProductLineItemFactory $productLineItemFactory
)
{
$this->settingsService = $settingsService;
$this->cartService = $cartService;
$this->cartRuleLoader = $cartRuleLoader;
$this->cachedRuleLoader = $cachedRuleLoader;
$this->productLineItemFactory = $productLineItemFactory;
}
//helper for the plugin config
private function getPluginConfig($salesChannelContext){
$config = $this->settingsService->getSettingsAsStruct($salesChannelContext->getSalesChannel()->getId());
return $config;
}
//helperfunction
//returns default por ruleset to match shopware standard config
public function getDefaultPorProductRuleset(){
$defaultruleset = [
"por_btn_visible" => false,
"por_dont_show_price" => false,
"por_add_to_cart_not_visible" => false,
"por_product_not_purchasable" => false,
];
return $defaultruleset;
}
//helperfunction
//returns default por ruleset to match shopware standard config
public function getDefaultPorCartRuleset(){
$defaultruleset = [
"show_por_btn" => false,
"cart_is_not_purchaseable" => false
];
return $defaultruleset;
}
//helperfunction
//calculates an array of rulesets to one major ruleset which applies to the father and all its children articles
public function sortRulesets($rulesets){
//default set
$fnRuleset = $this->getDefaultPorProductRuleset();
foreach($rulesets AS $ruleset){
if($ruleset->por_btn_visible == true){
$fnRuleset["por_btn_visible"] = true;
}
if($ruleset->por_dont_show_price == true){
$fnRuleset["por_dont_show_price"] = true;
}
if($ruleset->por_add_to_cart_not_visible == true){
$fnRuleset["por_add_to_cart_not_visible"] = true;
}
if($ruleset->por_product_not_purchasable == true){
$fnRuleset["por_product_not_purchasable"] = true;
}
}
return (new StructCollection())->assign($fnRuleset);
}
//cart rule evaluation
public function getCartRuleSet($salesChannelContext){
$config = $this->getPluginConfig($salesChannelContext);
$context = $salesChannelContext->getContext();
//load all rules on this saleschannel
$rulecollection = $this->cachedRuleLoader->load($context);
//get the cart - needed for validation of the rules
$currentCart = $this->cartService->getCart($salesChannelContext->getToken(), $salesChannelContext);
//filter for only active rules and sort them by priority
$ruleCollectionCleaned = $rulecollection->filterMatchingRules($currentCart, $salesChannelContext);
$ruleCollectionCleaned->sortByPriority();
$cartRuleset = $this->getDefaultPorCartRuleset();
$cartRuleset["show_por_btn"] = $this->getCartShowPorBtn($ruleCollectionCleaned, $config);
$cartRuleset["cart_is_not_purchaseable"] = $this->getCartIsNotPurchaseable($ruleCollectionCleaned, $config);
return (new StructCollection())->assign($cartRuleset);
}
public function getCartShowPorBtn($ruleCollectionCleaned, $config): bool
{
if(empty($config->get('nbporcartshowporbtnrule'))){
return false;
}
if($ruleCollectionCleaned->has($config->get('nbporcartshowporbtnrule'))){
return true;
}
return false;
}
public function getCartIsNotPurchaseable($ruleCollectionCleaned, $config): bool
{
if(empty($config->get('nbporcartisnotpurchaseablerule'))){
return false;
}
if($ruleCollectionCleaned->has($config->get('nbporcartisnotpurchaseablerule'))){
return true;
}
return false;
}
/**
* @throws InvalidQuantityException
* @throws InvalidPayloadException
*/
public function createMockLineItem(SalesChannelProductEntity $product, int $quantity = 1): LineItem
{
$payload = array_filter($product->getVars(), function($v)
{
return is_scalar($v) || is_array($v);
});
return (new LineItem(
Uuid::randomHex(),
LineItem::PRODUCT_LINE_ITEM_TYPE,
$product->getId(),
$quantity
))
->setPayload($payload)
->setPrice($product->getCalculatedPrice())
->setCover($product->getCover() != null ? $product->getCover()->getMedia() : null)
->setDeliveryInformation(
new DeliveryInformation(
(int) $product->getAvailableStock(),
(float) $product->getWeight(),
$product->getShippingFree() === true,
$product->getRestockTime(),
$product->getDeliveryTime() != null ? DeliveryTime::createFromEntity($product->getDeliveryTime()) : null,
$product->getHeight(),
$product->getWidth(),
$product->getLength()
)
);
}
//returns the price on request ruleset for a single product
public function getProductRuleSet(SalesChannelContext $salesChannelContext, LineItem $lineItem){
$config = $this->getPluginConfig($salesChannelContext);
$productRuleset = $this->getDefaultPorProductRuleset();
$productRuleset["por_btn_visible"] = $this->checkLineItemPorRule($salesChannelContext, $config->get('nbpordetailporbtnvisiblerule') , $lineItem);
$productRuleset["por_dont_show_price"] = $this->checkLineItemPorRule($salesChannelContext, $config->get('nbpordetaildontshowpricerule') , $lineItem);
$productRuleset["por_add_to_cart_not_visible"] = $this->checkLineItemPorRule($salesChannelContext, $config->get('nbpordetailaddtocartbtnnotvisiblerule') , $lineItem);
$productRuleset["por_product_not_purchasable"] = $this->checkLineItemPorRule($salesChannelContext, $config->get('nbpordetailproductnotpurchaseablerule') , $lineItem);
return (new StructCollection())->assign($productRuleset);
}
//helper function
//evaulates a rule on a lineitem
public function checkLineItemPorRule(SalesChannelContext $salesChannelContext, ?string $ruleId, LineItem $lineItem)
{
$context = $salesChannelContext->getContext();
$cachedRulesCollection = $this->cachedRuleLoader->load($context);
$rule = $cachedRulesCollection->get($ruleId);
if($rule == NULL)
return false;
$lineItemScope = new LineItemScope($lineItem, $salesChannelContext);
//if rule matches on lineitem we have por case
if($rule->getPayload()->match($lineItemScope)){
return true;
}
return false;
}
}