diff --git a/src/Components/RefundManager/RefundCalculationHelper/RefundCalculationHelper.php b/src/Components/RefundManager/RefundCalculationHelper/RefundCalculationHelper.php new file mode 100644 index 000000000..ced084e0b --- /dev/null +++ b/src/Components/RefundManager/RefundCalculationHelper/RefundCalculationHelper.php @@ -0,0 +1,56 @@ +refundItems = []; + } + + /** + * @param RefundItem $refundItem + * @return void + */ + public function addRefundItem(RefundItem $refundItem) + { + $this->refundItems[] = $refundItem; + } + + /** + * @param string $orderLineId + * @return int + */ + public function getRefundQuantityForMollieId(string $orderLineId): int + { + $refundQuantity = 0; + foreach ($this->refundItems as $refundItem) { + if ($refundItem->getMollieLineID()===$orderLineId) { + $refundQuantity += $refundItem->getQuantity(); + } + } + return $refundQuantity; + } + + /** + * @param string $orderLineId + * @return float + */ + public function getRefundAmountForMollieId(string $orderLineId): float + { + $refundAmount = 0; + foreach ($this->refundItems as $refundItem) { + if ($refundItem->getMollieLineID()===$orderLineId) { + $refundAmount += $refundItem->getAmount(); + } + } + return $refundAmount; + } +} diff --git a/src/Components/RefundManager/RefundData/OrderItem/DeliveryItem.php b/src/Components/RefundManager/RefundData/OrderItem/DeliveryItem.php index 7844a861b..85cf0a7e8 100644 --- a/src/Components/RefundManager/RefundData/OrderItem/DeliveryItem.php +++ b/src/Components/RefundManager/RefundData/OrderItem/DeliveryItem.php @@ -53,4 +53,12 @@ public function toArray(): array $this->alreadyRefundedQty ); } + + /** + * @return OrderDeliveryEntity + */ + public function getDelivery(): OrderDeliveryEntity + { + return $this->delivery; + } } diff --git a/src/Components/RefundManager/RefundData/OrderItem/ProductItem.php b/src/Components/RefundManager/RefundData/OrderItem/ProductItem.php index f809f1ac5..1aebc1c0b 100644 --- a/src/Components/RefundManager/RefundData/OrderItem/ProductItem.php +++ b/src/Components/RefundManager/RefundData/OrderItem/ProductItem.php @@ -99,4 +99,12 @@ private function getProductNumber(): string return (string)$this->lineItem->getPayload()['productNumber']; } + + /** + * @return OrderLineItemEntity + */ + public function getLineItem(): OrderLineItemEntity + { + return $this->lineItem; + } } diff --git a/src/Components/RefundManager/RefundData/RefundData.php b/src/Components/RefundManager/RefundData/RefundData.php index b96c93cd3..6291d1d6c 100644 --- a/src/Components/RefundManager/RefundData/RefundData.php +++ b/src/Components/RefundManager/RefundData/RefundData.php @@ -3,6 +3,11 @@ namespace Kiener\MolliePayments\Components\RefundManager\RefundData; use Kiener\MolliePayments\Components\RefundManager\RefundData\OrderItem\AbstractItem; +use Kiener\MolliePayments\Components\RefundManager\RefundData\OrderItem\DeliveryItem; +use Kiener\MolliePayments\Components\RefundManager\RefundData\OrderItem\ProductItem; +use Kiener\MolliePayments\Service\Refund\Item\RefundItem; +use Kiener\MolliePayments\Struct\OrderDeliveryEntity\OrderDeliveryEntityAttributes; +use Kiener\MolliePayments\Struct\OrderLineItemEntity\OrderLineItemEntityAttributes; use Mollie\Api\Resources\Refund; class RefundData @@ -43,6 +48,7 @@ class RefundData */ private $roundingItemTotal; + /** * @param AbstractItem[] $cartItems * @param Refund[] $refunds @@ -156,4 +162,62 @@ public function toArray() 'refunds' => $refundsArray, ]; } + + /** + * @param string $mollieId + * @return int + */ + public function getRefundedQuantity(string $mollieId): int + { + foreach ($this->orderItems as $orderItem) { + if ($orderItem instanceof ProductItem) { + $orderItemAttributes = new OrderLineItemEntityAttributes($orderItem->getLineItem()); + } + + if ($orderItem instanceof DeliveryItem) { + $orderItemAttributes = new OrderDeliveryEntityAttributes($orderItem->getDelivery()); + } + + if (empty($orderItemAttributes)) { + continue; + } + + if ($mollieId === $orderItemAttributes->getMollieOrderLineID()) { + $refundArray = $orderItem->toArray(); + return $refundArray['refunded']; + } + } + return 0; + } + + /** + * @param string $mollieId + * @return float + */ + public function getRefundedAmount(string $mollieId):float + { + $totalAmount = 0; + + ## The amount is being calculated via the refunds because it might have been a custom amount. + /** @var array $refund */ + foreach ($this->refunds as $refund) { + if (!isset($refund['metadata'])) { + continue; + } + + $metadata = $refund['metadata']; + if (!isset($metadata['composition'])) { + continue; + } + + $composition = $metadata['composition']; + + foreach ($composition as $compositionItem) { + if ($compositionItem['mollieLineId']===$mollieId) { + $totalAmount+=$compositionItem['amount']; + } + } + } + return $totalAmount; + } } diff --git a/src/Components/RefundManager/RefundManager.php b/src/Components/RefundManager/RefundManager.php index b1de086ce..92912425b 100644 --- a/src/Components/RefundManager/RefundManager.php +++ b/src/Components/RefundManager/RefundManager.php @@ -4,7 +4,6 @@ use Kiener\MolliePayments\Compatibility\Bundles\FlowBuilder\FlowBuilderDispatcherAdapterInterface; use Kiener\MolliePayments\Compatibility\Bundles\FlowBuilder\FlowBuilderEventFactory; -use Kiener\MolliePayments\Compatibility\Bundles\FlowBuilder\FlowBuilderFactory; use Kiener\MolliePayments\Compatibility\Bundles\FlowBuilder\FlowBuilderFactoryInterface; use Kiener\MolliePayments\Components\RefundManager\Builder\RefundDataBuilder; use Kiener\MolliePayments\Components\RefundManager\Integrators\StockManagerInterface; @@ -14,19 +13,18 @@ use Kiener\MolliePayments\Components\RefundManager\Request\RefundRequestItemRoundingDiff; use Kiener\MolliePayments\Exception\CouldNotCreateMollieRefundException; use Kiener\MolliePayments\Service\MollieApi\Order; -use Kiener\MolliePayments\Service\OrderService; use Kiener\MolliePayments\Service\OrderServiceInterface; use Kiener\MolliePayments\Service\Refund\Item\RefundItem; -use Kiener\MolliePayments\Service\Refund\RefundService; use Kiener\MolliePayments\Service\Refund\RefundServiceInterface; -use Kiener\MolliePayments\Service\Stock\StockManager; use Kiener\MolliePayments\Struct\MollieApi\OrderLineMetaDataStruct; use Kiener\MolliePayments\Struct\Order\OrderAttributes; +use Kiener\MolliePayments\Struct\OrderDeliveryEntity\OrderDeliveryEntityAttributes; use Kiener\MolliePayments\Struct\OrderLineItemEntity\OrderLineItemEntityAttributes; use Mollie\Api\Resources\OrderLine; use Mollie\Api\Resources\Refund; use Psr\Log\LoggerInterface; use Shopware\Core\Checkout\Order\Aggregate\OrderDelivery\OrderDeliveryCollection; +use Shopware\Core\Checkout\Order\Aggregate\OrderDelivery\OrderDeliveryEntity; use Shopware\Core\Checkout\Order\Aggregate\OrderLineItem\OrderLineItemCollection; use Shopware\Core\Checkout\Order\Aggregate\OrderLineItem\OrderLineItemEntity; use Shopware\Core\Checkout\Order\OrderEntity; @@ -155,7 +153,7 @@ public function refund(OrderEntity $order, RefundRequest $request, Context $cont # we have a full refund, but only with amount # and no items. to make sure that we have clean data # we have to extract all items, so that they will be added to the metadata - $requestItems = $this->buildRequestItemsFromOrder($order); + $requestItems = $this->buildRequestItemsFromOrder($order, $context); $request->setItems($requestItems); $this->appendRoundingItemFromMollieOrder($request, $mollieOrder); @@ -213,7 +211,7 @@ public function refund(OrderEntity $order, RefundRequest $request, Context $cont } - if (! $refund instanceof Refund) { + if (!$refund instanceof Refund) { # a problem happened, lets finish with an exception throw new CouldNotCreateMollieRefundException('', (string)$order->getOrderNumber()); } @@ -298,27 +296,41 @@ private function getOrderItem(OrderEntity $orderEntity, string $lineID): ?OrderL * @param OrderEntity $order * @return array */ - private function buildRequestItemsFromOrder(OrderEntity $order) + private function buildRequestItemsFromOrder(OrderEntity $order, Context $context) { $items = []; + $refundData = $this->builderData->buildRefundData($order, $context); + if ($order->getLineItems() instanceof OrderLineItemCollection) { + /** @var OrderLineItemEntity $lineItem */ foreach ($order->getLineItems() as $lineItem) { + $orderLineItemAttributes = new OrderLineItemEntityAttributes($lineItem); + + $alreadyRefundedQuantity = $refundData->getRefundedQuantity($orderLineItemAttributes->getMollieOrderLineID()); + $alreadyRefundedAmount = $refundData->getRefundedAmount($orderLineItemAttributes->getMollieOrderLineID()); + $items[] = new RefundRequestItem( $lineItem->getId(), - $lineItem->getTotalPrice(), - $lineItem->getQuantity(), + $lineItem->getTotalPrice() - $alreadyRefundedAmount, + $lineItem->getQuantity() - $alreadyRefundedQuantity, 0 ); } } if ($order->getDeliveries() instanceof OrderDeliveryCollection) { + /** @var OrderDeliveryEntity $delivery */ foreach ($order->getDeliveries() as $delivery) { + $orderLineItemAttributes = new OrderDeliveryEntityAttributes($delivery); + + $alreadyRefundedQuantity = $refundData->getRefundedQuantity($orderLineItemAttributes->getMollieOrderLineID()); + $alreadyRefundedAmount = $refundData->getRefundedAmount($orderLineItemAttributes->getMollieOrderLineID()); + $items[] = new RefundRequestItem( $delivery->getId(), - $delivery->getShippingCosts()->getTotalPrice(), - $delivery->getShippingCosts()->getQuantity(), + $delivery->getShippingCosts()->getTotalPrice() - $alreadyRefundedAmount, + $delivery->getShippingCosts()->getQuantity() - $alreadyRefundedQuantity, 0 ); } @@ -426,4 +438,31 @@ private function convertToRefundItems(RefundRequest $request, OrderEntity $order return $serviceItems; } + + + /** + * Get the orderLineId from both OrderDeliveryEntity and OrderLineItemEntity + * @param OrderDeliveryEntity|OrderLineItemEntity $lineItem + * @return string + */ + public function getOrderLineId($lineItem): string + { + $customFields = $lineItem->getCustomFields(); + + if (!isset($customFields)) { + return ""; + } + + if (!isset($customFields['mollie_payments'])) { + return ""; + } + + $molliePayments = $customFields['mollie_payments']; + + if (!isset($molliePayments['order_line_id'])) { + return ""; + } + + return $molliePayments['order_line_id']; + } } diff --git a/src/Resources/config/services/services.xml b/src/Resources/config/services/services.xml index 6e8593bf6..601525ff7 100644 --- a/src/Resources/config/services/services.xml +++ b/src/Resources/config/services/services.xml @@ -95,6 +95,7 @@ + diff --git a/src/Service/Order/UpdateOrderLineItems.php b/src/Service/Order/UpdateOrderLineItems.php index cddedf201..35fe3ce84 100644 --- a/src/Service/Order/UpdateOrderLineItems.php +++ b/src/Service/Order/UpdateOrderLineItems.php @@ -17,13 +17,19 @@ class UpdateOrderLineItems */ private $orderLineRepository; + /** + * @var EntityRepositoryInterface + */ + private $orderDeliveryRepository; + /** * @param EntityRepositoryInterface $orderLineRepository */ - public function __construct(EntityRepositoryInterface $orderLineRepository) + public function __construct(EntityRepositoryInterface $orderLineRepository, EntityRepositoryInterface $orderDeliveryRepository) { $this->orderLineRepository = $orderLineRepository; + $this->orderDeliveryRepository = $orderDeliveryRepository; } /** @@ -34,10 +40,7 @@ public function updateOrderLineItems(Order $mollieOrder, SalesChannelContext $sa { /** @var OrderLine $orderLine */ foreach ($mollieOrder->lines() as $orderLine) { - if ($orderLine->type === OrderLineType::TYPE_SHIPPING_FEE) { - continue; - } - + ##Contrary to the name, this can also be an order_delivery id since these are also kind of line items $shopwareLineItemId = (string)$orderLine->metadata->orderLineItemId; if (empty($shopwareLineItemId)) { @@ -53,7 +56,11 @@ public function updateOrderLineItems(Order $mollieOrder, SalesChannelContext $sa ] ]; - $this->orderLineRepository->update([$data], $salesChannelContext->getContext()); + if ($orderLine->type === OrderLineType::TYPE_SHIPPING_FEE) { + $this->orderDeliveryRepository->update([$data], $salesChannelContext->getContext()); + } else { + $this->orderLineRepository->update([$data], $salesChannelContext->getContext()); + } } } } diff --git a/src/Struct/OrderDeliveryEntity/OrderDeliveryEntityAttributes.php b/src/Struct/OrderDeliveryEntity/OrderDeliveryEntityAttributes.php new file mode 100644 index 000000000..1c666861f --- /dev/null +++ b/src/Struct/OrderDeliveryEntity/OrderDeliveryEntityAttributes.php @@ -0,0 +1,16 @@ +item = $lineItem; + private $item; - $this->voucherType = $this->getCustomFieldValue($lineItem, 'voucher_type'); - $this->mollieOrderLineID = $this->getCustomFieldValue($lineItem, 'order_line_id'); - $this->subscriptionProduct = (bool)$this->getCustomFieldValue($lineItem, 'subscription_enabled'); - $this->subscriptionInterval = (int)$this->getCustomFieldValue($lineItem, 'subscription_interval'); - $this->subscriptionIntervalUnit = (string)$this->getCustomFieldValue($lineItem, 'subscription_interval_unit'); - $this->subscriptionRepetitionCount = (int)$this->getCustomFieldValue($lineItem, 'subscription_repetition'); + /** + * @param OrderLineItemEntity $entity + */ + public function __construct(OrderLineItemEntity $entity) + { + parent::__construct($entity); + $this->item = $entity; + $this->voucherType = $this->getCustomFieldValue($entity, 'voucher_type'); + + $this->subscriptionProduct = (bool)$this->getCustomFieldValue($entity, 'subscription_enabled'); + $this->subscriptionInterval = (int)$this->getCustomFieldValue($entity, 'subscription_interval'); + $this->subscriptionIntervalUnit = (string)$this->getCustomFieldValue($entity, 'subscription_interval_unit'); + $this->subscriptionRepetitionCount = (int)$this->getCustomFieldValue($entity, 'subscription_repetition'); } /** @@ -80,14 +74,6 @@ public function getVoucherType() return $this->voucherType; } - /** - * @return string - */ - public function getMollieOrderLineID(): string - { - return $this->mollieOrderLineID; - } - /** * @return bool */ @@ -142,25 +128,18 @@ public function isPromotion(): bool return false; } - /** - * Somehow there are 2 custom fields? in payload and custom fields? - * ....mhm...lets test always both - * @param OrderLineItemEntity $lineItem - * @param string $keyName - * @return string - */ - private function getCustomFieldValue(OrderLineItemEntity $lineItem, string $keyName): string + protected function getCustomFieldValue(Entity $entity, string $keyName): string { $foundValue = ''; # --------------------------------------------------------------------------- - # search in payload + # first search in payload - if ($lineItem->getPayload() !== null) { + if ($entity instanceof OrderLineItemEntity && $entity->getPayload() !== null) { # check if we have customFields in our payload - if (array_key_exists('customFields', $lineItem->getPayload())) { + if (array_key_exists('customFields', $entity->getPayload())) { # load the custom fields - $customFields = $lineItem->getPayload()['customFields']; + $customFields = $entity->getPayload()['customFields']; if (is_array($customFields)) { # --------------------------------------------------------------------------- @@ -180,28 +159,8 @@ private function getCustomFieldValue(OrderLineItemEntity $lineItem, string $keyN } } - # --------------------------------------------------------------------------- - # search in custom fields - if ($foundValue === '') { - # check if we have customFields - $customFields = $lineItem->getCustomFields(); - - if ($customFields !== null) { - # --------------------------------------------------------------------------- - # search in new structure - $fullKey = 'mollie_payments_product_' . $keyName; - $foundValue = (array_key_exists($fullKey, $customFields)) ? (string)$customFields[$fullKey] : ''; - - # old structure - # check if we have a mollie entry - if ($foundValue === '' && array_key_exists('mollie_payments', $customFields)) { - # load the mollie entry - $mollieData = $customFields['mollie_payments']; - # assign our value if we have it - $foundValue = (array_key_exists($keyName, $mollieData)) ? (string)$mollieData[$keyName] : ''; - } - } + $foundValue = parent::getCustomFieldValue($entity, $keyName); } return $foundValue; diff --git a/src/Struct/OrderXEntityAttributes.php b/src/Struct/OrderXEntityAttributes.php new file mode 100644 index 000000000..f6aab818c --- /dev/null +++ b/src/Struct/OrderXEntityAttributes.php @@ -0,0 +1,72 @@ +entity = $entity; + + $this->mollieOrderLineID = $this->getCustomFieldValue($entity, 'order_line_id'); + } + + /** + * @return string + */ + public function getMollieOrderLineID(): string + { + return $this->mollieOrderLineID; + } + + /** + * Somehow there are 2 custom fields? in payload and custom fields? + * ....mhm...lets test always both + * @param Entity $entity + * @param string $keyName + * @return string + */ + protected function getCustomFieldValue(Entity $entity, string $keyName): string + { + $foundValue = ''; + + # --------------------------------------------------------------------------- + # search in custom fields + + if (method_exists($entity, 'getCustomFields')) { + + # check if we have customFields + $customFields = $entity->getCustomFields(); + + if ($customFields !== null) { + # --------------------------------------------------------------------------- + # search in new structure + $fullKey = 'mollie_payments_product_' . $keyName; + $foundValue = (array_key_exists($fullKey, $customFields)) ? (string)$customFields[$fullKey] : ''; + + # old structure + # check if we have a mollie entry + if ($foundValue === '' && array_key_exists('mollie_payments', $customFields)) { + # load the mollie entry + $mollieData = $customFields['mollie_payments']; + # assign our value if we have it + $foundValue = (array_key_exists($keyName, $mollieData)) ? (string)$mollieData[$keyName] : ''; + } + } + } + + return $foundValue; + } +} diff --git a/tests/PHPUnit/Fakes/FakeRefundService.php b/tests/PHPUnit/Fakes/FakeRefundService.php index a97485601..2cc46f0c4 100644 --- a/tests/PHPUnit/Fakes/FakeRefundService.php +++ b/tests/PHPUnit/Fakes/FakeRefundService.php @@ -111,7 +111,8 @@ public function cancel(OrderEntity $order, string $refundId): bool */ public function getRefunds(OrderEntity $order): array { - // TODO: Implement getRefunds() method. + // FIXME: Only implements that nothing has been refunded yet, this might have to be changed. + return []; } /**