diff --git a/src/Components/RefundManager/Builder/RefundDataBuilder.php b/src/Components/RefundManager/Builder/RefundDataBuilder.php index fad077cef..2128e8a43 100644 --- a/src/Components/RefundManager/Builder/RefundDataBuilder.php +++ b/src/Components/RefundManager/Builder/RefundDataBuilder.php @@ -19,6 +19,7 @@ use Kiener\MolliePayments\Struct\OrderLineItemEntity\OrderLineItemEntityAttributes; use Mollie\Api\Resources\OrderLine; use Mollie\Api\Resources\Refund; +use Shopware\Core\Checkout\Cart\LineItem\LineItem; use Shopware\Core\Checkout\Cart\Price\Struct\CalculatedPrice; use Shopware\Core\Checkout\Order\Aggregate\OrderDelivery\OrderDeliveryEntity; use Shopware\Core\Checkout\Order\Aggregate\OrderLineItem\OrderLineItemEntity; @@ -99,9 +100,14 @@ public function buildRefundData(OrderEntity $order, Context $context): RefundDat $refundItems = []; $refundPromotionItems = []; $refundDeliveryItems = []; + $orderLineItems = $order->getLineItems(); - if ($order->getLineItems() !== null) { - foreach ($order->getLineItems() as $item) { + if ($orderLineItems !== null) { + $orderLineItems = $orderLineItems->filter(function (OrderLineItemEntity $orderLineItemEntity) { + return $orderLineItemEntity->getType() !== LineItem::CREDIT_LINE_ITEM_TYPE; + }); + + foreach ($orderLineItems as $item) { $lineItemAttribute = new OrderLineItemEntityAttributes($item); $mollieOrderLineId = $lineItemAttribute->getMollieOrderLineID(); diff --git a/src/Components/RefundManager/RefundManager.php b/src/Components/RefundManager/RefundManager.php index 1e6106bff..0ce4b3cf0 100644 --- a/src/Components/RefundManager/RefundManager.php +++ b/src/Components/RefundManager/RefundManager.php @@ -245,7 +245,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()); } @@ -324,7 +324,7 @@ public function cancelRefund(string $orderId, string $refundId, Context $context # first try to cancel on the mollie side $success = $this->refundService->cancel($order, $refundId); - if (!$success) { + if (! $success) { return false; } @@ -521,12 +521,13 @@ private function convertToRepositoryArray(array $serviceItems): array if ($item->getQuantity() < 0) { continue; } - + $quantity = max(1, $item->getQuantity()); + $amount = round($item->getAmount() / $quantity, 2); $row = RefundItemEntity::createArray( $item->getMollieLineID(), $item->getShopwareReference(), - $item->getQuantity(), - $item->getAmount(), + $quantity, + $amount, $item->getShopwareLineID(), $item->getShopwareLineVersionId(), null diff --git a/src/Controller/Api/Order/RefundControllerBase.php b/src/Controller/Api/Order/RefundControllerBase.php index 77f9a8904..3ca39f33b 100644 --- a/src/Controller/Api/Order/RefundControllerBase.php +++ b/src/Controller/Api/Order/RefundControllerBase.php @@ -7,7 +7,6 @@ use Kiener\MolliePayments\Components\RefundManager\Request\RefundRequestItem; use Kiener\MolliePayments\Exception\PaymentNotFoundException; use Kiener\MolliePayments\Service\OrderService; -use Kiener\MolliePayments\Service\Refund\Exceptions\CreditNoteException; use Kiener\MolliePayments\Service\Refund\RefundCreditNoteService; use Kiener\MolliePayments\Service\Refund\RefundService; use Kiener\MolliePayments\Traits\Api\ApiTrait; @@ -55,10 +54,10 @@ class RefundControllerBase extends AbstractController * @param LoggerInterface $logger */ public function __construct( - OrderService $orderService, - RefundManagerInterface $refundManager, - RefundService $refundService, - LoggerInterface $logger, + OrderService $orderService, + RefundManagerInterface $refundManager, + RefundService $refundService, + LoggerInterface $logger, RefundCreditNoteService $creditNoteService ) { $this->orderService = $orderService; @@ -181,7 +180,7 @@ public function refundOrderID(RequestDataBag $data, Context $context): JsonRespo $items = $itemsBag->all(); } - $response = $this->refundAction( + return $this->refundAction( $orderId, '', $description, @@ -190,32 +189,6 @@ public function refundOrderID(RequestDataBag $data, Context $context): JsonRespo $items, $context ); - - if ($response->getStatusCode() === 200 && $response->getContent() !== false && count($items) > 0) { - $refundId = json_decode($response->getContent(), true)['refundId']; - if ($this->creditNoteService->containsRefundedLineItems($items)) { - try { - $this->creditNoteService->addCreditNoteToOrder($orderId, $refundId, $items, $context); - } catch (CreditNoteException $exception) { - if ($exception->getCode() === CreditNoteException::CODE_ADDING_CREDIT_NOTE_LINE_ITEMS) { - $this->logger->error($exception->getMessage(), ['code' => $exception->getCode(),]); - return $this->buildErrorResponse($exception->getMessage()); - } - if ($exception->getCode() === CreditNoteException::CODE_WARNING_LEVEL) { - $this->logger->warning($exception->getMessage(), ['code' => $exception->getCode(),]); - } - } - } - if ($this->creditNoteService->hasCustomAmounts($items, (float) $amount)) { - try { - $this->creditNoteService->addCustomAmountsCreditNote($orderId, $refundId, $items, (float)$amount, $context); - } catch (CreditNoteException $exception) { - $this->logger->warning($exception->getMessage(), ['code' => $exception->getCode(),]); - } - } - } - - return $response; } /** @@ -228,23 +201,7 @@ public function cancel(RequestDataBag $data, Context $context): JsonResponse { $orderId = $data->getAlnum('orderId'); $refundId = $data->get('refundId'); - $response = $this->cancelRefundAction($orderId, $refundId, $context); - - if ($response->getStatusCode() === 200) { - try { - $this->creditNoteService->cancelCreditNoteToOrder($orderId, $refundId, $context); - } catch (CreditNoteException $exception) { - if ($exception->getCode() === CreditNoteException::CODE_REMOVING_CREDIT_NOTE_LINE_ITEMS) { - $this->logger->error($exception->getMessage(), ['code' => $exception->getCode(),]); - return $this->buildErrorResponse($exception->getMessage()); - } - if ($exception->getCode() === CreditNoteException::CODE_WARNING_LEVEL) { - $this->logger->warning($exception->getMessage(), ['code' => $exception->getCode(),]); - } - } - } - - return $response; + return $this->cancelRefundAction($orderId, $refundId, $context); } /** @@ -374,7 +331,7 @@ private function listTotalAction(string $orderId, Context $context): JsonRespons private function refundAction(string $orderId, string $orderNumber, string $description, string $internalDescription, ?float $amount, array $items, Context $context): JsonResponse { try { - if (!empty($orderId)) { + if (! empty($orderId)) { $order = $this->orderService->getOrder($orderId, $context); } else { if (empty($orderNumber)) { @@ -407,6 +364,8 @@ private function refundAction(string $orderId, string $orderNumber, string $desc $context ); + $this->creditNoteService->createCreditNotes($order, $refund, $refundRequest, $context); + return $this->json([ 'success' => true, 'refundId' => $refund->id @@ -428,6 +387,9 @@ private function cancelRefundAction(string $orderId, string $refundId, Context $ { try { $success = $this->refundManager->cancelRefund($orderId, $refundId, $context); + + $this->creditNoteService->cancelCreditNotes($orderId, $context); + return $this->json([ 'success' => $success ]); diff --git a/src/Resources/config/services/services.xml b/src/Resources/config/services/services.xml index 3f9a508a2..fd0d003d2 100644 --- a/src/Resources/config/services/services.xml +++ b/src/Resources/config/services/services.xml @@ -63,14 +63,13 @@ - + - diff --git a/src/Service/Refund/RefundCreditNoteService.php b/src/Service/Refund/RefundCreditNoteService.php index d68a9a932..6a14fd880 100644 --- a/src/Service/Refund/RefundCreditNoteService.php +++ b/src/Service/Refund/RefundCreditNoteService.php @@ -3,19 +3,22 @@ namespace Kiener\MolliePayments\Service\Refund; -use Kiener\MolliePayments\Service\Refund\Exceptions\CreditNoteException; +use Kiener\MolliePayments\Components\RefundManager\Request\RefundRequest; +use Kiener\MolliePayments\Service\CustomFieldsInterface; use Kiener\MolliePayments\Service\SettingsService; +use Mollie\Api\Resources\Refund; use Psr\Log\LoggerInterface; use Shopware\Core\Checkout\Cart\LineItem\LineItem; use Shopware\Core\Checkout\Cart\Price\Struct\CalculatedPrice; -use Shopware\Core\Checkout\Cart\Price\Struct\QuantityPriceDefinition; use Shopware\Core\Checkout\Cart\Tax\Struct\CalculatedTaxCollection; use Shopware\Core\Checkout\Cart\Tax\Struct\TaxRuleCollection; +use Shopware\Core\Checkout\Order\Aggregate\OrderDelivery\OrderDeliveryEntity; use Shopware\Core\Checkout\Order\Aggregate\OrderLineItem\OrderLineItemEntity; use Shopware\Core\Checkout\Order\OrderEntity; use Shopware\Core\Framework\Context; use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository; use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria; +use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter; use Shopware\Core\Framework\Uuid\Uuid; class RefundCreditNoteService @@ -50,16 +53,11 @@ class RefundCreditNoteService */ private $logger; - /** - * @var RefundSummarizationService - */ - private $refundSummarizationService; public function __construct( EntityRepository $orderRepository, EntityRepository $orderLineItemRepository, SettingsService $settingsService, - RefundSummarizationService $refundSummarizationService, LoggerInterface $logger ) { $this->orderRepository = $orderRepository; @@ -69,193 +67,158 @@ public function __construct( $this->prefix = $settings->getRefundManagerCreateCreditNotesPrefix(); $this->suffix = $settings->getRefundManagerCreateCreditNotesSuffix(); $this->logger = $logger; - $this->refundSummarizationService = $refundSummarizationService; } /** - * @param array $lineItems - * @throws CreditNoteException + * @param string $orderId + * @param string $refundId + * @param float $unitPrice + * @param int $quantity + * @param float $totalAmount + * @param null|OrderLineItemEntity $orderLineItemEntity + * @param null|OrderDeliveryEntity $orderDeliveryEntity + * @return array */ - public function addCreditNoteToOrder(string $orderId, string $refundId, array $lineItems, Context $context): void + private function getLineItemArray(string $orderId, string $refundId, float $unitPrice, int $quantity, float $totalAmount, ?OrderLineItemEntity $orderLineItemEntity = null, ?OrderDeliveryEntity $orderDeliveryEntity = null): array { - if (!$this->enabled) { - $this->logger->debug('Credit note creation is disabled'); - return; + $id = $orderId . 'custom-amount'; + $label = $totalAmount; + + $taxCollection = new CalculatedTaxCollection(); + $taxRuleCollection = new TaxRuleCollection(); + + if ($orderLineItemEntity !== null) { + $id = $orderLineItemEntity->getId(); + $label = $orderLineItemEntity->getLabel(); + $price = $orderLineItemEntity->getPrice(); + if ($price !== null) { + $taxCollection = $price->getCalculatedTaxes(); + $taxRuleCollection = $price->getTaxRules(); + } } + if ($orderDeliveryEntity !== null) { + $id = $orderDeliveryEntity->getId(); + $shippingMethod = $orderDeliveryEntity->getShippingMethod(); + $label = 'Shipping'; + if ($shippingMethod !== null) { + $label = $shippingMethod->getName(); + } + $price = $orderDeliveryEntity->getShippingCosts(); - if (empty($orderId) || empty($refundId)) { - throw CreditNoteException::forAddingLineItems(sprintf('OrderId or RefundId is empty. OrderID: %s RefundID: %s', $orderId, $refundId)); + $taxCollection = $price->getCalculatedTaxes(); + $taxRuleCollection = $price->getTaxRules(); } - if (empty($lineItems)) { - throw CreditNoteException::forAddingLineItems(sprintf('No line items found for credit note. OrderID: %s RefundID: %s', $orderId, $refundId)); - } + $label = trim(sprintf('%s%s%s', $this->prefix, $label, $this->suffix)); - $data = ['id' => $orderId, 'lineItems' => []]; - foreach ($lineItems as ['id' => $lineItemId, 'amount' => $amount]) { - if ($amount === 0) { - // refund manager front end sends all line items, even if they are not going to be refunded - continue; - } - $lineItem = $this->orderLineItemRepository->search(new Criteria([$lineItemId]), $context)->first(); - if (!$lineItem instanceof OrderLineItemEntity) { - continue; - } - $price = $lineItem->getPrice(); - if (!$price instanceof CalculatedPrice) { - continue; - } - $taxRules = $price->getTaxRules(); - $totalPrice = $lineItem->getTotalPrice(); - $quantity = $lineItem->getQuantity(); - if ($totalPrice <= 0 || $quantity <= 0) { - continue; - } - $unitPrice = round($totalPrice / $quantity, 2); - $totalPrice *= -1; - $unitPrice *= -1; - $data['lineItems'][] = [ - 'id' => Uuid::fromBytesToHex(md5($lineItemId, true)), #@todo remove once 6.4 reached end of life - 'identifier' => Uuid::fromBytesToHex(md5($lineItem->getIdentifier(), true)),#@todo remove once 6.4 reached end of life - 'quantity' => $quantity, - 'label' => sprintf('%s%s%s', $this->prefix, $lineItem->getLabel(), $this->suffix), - 'type' => LineItem::CREDIT_LINE_ITEM_TYPE, - 'price' => new CalculatedPrice($unitPrice, $totalPrice, new CalculatedTaxCollection(), $taxRules), - 'priceDefinition' => new QuantityPriceDefinition($totalPrice, $taxRules, $quantity), - 'customFields' => [ - 'mollie_payments' => [ - 'type' => 'refund', - 'refundId' => $refundId, - 'lineItemId' => $lineItemId - ], + return [ + 'id' => Uuid::fromBytesToHex(md5($id, true)), #@todo remove once 6.4 reached end of life + 'identifier' => Uuid::fromBytesToHex(md5($id, true)), #@todo remove once 6.4 reached end of life + 'quantity' => $quantity, + 'label' => $label, + 'type' => LineItem::CREDIT_LINE_ITEM_TYPE, + 'price' => new CalculatedPrice($unitPrice, $totalAmount, $taxCollection, $taxRuleCollection), + 'customFields' => [ + CustomFieldsInterface::MOLLIE_KEY => [ + 'type' => 'refund', + 'refundId' => $refundId ], - ]; - } - - if (empty($data['lineItems'])) { - throw CreditNoteException::forAddingLineItems(sprintf('No credit note line items found for order. OrderID: %s RefundID: %s', $orderId, $refundId), CreditNoteException::CODE_WARNING_LEVEL); - } - - $this->logger->debug('Adding credit note to order', ['orderId' => $orderId, 'refundId' => $refundId, 'lineItems' => $data['lineItems']]); - $this->orderRepository->upsert([$data], $context); + ], + ]; } - /** - * @throws CreditNoteException - */ - public function cancelCreditNoteToOrder(string $orderId, string $refundId, Context $context): void + public function createCreditNotes(OrderEntity $order, Refund $refund, RefundRequest $refundRequest, Context $context): void { - if (empty($orderId) || empty($refundId)) { - throw CreditNoteException::forRemovingLineItems(sprintf('OrderId or RefundId is empty. OrderID: %s RefundID: %s', $orderId, $refundId)); - } - - $criteria = new Criteria([$orderId]); - $criteria->addAssociation('lineItems'); - $searchResult = $this->orderRepository->search($criteria, $context); - $order = $searchResult->first(); - - if (!$order instanceof OrderEntity) { - throw CreditNoteException::forRemovingLineItems(sprintf('Order not found. OrderID: %s RefundID: %s', $orderId, $refundId)); - } - - $lineItems = $order->getLineItems(); - - if ($lineItems === null) { - throw CreditNoteException::forRemovingLineItems( - sprintf('No line items found for order. OrderID: %s RefundID: %s', $orderId, $refundId), - CreditNoteException::CODE_WARNING_LEVEL - ); + if (! $this->enabled) { + $this->logger->debug('Credit note creation is disabled'); + return; } - - $ids = []; - foreach ($lineItems as $lineItem) { - /** @var OrderLineItemEntity $lineItem */ - $customFields = $lineItem->getCustomFields(); - if (!isset($customFields['mollie_payments'], $customFields['mollie_payments']['type']) || $customFields['mollie_payments']['type'] !== 'refund') { - continue; - } - - $lineItemRefundId = $customFields['mollie_payments']['refundId']; - if ($lineItemRefundId !== $refundId) { - continue; + $orderId = $order->getId(); + $refundId = $refund->id; + + $lineItems = []; + + + $orderLineItems = $order->getLineItems(); + $orderDeliveries = $order->getDeliveries(); + $refundAmount = $refundRequest->getAmount(); + + if (count($refundRequest->getItems()) > 0) { + foreach ($refundRequest->getItems() as $refundLineItem) { + $orderLineItemId = $refundLineItem->getLineId(); + $totalAmount = $refundLineItem->getAmount(); + $quantity = max(1, $refundLineItem->getQuantity()); + if ($totalAmount <= 0.0) { + continue; + } + $unitPrice = $totalAmount / $quantity; + + $refundAmount -= $totalAmount; + $refundAmount = round($refundAmount, 2); + + if ($orderLineItems === null) { + continue; + } + + $filteredOrderLineItems = $orderLineItems->filter(function (OrderLineItemEntity $item) use ($orderLineItemId) { + return $item->getId() === $orderLineItemId; + }); + + if ($filteredOrderLineItems->count() === 0) { + if ($orderDeliveries === null) { + continue; + } + + $filteredOrderDeliveries = $orderDeliveries->filter(function (OrderDeliveryEntity $item) use ($orderLineItemId) { + return $item->getId() === $orderLineItemId; + }); + if ($filteredOrderDeliveries->count() === 0) { + continue; + } + + $orderDelivery = $filteredOrderDeliveries->first(); + $lineItems[] = $this->getLineItemArray($orderId, $refundId, $unitPrice, $quantity, $totalAmount, null, $orderDelivery); + continue; + } + $orderLineItem = $filteredOrderLineItems->first(); + + $lineItems[] = $this->getLineItemArray($orderId, $refundId, $unitPrice, $quantity, $totalAmount, $orderLineItem); } - - $ids[] = ['id' => $lineItem->getId()]; } - if (empty($ids)) { - throw CreditNoteException::forRemovingLineItems( - sprintf('No credit note line items found for order. OrderID: %s RefundID: %s', $orderId, $refundId), - CreditNoteException::CODE_WARNING_LEVEL - ); + if ($refundAmount > 0) { + $lineItems[] = $this->getLineItemArray($orderId, $refundId, $refundAmount, 1, $refundAmount); } - $this->orderLineItemRepository->delete($ids, $context); + $this->logger->debug('Adding credit note to order', ['orderId' => $orderId, 'refundId' => $refundId, 'lineItems' => $lineItems]); + $this->orderRepository->upsert([[ + 'id' => $orderId, + 'lineItems' => $lineItems, + ]], $context); } - /** - * Checks if the provided line items contain any refunded amounts. - * - * This method evaluates the total refund amount of the given line items by - * using the `getLineItemsRefundSum` method from `RefundSummarizationService`. - * If the summed refund amount is greater than zero, it indicates that - * the items contain refunded line items. - * - * @param array $items Array of items, each potentially containing an 'amount' field. - * @return bool True if the items contain refunded line items (sum of 'amount' > 0), false otherwise. - */ - public function containsRefundedLineItems(array $items): bool + public function cancelCreditNotes(string $orderId, Context $context): void { - // Checks if the total refund sum of line items is greater than zero, - // which indicates that there are refunded line items present. - return $this->refundSummarizationService->getLineItemsRefundSum($items) > 0; - } - /** - * @param array $items - * @param float $refundAmount - * @return bool - */ - public function hasCustomAmounts(array $items, float $refundAmount): bool - { - return $this->refundSummarizationService->customAmountInLineItems($items, $refundAmount) !== 0.0; - } - - /** - * @param string $orderId - * @param string $refundId - * @param array $items - * @param float $refundAmount - * @param Context $context - * @throws CreditNoteException - */ - public function addCustomAmountsCreditNote(string $orderId, string $refundId, array $items, float $refundAmount, Context $context): void - { - $customAmount = $this->refundSummarizationService->customAmountInLineItems($items, $refundAmount); + //do not check for enabled, because credit notes could be created before and you still want to delete them, even if the feature is disabled now - if (empty($orderId) || empty($refundId)) { - throw CreditNoteException::forAddingLineItems(sprintf('OrderId or RefundId is empty. OrderID: %s RefundID: %s', $orderId, $refundId)); + $criteria = new Criteria(); + $criteria->addFilter(new EqualsFilter('orderId', $orderId)); + $criteria->addFilter(new EqualsFilter('type', LineItem::CREDIT_LINE_ITEM_TYPE)); + $this->logger->debug('Start cancel credit notes', ['orderId' => $orderId]); + $searchResult = $this->orderLineItemRepository->searchIds($criteria, $context); + if ($searchResult->getTotal() === 0) { + $this->logger->debug('No credit notes found', ['orderId' => $orderId]); + return; } + $ids = $searchResult->getIds(); + $toDelete = []; + foreach ($ids as $id) { + $toDelete[] = ['id' => $id]; + } + $this->orderLineItemRepository->delete($toDelete, $context); - $data = ['id' => $orderId, 'lineItems' => []]; - - $data['lineItems'][] = [ - 'id' => Uuid::fromBytesToHex(md5($orderId . 'custom-amount', true)), #@todo remove once 6.4 reached end of life - 'identifier' => Uuid::fromBytesToHex(md5($orderId . 'custom-amount', true)), #@todo remove once 6.4 reached end of life - 'quantity' => 1, - 'label' => sprintf('%s%s%s', $this->prefix, $customAmount, $this->suffix), - 'type' => LineItem::CREDIT_LINE_ITEM_TYPE, - 'price' => new CalculatedPrice($customAmount, $customAmount, new CalculatedTaxCollection(), new TaxRuleCollection()), - 'customFields' => [ - 'mollie_payments' => [ - 'type' => 'refund', - 'refundId' => $refundId - ], - ], - ]; - - $this->logger->debug('Adding credit note to order', ['orderId' => $orderId, 'refundId' => $refundId, 'lineItems' => $data['lineItems']]); - $this->orderRepository->upsert([$data], $context); + $this->logger->debug('Deleted credit notes from order', ['orderId' => $orderId, 'total' => count($ids)]); } } diff --git a/src/Service/Refund/RefundSummarizationService.php b/src/Service/Refund/RefundSummarizationService.php deleted file mode 100644 index a53da9342..000000000 --- a/src/Service/Refund/RefundSummarizationService.php +++ /dev/null @@ -1,54 +0,0 @@ - $items Array of items, each containing an 'amount' field. - * The 'amount' field should be convertible to a float. - * @return float Total refund sum calculated from the 'amount' values in the provided items. - */ - public function getLineItemsRefundSum(array $items): float - { - // Extracts the 'amount' values from each item in the array - $amounts = array_column($items, 'amount'); - - // Converts each extracted amount to a float to ensure accurate summation - $amounts = array_map('floatval', $amounts); - - // Sums up all converted amounts and returns the result as the total refund amount - return array_sum($amounts); - } - - /** - * @param array $items - * @param float $refundAmounts - * @return float - */ - public function customAmountInLineItems(array $items, float $refundAmounts): float - { - // Extracts the 'amount' values from each item in the array - $amounts = array_column($items, 'amount'); - - // Converts each extracted amount to a float to ensure accurate summation - $amounts = array_map('floatval', $amounts); - - foreach ($amounts as $amount) { - $refundAmounts -= $amount; - } - - return (float) $refundAmounts; - } -} diff --git a/tests/PHPUnit/Components/RefundManager/RefundManagerTest.php b/tests/PHPUnit/Components/RefundManager/RefundManagerTest.php index 08cf3e38c..85d2e7b44 100644 --- a/tests/PHPUnit/Components/RefundManager/RefundManagerTest.php +++ b/tests/PHPUnit/Components/RefundManager/RefundManagerTest.php @@ -210,7 +210,7 @@ public function testValidItemsAreAdded(int $qty, float $itemPrice): void [ "mollieLineId" => "odl_123", "label" => "product-id-1", - "quantity" => $qty, + "quantity" => max($qty,1), "amount" => $itemPrice, "orderLineItemId" => "line-1", 'orderLineItemVersionId' => null, diff --git a/tests/PHPUnit/Service/Refund/RefundCreditNoteServiceTest.php b/tests/PHPUnit/Service/Refund/RefundCreditNoteServiceTest.php deleted file mode 100644 index 9d6c97561..000000000 --- a/tests/PHPUnit/Service/Refund/RefundCreditNoteServiceTest.php +++ /dev/null @@ -1,246 +0,0 @@ -enabled = true; - $this->prefix = 'prefix'; - $this->suffix = 'suffix'; - $this->orderRepository = $this->createMock(EntityRepository::class); - $this->orderLineRepository = $this->createMock(EntityRepository::class); - $this->settingsService = $this->createMock(SettingsService::class); - $this->logger = $this->createMock(LoggerInterface::class); - $this->context = $this->createMock(Context::class); - } - - public function testCreatesLogEntryWhenServiceIsDisabledInConfig(): void - { - $this->enabled = false; - $this->logger->expects($this->once())->method('debug'); - $this->orderRepository->expects($this->never())->method('search'); - $this->orderRepository->expects($this->never())->method('upsert'); - - $this->service()->addCreditNoteToOrder('orderId', 'refundId', [], $this->context); - } - - public function testThrowsExceptionWhenOrderIdIsEmpty(): void - { - $this->expectException(CreditNoteException::class); - - $this->service()->addCreditNoteToOrder('', 'refundId', [], $this->context); - } - - public function testThrowsExceptionWhenRefundIdIsEmpty(): void - { - $this->expectException(CreditNoteException::class); - - $this->service()->addCreditNoteToOrder('orderId', '', [], $this->context); - } - - public function throwsExceptionWhenNoLineItemsAreProvided(): void - { - $this->expectException(CreditNoteException::class); - - $this->service()->addCreditNoteToOrder('orderId', 'refundId', [], $this->context); - } - - /** - * @throws CreditNoteException - */ - public function testCanAddRefundLineItems(): void - { - $data = [ - ['id' => $lineItemId = Uuid::randomBytes(), 'amount' => 1] - ]; - - $price = $this->createConfiguredMock(CalculatedPrice::class, [ - 'getTaxRules' => $rules = $this->createMock(TaxRuleCollection::class) - ]); - $lineItemEntity = $this->createConfiguredMock(OrderLineItemEntity::class, - [ - 'getId' => $lineItemId, - 'getIdentifier' => 'lineItemIdentifier', - 'getPrice' => $price, - 'getQuantity' => 1, - 'getTotalPrice' => 9.99, - 'getLabel' => 'label' - ] - ); - $searchResult = $this->createConfiguredMock(EntitySearchResult::class, ['first' => $lineItemEntity]); - $this->orderLineRepository->expects($this->once())->method('search')->willReturn($searchResult); - - $expectedDataArray = [ - 'id' => $orderId = 'orderId', - 'lineItems' => [ - [ - 'id' => Uuid::fromStringToHex($lineItemId), - 'identifier' => Uuid::fromStringToHex('lineItemIdentifier'), - 'quantity' => 1, - 'label' => sprintf('%s%s%s', $this->prefix, $lineItemEntity->getLabel(), $this->suffix), - 'type' => LineItem::CREDIT_LINE_ITEM_TYPE, - 'price' => new CalculatedPrice(-9.99, -9.99, new CalculatedTaxCollection(), $rules), - 'priceDefinition' => new QuantityPriceDefinition(-9.99, $rules, 1), - 'customFields' => [ - 'mollie_payments' => [ - 'type' => 'refund', - 'refundId' => $refundId = 'refundId', - 'lineItemId' => $lineItemId - ], - ], - ] - ] - ]; - - $this->logger->expects($this->once())->method('debug'); - $this->orderRepository->expects($this->once())->method('upsert')->with( - $this->equalTo([$expectedDataArray]), - $this->equalTo($this->context) - ); - - $this->service()->addCreditNoteToOrder($orderId, $refundId, $data, $this->context); - } - - public function testThrowsExceptionWhenOrderIdIsEmptyWhenCanceling(): void - { - $this->expectException(CreditNoteException::class); - - $this->service()->cancelCreditNoteToOrder('', 'refundId', $this->context); - } - - public function testThrowsExceptionWhenRefundIdIsEmptyWhenCanceling(): void - { - $this->expectException(CreditNoteException::class); - - $this->service()->cancelCreditNoteToOrder('orderId', '', $this->context); - } - - public function testThrowsExceptionWhenNoCreditLineItemsWouldBeUpserted(): void - { - $this->expectException(CreditNoteException::class); - - $data = [ - ['id' => Uuid::randomBytes(), 'amount' => 1] - ]; - - $searchResult = $this->createConfiguredMock(EntitySearchResult::class, ['first' => null]); - $this->orderLineRepository->expects($this->once())->method('search')->willReturn($searchResult); - - $this->service()->addCreditNoteToOrder('OrderId', 'RefundId', $data, $this->context); - } - - /** - * @throws CreditNoteException - */ - public function testDeletesAssociatedLineItemsWhenCanceling(): void - { - $orderId = 'orderId'; - $refundId = 'refundId'; - - $searchResult = $this->createConfiguredMock(EntitySearchResult::class, ['first' => $order = $this->createMock(OrderEntity::class)]); - $this->orderRepository->expects($this->once())->method('search')->willReturn($searchResult); - - $lineItems = new OrderLineItemCollection([ - $lineItem = $this->createConfiguredMock(OrderLineItemEntity::class, [ - 'getId' => $expected = Uuid::randomHex(), - 'getCustomFields' => ['mollie_payments' => ['type' => 'refund', 'refundId' => $refundId]] - ]) - ]); - - $order->expects($this->once())->method('getLineItems')->willReturn($lineItems); - - $this->orderLineRepository->expects($this->once())->method('delete') - ->with( - $this->equalTo([['id' => $expected]]), - $this->equalTo($this->context) - ); - - $this->service()->cancelCreditNoteToOrder($orderId, $refundId, $this->context); - } - - - private function service(?RefundSummarizationService $summarizationService = null): RefundCreditNoteService - { - $settingsStruct = new MollieSettingStruct(); - $settingsStruct->setRefundManagerCreateCreditNotesEnabled($this->enabled); - $settingsStruct->setRefundManagerCreateCreditNotesPrefix($this->prefix); - $settingsStruct->setRefundManagerCreateCreditNotesSuffix($this->suffix); - - $this->settingsService->method('getSettings')->willReturn($settingsStruct); - - return new RefundCreditNoteService( - $this->orderRepository, - $this->orderLineRepository, - $this->settingsService, - $summarizationService ?? new RefundSummarizationServiceFake(), - $this->logger - ); - } -} \ No newline at end of file diff --git a/tests/PHPUnit/Service/Refund/RefundSummarizationServiceTest.php b/tests/PHPUnit/Service/Refund/RefundSummarizationServiceTest.php deleted file mode 100644 index df51b390f..000000000 --- a/tests/PHPUnit/Service/Refund/RefundSummarizationServiceTest.php +++ /dev/null @@ -1,47 +0,0 @@ -service = new RefundSummarizationService(); - } - - /** - * @dataProvider summarizeDataProvider - */ - public function testCanSummarize(float ...$values): void - { - $expected = 0; - $dataSet = []; - - foreach ($values as $value) { - $dataSet[] = ['amount' => $value]; - $expected += $value; - } - - $actual = $this->service->getLineItemsRefundSum($dataSet); - - static::assertEquals($expected, $actual); - } - - public static function summarizeDataProvider(): array - { - return [ - 'single value' => [10.0], - 'multiple values' => [10.0, 20.0, 30.0], - ]; - } -} \ No newline at end of file