<?php declare(strict_types=1);
namespace Dmits\PaymentCost\Storefront\Subscriber;
use Shopware\Core\Checkout\Cart\Price\Struct\CartPrice;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsAnyFilter;
use Shopware\Core\Framework\Struct\ArrayStruct;
use Shopware\Core\System\SalesChannel\SalesChannelContext;
use Shopware\Storefront\Page\Checkout\Confirm\CheckoutConfirmPageLoadedEvent;
use Shopware\Core\Checkout\Cart\Cart;
use Shopware\Core\Checkout\Payment\PaymentMethodCollection;
use Shopware\Core\Checkout\Payment\PaymentMethodEntity;
use Shopware\Core\Framework\Context;
use Shopware\Core\Framework\DataAbstractionLayer\EntityCollection;
use Shopware\Storefront\Page\Checkout\Finish\CheckoutFinishPageLoadedEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Shopware\Core\Framework\Uuid\Exception\InvalidUuidException;
use Shopware\Core\Framework\Uuid\Uuid;
use Shopware\Storefront\Page\Account\Order\AccountEditOrderPageLoadedEvent;
use Shopware\Storefront\Event\RouteRequest\SetPaymentOrderRouteRequestEvent;
use Dmits\PaymentCost\Core\Content\PaymentCost\PaymentCostCollection;
use Dmits\PaymentCost\Core\Content\PaymentCost\PaymentCostEntity;
use Dmits\PaymentCost\Service\ConfigService;
use Dmits\PaymentCost\Service\PaymentMethodPaymentCostService;
use Shopware\Core\Checkout\Cart\Price\Struct\CalculatedPrice;
use Shopware\Core\Checkout\Cart\Tax\Struct\CalculatedTaxCollection;
use Shopware\Core\Checkout\Cart\Tax\Struct\TaxRuleCollection;
use Shopware\Core\Content\Product\Cart\ProductCartProcessor;
use Shopware\Core\Checkout\Cart\Tax\Struct\CalculatedTax;
use Shopware\Core\Checkout\Order\Aggregate\OrderLineItem\OrderLineItemCollection;
use Shopware\Core\Checkout\Order\Aggregate\OrderLineItem\OrderLineItemEntity;
use Shopware\Core\Checkout\Cart\Price\Struct\QuantityPriceDefinition;
use Shopware\Core\Checkout\Cart\Tax\Struct\TaxRule;
use Shopware\Core\Checkout\Cart\Price\QuantityPriceCalculator;
use Shopware\Core\Checkout\Cart\Price\CashRounding;
use Shopware\Core\Checkout\Cart\Price\GrossPriceCalculator;
use Shopware\Core\Checkout\Cart\Price\NetPriceCalculator;
use Shopware\Core\Checkout\Cart\Tax\TaxCalculator;
use Shopware\Core\Checkout\Cart\LineItem\LineItem;
/**
* Class PageSubscriber
*
* @package Dmits\PaymentCost\Subscriber
*/
class PageSubscriber implements EventSubscriberInterface
{
/**
* @var configService
*/
protected $configService;
/**
* @var PaymentMethodPaymentCostService
*/
protected $paymentcostService;
/**
* @var EntityRepositoryInterface
*/
private $orderRepository;
/**
* @var EntityRepositoryInterface
*/
private $orderLineItemRepository;
/**
* @var EntityRepositoryInterface
*/
private EntityRepositoryInterface $productRepository;
/**
* PageSubscriber constructor.
*
* @param ConfigService $configService
* @param PaymentMethodPaymentCostService $paymentcostService
* @param EntityRepositoryInterface $orderRepository
*/
public function __construct(
ConfigService $configService,
PaymentMethodPaymentCostService $paymentcostService,
EntityRepositoryInterface $orderRepository,
EntityRepositoryInterface $orderLineItemRepository,
EntityRepositoryInterface $productRepository
)
{
$this->configService = $configService;
$this->paymentcostService = $paymentcostService;
$this->orderRepository = $orderRepository;
$this->orderLineItemRepository = $orderLineItemRepository;
$this->productRepository = $productRepository;
}
/**
* @return array
*/
public static function getSubscribedEvents(): array
{
return [
CheckoutConfirmPageLoadedEvent::class => 'onCheckoutConfirmPageLoaded',
CheckoutFinishPageLoadedEvent::class => 'onCheckoutFinishedPageLoaded',
AccountEditOrderPageLoadedEvent::class => 'onAccountEditOrderPageLoaded',
SetPaymentOrderRouteRequestEvent::class=> 'onAccountChangePayment'
];
}
public function onAccountChangePayment(SetPaymentOrderRouteRequestEvent $event): void
{
$orderId=($event->getStoreApiRequest()->request->get('orderId'));
$selectedPaymentMethod=$event->getStoreApiRequest()->request->get('paymentMethodId');
$paymentCost = $this->paymentcostService->getPaymentCostForPaymentMethods([$selectedPaymentMethod], $event->getContext());
$selectedPaymentCost=$paymentCost->get($selectedPaymentMethod);
$name = $event->getSalesChannelContext()->getPaymentMethod()->getTranslation('name');
if($selectedPaymentCost){
if($selectedPaymentCost->getTitle()){
$name=$selectedPaymentCost->getTitle();
}
}
$criteria = new Criteria([$orderId]);
$order = $this->orderRepository->search($criteria, $event->getContext())->first();
$criteria = new Criteria();
$criteria->addFilter(new EqualsFilter('orderId',$orderId));
$lineItemId="";
$price="";
$orderLineItems = $this->orderLineItemRepository->search($criteria, $event->getContext());
foreach($orderLineItems->getElements() as $item){
if($item->getType()=="payment_cost"){
$lineItemId=$item->getId();
$price=$item->getPrice();
}
}
$newPrice= $this->calculatePrice($orderLineItems->getElements(),$order->getPrice(),$order->getTaxStatus(),$selectedPaymentCost,$event->getSalesChannelContext());
$calcCost=$newPrice["calcCost"];
$paymentcost_tax=$newPrice["calcTax"];
$netPrice=$newPrice["netPrice"];
$totalPrice=$newPrice["totalPrice"];
$positionPrice=$newPrice["positionPrice"];
$calculatedTaxes=$newPrice["calculatedTaxes"];
$taxRules=$newPrice["taxRules"];
$taxStatus=$newPrice["taxStatus"];
$tax=$paymentcost_tax;
$rules = new TaxRuleCollection([]);
if($tax>0){
$rules->add(
new TaxRule(
$tax,
100
)
);
}
$definition = new QuantityPriceDefinition($calcCost, $rules, 1, true);
$calculator = new QuantityPriceCalculator(
new GrossPriceCalculator(new TaxCalculator(), new CashRounding()),
new NetPriceCalculator(new TaxCalculator(), new CashRounding())
);
$newPrice=$calculator->calculate($definition, $event->getSalesChannelContext());
$definition = new QuantityPriceDefinition($calcCost, $rules, 1, true);
$update=['id'=>$lineItemId,'label'=>$name,"quantity"=>1,"priceDefinition"=>$definition,
"price"=>["unitPrice"=>$calcCost,"totalPrice"=>$calcCost,"quantity"=>1,"calculatedTaxes"=>$newPrice->getCalculatedTaxes(),"taxRules"=> $newPrice->getTaxRules()]
];
try{
if(isset($selectedPaymentCost) && $selectedPaymentCost->getProductId()){
if (Uuid::isValid($selectedPaymentCost->getProductId())) {
$criteria = new Criteria();
$criteria->addFilter(new EqualsFilter('id', $selectedPaymentCost->getProductId()));
$products = $this->productRepository->search($criteria, $event->getContext())->first();
if(isset($product) && $products->getId()==$selectedPaymentCost->getProductId()){
$update["productId"] =$products->getId();
$update["referencedId"] =$products->getId();
// $update["product"] =$products;
}
}
}
}catch(InvalidUuidException | Exception $e){
}
if($lineItemId){
if($calcCost==0){
$this->orderLineItemRepository->delete([$update],$event->getContext());
}else{
$this->orderLineItemRepository->update([$update],$event->getContext());
}
}elseif($calcCost!=0){
$update=['identifier'=>$this->getPaymentCostItemKey(),'orderId'=>$orderId,'label'=>$name,"quantity"=>1,"priceDefinition"=>$definition,
"type"=>PaymentMethodPaymentCostService::LINE_ITEM_TYPE, "good"=>false, "price"=>["unitPrice"=>$calcCost,"totalPrice"=>$calcCost,"quantity"=>1,"calculatedTaxes"=>$newPrice->getCalculatedTaxes(),"taxRules"=> $newPrice->getTaxRules()]
];
try{
if(isset($selectedPaymentCost) && $selectedPaymentCost->getProductId()){
if (Uuid::isValid($selectedPaymentCost->getProductId())) {
$criteria = new Criteria();
$criteria->addFilter(new EqualsFilter('id', $selectedPaymentCost->getProductId()));
$products = $this->productRepository->search($criteria, $event->getContext())->first();
if(isset($product) && $products->getId()==$selectedPaymentCost->getProductId()){
$update["productId"] =$products->getId();
$update["referencedId"] =$products->getId();
// $update["product"] =$products;
}
}
}
}catch(InvalidUuidException | Exception $e){
}
$this->orderLineItemRepository->create([$update],$event->getContext());
}
$price= new CartPrice($netPrice, $totalPrice, $positionPrice, $calculatedTaxes, $taxRules, $taxStatus);
$order->setPrice($price);
$updateOrder=['id'=>$orderId,"price"=>$order->getPrice()];
$this->orderRepository->update([$updateOrder],$event->getContext());
}
/**
* @return string
*/
private function getPaymentCostItemKey(): string
{
return md5(PaymentMethodPaymentCostService::LINE_ITEM_TYPE);
}
/**
* @param CheckoutFinishPageLoadedEvent $event
*/
public function onAccountEditOrderPageLoaded(AccountEditOrderPageLoadedEvent $event): void
{
$paymentMethods = $event->getPage()->getPaymentMethods();
$paymentCost = $this->paymentcostService->getPaymentCostForPaymentMethods(array_values($paymentMethods->getIds()), $event->getContext());
$selectedPaymentMethod = $event->getSalesChannelContext()->getPaymentMethod();
$selectedPaymentCost=$paymentCost->get($selectedPaymentMethod->getId());
$newPrice= $this->calculatePrice($event->getPage()->getOrder()->getLineItems(),$event->getPage()->getOrder()->getPrice(),$event->getPage()->getOrder()->getTaxStatus(),$selectedPaymentCost,$event->getSalesChannelContext());
$calcCost=$newPrice["calcCost"];
$paymentcost_tax=$newPrice["calcTax"];
$netPrice=$newPrice["netPrice"];
$totalPrice=$newPrice["totalPrice"];
$positionPrice=$newPrice["positionPrice"];
$calculatedTaxes=$newPrice["calculatedTaxes"];
$taxRules=$newPrice["taxRules"];
$taxStatus=$newPrice["taxStatus"];
$price= new CartPrice($netPrice, $totalPrice, $positionPrice, $calculatedTaxes, $taxRules, $taxStatus);
$event->getPage()->getOrder()->setPrice($price);
$name = $event->getSalesChannelContext()->getPaymentMethod()->getTranslation('name');
if(isset($selectedPaymentCost) && $selectedPaymentCost->getTitle()){
$name=$selectedPaymentCost->getTitle();
}
$lineitem= $this->createPaymentCost($name, $event->getContext(),$event->getSalesChannelContext(),$calcCost,$paymentcost_tax);
$newCartItem=array();
foreach($event->getPage()->getOrder()->getLineItems() as $cartItem){
if($cartItem->getType()!="payment_cost"){
$newCartItem[]=$cartItem;
}
}
if($calcCost!=0){
$newCartItem[]=$lineitem;
}
$event->getPage()->getOrder()->setLineItems(new OrderLineItemCollection($newCartItem));
$this->paymentcostService->applyPaymentCostToSelectedPaymentMethod(
$selectedPaymentMethod,
$paymentCost,
$calcCost
);
$this->paymentcostService->applyPaymentCostToAvailablePaymentMethods($paymentCost, $paymentMethods);
}
/**
* @param PaymentMethodEntity $paymentMethod
* @param SalesChannelContext $salesChannelContext
*
* @return LineItem
*/
private function createPaymentCost($name,Context $context, SalesChannelContext $salesChannelContext,$price,$tax): OrderLineItemEntity
{
$lineItem=$this->buildOrderLineItemEntity(md5(PaymentMethodPaymentCostService::LINE_ITEM_TYPE),$name,$price,$tax,$context,$salesChannelContext);
return $lineItem;
}
private function buildOrderLineItemEntity($id,$name,$price,$tax,$context,$salesChannelContext){
$rules = new TaxRuleCollection([]);
if($tax>0){
$rules->add(
new TaxRule(
$tax,
100
)
);
}
$definition = new QuantityPriceDefinition($price, $rules, 1, true);
$calculator = new QuantityPriceCalculator(
new GrossPriceCalculator(new TaxCalculator(), new CashRounding()),
new NetPriceCalculator(new TaxCalculator(), new CashRounding())
);
$newPrice=$calculator->calculate($definition, $salesChannelContext);
$orderLineItemEntity = new OrderLineItemEntity();
$orderLineItemEntity->setId($id);
$orderLineItemEntity->setType(PaymentMethodPaymentCostService::LINE_ITEM_TYPE);
$orderLineItemEntity->setPosition(1);
$orderLineItemEntity->setIdentifier($id);
// $orderLineItemEntity->setReferencedId($id);
$orderLineItemEntity->setLabel($name);
$orderLineItemEntity->setGood(false);
$orderLineItemEntity->setRemovable(false);
$orderLineItemEntity->setStackable(true);
$orderLineItemEntity->setPrice($newPrice);
$orderLineItemEntity->setQuantity(1);
return $orderLineItemEntity;
}
/**
* @param CheckoutFinishPageLoadedEvent $event
*/
public function onCheckoutFinishedPageLoaded(CheckoutFinishPageLoadedEvent $event): void
{
if (!$this->isActive($event->getSalesChannelContext())) {
return;
}
$paymentCostItem="";
foreach($event->getPage()->getOrder()->getLineItems() as $item){
if($item->getType()=="payment_cost"){
$calcCost=$item->getPrice()->getTotalPrice();
$paymentCostItem=$item;
}
}
$selectedPaymentMethod = $event->getSalesChannelContext()->getPaymentMethod();
$selectedPaymentCost = $this->paymentcostService->getPaymentCostForPaymentMethod($selectedPaymentMethod, $event->getContext());
$name="";
if($paymentCostItem!=""){
$name=$paymentCostItem->getLabel();
if($selectedPaymentCost->getTitle()){
$name=$selectedPaymentCost->getTitle();
}
}
try{
if(isset($selectedPaymentCost) && $selectedPaymentCost->getProductId()){
if (Uuid::isValid($selectedPaymentCost->getProductId())) {
$criteria = new Criteria();
$criteria->addFilter(new EqualsFilter('id', $selectedPaymentCost->getProductId()));
$product = $this->productRepository->search($criteria, $event->getContext())->first();
if(isset($product) && $product->getId()==$selectedPaymentCost->getProductId()){
$paymentCostItem->setProductId($product->getId());
}
$update=['id'=>$paymentCostItem->getId(),'productId'=>$paymentCostItem->getProductId(),'referencedId'=>$paymentCostItem->getProductId(),'label'=>$name,"quantity"=>1,"priceDefinition"=>$paymentCostItem->getPriceDefinition(),
"price"=>$paymentCostItem->getPrice()
];
$this->orderLineItemRepository->update([$update],$event->getContext());
}
}
}catch(InvalidUuidException | Exception $e){
}
if (!is_null($selectedPaymentCost)) {
$selectedPaymentMethod->addExtension('DmitsPaymentCost', new ArrayStruct([
'paymentcost_amount' => $selectedPaymentCost->getAmount(),
'paymentcost_amount_per' => $selectedPaymentCost->getAmountPer(),
]));
}
}
/**
* @param CheckoutConfirmPageLoadedEvent $event
*/
public function onCheckoutConfirmPageLoaded(CheckoutConfirmPageLoadedEvent $event): void
{
if (!$this->isActive($event->getSalesChannelContext())) {
return;
}
$paymentMethods = $event->getPage()->getPaymentMethods();
$paymentCost = $this->paymentcostService->getPaymentCostForPaymentMethods(array_values($paymentMethods->getIds()), $event->getContext());
$selectedPaymentMethod = $event->getSalesChannelContext()->getPaymentMethod();
$calcCost=0;
foreach($event->getPage()->getCart()->getLineItems() as $item){
if($item->getType()=="payment_cost"){
$calcCost=$item->getPrice()->getTotalPrice();
}
}
$this->paymentcostService->applyPaymentCostToSelectedPaymentMethod(
$selectedPaymentMethod,
$paymentCost,
$calcCost
);
$this->paymentcostService->applyPaymentCostToAvailablePaymentMethods($paymentCost, $paymentMethods);
}
/**
* @param SalesChannelContext $salesChannelContext
*
* @return bool
*/
protected function isActive(SalesChannelContext $salesChannelContext): bool
{
return $this->configService->getIsActive($salesChannelContext->getSalesChannel());
}
/**
*
* @param unknown $lineItems
* @param unknown $orderPrice
* @param unknown $orderTaxStatus
* @param unknown $paymentCosts
* @return number[]|\Shopware\Core\Checkout\Cart\Tax\Struct\CalculatedTaxCollection[]|unknown[]
*/
protected function calculatePrice($lineItems,$orderPrice,$orderTaxStatus,$paymentCosts,$context){
$rounding=new CashRounding();
$calcCost=0;
foreach($lineItems as $item){
if($item->getType()=="payment_cost"){
$calcCostObject=$item->getPrice();
$calcCost=$item->getPrice()->getTotalPrice();
}
}
$array_payment=array();
if(!empty($calcCostObject)){
foreach($calcCostObject->getCalculatedTaxes()->sortByTax()->getElements() as $element){
$array_payment[$element->getTaxRate()]=array("tax"=>$element->getTax(),"price"=>$element->getPrice());
}
}
if(empty($array_payment)){
$array_payment[0]=array("tax"=>0,"price"=>$calcCost);
}
$netPrice=$orderPrice->getNetPrice();
$totalPrice=$orderPrice->getTotalPrice();
$positionPrice=$orderPrice->getPositionPrice();
$calculatedTaxes=$orderPrice->getCalculatedTaxes();
$taxRules=$orderPrice->getTaxRules();
$taxStatus=$orderPrice->getTaxStatus();
$calculatedTaxes1 = new CalculatedTaxCollection();
$paymentcost_absolut=0;
$paymentcost_per=0;
$paymentcost_tax=0;
$paymentcost_absolut_net=0;
$taxSum=0;
if($paymentCosts){
$paymentcost_absolut=$paymentCosts->getAmount();
$paymentcost_per=$paymentCosts->getAmountPer();
$paymentcost_tax= $paymentCosts->getTax();
if($paymentcost_tax>0){
$paymentcost_absolut_net=($paymentcost_absolut/(($paymentcost_tax/100)+1));
}else{
$paymentcost_absolut_net=$paymentcost_absolut;
}
}
$isPaymentTaxinOrderTax=false;
foreach($calculatedTaxes->getElements() as $key=> $element){
if(isset($array_payment[$key])){
$newTax =$element->getTax()- $array_payment[$key]["tax"];
if($newTax!=0){
$calculatedTaxes1->add(new CalculatedTax($newTax, $element->getTaxRate(), $newTax));
}
if($orderTaxStatus=="net"){
$netPrice=$netPrice-($array_payment[$key]["price"]);
$totalPrice=$totalPrice-$array_payment[$key]["price"]-$array_payment[$key]["tax"];
$positionPrice=$positionPrice-$array_payment[$key]["price"];
}else{
$netPrice=$netPrice-($array_payment[$key]["price"]-$array_payment[$key]["tax"]);
$totalPrice=$totalPrice-($array_payment[$key]["price"]);
$positionPrice=$positionPrice-$array_payment[$key]["price"];
}
$isPaymentTaxinOrderTax=true;
}else{
$calculatedTaxes1->add(new CalculatedTax($element->getTax(), $element->getTaxRate(), $element->getTax()));
}
}
if(!$isPaymentTaxinOrderTax && isset($array_payment[0])){
if($orderTaxStatus=="net"){
$netPrice=$netPrice-$array_payment[0]["price"];
$totalPrice=$totalPrice-$array_payment[0]["price"]-$array_payment[0]["tax"];
$positionPrice=$positionPrice-$array_payment[0]["price"];
}else{
$netPrice=$netPrice-($array_payment[0]["price"]-$array_payment[0]["tax"]);
$totalPrice=$totalPrice-$array_payment[0]["price"];
$positionPrice=$positionPrice-$array_payment[0]["price"];
}
}
$netPrice_add_perc=0;
$totalPrice_add_perc=0;
$positionPrice_add_perc=0;
if($paymentcost_per!=0){
$netPrice_add_perc=$netPrice/100*$paymentcost_per;
$totalPrice_add_perc=$totalPrice/100*$paymentcost_per;
$positionPrice_add_perc=$positionPrice/100*$paymentcost_per;
}
foreach($calculatedTaxes1->getElements() as $key=> $element){
if($key==$paymentcost_tax) {
if($orderTaxStatus=="net"){
$add_tax=($paymentcost_absolut+$netPrice_add_perc)-(($paymentcost_absolut+$netPrice_add_perc)/(($paymentcost_tax/100)+1));
}else{
$add_tax=($paymentcost_absolut+$totalPrice_add_perc)-(($paymentcost_absolut+$totalPrice_add_perc)/(($paymentcost_tax/100)+1));
}
$newTax= $element->getTax()+$add_tax;
$newTax= $rounding->mathRound($newTax,$context->getItemRounding());
$calculatedTaxes1->add(new CalculatedTax($newTax, $element->getTaxRate(), $newTax));
}
}
if($orderTaxStatus=="net"){
$calcCost=$paymentcost_absolut_net+$netPrice_add_perc;
}else{
$calcCost=$paymentcost_absolut+$totalPrice_add_perc;
}
$netPrice=$netPrice+$rounding->mathRound($paymentcost_absolut_net+$netPrice_add_perc,$context->getItemRounding());
foreach($calculatedTaxes1->getElements() as $key=> $element){
$taxSum=$taxSum+$element->getTax();
}
if($orderTaxStatus=="net"){
$positionPrice=$positionPrice+$paymentcost_absolut_net+$positionPrice_add_perc;
$totalPrice=$totalPrice+$rounding->mathRound($paymentcost_absolut+$totalPrice_add_perc,$context->getItemRounding());
}else{
$positionPrice=$positionPrice+$paymentcost_absolut+$positionPrice_add_perc;
$totalPrice=$totalPrice+$paymentcost_absolut+$totalPrice_add_perc;
}
$calcCost= $rounding->mathRound($calcCost,$context->getItemRounding());
if($orderTaxStatus=="net"){
$netPrice=$rounding->mathRound($netPrice,$context->getItemRounding());
$totalPrice=$rounding->mathRound($netPrice+$taxSum,$context->getItemRounding());
}else{
$netPrice=$rounding->mathRound($totalPrice-$taxSum,$context->getItemRounding());
$totalPrice=$rounding->mathRound($totalPrice,$context->getItemRounding());
}
$positionPrice=$rounding->mathRound($positionPrice,$context->getItemRounding());
return array("calcCost"=>$calcCost,
"calcTax"=>$paymentcost_tax,
"netPrice"=>$netPrice,
"totalPrice"=>$totalPrice,
"positionPrice"=>$positionPrice,
"calculatedTaxes"=>$calculatedTaxes1,
"taxRules"=>$taxRules,
"taxStatus"=>$taxStatus
);
}
}