Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PISHPS-240: Cancel authorized items #756

Merged
merged 12 commits into from
Jun 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion makefile
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ phpcheck: ## Starts the PHP syntax checks
@find . -name '*.php' -not -path "./vendor/*" -not -path "./tests/*" | xargs -n 1 -P4 php -l

phpmin: ## Starts the PHP compatibility checks
@php vendor/bin/phpcs -p --standard=PHPCompatibility --extensions=php --runtime-set testVersion 7.2 ./src
@php vendor/bin/phpcs -p --standard=PHPCompatibility --extensions=php --runtime-set testVersion 7.4 ./src

csfix: ## Starts the PHP CS Fixer
@PHP_CS_FIXER_IGNORE_ENV=1 php vendor/bin/php-cs-fixer fix --config=./.php_cs.php --dry-run
Expand Down
97 changes: 97 additions & 0 deletions src/Components/CancelManager/CancelItemFacade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<?php
declare(strict_types=1);

namespace Kiener\MolliePayments\Components\CancelManager;

use Kiener\MolliePayments\Components\RefundManager\Integrators\StockManagerInterface;
use Kiener\MolliePayments\Factory\MollieApiFactory;
use Kiener\MolliePayments\Repository\OrderLineItem\OrderLineItemRepositoryInterface;
use Mollie\Api\MollieApiClient;
use Psr\Log\LoggerInterface;
use Shopware\Core\Checkout\Order\Aggregate\OrderLineItem\OrderLineItemEntity;
use Shopware\Core\Framework\Context;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;

/**
* @final
*/
class CancelItemFacade
{
private MollieApiClient $client;


private LoggerInterface $logger;
private OrderLineItemRepositoryInterface $orderLineItemRepository;
private StockManagerInterface $stockManager;

public function __construct(MollieApiFactory $clientFactory, OrderLineItemRepositoryInterface $orderLineItemRepository, StockManagerInterface $stockManager, LoggerInterface $logger)
{
$this->client = $clientFactory->getClient();
$this->logger = $logger;
$this->orderLineItemRepository = $orderLineItemRepository;
$this->stockManager = $stockManager;
}

public function cancelItem(string $mollieOrderId, string $mollieLineId, string $shopwareLineId, int $quantity, bool $resetStock, Context $context): CancelItemResponse
{
$response = new CancelItemResponse();
$logArguments = ['mollieOrderId' => $mollieOrderId, 'mollieLineId' => $mollieLineId, 'shopwareLineId' => $shopwareLineId, 'quantity' => $quantity, 'resetStock' => (string)$resetStock];
try {
$this->logger->info('Initiated cancelling an item', $logArguments);

if ($quantity === 0) {
$this->logger->error('Cancelling item failed, quantity is 0', $logArguments);
return $response->failedWithMessage('quantityZero');
}

$mollieOrder = $this->client->orders->get($mollieOrderId);

$orderLine = $mollieOrder->lines()->get($mollieLineId);

if ($orderLine === null) {
$this->logger->error('Cancelling item failed, lineItem does not exists in order', $logArguments);
return $response->failedWithMessage('invalidLine');
}
if ($quantity > $orderLine->cancelableQuantity) {
$logArguments['cancelableQuantity'] = $orderLine->cancelableQuantity;

$this->logger->error('Cancelling item failed, cancelableQuantity is too high', $logArguments);
return $response->failedWithMessage('quantityTooHigh');
}

//First we reset the stocks, just in case something went wrong the customer still have the chance to cancel the item on mollie page
if ($resetStock) {
$this->logger->info('Start to reset stocks', $logArguments);
$criteria = new Criteria([$shopwareLineId]);
$searchResult = $this->orderLineItemRepository->search($criteria, $context);
if ($searchResult->count() === 0) {
$this->logger->error('Failed to reset stocks in cancel process, shopware line item not found', $logArguments);
return $response->failedWithMessage('invalidShopwareLineId');
}

/** @var OrderLineItemEntity $shopwareLineItem */
$shopwareLineItem = $searchResult->first();

$this->stockManager->increaseStock($shopwareLineItem, $quantity);

$this->logger->info('Stock rested', $logArguments);
}


$lines = [
'id' => $orderLine->id,
'quantity' => $quantity
];

$mollieOrder->cancelLines(['lines' => [$lines]]);
$this->logger->info('Item cancelled successful', ['orderId' => $mollieOrderId, 'mollieLineId' => $mollieLineId, 'quantity' => $quantity]);


$response = $response->withData($lines);
} catch (\Throwable $e) {
$response = $response->failedWithMessage($e->getMessage());
}

return $response;
}
}
70 changes: 70 additions & 0 deletions src/Components/CancelManager/CancelItemResponse.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<?php
declare(strict_types=1);

