261 lines
8.4 KiB
PHP
261 lines
8.4 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace KupShop\PreordersBundle\Service;
|
|
|
|
use KupShop\I18nBundle\Entity\Currency;
|
|
use KupShop\I18nBundle\Util\PriceConverter;
|
|
use KupShop\KupShopBundle\Context\ContextManager;
|
|
use KupShop\KupShopBundle\Context\CurrencyContext;
|
|
use KupShop\KupShopBundle\Context\EmptiableContext;
|
|
use KupShop\KupShopBundle\Context\LanguageContext;
|
|
use KupShop\KupShopBundle\Context\PriceLevelContext;
|
|
use KupShop\KupShopBundle\Context\UserContext;
|
|
use KupShop\KupShopBundle\Exception\InvalidArgumentException;
|
|
use KupShop\KupShopBundle\Util\Contexts;
|
|
use KupShop\KupShopBundle\Util\Price\TotalPrice;
|
|
use KupShop\OrderingBundle\Util\Purchase\PurchaseUtil;
|
|
use KupShop\PreordersBundle\Entity\Preorder;
|
|
use KupShop\PreordersBundle\Entity\UserPreorder;
|
|
use KupShop\PricelistBundle\Context\PricelistContext;
|
|
use Query\Operator;
|
|
|
|
class DynamicContexts
|
|
{
|
|
/**
|
|
* @var array<int, string> Mapping of pricelist IDs to currency (cache in case of multiple calls to activatePreorder)
|
|
*/
|
|
private array $pricelistCurrenciesCache = [];
|
|
|
|
public function __construct(
|
|
private readonly ContextManager $contextManager,
|
|
private readonly PurchaseUtil $purchaseUtil,
|
|
private readonly PreorderUtil $listUtil,
|
|
private readonly PricelistContext $pricelistContext,
|
|
private readonly PriceConverter $priceConverter,
|
|
) {
|
|
}
|
|
|
|
/**
|
|
* @template T of mixed
|
|
*
|
|
* @param callable(): T $callback
|
|
*
|
|
* @return T
|
|
*
|
|
* @throws InvalidArgumentException
|
|
*/
|
|
public function withDynamicPrices(Preorder $preorder, array $items, callable $callback)
|
|
{
|
|
$contexts = array_merge(
|
|
$this->getBaseContexts($preorder),
|
|
$this->getDynamicContexts($preorder, $items),
|
|
);
|
|
|
|
return $this->contextManager->activateContexts($contexts, $callback);
|
|
}
|
|
|
|
/**
|
|
* @template T of mixed
|
|
*
|
|
* @param callable(): T $callback
|
|
*
|
|
* @return T
|
|
*
|
|
* @throws InvalidArgumentException
|
|
*/
|
|
public function withBasePrices(Preorder $preorder, callable $callback)
|
|
{
|
|
$contexts = $this->getBaseContexts($preorder);
|
|
|
|
return $this->contextManager->activateContexts($contexts, $callback);
|
|
}
|
|
|
|
protected function getBaseContexts(Preorder $preorder, ?\User $user = null): array
|
|
{
|
|
$contexts = [];
|
|
if ($user) {
|
|
$contexts[UserContext::class] = $user;
|
|
}
|
|
|
|
if (PreorderUtil::fieldIsComposedOfDigits($preorder, 'id_pricelist')) {
|
|
$contexts[PricelistContext::class] = (int) $preorder['id_pricelist'];
|
|
} else {
|
|
$contexts[PricelistContext::class] = ContextManager::FORCE_EMPTY;
|
|
}
|
|
|
|
if (PreorderUtil::fieldIsComposedOfDigits($preorder, 'id_price_level')) {
|
|
$contexts[PriceLevelContext::class] = (int) $preorder['id_price_level'];
|
|
} else {
|
|
$contexts[PriceLevelContext::class] = ContextManager::FORCE_EMPTY;
|
|
}
|
|
|
|
return $contexts;
|
|
}
|
|
|
|
/**
|
|
* @throws InvalidArgumentException
|
|
*/
|
|
private function getDynamicContexts(Preorder $preorder, array $items = []): array
|
|
{
|
|
$contexts = [];
|
|
|
|
// Pokud nemáme nastaveny dynamické ceny, tak není potřeba již volat výpočet total price
|
|
if (!$preorder->hasDynamicPrice()) {
|
|
return $contexts;
|
|
}
|
|
|
|
/* @var TotalPrice $total */
|
|
$this->withBasePrices($preorder, function () use ($items, &$total) {
|
|
$productList = $this->listUtil->getPreorderProductList($items);
|
|
$purchaseState = PreorderUtil::toPurchaseState($productList->getProducts());
|
|
|
|
$total = $this->purchaseUtil->recalculateTotalPrices($purchaseState)
|
|
->getTotalPrice();
|
|
});
|
|
|
|
// convert currency of total price to default preorder's price-list's currency
|
|
if ($pricelistId = $preorder['id_pricelist'] ?? false) {
|
|
/** @var Currency $currency */
|
|
$currency = $this->contextManager->activateContexts(
|
|
[PricelistContext::class => $pricelistId],
|
|
fn () => $this->pricelistContext->getActive()->getCurrency(),
|
|
);
|
|
|
|
if ($total->getCurrency()->getId() !== $currency->getId()) {
|
|
$total = $this->priceConverter->convertPrice(
|
|
$total->getCurrency(),
|
|
$currency,
|
|
$total->getPriceWithoutVat(),
|
|
);
|
|
}
|
|
}
|
|
|
|
if (method_exists($total, 'getPriceWithoutVat')) {
|
|
$total = $total->getPriceWithoutVat();
|
|
}
|
|
|
|
if ($idPriceLevel = $preorder->getPriceLevelID($total)) {
|
|
$contexts[PriceLevelContext::class] = $idPriceLevel;
|
|
}
|
|
if ($idPricelist = $preorder->getPricelistID($total)) {
|
|
$contexts[PricelistContext::class] = $idPricelist;
|
|
}
|
|
|
|
return $contexts;
|
|
}
|
|
|
|
/**
|
|
* @template T of mixed
|
|
*
|
|
* @param (callable(): T)|null $callback
|
|
*
|
|
* @return T
|
|
*
|
|
* @throws InvalidArgumentException
|
|
*/
|
|
public function activateUserPreorder(UserPreorder $userPreorder, ?callable $callback = null)
|
|
{
|
|
$contexts = array_merge(
|
|
$this->getBaseContexts($userPreorder->getPreorder()),
|
|
$this->getDynamicContexts($userPreorder->getPreorder(), $userPreorder->getItems()),
|
|
);
|
|
|
|
$customData = $userPreorder->getCustomData();
|
|
|
|
if ($lang = $customData->get('language') ?? false) {
|
|
$contexts[LanguageContext::class] = $lang;
|
|
}
|
|
|
|
if ($currency = $customData->get('currency') ?? false) {
|
|
$contexts[CurrencyContext::class] = $currency;
|
|
}
|
|
|
|
if ($callback) {
|
|
return $this->contextManager->activateContexts($contexts, $callback);
|
|
}
|
|
|
|
foreach ($contexts as $class => $id) {
|
|
Contexts::get($class)?->activate($id === null ? null : (string) $id);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* @template T of mixed
|
|
*
|
|
* @param (callable(): T)|null $callback
|
|
*
|
|
* @return T
|
|
*
|
|
* @throws InvalidArgumentException
|
|
*/
|
|
public function activatePreorder(Preorder $preorder, ?\User $user, ?callable $callback = null)
|
|
{
|
|
$contexts = $this->getBaseContexts($preorder, $user);
|
|
|
|
if ($user) {
|
|
$contexts = array_merge(
|
|
$contexts,
|
|
$this->getDynamicContexts($preorder, $preorder->getItemsByUser($user)),
|
|
);
|
|
}
|
|
|
|
// Activate the appropriate currency in case of the preorder having a different currency than the site.
|
|
// E.g. user is on the czech site with CZK, but the preorder pricelist is EUR -> prices displayed in CZK,
|
|
// but total price is calculated based on the pricelist = EUR values with Kč.
|
|
// "Appropriate currency" = currency of the default pricelist in preorder settings.
|
|
if (null !== ($priceListId = $preorder->getPricelistID())) {
|
|
$currency = $this->getCurrencyFromPricelist($priceListId);
|
|
|
|
if ($currency) {
|
|
$contexts[CurrencyContext::class] = $currency;
|
|
}
|
|
}
|
|
|
|
if ($callback) {
|
|
return $this->contextManager->activateContexts($contexts, $callback);
|
|
}
|
|
|
|
foreach ($contexts as $class => $id) {
|
|
$context = Contexts::get($class);
|
|
|
|
switch ($id) {
|
|
case ContextManager::FORCE_EMPTY:
|
|
if (!($context instanceof EmptiableContext)) {
|
|
throw new \LogicException('cannot force non-emptiable context to be empty');
|
|
}
|
|
|
|
$context->forceEmpty();
|
|
break;
|
|
|
|
case null:
|
|
$context->clearCache();
|
|
break;
|
|
|
|
default:
|
|
$context->activate(is_int($id) ? (string) $id : $id);
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private function getCurrencyFromPricelist(int $pricelistId): ?string
|
|
{
|
|
if (!array_key_exists($pricelistId, $this->pricelistCurrenciesCache)) {
|
|
$currency = sqlQueryBuilder()->select('currency')
|
|
->from('pricelists')
|
|
->andWhere(Operator::equals(['id' => $pricelistId]))
|
|
->execute()
|
|
->fetchOne();
|
|
|
|
$this->pricelistCurrenciesCache[$pricelistId] = $currency ?: null;
|
|
}
|
|
|
|
return $this->pricelistCurrenciesCache[$pricelistId];
|
|
}
|
|
}
|