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()); } }