Skip to content

Commit

Permalink
Merge pull request mollie#869 from mollie/PIPRES-349-update-subscript…
Browse files Browse the repository at this point in the history
…ion-carrier-handler

PIPRES-349: Update subscription carrier handler
  • Loading branch information
JevgenijVisockij authored May 14, 2024
2 parents 2ef0803 + 158a306 commit 430064b
Show file tree
Hide file tree
Showing 10 changed files with 744 additions and 2 deletions.
34 changes: 34 additions & 0 deletions mollie.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
use Mollie\Service\ExceptionService;
use Mollie\ServiceProvider\LeagueServiceContainerProvider;
use Mollie\Subscription\Handler\CustomerAddressUpdateHandler;
use Mollie\Subscription\Handler\UpdateSubscriptionCarrierHandler;
use Mollie\Subscription\Install\AttributeInstaller;
use Mollie\Subscription\Install\DatabaseTableInstaller;
use Mollie\Subscription\Install\HookInstaller;
Expand Down Expand Up @@ -1271,6 +1272,39 @@ public function hookActionObjectAddressDeleteAfter(array $params): void
$this->addPreventDeleteErrorMessage();
}

public function hookActionCarrierUpdate(array $params): void
{
$oldCarrierId = $params['id_carrier'] ?? 0;
$newCarrier = $params['carrier'] ?? null;

if (empty($oldCarrierId) || empty($newCarrier)) {
return;
}

/** @var UpdateSubscriptionCarrierHandler $subscriptionCarrierUpdateHandler */
$subscriptionCarrierUpdateHandler = $this->getService(UpdateSubscriptionCarrierHandler::class);

/** @var ConfigurationAdapter $configuration */
$configuration = $this->getService(ConfigurationAdapter::class);

/** @var PrestaLoggerInterface $logger */
$logger = $this->getService(PrestaLoggerInterface::class);

if ((int) $oldCarrierId !== (int) $configuration->get(Config::MOLLIE_SUBSCRIPTION_ORDER_CARRIER_ID)) {
return;
}

$failedSubscriptionOrderIdsToUpdate = $subscriptionCarrierUpdateHandler->run((int) $newCarrier->id);

if (empty($failedSubscriptionOrderIdsToUpdate)) {
return;
}

$logger->error('Failed to update subscription carrier for all orders.', [
'failed_subscription_order_ids' => json_encode($failedSubscriptionOrderIdsToUpdate),
]);
}

public function hookActionFrontControllerAfterInit(): void
{
$this->frontControllerAfterInit();
Expand Down
1 change: 1 addition & 0 deletions src/Install/Installer.php
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ public static function getHooks()
'actionObjectOrderPaymentAddAfter',
'displayProductAdditionalInfo',
'displayCustomerAccount',
'actionCarrierUpdate',
];
}

Expand Down
1 change: 0 additions & 1 deletion src/Install/Uninstall.php
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,6 @@ private function deleteConfig()
Config::METHODS_CONFIG,
Config::MOLLIE_MAIL_WHEN_COMPLETED,
Config::MOLLIE_API_KEY_TEST,
Config::MOLLIE_SUBSCRIPTION_ORDER_CARRIER_ID,
Config::MOLLIE_SUBSCRIPTION_ENABLED,
];

Expand Down
4 changes: 3 additions & 1 deletion src/Logger/PrestaLogger.php
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,9 @@ public function info($message, array $context = [])

public function debug($message, array $context = [])
{
throw new NotImplementedException('not implemented method');
// TODO implement single method, which handles logging

$this->info($message, $context);
}

public function log($level, $message, array $context = [])
Expand Down
34 changes: 34 additions & 0 deletions subscription/Controller/Symfony/SubscriptionController.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,15 @@
namespace Mollie\Subscription\Controller\Symfony;

use Exception;
use Mollie\Adapter\ConfigurationAdapter;
use Mollie\Adapter\Shop;
use Mollie\Config\Config;
use Mollie\Logger\PrestaLoggerInterface;
use Mollie\Subscription\Exception\SubscriptionApiException;
use Mollie\Subscription\Filters\SubscriptionFilters;
use Mollie\Subscription\Grid\SubscriptionGridDefinitionFactory;
use Mollie\Subscription\Handler\SubscriptionCancellationHandler;
use Mollie\Subscription\Handler\UpdateSubscriptionCarrierHandler;
use Mollie\Utility\PsVersionUtility;
use PrestaShop\PrestaShop\Core\Form\FormHandlerInterface;
use PrestaShop\PrestaShop\Core\Grid\GridFactoryInterface;
Expand Down Expand Up @@ -96,6 +100,8 @@ public function submitOptionsAction(Request $request): RedirectResponse
return $this->redirectToRoute('admin_subscription_index');
}

$this->updateSubscriptionCarrier($form->getData()['carrier']);

$formHandler->save($form->getData());

$this->addFlash(
Expand All @@ -106,6 +112,34 @@ public function submitOptionsAction(Request $request): RedirectResponse
return $this->redirectToRoute('admin_subscription_index');
}

