src/Ecommerce/PriceSystem/PapyrusPriceSystem.php line 59

Open in your IDE?
  1. <?php
  2. namespace App\Ecommerce\PriceSystem;
  3. use App\Model\DataObject\Customer;
  4. use Carbon\Carbon;
  5. use Pimcore\Bundle\EcommerceFrameworkBundle\CartManager\CartPriceModificator\CartPriceModificatorInterface;
  6. use Pimcore\Bundle\EcommerceFrameworkBundle\Model\CheckoutableInterface;
  7. use Pimcore\Bundle\EcommerceFrameworkBundle\Model\Currency;
  8. use Pimcore\Bundle\EcommerceFrameworkBundle\PriceSystem\PriceInfoInterface;
  9. use Pimcore\Bundle\EcommerceFrameworkBundle\PriceSystem\PriceSystemInterface;
  10. use Pimcore\Bundle\EcommerceFrameworkBundle\PriceSystem\TaxManagement\TaxCalculationService;
  11. use Pimcore\Bundle\EcommerceFrameworkBundle\PriceSystem\TaxManagement\TaxEntry;
  12. use Pimcore\Bundle\EcommerceFrameworkBundle\Type\Decimal;
  13. use Pimcore\Db;
  14. use Pimcore\Model\DataObject\OnlineShopTaxClass;
  15. use Symfony\Component\DependencyInjection\ContainerInterface;
  16. use Symfony\Component\Security\Core\Security;
  17. class PapyrusPriceSystem implements PriceSystemInterface
  18. {
  19.     protected ContainerInterface $container;
  20.     protected ?Customer $customer;
  21.     protected array $customerPrices = [];
  22.     protected array $customerDiscounts = [];
  23.     public function __construct(Security $security, ContainerInterface $container)
  24.     {
  25.         $this->container = $container;
  26.         $this->customer = $security->getUser();
  27.         if( $this->customer ){
  28.             $customerPrices = [];
  29.             $customerDiscounts = [];
  30.             // Get ObjectMetadata for Customer
  31.             // Query becasuse fetching with Pimcore gives too many queries
  32.             $customPrices = Db::get()
  33.                 ->fetchAllAssociative('SELECT * FROM object_metadata_7 WHERE o_id = ? AND fieldname = ?'
  34.                     , [
  35.                         $this->customer->getId(),
  36.                         'customerPrices',
  37.                     ]
  38.                 );
  39.             foreach( $customPrices as $customPrice ){
  40.                 if( $customPrice['column'] === 'price' ){
  41.                     $customerPrices[$customPrice['dest_id']] = (float)$customPrice['data'];
  42.                 }
  43.                 if( $customPrice['column'] === 'discountPercentage' ){
  44.                     $customerDiscounts[$customPrice['dest_id']] = (float)$customPrice['data'];
  45.                 }
  46.             }
  47.             $this->customerPrices = $customerPrices;
  48.             $this->customerDiscounts = $customerDiscounts;
  49.         }
  50.     }
  51.     public function getPriceInfo(CheckoutableInterface $product, $quantityScale = null , $products = null): PriceInfoInterface
  52.     {
  53.         $currency = new Currency('EUR');
  54.         if( !$this->customer || $this->customer->getShowPrices() === false ){
  55.             $price = new Price(Decimal::create(0), $currency);
  56.             return new PapyrusPriceInfo($price, $quantityScale, $price, $price);
  57.         }
  58.         // Client specific price
  59.         [$clientDefaultPrice, $clientPrice, $clientDiscount] = $this->getCustomerPrice($product);
  60.         // Default Price
  61.         [$defaultPrice, $price, $discount] = $this->getDefaultPrice($product);
  62.         // If there is a client Price, always use the client price
  63.         if( $clientPrice ){
  64.             $defaultPrice = $clientDefaultPrice;
  65.             $price = $clientPrice;
  66.             $discount = $clientDiscount;
  67.         }
  68.         $totalPrice = Decimal::create($price->mul($quantityScale));
  69.         $totalPrice = new Price($totalPrice, $currency);
  70.         $price = new Price($price, $currency);
  71.         $defaultPrice = new Price($defaultPrice, $currency);
  72.         $taxClass = $this->getDefaultTaxClass();
  73.         if( $this->customerHasToPayTax($this->customer) ){
  74.             $taxClass = $this->getTaxClassForProduct($product);
  75.         }
  76.         $price->setTaxEntries(TaxEntry::convertTaxEntries($taxClass));
  77.         $defaultPrice->setTaxEntries(TaxEntry::convertTaxEntries($taxClass));
  78.         $totalPrice->setTaxEntries(TaxEntry::convertTaxEntries($taxClass));
  79.         $taxCalculationService = new TaxCalculationService();
  80.         $taxCalculationService->updateTaxes($price);
  81.         $taxCalculationService->updateTaxes($defaultPrice);
  82.         $taxCalculationService->updateTaxes($totalPrice);
  83.         return new PapyrusPriceInfo($price, $quantityScale, $totalPrice, $defaultPrice, $discount);
  84.     }
  85.     private function getCustomerPrice($product): array{
  86.         if( !$this->customer ){
  87.             return [null,null,0];
  88.         }
  89.         $customerPrice = $this->customerPrices[$product->getId()] ?? 0;
  90.         $customerDiscount = $this->customerDiscounts[$product->getId()] ?? 0;
  91.         if( !$customerPrice && !$customerDiscount ){
  92.             return [null,null,0];
  93.         }
  94.         $price = $customerPrice;
  95.         $discountPercentage = $customerDiscount;
  96.         return [
  97.             Decimal::create($price),
  98.             Decimal::create($price * (1 - ($discountPercentage / 100))),
  99.             $discountPercentage
  100.         ];
  101.     }
  102.     private function getDefaultPrice($product): array
  103.     {
  104.         $priceCode = $this->customer->getPriceCode() ?: 1;
  105.         $functionName = 'getPrice'.$priceCode;
  106.         $price = call_user_func( array( $product, $functionName ) );
  107.         $price = Decimal::create($price);
  108.         $defaultPrice = $price;
  109.         $discount = 0;
  110.         [$promoPrice, $promoDiscount] = $this->getPapyrusPromoPrice($defaultPrice, $product);
  111.         if( $promoPrice && $promoPrice->lessThan($price) ){
  112.             $price = $promoPrice;
  113.             $discount = $promoDiscount;
  114.         }
  115.         // Verreken klantkorting
  116.         $customerDiscount = $this->customer->getDiscount();
  117.         if( $customerDiscount && $product->getAllowCustomerDiscount() )
  118.         {
  119.             $price = $price->asNumeric() * (1 - ($customerDiscount / 100));
  120.             $price = Decimal::create($price);
  121.             // When user has general discount
  122.             // Show promo price if available as price where general discount is applied
  123.             if( $promoPrice && !$discount ){
  124.                 $defaultPrice = $promoPrice;
  125.             }
  126.             // Calculate total discount percentage from $discount en $customerDiscount
  127.             $discount = 100 - ((100 - $discount) * (100 - $customerDiscount) / 100);
  128.         }
  129.         return [$defaultPrice, $price, $discount];
  130.     }
  131.     private function getPapyrusPromoPrice($price, $product): array
  132.     {
  133.         $promoPercentage = $product->getPromoPercentage();
  134.         $promoPrice = $product->getPromoPrice();
  135.         $promoStart = $product->getPromoDate1()?->startOfDay();
  136.         $promoEnd = $product->getPromoDate2()?->endOfDay();
  137.         if( !$promoPercentage && !$promoPrice ){
  138.             return [0,0];
  139.         }
  140.         $now = new Carbon();
  141.         if( !$now->isBetween($promoStart, $promoEnd) ){
  142.             return [0,0];
  143.         }
  144.         $newPrice = $price;
  145.         // Promo Price
  146.         if( $promoPrice > 0 ){
  147.             $promoPrice = Decimal::create($promoPrice);
  148.             if( $promoPrice->lessThan($price) ) {
  149.                 $newPrice = $promoPrice;
  150.             }
  151.             // Return, combination of price en percentage is nog possible
  152.             return [$newPrice,0];
  153.         }
  154.         $discount = 0;
  155.         $promoPercentagePrice = $price->asNumeric() * (1 - ($promoPercentage / 100));
  156.         $promoPercentagePrice = Decimal::create($promoPercentagePrice);
  157.         if ( $promoPercentagePrice->lessThan($price) ){
  158.             $newPrice = $promoPercentagePrice;
  159.             $discount = $promoPercentage;
  160.         }
  161.         return [$newPrice, $discount];
  162.     }
  163.     public function filterProductIds($productIds, $fromPrice, $toPrice, $order, $offset, $limit)
  164.     {
  165.         // TODO: Implement filterProductIds() method.
  166.     }
  167.     private function customerHasToPayTax(Customer $customer): bool
  168.     {
  169.         $vatNumber = $customer->getVAT();
  170.         if( $vatNumber && !str_starts_with($vatNumber, 'BE') ){
  171.             return false;
  172.         }
  173.         return true;
  174.     }
  175.     private function getDefaultTaxClass(): OnlineShopTaxClass
  176.     {
  177.         $defaultTaxClass = new OnlineShopTaxClass();
  178.         $defaultTaxClass->setTaxEntryCombinationType(TaxEntry::CALCULATION_MODE_COMBINE);
  179.         return $defaultTaxClass;
  180.     }
  181.     public function getTaxClassForProduct(CheckoutableInterface $product)
  182.     {
  183.         $defaultTaxClass = $this->getDefaultTaxClass();
  184.         return match((int)$product->getVatcode()){
  185.             1 => OnlineShopTaxClass::getById($this->container->getParameter('tax_6')),
  186.             2 => OnlineShopTaxClass::getById($this->container->getParameter('tax_12')),
  187.             3 => OnlineShopTaxClass::getById($this->container->getParameter('tax_21')),
  188.             default => $defaultTaxClass,
  189.         };
  190.     }
  191.     public function getTaxClassForPriceModification(CartPriceModificatorInterface $modificator)
  192.     {
  193.     }
  194. }