Skip to content

Commit

Permalink
PISHPS-385: creditline items on refund (#884)
Browse files Browse the repository at this point in the history
* PISHPS-385: refunds with just an amount and no reference to the line item

* PISHPS-385: custom amount line item

* PISHPS-385: stan fix
  • Loading branch information
m-muxfeld-diw authored Nov 6, 2024
1 parent 54555e8 commit 72244a9
Show file tree
Hide file tree
Showing 7 changed files with 214 additions and 8 deletions.
23 changes: 16 additions & 7 deletions src/Controller/Api/Order/RefundControllerBase.php
Original file line number Diff line number Diff line change
Expand Up @@ -193,14 +193,23 @@ public function refundOrderID(RequestDataBag $data, Context $context): JsonRespo

if ($response->getStatusCode() === 200 && $response->getContent() !== false && count($items) > 0) {
$refundId = json_decode($response->getContent(), true)['refundId'];
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 ($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 ($exception->getCode() === CreditNoteException::CODE_WARNING_LEVEL) {
}
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(),]);
}
}
Expand Down
4 changes: 4 additions & 0 deletions src/Resources/config/services/services.xml
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,14 @@
<argument type="service" id="mollie_payments.logger"/>
</service>

<service id="Kiener\MolliePayments\Service\Refund\RefundSummarizationService"/>


<service id="Kiener\MolliePayments\Service\Refund\RefundCreditNoteService">
<argument type="service" id="order.repository"/>
<argument type="service" id="order_line_item.repository"/>
<argument type="service" id="Kiener\MolliePayments\Service\SettingsService"/>
<argument type="service" id="Kiener\MolliePayments\Service\Refund\RefundSummarizationService"/>
<argument type="service" id="mollie_payments.logger"/>
</service>

Expand Down
73 changes: 73 additions & 0 deletions src/Service/Refund/RefundCreditNoteService.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
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\OrderLineItem\OrderLineItemEntity;
use Shopware\Core\Checkout\Order\OrderEntity;
use Shopware\Core\Framework\Context;
Expand Down Expand Up @@ -49,10 +50,16 @@ class RefundCreditNoteService
*/
private $logger;

/**
* @var RefundSummarizationService
*/
private $refundSummarizationService;

public function __construct(
EntityRepository $orderRepository,
EntityRepository $orderLineItemRepository,
SettingsService $settingsService,
RefundSummarizationService $refundSummarizationService,
LoggerInterface $logger
) {
$this->orderRepository = $orderRepository;
Expand All @@ -62,6 +69,7 @@ public function __construct(
$this->prefix = $settings->getRefundManagerCreateCreditNotesPrefix();
$this->suffix = $settings->getRefundManagerCreateCreditNotesSuffix();
$this->logger = $logger;
$this->refundSummarizationService = $refundSummarizationService;
}

/**
Expand Down Expand Up @@ -185,4 +193,69 @@ public function cancelCreditNoteToOrder(string $orderId, string $refundId, Conte

$this->orderLineItemRepository->delete($ids, $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<int|string, mixed> $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
{
// 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<int|string, mixed> $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<int|string, mixed> $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);

if (empty($orderId) || empty($refundId)) {
throw CreditNoteException::forAddingLineItems(sprintf('OrderId or RefundId is empty. OrderID: %s RefundID: %s', $orderId, $refundId));
}

$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);
}
}
54 changes: 54 additions & 0 deletions src/Service/Refund/RefundSummarizationService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php
declare(strict_types=1);

namespace Kiener\MolliePayments\Service\Refund;

/**
* Service to handle the calculation of refund amounts from line items.
* This class provides functionality to sum up the refund amounts for a collection of line items.
*/
class RefundSummarizationService
{
/**
* Calculates the total refund sum for a given list of line items.
*
* This method accepts an array of items, each containing an 'amount' field, and calculates the total refund amount
* by summing the values of these fields. The 'amount' field is expected to be convertible to a float, and this method
* will treat all values as floating-point numbers.
*
* @param array<int|string, mixed> $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<int|string, mixed> $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;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php
declare(strict_types=1);

namespace MolliePayments\Tests\Service\Refund\Fakes;


use Kiener\MolliePayments\Service\Refund\RefundSummarizationService;

class RefundSummarizationServiceFake extends RefundSummarizationService
{
#[\Override]
public function getLineItemsRefundSum(array $items): float
{
return 1;
}
}
5 changes: 4 additions & 1 deletion tests/PHPUnit/Service/Refund/RefundCreditNoteServiceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@

use Kiener\MolliePayments\Service\Refund\Exceptions\CreditNoteException;
use Kiener\MolliePayments\Service\Refund\RefundCreditNoteService;
use Kiener\MolliePayments\Service\Refund\RefundSummarizationService;
use Kiener\MolliePayments\Service\SettingsService;
use Kiener\MolliePayments\Setting\MollieSettingStruct;
use MolliePayments\Tests\Service\Refund\Fakes\RefundSummarizationServiceFake;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Psr\Log\LoggerInterface;
Expand Down Expand Up @@ -224,7 +226,7 @@ public function testDeletesAssociatedLineItemsWhenCanceling(): void
}


private function service(): RefundCreditNoteService
private function service(?RefundSummarizationService $summarizationService = null): RefundCreditNoteService
{
$settingsStruct = new MollieSettingStruct();
$settingsStruct->setRefundManagerCreateCreditNotesEnabled($this->enabled);
Expand All @@ -237,6 +239,7 @@ private function service(): RefundCreditNoteService
$this->orderRepository,
$this->orderLineRepository,
$this->settingsService,
$summarizationService ?? new RefundSummarizationServiceFake(),
$this->logger
);
}
Expand Down
47 changes: 47 additions & 0 deletions tests/PHPUnit/Service/Refund/RefundSummarizationServiceTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php
declare(strict_types=1);

namespace MolliePayments\Tests\Service\Refund;


use Kiener\MolliePayments\Service\Refund\RefundSummarizationService;
use PHPUnit\Framework\TestCase;

class RefundSummarizationServiceTest extends TestCase
{
/**
* @var RefundSummarizationService
*/
private $service;

protected function setUp(): void
{
$this->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],
];
}
}

0 comments on commit 72244a9

Please sign in to comment.