221 lines
8.4 KiB
PHP
221 lines
8.4 KiB
PHP
<?php
|
|
|
|
namespace KupShop\BonusProgramBundle\Actions;
|
|
|
|
use KupShop\AdminBundle\Admin\ProductsFilter;
|
|
use KupShop\BonusProgramBundle\Utils\BonusComputer;
|
|
use KupShop\KupShopBundle\Context\CurrencyContext;
|
|
use KupShop\KupShopBundle\Context\UserContext;
|
|
use KupShop\KupShopBundle\Util\Contexts;
|
|
use KupShop\KupShopBundle\Util\Price\Price;
|
|
use KupShop\KupShopBundle\Util\Price\PriceCalculator;
|
|
use KupShop\OrderDiscountBundle\Entity\OrderDiscount;
|
|
use KupShop\OrderingBundle\Entity\Purchase\PurchaseState;
|
|
use Symfony\Contracts\Service\Attribute\Required;
|
|
|
|
class BonusPointsEarningAction extends BonusPointsAction
|
|
{
|
|
public const CALCULATION_TYPE_PERC = 'perc';
|
|
public const CALCULATION_TYPE_PRICE = 'price';
|
|
|
|
protected static $type = 'bonus_points_earning';
|
|
protected $adminTemplate = 'actions/bonus_points_earning.tpl';
|
|
protected static $position = 119;
|
|
|
|
private BonusComputer $bonusComputer;
|
|
|
|
#[Required]
|
|
final public function setBonusComputer(BonusComputer $bonusComputer): void
|
|
{
|
|
$this->bonusComputer = $bonusComputer;
|
|
}
|
|
|
|
public function applyResult(PurchaseState &$purchaseState, OrderDiscount $orderDiscount, array $data)
|
|
{
|
|
$points = $this->getPoints($purchaseState, $data);
|
|
if (is_null($points)) {
|
|
return;
|
|
}
|
|
|
|
$this->messages = [];
|
|
|
|
$userContext = Contexts::get(UserContext::class);
|
|
if ($userContext->getActiveId()) {
|
|
$purchaseState->addUsedDiscount($orderDiscount->getId());
|
|
if ($message = $data['messages']['success'] ?? '') {
|
|
$this->messages['success'] = $message;
|
|
}
|
|
} else {
|
|
if ($message = $data['messages']['warning'] ?? '') {
|
|
$this->messages['warning'] = $message;
|
|
}
|
|
}
|
|
}
|
|
|
|
public function getPoints(PurchaseState $purchaseState, array $data): ?\Decimal
|
|
{
|
|
$calculationType = $data['points_calculation_type'] ?? self::CALCULATION_TYPE_PERC;
|
|
|
|
$calculatePoints = null;
|
|
|
|
if ($calculationType === self::CALCULATION_TYPE_PERC) {
|
|
$coefficient = toDecimal($data['points_from_price_percent'] ?? -100)->div(toDecimal(100), 8);
|
|
if (!$coefficient->isNegative()) {
|
|
$calculatePoints = fn (Price $price) => $this->calculatePointsPercentage($price, $coefficient);
|
|
}
|
|
} elseif ($calculationType === self::CALCULATION_TYPE_PRICE) {
|
|
$pointValue = toDecimal($data['points_from_price_value'] ?? -100);
|
|
if ($pointValue->isPositive() && !empty($data['points_from_price_currency'])) {
|
|
$pointValue = new Price(
|
|
$pointValue,
|
|
Contexts::get(CurrencyContext::class)->getOrDefault($data['points_from_price_currency']),
|
|
0
|
|
);
|
|
|
|
$calculatePoints = fn (Price $price) => $this->calculatePointsPrice($price, $pointValue);
|
|
}
|
|
}
|
|
|
|
if (!$calculatePoints) {
|
|
return null;
|
|
}
|
|
|
|
$products = $this->purchaseUtil->getProductsApplicableByProductsFilter($purchaseState, $data['filter'] ?? []);
|
|
if (empty($products)) {
|
|
return null;
|
|
}
|
|
|
|
$pointsSumRounding = ($data['points_sum_rounding'] ?? 'N') === 'Y';
|
|
|
|
$points = \DecimalConstants::zero();
|
|
|
|
foreach ($products as $item) {
|
|
if ($productPoints = $item->getProduct()->bonus_points ?? null) {
|
|
if ($productPoints->isZero()) {
|
|
$item->bonus_points = $productPoints;
|
|
} else {
|
|
$bonus_points = $productPoints->mul(toDecimal($item->getPieces()));
|
|
// pokud polozka byla zlevnena, sleva se musi uplatnit i na body - body * (cena po sleve / puvodni cena)
|
|
$productPrice = $item->getPriceWithVat();
|
|
$productPriceWithDiscounts = $item->getPriceWithDiscountsWithVat();
|
|
// pri editaci objednavky (createStateFromOrder) nemame $item->discounts,
|
|
// $item->price je cena po sleve, puvodni cenu (za kus) jde zjistit jen z note..
|
|
if ($item->getNote()['priceWithoutDiscounts'] ?? null) {
|
|
$productPrice = toDecimal($item->getNote()['priceWithoutDiscounts'])->mul(toDecimal($item->getPieces()));
|
|
}
|
|
if ($productPrice->isZero() || $productPriceWithDiscounts->isZero()) {
|
|
$item->bonus_points = \DecimalConstants::zero();
|
|
} else {
|
|
// body * (cena po sleve / puvodni cena)
|
|
$bonus_points = $bonus_points->mul($productPriceWithDiscounts->div($productPrice));
|
|
// pokud chci zaokrouhlovat az sumu bodu, tak tady zaorkouhleni neprovedu a provedu ho az na konci na celkovy pocet bodu
|
|
$item->bonus_points = $pointsSumRounding ? $bonus_points : $this->roundPoints($bonus_points, $data);
|
|
}
|
|
}
|
|
} else {
|
|
$generate_coupon = ($item->getProduct()->getData()['generate_coupon'] ?? 'N');
|
|
if ($generate_coupon == 'Y') {
|
|
continue;
|
|
}
|
|
$price = $item->getPriceWithDiscounts();
|
|
|
|
$itemPoints = $calculatePoints($price);
|
|
|
|
$item->bonus_points = $pointsSumRounding ? $itemPoints : $this->roundPoints($itemPoints, $data);
|
|
}
|
|
$item->addNote('bonus_points', $item->bonus_points);
|
|
$points = $points->add($item->bonus_points);
|
|
}
|
|
|
|
// zaokrouhleni sumy body
|
|
if ($pointsSumRounding) {
|
|
$points = $this->roundPoints($points, $data);
|
|
// potrebuju rouding data kvuli \KupShop\BonusProgramBundle\Utils\BonusComputer::countBonusPoints, kde to potrebuju pak taky zaokrouhlit
|
|
$purchaseState->setCustomData(['bonus_points_rounding' => array_filter($data, fn ($x) => in_array($x, ['points_round_direction', 'points_precision']), ARRAY_FILTER_USE_KEY)]);
|
|
}
|
|
|
|
return $points;
|
|
}
|
|
|
|
protected function roundPoints(\Decimal $points, array $data): \Decimal
|
|
{
|
|
return $this->bonusComputer->roundPoints(
|
|
$points,
|
|
$data['points_round_direction'] ?? 'round',
|
|
(int) ($data['points_precision'] ?? 0)
|
|
);
|
|
}
|
|
|
|
protected function getVars($vars)
|
|
{
|
|
$vars = parent::getVars($vars);
|
|
|
|
$vars['data']['points_calculation_type'] = !empty($vars['data']['points_calculation_type']) ? $vars['data']['points_calculation_type'] : self::CALCULATION_TYPE_PERC;
|
|
|
|
return $vars;
|
|
}
|
|
|
|
public function checkValidity(&$data): bool
|
|
{
|
|
$valid = false;
|
|
|
|
if (trim($data['points_from_price_percent'] ?? '') != '') {
|
|
$data['points_from_price_percent'] = toDecimal($data['points_from_price_percent'])->asFloat();
|
|
if ($data['points_from_price_percent'] >= 0) {
|
|
$valid = true;
|
|
}
|
|
}
|
|
|
|
if (trim($data['points_from_price_value'] ?? '') != '') {
|
|
$data['points_from_price_value'] = toDecimal($data['points_from_price_value'])->asFloat();
|
|
if ($data['points_from_price_value'] >= 0) {
|
|
$valid = true;
|
|
}
|
|
}
|
|
|
|
if (!$valid) {
|
|
throw new \Exception($this->getName().' ['.$this->getType().'] - data is not valid.');
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public function handleData($data)
|
|
{
|
|
$data = parent::handleData($data);
|
|
$data['points_from_price_percent'] = ($data['points_from_price_percent'] ?? 0);
|
|
$data['filter'] = ProductsFilter::cleanFilter($data['filter'] ?? []);
|
|
|
|
return $data;
|
|
}
|
|
|
|
public function getDelayedExecution(): ?int
|
|
{
|
|
return 2;
|
|
}
|
|
|
|
private function calculatePointsPercentage(Price $price, \Decimal $coefficient): \Decimal
|
|
{
|
|
$price = PriceCalculator::convert(
|
|
$price,
|
|
Contexts::get(CurrencyContext::class)->getDefault()
|
|
);
|
|
|
|
$price = $price->getPriceWithVat(false)->value(2);
|
|
|
|
return $price->mul($coefficient);
|
|
}
|
|
|
|
private function calculatePointsPrice(Price $price, Price $pointValue): \Decimal
|
|
{
|
|
$price = PriceCalculator::convert(
|
|
$price,
|
|
$pointValue->getCurrency()
|
|
);
|
|
|
|
$price = $price->getPriceWithVat(false)->value(2);
|
|
|
|
return $price->div($pointValue->getValue());
|
|
}
|
|
}
|