<?php declare(strict_types=1);
/**
* PremSoft
* Copyright © 2020 Premsoft - Sven Mittreiter
*
* @copyright Copyright (c) 2020, premsoft - Sven Mittreiter (http://www.premsoft.de)
* @author Sven Mittreiter <info@premsoft.de>
*/
namespace Prems\Plugin\PremsIndividualOffer6\Core\Offer\Storefront;
use Prems\Plugin\PremsIndividualOffer6\Core\Entity\Offer\Aggregate\OfferItem\OfferItemCollection;
use Prems\Plugin\PremsIndividualOffer6\Core\Entity\Offer\Aggregate\OfferItem\OfferItemEntity;
use Prems\Plugin\PremsIndividualOffer6\Core\Entity\Offer\Aggregate\OfferMessages\OfferMessageCollection;
use Prems\Plugin\PremsIndividualOffer6\Core\Entity\Offer\OfferDefinition;
use Prems\Plugin\PremsIndividualOffer6\Core\Entity\Offer\OfferEntity;
use Prems\Plugin\PremsIndividualOffer6\Core\Media\MediaService;
use Prems\Plugin\PremsIndividualOffer6\Event\OfferCreatedEvent;
use Prems\Plugin\PremsIndividualOffer6\PremsIndividualOffer6;
use Shopware\Core\Checkout\Cart\Cart;
use Shopware\Core\Checkout\Cart\CartPersisterInterface;
use Shopware\Core\Checkout\Cart\Error\ErrorCollection;
use Shopware\Core\Checkout\Cart\LineItem\LineItem;
use Shopware\Core\Checkout\Cart\Price\AbsolutePriceCalculator;
use Shopware\Core\Checkout\Cart\Price\AmountCalculator;
use Shopware\Core\Checkout\Cart\Price\PercentagePriceCalculator;
use Shopware\Core\Checkout\Cart\Price\QuantityPriceCalculator;
use Shopware\Core\Checkout\Cart\Price\Struct\AbsolutePriceDefinition;
use Shopware\Core\Checkout\Cart\Price\Struct\CartPrice;
use Shopware\Core\Checkout\Cart\Price\Struct\CalculatedPrice;
use Shopware\Core\Checkout\Cart\Price\Struct\PercentagePriceDefinition;
use Shopware\Core\Checkout\Cart\Price\Struct\PriceCollection;
use Shopware\Core\Checkout\Cart\Price\Struct\QuantityPriceDefinition;
use Shopware\Core\Checkout\Cart\Price\Struct\ReferencePriceDefinition;
use Shopware\Core\Checkout\Cart\SalesChannel\CartService;
use Shopware\Core\Checkout\Cart\Tax\PercentageTaxRuleBuilder;
use Shopware\Core\Checkout\Cart\Tax\Struct\TaxRule;
use Shopware\Core\Checkout\Cart\Tax\Struct\TaxRuleCollection;
use Shopware\Core\Checkout\Customer\CustomerEntity;
use Shopware\Core\Checkout\Shipping\ShippingMethodEntity;
use Shopware\Core\Content\Product\Cart\ProductLineItemFactory;
use Shopware\Core\Content\Product\SalesChannel\Detail\AbstractProductDetailRoute;
use Shopware\Core\Content\Product\SalesChannel\SalesChannelProductEntity;
use Shopware\Core\Content\Product\ProductEntity;
use Shopware\Core\Defaults;
use Shopware\Core\Framework\Context;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
use Shopware\Core\Framework\DataAbstractionLayer\Event\EntityWrittenContainerEvent;
use Shopware\Core\Framework\DataAbstractionLayer\Exception\InconsistentCriteriaIdsException;
use Shopware\Core\Framework\DataAbstractionLayer\Pricing\Price;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Aggregation\Bucket\FilterAggregation;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Aggregation\Metric\CountAggregation;
use Shopware\Core\Framework\DataAbstractionLayer\Search\AggregationResult\AggregationResultCollection;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\DataAbstractionLayer\Search\EntitySearchResult;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\AndFilter;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\MultiFilter;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\NotFilter;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Sorting\FieldSorting;
use Shopware\Core\Framework\Struct\ArrayEntity;
use Shopware\Core\Framework\Uuid\Uuid;
use Shopware\Core\System\NumberRange\ValueGenerator\NumberRangeValueGeneratorInterface;
use Shopware\Core\System\SalesChannel\Entity\SalesChannelRepositoryInterface;
use Shopware\Core\System\SalesChannel\SalesChannelContext;
use Shopware\Core\Checkout\Customer\Aggregate\CustomerGroup\CustomerGroupEntity;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpFoundation\Request;
use Shopware\Core\Framework\Struct\ArrayStruct;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
use Shopware\Core\Framework\DataAbstractionLayer\Pricing\PriceCollection as DALPriceCollection;
class OfferService
{
/**
* @var EntityRepositoryInterface
*/
private $offerRepository;
/**
* @var EntityRepositoryInterface
*/
private $offerItemRepository;
/**
* @var EntityRepositoryInterface
*/
private $offerMessageRepository;
/**
* @var SalesChannelRepositoryInterface
*/
private $productRepository;
/**
* @var CartService
*/
private $cartService;
/**
* @var NumberRangeValueGeneratorInterface
*/
private $numberRangeValueGenerator;
/**
* @var ProductLineItemFactory
*/
private $productLineItemFactory;
/**
* @var ConfigService
*/
private $configService;
/**
* @var AbstractProductDetailRoute
*/
private $productLoader;
/** @var MediaService $mediaService */
private $mediaService;
private EventDispatcherInterface $eventDispatcher;
/**
* @var CartPersisterInterface
*/
private CartPersisterInterface $cartPersister;
/**
* @var AmountCalculator
*/
private AmountCalculator $amountCalculator;
/**
* @var QuantityPriceCalculator
*/
private QuantityPriceCalculator $quantityPriceCalculator;
/**
* @var PercentagePriceCalculator
*/
private PercentagePriceCalculator $percentagePriceCalculator;
/**
* @var AbsolutePriceCalculator
*/
private AbsolutePriceCalculator $absolutePriceCalculator;
/**
* @var PercentageTaxRuleBuilder
*/
private PercentageTaxRuleBuilder $percentageTaxRuleBuilder;
public function __construct(
EntityRepositoryInterface $offerRepository,
EntityRepositoryInterface $offerItemRepository,
EntityRepositoryInterface $offerMessageRepository,
SalesChannelRepositoryInterface $productRepository,
CartService $cartService,
NumberRangeValueGeneratorInterface $numberRangeValueGenerator,
ProductLineItemFactory $productLineItemFactory,
ConfigService $configService,
AbstractProductDetailRoute $productLoader,
MediaService $mediaService,
EventDispatcherInterface $eventDispatcher,
CartPersisterInterface $cartPersister,
AmountCalculator $amountCalculator,
QuantityPriceCalculator $quantityPriceCalculator,
PercentagePriceCalculator $percentagePriceCalculator,
AbsolutePriceCalculator $absolutePriceCalculator,
PercentageTaxRuleBuilder $percentageTaxRuleBuilder
) {
$this->offerRepository = $offerRepository;
$this->offerItemRepository = $offerItemRepository;
$this->offerMessageRepository = $offerMessageRepository;
$this->productRepository = $productRepository;
$this->cartService = $cartService;
$this->numberRangeValueGenerator = $numberRangeValueGenerator;
$this->productLineItemFactory = $productLineItemFactory;
$this->configService = $configService;
$this->productLoader = $productLoader;
$this->mediaService = $mediaService;
$this->eventDispatcher = $eventDispatcher;
$this->cartPersister = $cartPersister;
$this->amountCalculator = $amountCalculator;
$this->quantityPriceCalculator = $quantityPriceCalculator;
$this->percentagePriceCalculator = $percentagePriceCalculator;
$this->absolutePriceCalculator = $absolutePriceCalculator;
$this->percentageTaxRuleBuilder = $percentageTaxRuleBuilder;
}
/**
* Return all offers for an user
*
* @param SalesChannelContext $salesChannelContext
* @param Request $request
*
* @return EntitySearchResult
*
* @throws InconsistentCriteriaIdsException
*/
public function getOffersForUser(SalesChannelContext $salesChannelContext, Request $request)
{
$criteria = (new Criteria())
->addFilter(new AndFilter([
new EqualsFilter('prems_individual_offer.customerId', $salesChannelContext->getCustomer()->getId()),
new EqualsFilter('prems_individual_offer.salesChannelId', $salesChannelContext->getSalesChannelId())
]))
->addAssociation('items')
->addAssociation('items.product')
->addAssociation('items.product.cover')
->addAssociation('items.product.prices')
->addSorting(new FieldSorting('createdAt', FieldSorting::DESCENDING));
$filters = [];
if ($request->query->get('openRequest')) {
$filters[] = new MultiFilter(
MultiFilter::CONNECTION_AND,
[
new EqualsFilter('prems_individual_offer.offered', 0),
new EqualsFilter('prems_individual_offer.accepted', 0),
new EqualsFilter('prems_individual_offer.declined', 0)
]
);
}
if ($request->query->get('received')) {
$filters[] = new MultiFilter(
MultiFilter::CONNECTION_AND,
[
new EqualsFilter('prems_individual_offer.offered', 1),
new EqualsFilter('prems_individual_offer.accepted', 0),
new EqualsFilter('prems_individual_offer.declined', 0)
]
);
}
if ($request->query->get('ordered')) {
$filters[] = new MultiFilter(
MultiFilter::CONNECTION_AND,
[
new EqualsFilter('prems_individual_offer.offered', 1),
new EqualsFilter('prems_individual_offer.accepted', 1),
new EqualsFilter('prems_individual_offer.declined', 0)
]
);
}
if ($request->query->get('declined')) {
$filters[] = new EqualsFilter('prems_individual_offer.declined', 1);
}
if (!empty($filters)) {
$criteria->addFilter(new MultiFilter(MultiFilter::CONNECTION_OR, $filters));
}
$offers = $this->offerRepository->search($criteria, $salesChannelContext->getContext());
if (!empty($offers)) {
/** @var OfferEntity $offer */
foreach ($offers as $offer) {
$currentDate = date('Y-m-d H:i:s');
$offerDays = (int) $offer->getOfferDaysValid();
$offerDate = date('Y-m-d H:i:s', strtotime($offer->getUpdatedAtDateTime() . " +{$offerDays} day"));
if (($offer->getOfferDaysValid() !== null && $offer->getOfferDaysValid() !== '') &&
$offer->getUpdatedAtDateTime() !== null &&
$offerDate <= $currentDate &&
($offer->isOffered() && !$offer->isAccepted() && !$offer->isDeclined())
) {
$offer->setDeclined(true);
$this->offerRepository->update([[
'id' => $offer->getId(),
'declined' => $offer->isDeclined(),
'updated_at' => $offer->getUpdatedAtDateTime()
]], $salesChannelContext->getContext());
}
}
}
// skip offers without items
$criteria->addFilter(
new NotFilter(
NotFilter::CONNECTION_AND,
[
new EqualsFilter('prems_individual_offer.items.id', null)
]
)
);
$offers = $this->offerRepository->search($criteria, $salesChannelContext->getContext());
if (!empty($offers)) {
/** @var OfferEntity $offer */
foreach ($offers as $offer) {
$criteriaMessages = (new Criteria())
->addFilter(new EqualsFilter('prems_individual_offer_messages.offerId', $offer->getId()))
->addFilter(new EqualsFilter('prems_individual_offer_messages.customerId', $salesChannelContext->getCustomer()->getId()))
->addAssociation('user')
->addAssociation('customer')
->addSorting(new FieldSorting('prems_individual_offer_messages.createdAt', FieldSorting::DESCENDING));
/** @var OfferMessageCollection $messages */
$messages = $this->offerMessageRepository->search($criteriaMessages, $salesChannelContext->getContext())->getEntities();
$offer->setMessages($messages);
}
}
return $offers;
}
/**
* @param Context $context
* @return EntitySearchResult
*/
public function updateOffersStatus(Context $context): EntitySearchResult
{
$criteria = new Criteria();
$offers = $this->offerRepository->search($criteria, $context);
if (!empty($offers)) {
/** @var OfferEntity $offer */
foreach ($offers as $offer) {
$currentDate = date('Y-m-d H:i:s');
$offerDays = (int) $offer->getOfferDaysValid();
$offerDate = date('Y-m-d H:i:s', strtotime($offer->getUpdatedAtDateTime() . " +{$offerDays} day"));
if(($offer->getOfferDaysValid() !== null && $offer->getOfferDaysValid() !== '') &&
$offer->getUpdatedAtDateTime() !== null &&
$offerDate <= $currentDate &&
($offer->isOffered() && !$offer->isAccepted() && !$offer->isDeclined())
) {
$offer->setDeclined(true);
$this->offerRepository->update([[
'id' => $offer->getId(),
'declined' => $offer->isDeclined(),
'updated_at' => $offer->getUpdatedAtDateTime()
]], $context);
}
}
}
return $offers;
}
/**
* @param CustomerEntity $customer
* @param Context $context
* @return AggregationResultCollection
*/
public function getOfferStatusAggregations(CustomerEntity $customer, Context $context): AggregationResultCollection
{
$criteria = (new Criteria())
->addFilter(new EqualsFilter('prems_individual_offer.customerId', $customer->getId()))
->addAggregation(new FilterAggregation('offered', new CountAggregation('offeredCount', 'prems_individual_offer.id'), [
new MultiFilter(
MultiFilter::CONNECTION_AND,
[
new EqualsFilter('prems_individual_offer.offered', 0),
new EqualsFilter('prems_individual_offer.accepted', 0),
new EqualsFilter('prems_individual_offer.declined', 0),
new NotFilter(
NotFilter::CONNECTION_AND,
[
new EqualsFilter('prems_individual_offer.items.id', null)
]
)
]
)
]))
->addAggregation(new FilterAggregation('declined', new CountAggregation('declinedCount', 'prems_individual_offer.id'), [
new MultiFilter(
MultiFilter::CONNECTION_AND,
[
new EqualsFilter('prems_individual_offer.declined', 1),
new NotFilter(
NotFilter::CONNECTION_AND,
[
new EqualsFilter('prems_individual_offer.items.id', null)
]
)
]
)
]))
->addAggregation(new FilterAggregation('received', new CountAggregation('receivedCount', 'prems_individual_offer.id'), [
new MultiFilter(
MultiFilter::CONNECTION_AND,
[
new EqualsFilter('prems_individual_offer.offered', 1),
new EqualsFilter('prems_individual_offer.accepted', 0),
new EqualsFilter('prems_individual_offer.declined', 0),
new NotFilter(
NotFilter::CONNECTION_AND,
[
new EqualsFilter('prems_individual_offer.items.id', null)
]
)
]
)
]))
->addAggregation(new FilterAggregation('ordered', new CountAggregation('orderedCount', 'prems_individual_offer.id'), [
new MultiFilter(
MultiFilter::CONNECTION_AND,
[
new EqualsFilter('prems_individual_offer.offered', 1),
new EqualsFilter('prems_individual_offer.accepted', 1),
new EqualsFilter('prems_individual_offer.declined', 0),
new NotFilter(
NotFilter::CONNECTION_AND,
[
new EqualsFilter('prems_individual_offer.items.id', null)
]
)
]
)
]));
return $this->offerRepository->search($criteria, $context)->getAggregations();
}
/**
* Get on offer by offer ID
* @param string $offerId
* @param Context $context
* @param CustomerEntity|null $customer
* @return OfferEntity
*/
public function getOfferById(string $offerId, Context $context, CustomerEntity $customer = null)
{
$criteria = new Criteria([ $offerId ]);
if ($customer) {
$criteria->addFilter(new EqualsFilter('prems_individual_offer.customerId', $customer->getId()));
}
$criteria
->addAssociation('items')
->addAssociation('items.product')
->addAssociation('items.product.prices')
->addAssociation('items.product.cover')
->addAssociation('items.product.options.group')
->addAssociation('customer')
->addAssociation('customer.addresses')
->addAssociation('customer.salutation')
->addAssociation('customer.defaultBillingAddress')
->addAssociation('customer.activeBillingAddress')
->addAssociation('customer.defaultBillingAddress.country')
->addAssociation('customer.activeBillingAddress.country')
->addAssociation('shippingMethod.tax')
->addAssociation('currency')
->addAssociation('salesChannel.domains')
->addAssociation('salesChannel.mailHeaderFooter')
->addAssociation('items.product.deliveryTime')
->addAssociation('language.locale');
$criteria
->getAssociation('items')
->addSorting(new FieldSorting('position'));
$context->setConsiderInheritance(true);
$offer = $this->offerRepository->search($criteria, $context)->first();
if ($offer && $customer != null) {
$criteriaMessages = (new Criteria())
->addFilter(new EqualsFilter('prems_individual_offer_messages.offerId', $offer->getId()))
->addFilter(new EqualsFilter('prems_individual_offer_messages.customerId', $customer->getId()))
->addAssociation('user')
->addAssociation('customer')
->addSorting(new FieldSorting('prems_individual_offer_messages.createdAt', FieldSorting::DESCENDING));
$messages = $this->offerMessageRepository->search($criteriaMessages, $context)->getEntities();
$offer->setMessages($messages);
}
return $offer;
}
/**
* Returns the offer language ID
*
* @param string $offerId
* @param Context $context
* @return string|null
*/
public function getOfferLanguageId(string $offerId, Context $context): ?string
{
$criteria = new Criteria([$offerId]);
/** @var OfferEntity $offer */
if ($offer = $this->offerRepository->search($criteria, $context)->getEntities()->first()) {
return $offer->getLanguageId();
}
return null;
}
/**
* Get on product by product ID
* @param string $productId
* @param SalesChannelContext $context
* @return ProductEntity
*/
public function getProductById(string $productId, SalesChannelContext $context): ProductEntity
{
$criteria = new Criteria([$productId]);
$criteria->addAssociation('cover');
$criteria->addAssociation('prices');
return $this->productRepository->search($criteria, $context)->first();
}
/**
* Add product to the offer
*
* @param array $item
* @param SalesChannelProductEntity $product
* @param SalesChannelContext $salesChannelContext
* @return void
*/
public function addProductOfferItem(array $item, SalesChannelProductEntity $product, SalesChannelContext $salesChannelContext): void
{
$customTaxRules = null;
// use custom tax rules if offer item is not new
if (isset($item['id']) && isset($item['priceDefinition']) && !empty($item['priceDefinition']['taxRules'])) {
$customPriceDefinition = QuantityPriceDefinition::fromArray($item['priceDefinition']);
$customTaxRules = $customPriceDefinition->getTaxRules();
}
// new price definition
$priceDefinition = $this->getPriceDefinition($product, $item['quantity'], $item['price'], $customTaxRules);
$calculatedPrice = $this->quantityPriceCalculator->calculate($priceDefinition, $salesChannelContext);
// old price definition with original price
$oldPriceDefinition = $this->getPriceDefinition($product, $item['quantity']);
// if offer item is new
if (!isset($item['id'])) {
$offerItemsCount = $this->getOfferItemsCount($item['offerId'], $salesChannelContext->getContext());
$item['position'] = ++$offerItemsCount;
}
$item = array_merge(
$item,
[
'price' => $priceDefinition->getPrice(),
'oldPrice' => $oldPriceDefinition->getPrice(),
'totalPrice' => $calculatedPrice->getTotalPrice(),
'itemOption' => null,
'priceDefinition' => $priceDefinition
]
);
$this->offerItemRepository->upsert([$item], $salesChannelContext->getContext());
}
/**
* Add custom item to the offer
*
* custom type or credit type
*
* @param array $item
* @param Context $context
* @return EntityWrittenContainerEvent
*/
public function addCustomOfferItem(array $item, Context $context): EntityWrittenContainerEvent
{
// if offer item is new
if (!isset($item['id'])) {
$offerItemsCount = $this->getOfferItemsCount($item['offerId'], $context);
$item['position'] = ++$offerItemsCount;
}
$priceDefinition = QuantityPriceDefinition::fromArray($item['priceDefinition']);
$item = array_merge(
$item,
[
'oldPrice' => $item['price'],
'totalPrice' => $item['price'],
'priceDefinition' => (new QuantityPriceDefinition($item['price'], $priceDefinition->getTaxRules(), $item['quantity']))
]
);
return $this->offerItemRepository->upsert([$item], $context);
}
/**
* Remove items from the offer
*
* @param array $items
* @param Context $context
* @return EntityWrittenContainerEvent
*/
public function removeOfferItems(array $items, Context $context): EntityWrittenContainerEvent
{
return $this->offerItemRepository->delete([$items], $context);
}
/**
* Recalculate the offer
*
* @param OfferEntity $offer
* @param SalesChannelContext $context
* @return void
*/
public function recalculateOffer(OfferEntity $offer, SalesChannelContext $context): void
{
$prices = new PriceCollection();
$originalPrices = new PriceCollection();
/** @var OfferItemEntity $offerItem */
foreach ($offer->getItems() as $offerItem) {
$priceDefinition = $offerItem->getPriceDefinition();
if (!$priceDefinition || $offerItem->getItemType() === LineItem::PROMOTION_LINE_ITEM_TYPE) {
$taxRate = 0;
if ($offerItem->getProduct() && $productTax = $offerItem->getProduct()->getTax()) {
$taxRate = $productTax->getTaxRate();
if ($taxRule = $context->getTaxRules()->get($productTax->getId())->getRules()->first()) {
$taxRate = $taxRule->getTaxRate();
}
}
$taxRules = new TaxRuleCollection([new TaxRule($taxRate ?? 0)]);
$priceDefinition = new QuantityPriceDefinition($offerItem->getPrice(), $taxRules, $offerItem->getQuantity());
}
$prices->add($this->quantityPriceCalculator->calculate($priceDefinition, $context));
$originalPrices->add($this->getOfferOriginalQuantityPrice($offerItem, $priceDefinition->getTaxRules(), $context));
}
$shippingCosts = $this->getOfferShippingCosts($offer, $context);
$price = $this->amountCalculator->calculate($prices, $shippingCosts, $context);
$originalPrice = $this->amountCalculator->calculate($originalPrices, new PriceCollection(), $context);
$data = [
'id' => $offer->getId(),
'price' => $price,
'totalDiscount' => $this->calculateOfferTotalDiscount($originalPrice, $price)
];
$this->offerRepository->update([$data], $context->getContext());
}
/**
* Returns the offer shipping costs
*
* @param OfferEntity $offer
* @param SalesChannelContext $salesChannelContext
* @return PriceCollection
*/
public function getOfferShippingCosts(OfferEntity $offer, SalesChannelContext $salesChannelContext): PriceCollection
{
$shippingCosts = new PriceCollection();
if ($offer->getShippingCostsCalculationType() === OfferEntity::SHIPPING_COST_CALCULATION_TYPE_FIXED && $offer->getShippingCosts()) {
$prices = new DALPriceCollection([
new Price(
Defaults::CURRENCY,
$offer->getShippingCosts(),
$offer->getShippingCosts(),
false
),
]);
$shippingCosts->add(
$this->calculateShippingCosts(
$offer->getShippingMethod(),
$prices,
$offer->getItems(),
$salesChannelContext
)
);
}
return $shippingCosts;
}
/**
* Returns the offer original quantity price
*
* @param OfferItemEntity $item
* @param TaxRuleCollection $taxRules
* @param SalesChannelContext $salesChannelContext
* @return CalculatedPrice
*/
protected function getOfferOriginalQuantityPrice(OfferItemEntity $item, TaxRuleCollection $taxRules, SalesChannelContext $salesChannelContext): CalculatedPrice
{
$quantityPriceDefinition = new QuantityPriceDefinition($item->getOldPrice(), $taxRules, $item->getQuantity());
return $this->quantityPriceCalculator->calculate($quantityPriceDefinition, $salesChannelContext);
}
/**
* Calculate total offer discount percentage and price
*
* @param CartPrice $originalPrice
* @param CartPrice $price
* @return array
*/
protected function calculateOfferTotalDiscount(CartPrice $originalPrice, CartPrice $price): array
{
$price = $price->getPositionPrice();
$originalPrice = $originalPrice->getPositionPrice();
return [
'price' => $originalPrice - $price,
'percentage' => $originalPrice ? (($originalPrice - $price) * 100) / $originalPrice : $originalPrice
];
}
/**
* Get and return items of an offer
* @param string $offerId
* @param string $customerId
* @param SalesChannelContext $context
* @return null|OfferItemCollection
* @throws InconsistentCriteriaIdsException
*/
public function getItemsFromOffer(string $offerId, string $customerId, SalesChannelContext $context): ?OfferItemCollection
{
$criteria = new Criteria();
$criteria->addFilter(new EqualsFilter('id', $offerId))
->addFilter(new EqualsFilter('prems_individual_offer.customerId', $customerId))
->addAssociation('items')
->addAssociation('customer')
//->addAssociation('items.product.prices')
/**->addAssociation('items.product')
->addAssociation('items.product.cover')
->addAssociation('items.product.prices')*/;
/** @var OfferEntity|null $offer */
$offer = $this->offerRepository->search($criteria, $context->getContext())->first();
if (!$offer instanceof OfferEntity) {
throw new NotFoundHttpException();
}
$nestedItems = $offer->getNestedItems();
$elements = $nestedItems->getElements();
$productCriteria = new Criteria();
$productCriteria->addAssociation('options.group');
/** @var OfferItemEntity $element */
foreach($elements as $element) {
$clonedCriteria = clone $productCriteria;
/** @var SalesChannelProductEntity|null $product */
if (!$element->getProductId()) {
continue;
}
$result = $this->productLoader->load($element->getProductId(), new Request(), $context, $clonedCriteria);
/** @var SalesChannelProductEntity|null $product */
$product = $result->getProduct();
if ($product) {
$element->setProduct($product);
$element->setPriceNet();
}
}
return $nestedItems;
}
/**
* Determine if a offer request is generally allowed (Plugin activated for saleschannel, cart amount high enough)
* @param SalesChannelContext $context
*
* @return bool
*/
public function isOfferRequestAllowed(SalesChannelContext $context)
{
$offerSettings = $this->configService->getConfig($context);
$settings = $offerSettings->getVars();
if ($settings['disallowedCustomerGroups'] && is_array($settings['disallowedCustomerGroups'])) {
$customerGroup = $context->getCurrentCustomerGroup();
if (in_array($customerGroup->getId(), $settings['disallowedCustomerGroups'])) {
return false;
}
}
if ($settings['offerMinValueReached'] == true) {
return true;
}
return false;
}
/**
* Determine if the account offer nav item is allowed (Plugin activated for saleschannel, customer group allowed)
* @param SalesChannelContext $context
*
* @return bool
*/
public function isOfferNavAllowed(SalesChannelContext $context)
{
$offerSettings = $this->configService->getConfig($context);
$settings = $offerSettings->getVars();
if ($settings['disallowedCustomerGroups'] && is_array($settings['disallowedCustomerGroups'])) {
$customerGroup = $context->getCurrentCustomerGroup();
if (in_array($customerGroup->getId(), $settings['disallowedCustomerGroups'])) {
return false;
}
}
return true;
}
/**
* Creates an offer request for a customer
* @param CustomerEntity $customer
* @param CustomerGroupEntity $customerGroup
* @param SalesChannelContext $context
*
* @return EntityWrittenContainerEvent
* @throws \Exception
*/
public function createOfferRequest(CustomerEntity $customer, CustomerGroupEntity $customerGroup, SalesChannelContext $context): EntityWrittenContainerEvent
{
$items = [];
$cart = $this->cartService->getCart($context->getToken(), $context);
$lineItems = $cart->getLineItems()->filter(
function (LineItem $lineItem) {
return in_array($lineItem->getType(), [LineItem::PRODUCT_LINE_ITEM_TYPE, 'customized-products', LineItem::PROMOTION_LINE_ITEM_TYPE]);
}
);
$currencySymbol = $context->getCurrency()->getSymbol();
if ($lineItems) {
$i = 0;
foreach($lineItems as $lineItem) {
$i++;
$priceDefinition = $lineItem->getPriceDefinition() ?: $this->buildPriceDefinition($lineItem->getPrice(), $lineItem->getQuantity());
//$products[] = ['id' => $lineItem->getId()];
$data = [
'productId' => $lineItem->getId(),
'oldPrice' => $lineItem->getPrice()->getUnitPrice(),
'price' => $lineItem->getPrice()->getUnitPrice(),
'totalPrice' => $lineItem->getPrice()->getTotalPrice(),
'quantity' => $lineItem->getQuantity(),
'position' => $i,
'lineItem' => \base64_encode(\serialize($lineItem)),
'priceDefinition' => $priceDefinition,
'itemType' => LineItem::PRODUCT_LINE_ITEM_TYPE
];
// Possibility to use referencedID instead of productId for third party vendors
if ($lineItem->hasPayloadValue('prems_individual_offer_use_reference_id_instead_of_product_id')) {
$data['productId'] = $lineItem->getReferencedId();
}
if($lineItem->getType() == LineItem::PROMOTION_LINE_ITEM_TYPE) {
$data['productId'] = null;
$data['itemType'] = LineItem::PROMOTION_LINE_ITEM_TYPE;
}
if ($lineItem->getType() == 'customized-products') {
$productId = 0;
$children = $lineItem->getChildren();
$itemOption = [];
foreach($children as $child) {
if ($child->getType() == LineItem::PRODUCT_LINE_ITEM_TYPE) {
$productId = $child->getReferencedId();
}
if ($child->getType() == 'customized-products-option') {
$quantity = '';
$price = '';
$payloadValue = '';
if ($child->getQuantity() > 1) {
$quantity = $child->getQuantity() . ' x ';
}
if ($child->hasChildren()) {
$subChildren = $child->getChildren();
$optionValues = [];
foreach($subChildren as $subChild) {
if ($subChild->getPrice()->getTotalPrice() > 0) {
$price = ' '.$subChild->getPrice()->getTotalPrice() . ' ' . $currencySymbol;
}
$optionValues[] = $subChild->getLabel();
}
$itemOption[] = $quantity.$child->getLabel() . ' (' . implode('; ', $optionValues) . ')'.$payloadValue.$price;
} else {
if ($child->getPrice()->getTotalPrice() > 0) {
$price = ' '.$child->getPrice()->getTotalPrice() . ' ' . $currencySymbol;
}
$payload = $child->getPayload();
if ($payload && array_key_exists('value', $payload) && !is_array($payload['value'])) {
$payloadValue = ': '.$payload['value'];
}
if ($payload && array_key_exists('media', $payload) && is_array($payload['media'])) {
$media = $this->mediaService->getMediaById($payload['media']['0']['mediaId'], $context->getContext());
$payloadValue = ': '.$media->getUrl();
}
$itemOption[] = $quantity.$child->getLabel().$payloadValue.$price ;
}
}
}
//$data['itemOption'] = implode('<br>',$itemOption);
$data['itemOption'] = json_encode($itemOption);
$data['productId'] = $productId;
$data['itemType'] = 'swag-customized-products';
}
$items[] = $data;
}
}
//$this->numberRangeValueGenerator->previewPattern(OfferDefinition::ENTITY_NAME, '{n}', 1);
if ($customerGroup->getDisplayGross()) {
$taxStatus = 'gross';
} else {
$taxStatus = 'net';
}
$cart->setErrors(new ErrorCollection());
$cart->setData(null);
$offerId = Uuid::randomHex();
$offerData = [
'id' => $offerId,
'customerId' => $customer->getId(),
'taxStatus' => $taxStatus,
'offerNumber' => $this->numberRangeValueGenerator->getValue(
OfferDefinition::ENTITY_NAME,
$context->getContext(),
$context->getSalesChannel()->getId()
),
'pdfComment' => $this->configService->getConfig($context)->getDefaultPdfComment(),
'currencyFactor' => $context->getContext()->getCurrencyFactor(),
'currencyId' => $context->getContext()->getCurrencyId(),
'languageId' => $context->getLanguageId() ?: $context->getContext()->getLanguageId(),
'salesChannelId' => $context->getSalesChannel()->getId(),
'cart' => \base64_encode(\serialize($cart)),
'items' => $items,
'price' => $this->amountCalculator->calculate($lineItems->getPrices(), new PriceCollection(), $context)
];
if ($this->configService->getConfig($context)->getDefaultOfferDaysValid() > 0) {
$offerData['offer_days_valid'] = (int) $this->configService->getConfig($context)->getDefaultOfferDaysValid();
if ($offerData['offer_days_valid']) {
$offerData['offerExpiration'] = $this->calculateOfferExpirationDate($offerData['offer_days_valid']);
}
}
$offerCreation = $this->offerRepository->create([
$offerData
], $context->getContext());
$this->eventDispatcher->dispatch(new OfferCreatedEvent($offerId, $context));
return $offerCreation;
}
/**
* Determine if an offer is orderable
* @param OfferEntity $offer
* @return bool
*/
public function isOfferOrderable(OfferEntity $offer): bool
{
if ($offer->isOffered() && !$offer->isAccepted() && !$offer->isDeclined()) {
return true;
}
return false;
}
/**
* Determine if an offer is declineable
* @param OfferEntity $offer
* @return bool
*/
public function isOfferDeclineable(OfferEntity $offer): bool
{
if ($offer->isOffered() && !$offer->isAccepted() && !$offer->isDeclined()) {
return true;
}
return false;
}
/**
* Determine if customer is allowed to access an offer
* @param OfferEntity $offer
* @param CustomerEntity $customer
* @return bool
*/
public function hasAccessToOffer(OfferEntity $offer, CustomerEntity $customer): bool
{
$customerIsOwner = $customer->getId() === $offer->getCustomer()->getId();
return $customerIsOwner;
}
/**
* Determine if basket is in offer mode and then return offer mode
*
* @param SalesChannelContext $context
* @param SessionInterface $session
* @return string
*/
public function getOfferMode(SalesChannelContext $context, SessionInterface $session): string
{
// compatibility mode for older version with session value. If there is an entry then cancel offer mode.
// Code will be removed in newer versions
if ($session->has('prems_individual_offer')) {
$session->remove('prems_individual_offer');
$this->cancelOfferMode($context, $session);
}
$cart = $this->cartService->getCart($context->getToken(), $context);
if (!$cart->hasExtension('offer_mode_active')) {
return '';
}
$offerMode = $cart->getExtension('offer_mode_active');
if (!$offerMode || !$offerMode->getId()) {
return '';
}
return $offerMode->getId();
}
/**
* Set flag that basket is in offer mode
*
* @param Cart $cart
* @param string $offerId
*/
protected function activateOfferMode($cart, $offerId):void
{
$cart->addExtension(PremsIndividualOffer6::OFFER_MODE_EXTENSION_NAME, new ArrayEntity(['id' => $offerId]));
}
/**
* Set flag that basket is no longer in offer mode
*/
public function disableOfferMode(SalesChannelContext $context):void
{
$cart = $this->cartService->getCart($context->getToken(), $context);
// compatibility mode for older version with session value.
if ($cart->hasExtension(PremsIndividualOffer6::OFFER_MODE_EXTENSION_NAME)) {
$cart->removeExtension(PremsIndividualOffer6::OFFER_MODE_EXTENSION_NAME);
}
}
/**
* Set flag that basket is no longer in offer mode
*
* @param SalesChannelContext $context
* @param SessionInterface $session
* @return void
*/
public function restoreBackupCart(SalesChannelContext $context, SessionInterface $session):void
{
$this->cartService->deleteCart($context);
$backupCartToken = $session->remove('premsoftIndividualOffer6.backupCart');
if ($backupCartToken != null) {
$backupCart = $this->cartService->getCart($backupCartToken, $context);
$backupCart->setToken($context->getToken());
$backupCart->setName($this->cartService::SALES_CHANNEL);
$this->cartService->setCart($backupCart);
$this->cartPersister->save($backupCart, $context);
$this->cartPersister->delete($backupCartToken, $context);
}
}
/**
* @param SalesChannelContext $context
* @param SessionInterface $session
*/
public function cancelOfferMode(SalesChannelContext $context, SessionInterface $session): void
{
$this->disableOfferMode($context);
$context->removeState(PremsIndividualOffer6::OFFER_MODE_STATE);
$this->restoreBackupCart($context, $session);
}
/**
* Add an offer to basket and set basket to offer mode
*
* @param OfferEntity $offer
* @param SalesChannelContext $context
* @param SessionInterface $session
* @return void
*/
public function addOfferToBasket(OfferEntity $offer, SalesChannelContext $context, SessionInterface $session)
{
// Backup current cart
$backupCart = $this->cartService->getCart($context->getToken(), $context);
$backupCartToken = $session->get('premsoftIndividualOffer6.backupCart');
if ($backupCartToken == null) {
$backupCartToken = Uuid::randomHex();
$session->set('premsoftIndividualOffer6.backupCart', $backupCartToken);
}
$backupCart->setToken($backupCartToken);
$backupCart->setName('premsoftIndividualOffer6.backupCart');
$this->cartService->setCart($backupCart);
$this->cartPersister->save($backupCart, $context);
// Create new Cart
$cart = $this->cartService->createNew($context->getToken());
$this->activateOfferMode($cart, $offer->getId());
$products = [];
/** @var OfferItemEntity $item */
foreach ($offer->getItems() as $item) {
// Swag Custom Product found?
if ($item->getItemType() == 'swag-customized-products') {
$product = \unserialize(base64_decode((string)$item->getLineItem()));
if ($product && $product instanceof LineItem) {
$product->setStackable(true);
$product->setQuantity($item->getQuantity());
$product->setRemovable(false);
$product->setStackable(false);
} else {
continue;
}
} elseif($item->getItemType() == LineItem::PROMOTION_LINE_ITEM_TYPE) {
$row = \unserialize(base64_decode((string) $item->getLineItem()));
$product = new LineItem(Uuid::randomHex(), LineItem::CUSTOM_LINE_ITEM_TYPE);
$product->assign($row->getVars());
$product->setType(LineItem::CUSTOM_LINE_ITEM_TYPE);
$product->setPriceDefinition(new QuantityPriceDefinition($product->getPrice()->getTotalPrice(), $product->getPrice()->getTaxRules()));
$product->setGood(true);
$product->setRemovable(false);
} elseif($item->getItemType() === LineItem::CREDIT_LINE_ITEM_TYPE || $item->getItemType() === LineItem::CUSTOM_LINE_ITEM_TYPE) {
$product = new LineItem(Uuid::randomHex(), $item->getItemType());
$product->setLabel($item->getName());
$product->setStackable(true);
$product->setRemovable(false);
$product->setQuantity($item->getQuantity());
$product->setReferencedId(Uuid::randomHex());
$product->setPriceDefinition($item->getPriceDefinition());
if ($item->getItemType() === LineItem::CREDIT_LINE_ITEM_TYPE) {
$product->setPriceDefinition(new AbsolutePriceDefinition($item->getPriceDefinition()->getPrice()));
}
} else {
$product = \unserialize(base64_decode((string) $item->getLineItem()));
if (!$product || !is_object($product)) {
$product = $this->productLineItemFactory->create($item->getProductId());
}
$product->setId(Uuid::randomHex());
$product->setStackable(true);
$product->setRemovable(false);
$product->setQuantity($item->getQuantity());
$product->setStackable(false);
}
$product->addExtension('prems', new ArrayStruct(
[
'price' => $product->getPrice(),
'originalId' => $item->getId(),
'customPriceDefinition' => $item->getPriceDefinition()
]
));
$products[] = $product;
}
$this->cartService->add($cart, $products, $context);
}
/**
* Declines an offer
* @param OfferEntity $offer
* @param Context $context
* @param int|null $offer_days_valid
*
* @return EntityWrittenContainerEvent
* @throws \Exception
*/
public function declineOffer(OfferEntity $offer, Context $context, $offer_days_valid = null)
{
$offer->setDeclined(true);
$data = [
'id' => $offer->getId(),
'declined' => $offer->isDeclined(),
'offer_days_valid' => $offer_days_valid,
'offerExpiration' => null
];
if ($offer_days_valid) {
$data['offerExpiration'] = $this->calculateOfferExpirationDate($offer_days_valid, $offer->getUpdatedAt());
}
return $this->offerRepository->update([$data], $context);
}
/**
* Reset an offer
* @param OfferEntity $offer
* @param Context $context
* @param int|null $offer_days_valid
*
* @return EntityWrittenContainerEvent
* @throws \Exception
*/
public function resetOffer(OfferEntity $offer, Context $context, $offer_days_valid = null)
{
$offer->setDeclined(true);
$data = [
'id' => $offer->getId(),
'declined' => false,
'accepted' => false,
'offered' => false,
'offer_days_valid' => $offer_days_valid,
'offerExpiration' => null
];
if ($offer_days_valid) {
$data['offerExpiration'] = $this->calculateOfferExpirationDate($offer_days_valid, $offer->getUpdatedAt());
}
return $this->offerRepository->update([$data], $context);
}
/**
* Accept an offer
* @param OfferEntity $offer
* @param Context $context
*
* @return EntityWrittenContainerEvent
*/
public function acceptOffer(OfferEntity $offer, Context $context)
{
$offer->setAccepted(true);
return $this->offerRepository->update([[
'id' => $offer->getId(),
'accepted' => $offer->isAccepted()
]], $context);
}
/**
* Creates an offer
* @param OfferEntity $offer
* @param Context $context
* @param int|null $offer_days_valid
*
* @return EntityWrittenContainerEvent
* @throws \Exception
*/
public function createOffer(OfferEntity $offer, Context $context, $offer_days_valid = null)
{
$offer->setOffered(true);
$offer->setAccepted(false);
$offer->setDeclined(false);
$data = [
'id' => $offer->getId(),
'offered' => $offer->isOffered(),
'accepted' => $offer->isAccepted(),
'declined' => $offer->isDeclined(),
'offer_days_valid' => $offer_days_valid,
'offerExpiration' => null,
'admin_updated_at' => date("Y-m-d H:i:s")
];
if ($offer_days_valid) {
$data['offerExpiration'] = $this->calculateOfferExpirationDate($offer_days_valid, $offer->getUpdatedAt());
}
return $this->offerRepository->update([$data], $context);
}
/**
* Change days an offer
* @param OfferEntity $offer
* @param Context $context
* @param int|null $offer_days_valid
*
* @return EntityWrittenContainerEvent
* @throws \Exception
*/
public function changeOfferDays(OfferEntity $offer, Context $context, $offer_days_valid = null)
{
$offer->setOffered(true);
$data = [
'id' => $offer->getId(),
'offer_days_valid' => $offer_days_valid ?? null,
'offerExpiration' => null
];
if ($offer_days_valid) {
$data['offerExpiration'] = $this->calculateOfferExpirationDate($offer_days_valid, $offer->getUpdatedAt());
}
return $this->offerRepository->update([$data], $context);
}
public function setOffered(string $offerId, Context $context): bool
{
$this->offerRepository->update([['id' => $offerId, 'offered' => true]], $context);
return true;
}
/**
* Adds Line items to cart
* @param Cart $cart
* @param LineItem|LineItem[] $items
* @param SalesChannelContext $context
* @return Cart
*/
public function addCartItems(Cart $cart, $items, SalesChannelContext $context): Cart
{
$cart = $this->cartService->add($cart, $items, $context);
return $cart;
}
/**
* @param Cart $cart
* @param string $id
* @param SalesChannelContext $context
* @return Cart
*/
public function removeCartItem(Cart $cart, string $id, SalesChannelContext $context): Cart
{
$cart = $this->cartService->remove($cart, $id, $context);
return $cart;
}
/**
* @param array $items
* @param Context $context
* @return EntityWrittenContainerEvent
*/
public function updateOfferItems(array $items, Context $context): EntityWrittenContainerEvent
{
return $this->offerItemRepository->update($items, $context);
}
/**
* @param array $data
* @param Context $context
* @return EntityWrittenContainerEvent
*/
public function updateOfferShipping(array $data, Context $context): EntityWrittenContainerEvent
{
return $this->offerRepository->update([$data], $context);
}
/**
* @param $days
* @param $date
* @throws \Exception
* @return \DateTimeImmutable
*/
public function calculateOfferExpirationDate($days, $date = null): \DateTimeImmutable
{
if (!$date) {
$date = new \DateTimeImmutable(date("Y-m-d H:i:s", time()));
}
if (is_string($date)) {
$date = new \DateTimeImmutable($date);
}
$interval = \DateInterval::createFromDateString("{$days} day");
return $date->add($interval);
}
/**
* Returns the offer items count
*
* @param string $offerId
* @param Context $context
* @return int
*/
private function getOfferItemsCount(string $offerId, Context $context): int
{
$criteria = new Criteria();
$criteria->addFilter(new EqualsFilter('offerId', $offerId));
return $this->offerItemRepository->search($criteria, $context)->getTotal();
}
/**
* @param SalesChannelProductEntity $product
* @param int $quantity
* @param float $customPrice
* @param null $customTaxRules
* @return QuantityPriceDefinition
*/
private function getPriceDefinition(SalesChannelProductEntity $product, int $quantity, float $customPrice = 0, $customTaxRules = null): QuantityPriceDefinition
{
if ($product->getCalculatedPrices()->count() === 0) {
return $this->buildPriceDefinition($product->getCalculatedPrice(), $quantity, $customPrice, $customTaxRules);
}
// keep loop reference to $price variable to get last quantity price in case of "null"
$price = $product->getCalculatedPrice();
foreach ($product->getCalculatedPrices() as $price) {
if ($quantity <= $price->getQuantity()) {
break;
}
}
return $this->buildPriceDefinition($price, $quantity, $customPrice, $customTaxRules);
}
/**
* @param CalculatedPrice $price
* @param int $quantity
* @param float $customPrice
* @param null $customTaxRules
* @return QuantityPriceDefinition
*/
private function buildPriceDefinition(CalculatedPrice $price, int $quantity, float $customPrice = 0, $customTaxRules = null): QuantityPriceDefinition
{
$unitPrice = $customPrice ?: $price->getUnitPrice();
$taxRules = $customTaxRules ?: $price->getTaxRules();
$definition = new QuantityPriceDefinition($unitPrice, $taxRules, $quantity);
if ($price->getListPrice() !== null) {
$definition->setListPrice($price->getListPrice()->getPrice());
}
if ($price->getReferencePrice() !== null) {
$definition->setReferencePriceDefinition(
new ReferencePriceDefinition(
$price->getReferencePrice()->getPurchaseUnit(),
$price->getReferencePrice()->getReferenceUnit(),
$price->getReferencePrice()->getUnitName()
)
);
}
return $definition;
}
/**
* @param ShippingMethodEntity $shippingMethod
* @param DALPriceCollection $priceCollection
* @param OfferItemCollection $offerItems
* @param SalesChannelContext $context
* @return CalculatedPrice
*/
private function calculateShippingCosts(ShippingMethodEntity $shippingMethod, DALPriceCollection $priceCollection, OfferItemCollection $offerItems, SalesChannelContext $context): CalculatedPrice
{
switch ($shippingMethod->getTaxType()) {
case ShippingMethodEntity::TAX_TYPE_HIGHEST:
$rules = $this->getOfferItemPrices($offerItems, $context)->getHighestTaxRule();
break;
case ShippingMethodEntity::TAX_TYPE_FIXED:
$tax = $shippingMethod->getTax();
if ($tax !== null) {
$rules = $context->buildTaxRules($tax->getId());
break;
}
// no break
default:
$rules = $this->percentageTaxRuleBuilder->buildRules(
$this->getOfferItemPrices($offerItems, $context)->sum()
);
}
$price = $this->getCurrencyPrice($priceCollection, $context);
$definition = new QuantityPriceDefinition($price, $rules, 1);
return $this->quantityPriceCalculator->calculate($definition, $context);
}
/**
* @param DALPriceCollection $priceCollection
* @param SalesChannelContext $context
* @return float
*/
private function getCurrencyPrice(DALPriceCollection $priceCollection, SalesChannelContext $context): float
{
/** @var Price $price */
$price = $priceCollection->getCurrencyPrice($context->getCurrency()->getId());
$value = $this->getPriceForTaxState($price, $context);
if ($price->getCurrencyId() === Defaults::CURRENCY) {
$value *= $context->getContext()->getCurrencyFactor();
}
return $value;
}
/**
* @param Price $price
* @param SalesChannelContext $context
* @return float
*/
private function getPriceForTaxState(Price $price, SalesChannelContext $context): float
{
if ($context->getTaxState() === CartPrice::TAX_STATE_GROSS) {
return $price->getGross();
}
return $price->getNet();
}
/**
* @param OfferItemCollection $offerItems
* @param SalesChannelContext $salesChannelContext
* @return PriceCollection
*/
private function getOfferItemPrices(OfferItemCollection $offerItems, SalesChannelContext $salesChannelContext): PriceCollection
{
$self = $this;
return new PriceCollection(
array_filter(array_map(static function (OfferItemEntity $offerItem) use ($self, $salesChannelContext) {
return $self->calculateOfferItemPrice($offerItem->getPriceDefinition(), $salesChannelContext);
}, array_values($offerItems->getElements())))
);
}
/**
* Return the offer item calculated price
*
* @param $definition
* @param SalesChannelContext $salesChannelContext
* @return CalculatedPrice
*/
private function calculateOfferItemPrice($definition, SalesChannelContext $salesChannelContext): CalculatedPrice
{
$prices = new PriceCollection();
if ($definition instanceof AbsolutePriceDefinition) {
return $this->absolutePriceCalculator->calculate($definition->getPrice(), $prices, $salesChannelContext);
}
if ($definition instanceof PercentagePriceDefinition) {
return $this->percentagePriceCalculator->calculate($definition->getPercentage(), $prices, $salesChannelContext);
}
return $this->quantityPriceCalculator->calculate($definition, $salesChannelContext);
}
}