private function updateSubscriptionCarrier(int $newCarrierId): void
{
/** @var ConfigurationAdapter $configuration */
$configuration = $this->module->getService(ConfigurationAdapter::class);
$oldCarrierId = $configuration->get(Config::MOLLIE_SUBSCRIPTION_ORDER_CARRIER_ID);

if (empty($oldCarrierId) || empty($newCarrierId)) {
$this->addFlash(
'error',
$this->module->l('Carrier not found', self::FILE_NAME)
);
}

/** @var UpdateSubscriptionCarrierHandler $subscriptionCarrierUpdateHandler */
$subscriptionCarrierUpdateHandler = $this->module->getService(UpdateSubscriptionCarrierHandler::class);

/** @var PrestaLoggerInterface $logger */
$logger = $this->module->getService(PrestaLoggerInterface::class);

$failedSubscriptionOrderIdsToUpdate = $subscriptionCarrierUpdateHandler->run($newCarrierId);

if (!empty($failedSubscriptionOrderIdsToUpdate)) {
$logger->error('Failed to update subscription carrier for all orders.', [
'failed_subscription_order_ids' => json_encode($failedSubscriptionOrderIdsToUpdate),
]);
}
}

/**
* Provides filters functionality.
*
Expand Down
213 changes: 213 additions & 0 deletions subscription/Handler/UpdateSubscriptionCarrierHandler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
<?php
/**
* Mollie https://www.mollie.nl
*
* @author Mollie B.V. <[email protected]>
* @copyright Mollie B.V.
* @license https://github.com/mollie/PrestaShop/blob/master/LICENSE.md
*
* @see https://github.com/mollie/PrestaShop
* @codingStandardsIgnoreStart
*/

namespace Mollie\Subscription\Handler;

use Mollie\Adapter\ConfigurationAdapter;
use Mollie\Adapter\Context;
use Mollie\Api\Types\SubscriptionStatus;
use Mollie\Config\Config;
use Mollie\Logger\PrestaLoggerInterface;
use Mollie\Service\MailService;
use Mollie\Subscription\Action\UpdateRecurringOrderAction;
use Mollie\Subscription\Action\UpdateSubscriptionAction;
use Mollie\Subscription\DTO\CloneOriginalSubscriptionCartData;
use Mollie\Subscription\DTO\SubscriptionOrderAmountProviderData;
use Mollie\Subscription\DTO\UpdateRecurringOrderData;
use Mollie\Subscription\DTO\UpdateSubscriptionData;
use Mollie\Subscription\Provider\SubscriptionOrderAmountProvider;
use Mollie\Subscription\Repository\RecurringOrderRepositoryInterface;

if (!defined('_PS_VERSION_')) {
exit;
}

