currencyContext = $currencyContext; $this->priceConverter = $priceConverter; } public function createDiscountPrice(\Decimal $price, $vatId = null, $forceVatValue = null): Price { $vatContext = Contexts::get(VatContext::class); if ($vatContext->isCountryOssActive()) { // use default vat of a country $vatId = null; } $vat = $forceVatValue ?? getVat($vatId); // $discountPrice = roundPrice($price->mul(DecimalConstants::negativeOne()), null, null, null, $this->currencyContext->getActive()); $discountPrice = $price->mul(\DecimalConstants::negativeOne()); $discountPrice = $discountPrice->removeVat($vat); return new Price($discountPrice, $this->currencyContext->getActive(), $vat); } public function calculateDiscountPrice(\Decimal $price, array $data, &$name = null) { $discountPrice = null; $discount = $data['discount'] ?? 0; switch ($data['unit']) { case 'perc': $discountPrice = $price->mul(toDecimal($discount))->div(\DecimalConstants::hundred()); $name .= ' ('.$discount.'%)'; break; case $this->currencyContext->getActiveId(): $discountPrice = toDecimal($discount); break; default: if ($currency = $this->priceConverter->getCurrency($data['unit'])) { $name .= ' ('.$discount.' '.$currency->getSymbol().')'; $discountPrice = $this->priceConverter->convert($currency->getId(), $this->currencyContext->getActiveId(), toDecimal($discount)); } } return $discountPrice; } public function getDiscountPerPiece(ProductPurchaseItem $purchaseItem, array $data, $min_discount = null) { if (!$min_discount) { $min_discount = \DecimalConstants::zero(); if (($data['unit'] == 'perc') && ('N' == ($data['combine'] ?? 'Y'))) { // u slevy je nastaveno: NEsčítat se slevou u produktu => uplatní se pouze vyšší sleva, minimalne % z teto slevy $min_discount = toDecimal($data['discount']); } } $piecePrice = $purchaseItem->getPriceWithDiscountsWithVat()->div(toDecimal($purchaseItem->getPieces())); if ($piecePrice->isZero()) { return null; } if ($min_discount->isZero()) { $discountPerPiece = $this->calculateDiscountPrice($piecePrice, $data); } else { // u slevy je nastaveno NEsčítat se slevou u produktu => uplatní se pouze vyšší sleva $product = $purchaseItem->getProduct(); if ($product->getProductPrice()->getValue()->isZero()) { return null; } $piecePriceWithoutDiscount = $product->getProductPrice()->getPriceWithoutDiscount(); $p = $purchaseItem->getPrice(); PriceCalculator::makeCompatible($p, $piecePriceWithoutDiscount); $piecePriceWithoutDiscount = $piecePriceWithoutDiscount->getPriceWithVat(); // produkt muze mit konfigurovatelne parametry => pridat priplatky k puvodni cene $config = $purchaseItem->getNote()['configurations'] ?? null; if ($config && findModule(\Modules::PRODUCTS_PARAMETERS, \Modules::SUB_CONFIGURATIONS)) { $product->fetchParameters(); foreach ($product->configurations as $id_parameter => $parameter) { if (!empty($config[$id_parameter]) && !empty($parameter->configuration_price)) { $values = $parameter->fetchValues(); if (array_key_exists($config[$id_parameter], $values)) { $configurationPriceWithVat = $values[$config[$id_parameter]]->configuration_price; $piecePriceWithoutDiscount = $piecePriceWithoutDiscount->add($configurationPriceWithVat); } } } } // piecePriceWithoutDiscount = puvodni cena bez slev, piecePrice = cena v kosiku, se slevami $discountPerPiecePrice = $piecePriceWithoutDiscount->sub($piecePrice); $productDiscount = $discountPerPiecePrice->div($piecePriceWithoutDiscount)->mul(\DecimalConstants::hundred()); if ($productDiscount->isZero()) { // zatim zadna sleva na produktu $discountPerPiece = $this->calculateDiscountPrice($piecePrice, $data); } else { if ($min_discount->lowerThanOrEqual($productDiscount)) { // sleva na produktu je vetsi nez tato sleva -> uplatní se pouze sleva na produktu return null; } // sleva na produktu je mensi nez tato sleva -> uplatní se tato sleva od puvodni ceny $discountPerPiece = $this->calculateDiscountPrice($piecePriceWithoutDiscount, $data); // snizit o castku, ktera uz byla odectena od puvodni ceny $discountPerPiece = $discountPerPiece->sub($discountPerPiecePrice); } } $piecePriceDiscounted = $piecePrice->sub($discountPerPiece, 2); $discountPerPiece = $piecePrice->sub($piecePriceDiscounted, 2); return $discountPerPiece; } /** * @param ProductPurchaseItem[] $products * @param string|null $name * @param \Decimal|null $min_discount * * @return \Decimal */ public function divideDiscountPrice($products, array $data, &$name = null, $min_discount = null) { $discountPrice = \DecimalConstants::zero(); if ($data['unit'] == 'perc') { $name .= ' ('.$data['discount'].'%)'; $discount_data = array_merge( ['id' => $data['id'], 'discount' => $data['discount'], 'unit' => $data['unit'], 'name' => $name], $data['trigger_data'] ?? [] ); foreach ($products as $productPurchaseItem) { $discountPerPiece = $this->getDiscountPerPiece($productPurchaseItem, $data, $min_discount); if (is_null($discountPerPiece)) { continue; } $discountPrice = $discountPrice->add($discountPerPiece->mul(toDecimal($productPurchaseItem->getPieces()))); $discountPerPiece = $discountPerPiece->removeVat($productPurchaseItem->getPrice()->getVat()); $discount_data['discountPrice'] = \Decimal::fromDecimal($discountPerPiece, \Decimal::$default_scale); $productPurchaseItem->addDiscount($discount_data); } return $discountPrice; } $discountSum = \DecimalConstants::zero(); $apply_discount_on_delivery = findModule(\Modules::DELIVERY_TYPES, \Modules::SUB_APPLY_DISCOUNT_ON_DELIVERY); if ($currency = $this->currencyContext->getSupported()[$data['unit']] ?? null) { $discountPrice = toDecimal($data['discount']); if ($data['unit'] != $this->currencyContext->getActiveId()) { $discountPrice = $this->priceConverter->convert($currency->getId(), $this->currencyContext->getActiveId(), $discountPrice); $discountPrice = roundPrice($discountPrice); } $totalPrice = $this->getProductsTotalPriceWithDiscounts($products)->getPriceWithVat(); $discountPrice = $apply_discount_on_delivery ? $discountPrice : min($discountPrice, $totalPrice); $discount_data = array_merge( ['id' => $data['id'], 'discount' => $data['discount'], 'unit' => $data['unit'], 'name' => $name], $data['trigger_data'] ?? [] ); $last = array_key_last($products); foreach ($products as $key => $productPurchaseItem) { $price = $productPurchaseItem->getPriceWithDiscountsWithVat(); if ($price->isZero()) { continue; } $pieces = toDecimal($productPurchaseItem->getPieces()); $piecePrice = $price->div($pieces); if ($key === $last) { $discount = $discountPrice->sub($discountSum); } else { $discount_perc = $price->div($totalPrice); $discount = $discountPrice->mul($discount_perc); } if ($price->lessThan($discount)) { $discount = $price; } $discountPerPiece = $discount->div($pieces, 2); $piecePriceDiscounted = $piecePrice->sub($discountPerPiece, 2); // $piecePriceDiscounted = roundPrice($piecePriceDiscounted, null, null, null, $productPurchaseItem->getPrice()->getCurrency()); $discountPerPiece = $piecePrice->sub($piecePriceDiscounted, 2); $discount = $discountPerPiece->mul($pieces); $discountPerPiece = $discountPerPiece->removeVat($productPurchaseItem->getPrice()->getVat()); $discount_perc = $discount->div($price)->mul(\DecimalConstants::hundred())->round(\Decimal::$default_scale); $discount_data['discount'] = $discount_perc; $discount_data['unit'] = 'perc'; $discount_data['discountPrice'] = \Decimal::fromDecimal($discountPerPiece, \Decimal::$default_scale); $productPurchaseItem->addDiscount($discount_data); $discountSum = $discountSum->add($discount); } if ($discountPrice->lowerThan($discountSum)) { $discountSum = $discountPrice; } } return $apply_discount_on_delivery ? $discountPrice : $discountSum; } /** * @param ProductPurchaseItem[] $items */ public function getProductsTotalPriceWithDiscounts($items): TotalPrice { $totalPriceWithVat = \DecimalConstants::zero(); $totalPriceWithoutVat = \DecimalConstants::zero(); foreach ($items as $item) { $priceWithVat = $item->getPriceWithDiscountsWithVat(); $priceWithoutVat = $priceWithVat->removeVat($item->getPrice()->getVat()); $totalPriceWithVat = $totalPriceWithVat->add($priceWithVat); $totalPriceWithoutVat = $totalPriceWithoutVat->add($priceWithoutVat); } return new TotalPrice($totalPriceWithVat, $totalPriceWithoutVat, $this->currencyContext->getActive()); } }