namespace Kiener\MolliePayments\Components\CancelManager;

/**
* @final
*/
class CancelItemResponse
{
private string $message = '';
private bool $success = true;

/**
* @var array<mixed>
*/
private array $data = [];

public function isSuccessful(): bool
{
return $this->success === true;
}

/**
* @return string
*/
public function getMessage(): string
{
return $this->message;
}

public function failedWithMessage(string $message): self
{
$clone = clone $this;
$clone->success = false;
$clone->message = $message;
return $clone;
}

/**
* @param array<mixed> $data
* @return $this
*/
public function withData(array $data): self
{
$clone = clone $this;
$clone->data = $data;
return $clone;
}

/**
* @return array<mixed>
*/
public function getData(): array
{
return $this->data;
}

/**
* @return array<mixed>
*/
public function toArray(): array
{
return [
'message' => $this->message,
'success' => $this->success,
'data' => $this->data
];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ interface StockManagerInterface
/**
* @param OrderLineItemEntity $lineItem
* @param int $quantity
* @param string $mollieRefundID
*
* @return void
*/
public function increaseStock(OrderLineItemEntity $lineItem, int $quantity, string $mollieRefundID): void;
public function increaseStock(OrderLineItemEntity $lineItem, int $quantity): void;
}
3 changes: 1 addition & 2 deletions src/Components/RefundManager/RefundManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -286,8 +286,7 @@ public function refund(OrderEntity $order, RefundRequest $request, Context $cont
# and now simply call our stock manager
$this->stockManager->increaseStock(
$orderItem,
$item->getStockIncreaseQty(),
$refund->id
$item->getStockIncreaseQty()
);
}
}
Expand Down
69 changes: 69 additions & 0 deletions src/Controller/Api/Order/CancelLineController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?php
declare(strict_types=1);

namespace Kiener\MolliePayments\Controller\Api\Order;

use Kiener\MolliePayments\Components\CancelManager\CancelItemFacade;
use Kiener\MolliePayments\Components\CancelManager\CancelManagerInterface;
use Kiener\MolliePayments\Factory\MollieApiFactory;
use Mollie\Api\Resources\OrderLine;
use Shopware\Core\Framework\Context;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

class CancelLineController extends AbstractController
{
private MollieApiFactory $clientFactory;
private CancelItemFacade $cancelItemFacade;

public function __construct(MollieApiFactory $clientFactory, CancelItemFacade $cancelItemFacade)
{
$this->clientFactory = $clientFactory;
$this->cancelItemFacade = $cancelItemFacade;
}

public function statusAction(Request $request, Context $context): Response
{
$orderId = $request->get('mollieOrderId');
$result = [];
$client = $this->clientFactory->getClient();
$mollieOrder = $client->orders->get($orderId);

$lines = $mollieOrder->lines();
if ($lines->count() > 0) {
/** @var OrderLine $line */
foreach ($lines as $line) {
$metadata = $line->metadata;
if (! property_exists($metadata, 'orderLineItemId')) {
continue;
}
$id = $metadata->orderLineItemId;

$result[$id] = [
'mollieOrderId' => $orderId,
'mollieId' => $line->id,
'status' => $line->status,
'isCancelable' => $line->isCancelable,
'cancelableQuantity' => $line->cancelableQuantity,
'quantityCanceled' => $line->quantityCanceled
];
}
}

return new JsonResponse($result);
}

public function cancelAction(Request $request, Context $context): Response
{
$mollieOrderId = $request->get('mollieOrderId');
$mollieLineId = $request->get('mollieLineId');
$quantity = $request->get('canceledQuantity');
$shopwareOrderLineId = $request->get('shopwareLineId');
$resetStock = $request->get('resetStock', false);

$result = $this->cancelItemFacade->cancelItem($mollieOrderId, $mollieLineId, $shopwareOrderLineId, $quantity, $resetStock, $context);
return new JsonResponse($result->toArray());
}
}
7 changes: 7 additions & 0 deletions src/Repository/OrderLineItem/OrderLineItemRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
use Shopware\Core\Framework\Context;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository;
use Shopware\Core\Framework\DataAbstractionLayer\Event\EntityWrittenContainerEvent;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\DataAbstractionLayer\Search\EntitySearchResult;