class UpdateSubscriptionCarrierHandler
{
/** @var ConfigurationAdapter */
private $configuration;
/** @var RecurringOrderRepositoryInterface */
private $recurringOrderRepository;
/** @var Context */
private $context;
/** @var UpdateSubscriptionAction */
private $updateSubscriptionAction;
/** @var UpdateRecurringOrderAction */
private $updateRecurringOrderAction;
/** @var PrestaLoggerInterface */
private $logger;
/** @var CloneOriginalSubscriptionCartHandler */
private $cloneOriginalSubscriptionCartHandler;
/** @var SubscriptionOrderAmountProvider */
private $subscriptionOrderAmountProvider;
/** @var MailService */
private $mailService;

public function __construct(
ConfigurationAdapter $configuration,
RecurringOrderRepositoryInterface $recurringOrderRepository,
Context $context,
UpdateSubscriptionAction $updateSubscriptionAction,
UpdateRecurringOrderAction $updateRecurringOrderAction,
PrestaLoggerInterface $logger,
CloneOriginalSubscriptionCartHandler $cloneOriginalSubscriptionCartHandler,
SubscriptionOrderAmountProvider $subscriptionOrderAmountProvider,
MailService $mailService
) {
$this->configuration = $configuration;
$this->recurringOrderRepository = $recurringOrderRepository;
$this->context = $context;
$this->updateSubscriptionAction = $updateSubscriptionAction;
$this->updateRecurringOrderAction = $updateRecurringOrderAction;
$this->logger = $logger;
$this->cloneOriginalSubscriptionCartHandler = $cloneOriginalSubscriptionCartHandler;
$this->subscriptionOrderAmountProvider = $subscriptionOrderAmountProvider;
$this->mailService = $mailService;
}

// TODO feature test this with mocked API request data
public function run(int $newCarrierId): array
{
$activeSubscriptionCarrierId = (int) $this->configuration->get(Config::MOLLIE_SUBSCRIPTION_ORDER_CARRIER_ID);

// TODO rethink this. If process failed in any way, maybe merchant would like to repeat it again. We need to track individual orders if they were updated.
if ($newCarrierId === $activeSubscriptionCarrierId) {
$this->logger->debug('Same subscription carrier is saved');

return [];
}

$this->configuration->updateValue(
Config::MOLLIE_SUBSCRIPTION_ORDER_CARRIER_ID,
$newCarrierId
);

/** @var array<array{
* id: int,
* mollie_customer_id: string,
* mollie_subscription_id: string,
* id_cart: int,
* id_recurring_product: int,
* id_invoice_address: int,
* id_delivery_address: int
* }> $recurringOrders
*/
$recurringOrders = $this->recurringOrderRepository->getAllOrdersBasedOnStatuses(
[
SubscriptionStatus::STATUS_PENDING,
SubscriptionStatus::STATUS_ACTIVE,
SubscriptionStatus::STATUS_SUSPENDED,
],
$this->context->getShopId()
);

$failedSubscriptionOrderIdsToUpdate = [];

foreach ($recurringOrders as $recurringOrder) {
try {
$duplicatedCart = $this->cloneOriginalSubscriptionCartHandler->run(
CloneOriginalSubscriptionCartData::create(
(int) $recurringOrder['id_cart'],
(int) $recurringOrder['id_recurring_product'],
(int) $recurringOrder['id_invoice_address'],
(int) $recurringOrder['id_delivery_address']
)
);
} catch (\Throwable $exception) {
$failedSubscriptionOrderIdsToUpdate[] = (string) $recurringOrder['mollie_subscription_id'];

$this->logger->error('Failed to clone subscription cart.', [
'Exception message' => $exception->getMessage(),
'Exception code' => $exception->getCode(),
]);

continue;
}

$subscriptionProduct = $duplicatedCart->getProducts()[0];

try {
$orderAmount = $this->subscriptionOrderAmountProvider->get(
SubscriptionOrderAmountProviderData::create(
(int) $duplicatedCart->id_address_delivery,
(int) $duplicatedCart->id,
(int) $duplicatedCart->id_customer,
$subscriptionProduct,
$newCarrierId,
(int) $duplicatedCart->id_currency,
(float) $subscriptionProduct['total_price_tax_incl']
)
);
} catch (\Throwable $exception) {
$failedSubscriptionOrderIdsToUpdate[] = (string) $recurringOrder['mollie_subscription_id'];

$this->logger->error('Failed to get subscription order amount.', [
'Exception message' => $exception->getMessage(),
'Exception code' => $exception->getCode(),
]);

continue;
}

try {
$this->updateSubscriptionAction->run(UpdateSubscriptionData::create(
(string) $recurringOrder['mollie_customer_id'],
(string) $recurringOrder['mollie_subscription_id'],
$orderAmount,
(int) $duplicatedCart->id_customer,
(int) $duplicatedCart->id,
$newCarrierId
));
} catch (\Throwable $exception) {
$failedSubscriptionOrderIdsToUpdate[] = (string) $recurringOrder['mollie_subscription_id'];

$this->logger->error('Failed to update subscription.', [
'Exception message' => $exception->getMessage(),
'Exception code' => $exception->getCode(),
]);

continue;
}

try {
$this->updateRecurringOrderAction->run(UpdateRecurringOrderData::create(
(int) $recurringOrder['id'],
$orderAmount->getValue()
));
} catch (\Throwable $exception) {
$failedSubscriptionOrderIdsToUpdate[] = (string) $recurringOrder['mollie_subscription_id'];

$this->logger->error('Failed to update recurring order record.', [
'Exception message' => $exception->getMessage(),
'Exception code' => $exception->getCode(),
]);

continue;
}

try {
$this->mailService->sendSubscriptionCarrierUpdateMail((int) $recurringOrder['id']);
} catch (\Throwable $exception) {
$failedSubscriptionOrderIdsToUpdate[] = (string) $recurringOrder['mollie_subscription_id'];

$this->logger->error('Failed to send subscription carrier update mail.', [
'Exception message' => $exception->getMessage(),
'Exception code' => $exception->getCode(),
]);

continue;
}
}

return $failedSubscriptionOrderIdsToUpdate;
}
}
24 changes: 24 additions & 0 deletions subscription/Repository/RecurringOrderRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,28 @@ public function __construct()
{
parent::__construct(\MolRecurringOrder::class);
}

public function getAllOrdersBasedOnStatuses(array $statuses, int $shopId): array
{
$query = new \DbQuery();

$query
->select(
'mro.id_mol_recurring_order as id, mro.mollie_subscription_id,
mro.mollie_customer_id, mro.id_cart,
mro.id_mol_recurring_orders_product as id_recurring_product,
mro.id_address_invoice, mro.id_delivery_address'
)
->from('mol_recurring_order', 'mro')
->leftJoin(
'orders', 'o',
'o.id_order = mro.id_order'
)
->where('mro.status IN (\'' . implode("','", $statuses) . '\')')
->where('o.id_shop = ' . $shopId);

$result = \Db::getInstance()->executeS($query);

return !empty($result) ? $result : [];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,5 @@

interface RecurringOrderRepositoryInterface extends ReadOnlyRepositoryInterface
{
public function getAllOrdersBasedOnStatuses(array $statuses, int $shopId): array;
}
Loading

0 comments on commit 430064b

Please sign in to comment.