class OrderLineItemRepository implements OrderLineItemRepositoryInterface
{
Expand All @@ -31,4 +33,9 @@ public function update(array $data, Context $context): EntityWrittenContainerEve
{
return $this->repoOrderLineItems->update($data, $context);
}

public function search(Criteria $criteria, Context $context): EntitySearchResult
{
return $this->repoOrderLineItems->search($criteria, $context);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

use Shopware\Core\Framework\Context;
use Shopware\Core\Framework\DataAbstractionLayer\Event\EntityWrittenContainerEvent;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\DataAbstractionLayer\Search\EntitySearchResult;

interface OrderLineItemRepositoryInterface
{
Expand All @@ -13,4 +15,11 @@ interface OrderLineItemRepositoryInterface
* @return EntityWrittenContainerEvent
*/
public function update(array $data, Context $context): EntityWrittenContainerEvent;

/**
* @param Criteria $criteria
* @param Context $context
* @return EntitySearchResult
*/
public function search(Criteria $criteria, Context $context): EntitySearchResult;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// eslint-disable-next-line no-undef
const ApiService = Shopware.Classes.ApiService;

export default class MolliePaymentsItemCancelService extends ApiService {

/**
*
* @param httpClient
* @param loginService
* @param apiEndpoint
*/
constructor(httpClient, loginService, apiEndpoint = 'mollie') {
super(httpClient, loginService, apiEndpoint);
}

/**
*
* @param data
* @returns {*}
*/
status(data = {mollieOrderId: null}) {
return this.__post('/status', data);
}

cancel(data = {
mollieOrderId: null,
mollieLineId: null,
shopwareLineId: null,
canceledQuantity: 0,
resetStock: false,
}) {
return this.__post('/cancel', data);
}

/**
*
* @param endpoint
* @param data
* @param headers
* @returns {*}
* @private
*/
__post(endpoint = '', data = {}, headers = {}) {
return this.httpClient
.post(
`_action/${this.getApiBasePath()}/cancel-item${endpoint}`,
JSON.stringify(data),
{
headers: this.getBasicHeaders(headers),
}
)
.then((response) => {
return ApiService.handleResponse(response);
})
.catch((error) => {
return ApiService.handleResponse(error.response);
});
}

}
8 changes: 8 additions & 0 deletions src/Resources/app/administration/src/init/api-service.init.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ import MolliePaymentsRefundService from '../core/service/api/mollie-payments-ref
import MolliePaymentsShippingService from '../core/service/api/mollie-payments-shipping.service';
import MolliePaymentsSupportService from '../core/service/api/mollie-payments-support.service';
import MolliePaymentsSubscriptionService from '../core/service/api/mollie-subscription.service';
import MolliePaymentsItemCancelService from '../core/service/api/mollie-payments-item-cancel.service';

import '../module/mollie-payments/rules/mollie-lineitem-subscription-rule';
import '../module/mollie-payments/rules/mollie-cart-subscription-rule';



// eslint-disable-next-line no-undef
const {Application} = Shopware;

Expand Down Expand Up @@ -59,6 +61,12 @@ Application.addServiceProvider('MolliePaymentsSubscriptionService', (container)
return new MolliePaymentsSubscriptionService(initContainer.httpClient, container.loginService);
});

Application.addServiceProvider('MolliePaymentsItemCancelService', (container) => {
const initContainer = Application.getContainer('init');

return new MolliePaymentsItemCancelService(initContainer.httpClient, container.loginService);
});


Application.addServiceProviderDecorator('ruleConditionDataProviderService', (ruleConditionService) => {
ruleConditionService.addCondition('mollie_lineitem_subscription_rule', {
Expand Down
Loading
Loading