From 1b53019f31f0355f1e26b67a16b71b9897529d18 Mon Sep 17 00:00:00 2001 From: Michiel Gerritsen Date: Thu, 25 May 2023 10:44:13 +0200 Subject: [PATCH 01/13] Bugfix: Select the correct subscriptions for the payment reminder #50 --- Cron/SendPrePaymentReminderEmailCron.php | 57 ++------ Model/SubscriptionToProductSearchResults.php | 11 ++ .../RetrieveRecordsForPrePaymentReminder.php | 89 ++++++++++++ .../SendPrePaymentReminderEmailCronTest.php | 136 +++++++++--------- ...trieveRecordsForPrePaymentReminderTest.php | 115 +++++++++++++++ etc/di.xml | 7 +- 6 files changed, 291 insertions(+), 124 deletions(-) create mode 100644 Model/SubscriptionToProductSearchResults.php create mode 100644 Service/Email/RetrieveRecordsForPrePaymentReminder.php create mode 100644 Test/Integration/Service/Email/RetrieveRecordsForPrePaymentReminderTest.php diff --git a/Cron/SendPrePaymentReminderEmailCron.php b/Cron/SendPrePaymentReminderEmailCron.php index f401b99..48e3397 100644 --- a/Cron/SendPrePaymentReminderEmailCron.php +++ b/Cron/SendPrePaymentReminderEmailCron.php @@ -6,12 +6,10 @@ namespace Mollie\Subscriptions\Cron; -use Magento\Framework\Api\FilterBuilder; -use Magento\Framework\Api\Search\FilterGroupBuilder; -use Magento\Framework\Api\SearchCriteriaBuilderFactory; use Mollie\Payment\Config as MollieConfig; use Mollie\Subscriptions\Api\SubscriptionToProductRepositoryInterface; use Mollie\Subscriptions\Config; +use Mollie\Subscriptions\Service\Email\RetrieveRecordsForPrePaymentReminder; use Mollie\Subscriptions\Service\Email\SendPrepaymentReminderEmail; use Mollie\Subscriptions\Service\Mollie\CheckIfSubscriptionIsActive; @@ -27,11 +25,6 @@ class SendPrePaymentReminderEmailCron */ private $config; - /** - * @var SearchCriteriaBuilderFactory - */ - private $searchCriteriaBuilderFactory; - /** * @var SubscriptionToProductRepositoryInterface */ @@ -48,64 +41,30 @@ class SendPrePaymentReminderEmailCron private $checkIfSubscriptionIsActive; /** - * @var FilterBuilder - */ - private $filterBuilder; - - /** - * @var FilterGroupBuilder + * @var RetrieveRecordsForPrePaymentReminder */ - private $filterGroupBuilder; + private $retrieveRecordsForPrePaymentReminder; public function __construct( MollieConfig $mollieConfig, Config $config, SubscriptionToProductRepositoryInterface $subscriptionToProductRepository, - SearchCriteriaBuilderFactory $searchCriteriaBuilderFactory, SendPrepaymentReminderEmail $sendPrepaymentReminderEmail, CheckIfSubscriptionIsActive $checkIfSubscriptionIsActive, - FilterBuilder $filterBuilder, - FilterGroupBuilder $filterGroupBuilder + RetrieveRecordsForPrePaymentReminder $retrieveRecordsForPrePaymentReminder ) { $this->mollieConfig = $mollieConfig; $this->config = $config; - $this->searchCriteriaBuilderFactory = $searchCriteriaBuilderFactory; $this->subscriptionToProductRepository = $subscriptionToProductRepository; $this->sendPrepaymentReminderEmail = $sendPrepaymentReminderEmail; $this->checkIfSubscriptionIsActive = $checkIfSubscriptionIsActive; - $this->filterBuilder = $filterBuilder; - $this->filterGroupBuilder = $filterGroupBuilder; + $this->retrieveRecordsForPrePaymentReminder = $retrieveRecordsForPrePaymentReminder; } public function execute() { - $today = (new \DateTime())->format('Y-m-d'); - - $interval = new \DateInterval('P' . $this->config->daysBeforePrepaymentReminder() . 'D'); - $prepaymentDate = (new \DateTimeImmutable())->sub($interval); - - $criteria = $this->searchCriteriaBuilderFactory->create(); - $criteria->addFilter('next_payment_date', $prepaymentDate->format('Y-m-d'), 'eq'); - - $lastReminderDateNull = $this->filterBuilder - ->setField('last_reminder_date') - ->setConditionType('null') - ->create(); - - $lastReminderDateNotToday = $this->filterBuilder - ->setField('last_reminder_date') - ->setConditionType('neq') - ->setValue($today) - ->create(); - - $criteria->setFilterGroups([ - $this->filterGroupBuilder - ->addFilter($lastReminderDateNull) - ->addFilter($lastReminderDateNotToday) - ->create() - ]); - - $subscriptions = $this->subscriptionToProductRepository->getList($criteria->create()); + $today = new \DateTimeImmutable(); + $subscriptions = $this->retrieveRecordsForPrePaymentReminder->execute($today); foreach ($subscriptions->getItems() as $subscription) { if (!$this->config->isPrepaymentReminderEnabled($subscription->getStoreId())) { continue; @@ -125,7 +84,7 @@ public function execute() $this->sendPrepaymentReminderEmail->execute($subscription); - $subscription->setLastReminderDate($today); + $subscription->setLastReminderDate($today->format('Y-m-d')); $this->subscriptionToProductRepository->save($subscription); } } diff --git a/Model/SubscriptionToProductSearchResults.php b/Model/SubscriptionToProductSearchResults.php new file mode 100644 index 0000000..4791800 --- /dev/null +++ b/Model/SubscriptionToProductSearchResults.php @@ -0,0 +1,11 @@ +config = $config; + $this->subscriptionToProductRepository = $subscriptionToProductRepository; + $this->searchCriteriaBuilderFactory = $searchCriteriaBuilderFactory; + $this->filterBuilder = $filterBuilder; + $this->filterGroupBuilder = $filterGroupBuilder; + } + + public function execute(\DateTimeImmutable $today): SubscriptionToProductSearchResultsInterface + { + $interval = new \DateInterval('P' . $this->config->daysBeforePrepaymentReminder() . 'D'); + $prepaymentDate = $today->add($interval); + + $criteria = $this->searchCriteriaBuilderFactory->create(); + + $nextPaymentDate = $this->filterBuilder + ->setField('next_payment_date') + ->setConditionType('eq') + ->setValue($prepaymentDate->format('Y-m-d')) + ->create(); + + $lastReminderDateNull = $this->filterBuilder + ->setField('last_reminder_date') + ->setConditionType('null') + ->create(); + + $lastReminderDateNotToday = $this->filterBuilder + ->setField('last_reminder_date') + ->setConditionType('neq') + ->setValue($today->format('Y-m-d')) + ->create(); + + $criteria->setFilterGroups([ + $this->filterGroupBuilder + ->addFilter($nextPaymentDate) + ->create(), + $this->filterGroupBuilder + ->addFilter($lastReminderDateNull) + ->addFilter($lastReminderDateNotToday) + ->create() + ]); + + return $this->subscriptionToProductRepository->getList($criteria->create()); + } +} diff --git a/Test/Integration/Cron/SendPrePaymentReminderEmailCronTest.php b/Test/Integration/Cron/SendPrePaymentReminderEmailCronTest.php index 95023e8..e5504c8 100644 --- a/Test/Integration/Cron/SendPrePaymentReminderEmailCronTest.php +++ b/Test/Integration/Cron/SendPrePaymentReminderEmailCronTest.php @@ -8,11 +8,29 @@ use Mollie\Subscriptions\Api\SubscriptionToProductRepositoryInterface; use Mollie\Subscriptions\Cron\SendPrePaymentReminderEmailCron; use Mollie\Subscriptions\Model\Adminhtml\Backend\SaveCronValue; +use Mollie\Subscriptions\Model\SubscriptionToProduct; use Mollie\Subscriptions\Service\Email\SendPrepaymentReminderEmail; use Mollie\Subscriptions\Service\Mollie\CheckIfSubscriptionIsActive; class SendPrePaymentReminderEmailCronTest extends IntegrationTestCase { + /** + * @var SubscriptionToProductRepositoryInterface + */ + private $repository; + + public function setUpWithoutVoid() + { + $this->repository = $this->objectManager->create(SubscriptionToProductRepositoryInterface::class); + } + + public function tearDown(): void + { + /** @var SubscriptionToProduct $model */ + $model = $this->objectManager->create(SubscriptionToProduct::class); + $model->getCollection()->getConnection()->truncateTable($model->getResource()->getMainTable()); + } + public function testHasADefaultScheduleAvailable(): void { /** @var ScopeConfigInterface $config */ @@ -24,11 +42,12 @@ public function testHasADefaultScheduleAvailable(): void } /** - * @magentoDbIsolation enabled - * @magentoConfigFixture default_store mollie_subscriptions/prepayment_reminder/enabled 1 + * @magentoConfigFixture default_store mollie_subscriptions/prepayment_reminder/enabled 0 */ - public function testSendsPrePaymentEmailWhenLastReminderDateIsNull(): void + public function testDoesNothingWhenNotEnabled(): void { + $this->createDefaultModels(); + $spy = $this->any(); $sendPrepaymentReminderEmailMock = $this->createMock(SendPrepaymentReminderEmail::class); $sendPrepaymentReminderEmailMock->expects($spy)->method('execute'); @@ -36,17 +55,6 @@ public function testSendsPrePaymentEmailWhenLastReminderDateIsNull(): void $checkIfSubscriptionIsActiveMock = $this->createMock(CheckIfSubscriptionIsActive::class); $checkIfSubscriptionIsActiveMock->method('execute')->willReturn(true); - /** @var SubscriptionToProductInterface $model */ - $model = $this->objectManager->create(SubscriptionToProductInterface::class); - $model->setCustomerId('cst_fakenumber'); - $model->setSubscriptionId('sub_fakesubscription'); - $model->setProductId(1); - $model->setStoreId(1); - - $model->setLastReminderDate(null); - - $this->objectManager->get(SubscriptionToProductRepositoryInterface::class)->save($model); - /** @var SendPrePaymentReminderEmailCron $instance */ $instance = $this->objectManager->create(SendPrePaymentReminderEmailCron::class, [ 'sendPrepaymentReminderEmail' => $sendPrepaymentReminderEmailMock, @@ -55,34 +63,22 @@ public function testSendsPrePaymentEmailWhenLastReminderDateIsNull(): void $instance->execute(); - $this->assertEquals(1, $spy->getInvocationCount()); + $this->assertEquals(0, $spy->getInvocationCount()); } /** - * @magentoDbIsolation enabled * @magentoConfigFixture default_store mollie_subscriptions/prepayment_reminder/enabled 1 */ - public function testSendsPrePaymentEmailWhenLastReminderDateIsInThePast(): void + public function testDoesNothingWhenTheSubscriptionIsNotActive(): void { - $yesterday = (new \DateTime())->modify('-1 day')->format('Y-m-d'); + $this->createDefaultModels(); $spy = $this->any(); $sendPrepaymentReminderEmailMock = $this->createMock(SendPrepaymentReminderEmail::class); $sendPrepaymentReminderEmailMock->expects($spy)->method('execute'); $checkIfSubscriptionIsActiveMock = $this->createMock(CheckIfSubscriptionIsActive::class); - $checkIfSubscriptionIsActiveMock->method('execute')->willReturn(true); - - /** @var SubscriptionToProductInterface $model */ - $model = $this->objectManager->create(SubscriptionToProductInterface::class); - $model->setCustomerId('cst_fakenumber'); - $model->setSubscriptionId('sub_fakesubscription'); - $model->setProductId(1); - $model->setStoreId(1); - - $model->setLastReminderDate($yesterday); - - $this->objectManager->get(SubscriptionToProductRepositoryInterface::class)->save($model); + $checkIfSubscriptionIsActiveMock->method('execute')->willReturn(false); /** @var SendPrePaymentReminderEmailCron $instance */ $instance = $this->objectManager->create(SendPrePaymentReminderEmailCron::class, [ @@ -92,16 +88,15 @@ public function testSendsPrePaymentEmailWhenLastReminderDateIsInThePast(): void $instance->execute(); - $this->assertEquals(1, $spy->getInvocationCount()); + $this->assertEquals(0, $spy->getInvocationCount()); } /** - * @magentoDbIsolation enabled * @magentoConfigFixture default_store mollie_subscriptions/prepayment_reminder/enabled 1 */ - public function testDoesNotSendsPrePaymentEmailWhenLastReminderDateIsToday(): void + public function testSendsTheEmailWhenRequired(): void { - $today = (new \DateTime())->format('Y-m-d'); + $models = $this->createDefaultModels(); $spy = $this->any(); $sendPrepaymentReminderEmailMock = $this->createMock(SendPrepaymentReminderEmail::class); @@ -110,17 +105,6 @@ public function testDoesNotSendsPrePaymentEmailWhenLastReminderDateIsToday(): vo $checkIfSubscriptionIsActiveMock = $this->createMock(CheckIfSubscriptionIsActive::class); $checkIfSubscriptionIsActiveMock->method('execute')->willReturn(true); - /** @var SubscriptionToProductInterface $model */ - $model = $this->objectManager->create(SubscriptionToProductInterface::class); - $model->setCustomerId('cst_fakenumber'); - $model->setSubscriptionId('sub_fakesubscription'); - $model->setProductId(1); - $model->setStoreId(1); - - $model->setLastReminderDate($today); - - $this->objectManager->get(SubscriptionToProductRepositoryInterface::class)->save($model); - /** @var SendPrePaymentReminderEmailCron $instance */ $instance = $this->objectManager->create(SendPrePaymentReminderEmailCron::class, [ 'sendPrepaymentReminderEmail' => $sendPrepaymentReminderEmailMock, @@ -129,41 +113,55 @@ public function testDoesNotSendsPrePaymentEmailWhenLastReminderDateIsToday(): vo $instance->execute(); - $this->assertEquals(0, $spy->getInvocationCount()); + $this->assertEquals(2, $spy->getInvocationCount()); + + // Assert that the last_reminder_date is updated. + $reloaded = $this->repository->get($models['future']->getEntityId()); + $this->assertEquals((new \DateTimeImmutable())->format('Y-m-d'), $reloaded->getLastReminderDate()); + + $reloaded = $this->repository->get($models['future_with_last_reminder_date']->getEntityId()); + $this->assertEquals((new \DateTimeImmutable())->format('Y-m-d'), $reloaded->getLastReminderDate()); } /** - * @magentoDbIsolation enabled - * @magentoConfigFixture default_store mollie_subscriptions/prepayment_reminder/enabled 1 + * @return SubscriptionToProductInterface[] + * @throws \Magento\Framework\Exception\LocalizedException */ - public function testIfTheSubscriptionIsNotActiveTheEmailWillNotBeSent(): void + private function createDefaultModels(): array { - $spy = $this->any(); - $sendPrepaymentReminderEmailMock = $this->createMock(SendPrepaymentReminderEmail::class); - $sendPrepaymentReminderEmailMock->expects($spy)->method('execute'); - - $checkIfSubscriptionIsActiveMock = $this->createMock(CheckIfSubscriptionIsActive::class); - $checkIfSubscriptionIsActiveMock->method('execute')->willReturn(false); + $today = new \DateTimeImmutable(); + $future = new \DateTimeImmutable('+3 day'); + + return [ + 'today' => $this->repository->save($this->createModel($today)), + 'today_with_last_reminder_date' => $this->repository->save($this->createModelWithLastReminderDate($today)), + 'future' => $this->repository->save($this->createModel($future)), + 'future_with_last_reminder_date' => $this->repository->save($this->createModelWithLastReminderDate($future)), + ]; + } + private function createModel(\DateTimeImmutable $date, \DateTimeImmutable $lastReminderDate = null): SubscriptionToProductInterface + { /** @var SubscriptionToProductInterface $model */ $model = $this->objectManager->create(SubscriptionToProductInterface::class); - $model->setCustomerId('cst_fakenumber'); - $model->setSubscriptionId('sub_fakesubscription'); - $model->setProductId(1); - $model->setStoreId(1); - - $model->setLastReminderDate(null); + $model->setNextPaymentDate($date->format('Y-m-d')); - $this->objectManager->get(SubscriptionToProductRepositoryInterface::class)->save($model); + $model->setCustomerId(1); + $model->setSubscriptionId(1); + $model->setProductId(1); - /** @var SendPrePaymentReminderEmailCron $instance */ - $instance = $this->objectManager->create(SendPrePaymentReminderEmailCron::class, [ - 'sendPrepaymentReminderEmail' => $sendPrepaymentReminderEmailMock, - 'checkIfSubscriptionIsActive' => $checkIfSubscriptionIsActiveMock, - ]); + if ($lastReminderDate) { + $model->setLastReminderDate($lastReminderDate->format('Y-m-d')); + } - $instance->execute(); + return $model; + } - $this->assertEquals(0, $spy->getInvocationCount()); + private function createModelWithLastReminderDate(\DateTimeImmutable $date): SubscriptionToProductInterface + { + return $this->createModel( + $date, + $date->sub(new \DateInterval('P7D')) + ); } } diff --git a/Test/Integration/Service/Email/RetrieveRecordsForPrePaymentReminderTest.php b/Test/Integration/Service/Email/RetrieveRecordsForPrePaymentReminderTest.php new file mode 100644 index 0000000..f836436 --- /dev/null +++ b/Test/Integration/Service/Email/RetrieveRecordsForPrePaymentReminderTest.php @@ -0,0 +1,115 @@ +repository = $this->objectManager->create(SubscriptionToProductRepositoryInterface::class); + } + + public function createDefaultModels(): void + { + $today = new \DateTimeImmutable(); + $future = new \DateTimeImmutable('+3 day'); + + $this->repository->save($this->createModel($today)); + $this->repository->save($this->createModelWithLastReminderDate($today)); + $this->repository->save($this->createModel($future)); + $this->repository->save($this->createModelWithLastReminderDate($future)); + } + + public function tearDown(): void + { + /** @var SubscriptionToProduct $model */ + $model = $this->objectManager->create(SubscriptionToProduct::class); + $model->getCollection()->getConnection()->truncateTable($model->getResource()->getMainTable()); + } + + /** + * @see \Mollie\Subscriptions\Service\Email\RetrieveRecordsForPrePaymentReminder::execute() + * @return void + */ + public function testRetrievesRecords(): void + { + $this->createDefaultModels(); + + $today = new \DateTimeImmutable(); + $future = new \DateTimeImmutable('+3 day'); + + /** @var RetrieveRecordsForPrePaymentReminder $instance */ + $instance = $this->objectManager->create(RetrieveRecordsForPrePaymentReminder::class); + $subscriptions = $instance->execute($today); + + $this->assertEquals($future->format('Y-m-d'), $subscriptions->getItems()[0]->getNextPaymentDate()); + $this->assertEquals(2, $subscriptions->getTotalCount()); + } + + /** + * @see \Mollie\Subscriptions\Service\Email\RetrieveRecordsForPrePaymentReminder::execute() + * @return void + */ + public function testRetrievesNothingWhenThereShouldBeNothing(): void + { + $this->createDefaultModels(); + + $today = new \DateTimeImmutable('+1 day'); + + /** @var RetrieveRecordsForPrePaymentReminder $instance */ + $instance = $this->objectManager->create(RetrieveRecordsForPrePaymentReminder::class); + $subscriptions = $instance->execute($today); + + $this->assertEquals(0, $subscriptions->getTotalCount()); + } + + public function testReceivesNothingWhenLastPaymentDateIsToday(): void + { + $today = new \DateTimeImmutable(); + $future = new \DateTimeImmutable('+3 day'); + + $this->repository->save($this->createModel($future, $today)); + + /** @var RetrieveRecordsForPrePaymentReminder $instance */ + $instance = $this->objectManager->create(RetrieveRecordsForPrePaymentReminder::class); + $subscriptions = $instance->execute($today); + + $this->assertEquals(0, $subscriptions->getTotalCount()); + } + + private function createModel(\DateTimeImmutable $date, \DateTimeImmutable $lastReminderDate = null): SubscriptionToProductInterface + { + /** @var SubscriptionToProductInterface $model */ + $model = $this->objectManager->create(SubscriptionToProductInterface::class); + $model->setNextPaymentDate($date->format('Y-m-d')); + + $model->setCustomerId(1); + $model->setSubscriptionId(1); + $model->setProductId(1); + + if ($lastReminderDate) { + $model->setLastReminderDate($lastReminderDate->format('Y-m-d')); + } + + return $model; + } + + private function createModelWithLastReminderDate(\DateTimeImmutable $date): SubscriptionToProductInterface + { + return $this->createModel( + $date, + $date->sub(new \DateInterval('P7D')) + ); + } +} diff --git a/etc/di.xml b/etc/di.xml index f540898..9a0d72c 100755 --- a/etc/di.xml +++ b/etc/di.xml @@ -12,13 +12,8 @@ - + - - - Magento\Framework\Filesystem\Driver\File - - MollieSubscriptionsError From 6679682b1d51c9d29e58081aaa45b15b9f4ba6bd Mon Sep 17 00:00:00 2001 From: Michiel Gerritsen Date: Thu, 1 Jun 2023 09:59:37 +0200 Subject: [PATCH 02/13] Feature: Add Subscription to version string --- .../Customer/Account/ActiveSubscriptions.php | 14 +++++------ Controller/Adminhtml/Subscriptions/Cancel.php | 12 +++++----- Controller/Api/Webhook.php | 11 ++++++++- Controller/Index/Cancel.php | 12 +++++----- Controller/Index/Restart.php | 12 +++++----- Cron/UpdateSubscriptionsWithAPriceUpdate.php | 16 ++++++------- Model/MollieSubscriptionsListing.php | 12 +++++----- .../CreateSubscriptions.php | 12 +++++----- .../SubscriptionToProductEmailVariables.php | 12 +++++----- Service/Mollie/MollieSubscriptionApi.php | 24 +++++++++++++++++++ ...ubscriptionToProductEmailVariablesTest.php | 7 +++--- 11 files changed, 89 insertions(+), 55 deletions(-) create mode 100644 Service/Mollie/MollieSubscriptionApi.php diff --git a/Block/Frontend/Customer/Account/ActiveSubscriptions.php b/Block/Frontend/Customer/Account/ActiveSubscriptions.php index af75431..1d990e9 100644 --- a/Block/Frontend/Customer/Account/ActiveSubscriptions.php +++ b/Block/Frontend/Customer/Account/ActiveSubscriptions.php @@ -9,8 +9,8 @@ use Magento\Customer\Helper\Session\CurrentCustomer; use Magento\Framework\Pricing\PriceCurrencyInterface; use Magento\Framework\View\Element\Template; -use Mollie\Payment\Model\Mollie; use Mollie\Subscriptions\DTO\SubscriptionResponse; +use Mollie\Subscriptions\Service\Mollie\MollieSubscriptionApi; class ActiveSubscriptions extends Template { @@ -20,9 +20,9 @@ class ActiveSubscriptions extends Template private $currentCustomer; /** - * @var Mollie + * @var MollieSubscriptionApi */ - private $mollie; + private $mollieSubscriptionApi; /** * @var PriceCurrencyInterface @@ -37,21 +37,21 @@ class ActiveSubscriptions extends Template public function __construct( Template\Context $context, CurrentCustomer $currentCustomer, - Mollie $mollie, + MollieSubscriptionApi $mollieSubscriptionApi, PriceCurrencyInterface $priceCurrency, array $data = [] ) { parent::__construct($context, $data); $this->currentCustomer = $currentCustomer; - $this->mollie = $mollie; + $this->mollieSubscriptionApi = $mollieSubscriptionApi; $this->priceCurrency = $priceCurrency; } /** * @return SubscriptionResponse[] */ - public function getSubscriptions() + public function getSubscriptions(): array { if ($this->subscriptions) { return $this->subscriptions; @@ -63,7 +63,7 @@ public function getSubscriptions() return []; } - $api = $this->mollie->getMollieApi(); + $api = $this->mollieSubscriptionApi->loadByStore($customer->getStoreId()); $subscriptions = $api->subscriptions->listForId($extensionAttributes->getMollieCustomerId()); $this->subscriptions = array_map(function ($subscription) use ($customer) { diff --git a/Controller/Adminhtml/Subscriptions/Cancel.php b/Controller/Adminhtml/Subscriptions/Cancel.php index d5058ac..c60380a 100644 --- a/Controller/Adminhtml/Subscriptions/Cancel.php +++ b/Controller/Adminhtml/Subscriptions/Cancel.php @@ -9,8 +9,8 @@ use Magento\Backend\App\Action; use Magento\Framework\Event\ManagerInterface; use Mollie\Payment\Config; -use Mollie\Payment\Model\Mollie; use Mollie\Subscriptions\Api\SubscriptionToProductRepositoryInterface; +use Mollie\Subscriptions\Service\Mollie\MollieSubscriptionApi; class Cancel extends Action { @@ -20,9 +20,9 @@ class Cancel extends Action private $config; /** - * @var Mollie + * @var MollieSubscriptionApi */ - private $mollie; + private $mollieSubscriptionApi; /** * @var SubscriptionToProductRepositoryInterface @@ -37,20 +37,20 @@ class Cancel extends Action public function __construct( Action\Context $context, Config $config, - Mollie $mollie, + MollieSubscriptionApi $mollieSubscriptionApi, SubscriptionToProductRepositoryInterface $subscriptionToProductRepository, ManagerInterface $eventManager ) { parent::__construct($context); $this->config = $config; - $this->mollie = $mollie; + $this->mollieSubscriptionApi = $mollieSubscriptionApi; $this->subscriptionToProductRepository = $subscriptionToProductRepository; $this->eventManager = $eventManager; } public function execute() { - $api = $this->mollie->getMollieApi($this->getRequest()->getParam('store_id')); + $api = $this->mollieSubscriptionApi->loadByStore($this->getRequest()->getParam('store_id')); $customerId = $this->getRequest()->getParam('customer_id'); $subscriptionId = $this->getRequest()->getParam('subscription_id'); diff --git a/Controller/Api/Webhook.php b/Controller/Api/Webhook.php index aa8ba58..453ddd3 100644 --- a/Controller/Api/Webhook.php +++ b/Controller/Api/Webhook.php @@ -33,6 +33,7 @@ use Mollie\Payment\Service\Mollie\ValidateMetadata; use Mollie\Payment\Service\Order\SendOrderEmails; use Mollie\Subscriptions\Config; +use Mollie\Subscriptions\Service\Mollie\MollieSubscriptionApi; use Mollie\Subscriptions\Service\Mollie\RetryUsingOtherStoreViews; class Webhook extends Action implements CsrfAwareActionInterface @@ -47,6 +48,11 @@ class Webhook extends Action implements CsrfAwareActionInterface */ private $mollie; + /** + * @var MollieSubscriptionApi + */ + private $mollieSubscriptionApi; + /** * @var MollieCustomerRepositoryInterface */ @@ -106,6 +112,7 @@ class Webhook extends Action implements CsrfAwareActionInterface * @var MollieApiClient */ private $api; + /** * @var ValidateMetadata */ @@ -115,6 +122,7 @@ public function __construct( Context $context, Config $config, Mollie $mollie, + MollieSubscriptionApi $mollieSubscriptionApi, MollieCustomerRepositoryInterface $mollieCustomerRepository, CartManagementInterface $cartManagement, CartRepositoryInterface $cartRepository, @@ -132,6 +140,7 @@ public function __construct( $this->config = $config; $this->mollie = $mollie; + $this->mollieSubscriptionApi = $mollieSubscriptionApi; $this->mollieCustomerRepository = $mollieCustomerRepository; $this->cartManagement = $cartManagement; $this->cartRepository = $cartRepository; @@ -267,7 +276,7 @@ private function returnOkResponse() public function getPayment(string $id): Payment { try { - $this->api = $this->mollie->getMollieApi(); + $this->api = $this->mollieSubscriptionApi->loadByStore(); return $this->api->payments->get($id); } catch (ApiException $exception) { diff --git a/Controller/Index/Cancel.php b/Controller/Index/Cancel.php index efc0e17..2e72541 100644 --- a/Controller/Index/Cancel.php +++ b/Controller/Index/Cancel.php @@ -14,9 +14,9 @@ use Magento\Framework\App\RequestInterface; use Magento\Framework\Event\ManagerInterface; use Mollie\Payment\Config; -use Mollie\Payment\Model\Mollie; use Mollie\Subscriptions\Api\SubscriptionToProductRepositoryInterface; use Mollie\Subscriptions\Service\Email\SendNotificationEmail; +use Mollie\Subscriptions\Service\Mollie\MollieSubscriptionApi; class Cancel extends Action implements HttpPostActionInterface { @@ -26,9 +26,9 @@ class Cancel extends Action implements HttpPostActionInterface private $config; /** - * @var Mollie + * @var MollieSubscriptionApi */ - private $mollie; + private $mollieSubscriptionApi; /** * @var SubscriptionToProductRepositoryInterface @@ -63,7 +63,7 @@ class Cancel extends Action implements HttpPostActionInterface public function __construct( Context $context, Config $config, - Mollie $mollie, + MollieSubscriptionApi $mollieSubscriptionApi, SubscriptionToProductRepositoryInterface $subscriptionToProductRepository, CurrentCustomer $currentCustomer, Session $customerSession, @@ -73,7 +73,7 @@ public function __construct( ) { parent::__construct($context); $this->config = $config; - $this->mollie = $mollie; + $this->mollieSubscriptionApi = $mollieSubscriptionApi; $this->subscriptionToProductRepository = $subscriptionToProductRepository; $this->currentCustomer = $currentCustomer; $this->customerSession = $customerSession; @@ -96,7 +96,7 @@ public function execute() $customer = $this->currentCustomer->getCustomer(); $extensionAttributes = $customer->getExtensionAttributes(); - $api = $this->mollie->getMollieApi(); + $api = $this->mollieSubscriptionApi->loadByStore($customer->getStoreId()); $subscriptionId = $this->getRequest()->getParam('subscription_id'); try { diff --git a/Controller/Index/Restart.php b/Controller/Index/Restart.php index 5bdb105..86d3574 100644 --- a/Controller/Index/Restart.php +++ b/Controller/Index/Restart.php @@ -17,11 +17,11 @@ use Magento\Store\Model\StoreManagerInterface; use Mollie\Api\Resources\Subscription; use Mollie\Payment\Config; -use Mollie\Payment\Model\Mollie; use Mollie\Subscriptions\Api\Data\SubscriptionToProductInterface; use Mollie\Subscriptions\Api\Data\SubscriptionToProductInterfaceFactory; use Mollie\Subscriptions\Api\SubscriptionToProductRepositoryInterface; use Mollie\Subscriptions\Service\Email\SendNotificationEmail; +use Mollie\Subscriptions\Service\Mollie\MollieSubscriptionApi; class Restart extends Action implements HttpPostActionInterface { @@ -31,9 +31,9 @@ class Restart extends Action implements HttpPostActionInterface private $config; /** - * @var Mollie + * @var MollieSubscriptionApi */ - private $mollie; + private $mollieSubscriptionApi; /** * @var CurrentCustomer @@ -83,7 +83,7 @@ class Restart extends Action implements HttpPostActionInterface public function __construct( Context $context, Config $config, - Mollie $mollie, + MollieSubscriptionApi $mollieSubscriptionApi, CurrentCustomer $currentCustomer, Session $customerSession, SubscriptionToProductInterfaceFactory $subscriptionToProductFactory, @@ -96,7 +96,7 @@ public function __construct( ) { parent::__construct($context); $this->config = $config; - $this->mollie = $mollie; + $this->mollieSubscriptionApi = $mollieSubscriptionApi; $this->currentCustomer = $currentCustomer; $this->customerSession = $customerSession; $this->subscriptionToProductFactory = $subscriptionToProductFactory; @@ -122,7 +122,7 @@ public function execute() $customer = $this->currentCustomer->getCustomer(); $extensionAttributes = $customer->getExtensionAttributes(); - $api = $this->mollie->getMollieApi(); + $api = $this->mollieSubscriptionApi->loadByStore($customer->getStoreId()); $subscriptionId = $this->getRequest()->getParam('subscription_id'); $canceledSubscription = $api->subscriptions->getForId( diff --git a/Cron/UpdateSubscriptionsWithAPriceUpdate.php b/Cron/UpdateSubscriptionsWithAPriceUpdate.php index ab1a280..babb92e 100644 --- a/Cron/UpdateSubscriptionsWithAPriceUpdate.php +++ b/Cron/UpdateSubscriptionsWithAPriceUpdate.php @@ -11,9 +11,9 @@ use Magento\Framework\Pricing\PriceCurrencyInterface; use Mollie\Payment\Config; use Mollie\Payment\Helper\General; -use Mollie\Payment\Model\Mollie; use Mollie\Subscriptions\Api\Data\SubscriptionToProductInterface; use Mollie\Subscriptions\Api\SubscriptionToProductRepositoryInterface; +use Mollie\Subscriptions\Service\Mollie\MollieSubscriptionApi; class UpdateSubscriptionsWithAPriceUpdate { @@ -23,9 +23,9 @@ class UpdateSubscriptionsWithAPriceUpdate private $config; /** - * @var Mollie + * @var MollieSubscriptionApi */ - private $mollie; + private $mollieSubscriptionApi; /** * @var General @@ -51,17 +51,17 @@ class UpdateSubscriptionsWithAPriceUpdate public function __construct( Config $config, - Mollie $mollie, + MollieSubscriptionApi $mollieSubscriptionApi, General $mollieHelper, SubscriptionToProductRepositoryInterface $subscriptionToProductRepository, ProductRepositoryInterface $productRepository, PriceCurrencyInterface $priceCurrency ) { - $this->subscriptionToProductRepository = $subscriptionToProductRepository; + $this->config = $config; + $this->mollieSubscriptionApi = $mollieSubscriptionApi; $this->mollieHelper = $mollieHelper; + $this->subscriptionToProductRepository = $subscriptionToProductRepository; $this->productRepository = $productRepository; - $this->mollie = $mollie; - $this->config = $config; $this->priceCurrency = $priceCurrency; } @@ -88,7 +88,7 @@ private function getApiForStore(int $storeId) return $this->apis[$storeId]; } - $api = $this->mollie->getMollieApi($storeId); + $api = $this->mollieSubscriptionApi->loadByStore($storeId); $this->apis[$storeId] = $api; return $api; diff --git a/Model/MollieSubscriptionsListing.php b/Model/MollieSubscriptionsListing.php index 8f87d5f..83af831 100644 --- a/Model/MollieSubscriptionsListing.php +++ b/Model/MollieSubscriptionsListing.php @@ -16,9 +16,9 @@ use Mollie\Api\Resources\SubscriptionCollection; use Mollie\Payment\Api\Data\MollieCustomerInterface; use Mollie\Payment\Api\MollieCustomerRepositoryInterface; -use Mollie\Payment\Model\Mollie; use Mollie\Subscriptions\Config; use Mollie\Subscriptions\DTO\SubscriptionResponse; +use Mollie\Subscriptions\Service\Mollie\MollieSubscriptionApi; class MollieSubscriptionsListing extends Listing { @@ -28,9 +28,9 @@ class MollieSubscriptionsListing extends Listing private $config; /** - * @var Mollie + * @var MollieSubscriptionApi */ - private $mollieModel; + private $mollieSubscriptionApi; /** * @var SearchCriteriaBuilderFactory @@ -70,7 +70,7 @@ class MollieSubscriptionsListing extends Listing public function __construct( ContextInterface $context, Config $config, - Mollie $mollieModel, + MollieSubscriptionApi $mollieSubscriptionApi, SearchCriteriaBuilderFactory $searchCriteriaBuilderFactory, CustomerInterfaceFactory $customerFactory, CustomerRepositoryInterface $customerRepository, @@ -79,7 +79,7 @@ public function __construct( array $data = [] ) { parent::__construct($context, $components, $data); - $this->mollieModel = $mollieModel; + $this->mollieSubscriptionApi = $mollieSubscriptionApi; $this->searchCriteriaBuilderFactory = $searchCriteriaBuilderFactory; $this->customerFactory = $customerFactory; $this->customerRepository = $customerRepository; @@ -90,7 +90,7 @@ public function __construct( public function getDataSourceData() { $storeId = $this->getContext()->getRequestParam('filters')['store_id'] ?? null; - $api = $this->mollieModel->getMollieApi($storeId); + $api = $this->mollieSubscriptionApi->loadByStore($storeId); $paging = $this->getContext()->getRequestParam('paging'); $result = $api->subscriptions->page( diff --git a/Observer/MollieProcessTransactionEnd/CreateSubscriptions.php b/Observer/MollieProcessTransactionEnd/CreateSubscriptions.php index 9b16305..860c25b 100644 --- a/Observer/MollieProcessTransactionEnd/CreateSubscriptions.php +++ b/Observer/MollieProcessTransactionEnd/CreateSubscriptions.php @@ -14,21 +14,21 @@ use Magento\Sales\Model\Order; use Mollie\Api\MollieApiClient; use Mollie\Payment\Config; -use Mollie\Payment\Model\Mollie; use Mollie\Subscriptions\Api\Data\SubscriptionToProductInterface; use Mollie\Subscriptions\Api\Data\SubscriptionToProductInterfaceFactory; use Mollie\Subscriptions\Api\SubscriptionToProductRepositoryInterface; use Mollie\Subscriptions\DTO\SubscriptionOption; use Mollie\Subscriptions\Service\Email\SendNotificationEmail; +use Mollie\Subscriptions\Service\Mollie\MollieSubscriptionApi; use Mollie\Subscriptions\Service\Mollie\SubscriptionOptions; use Mollie\Subscriptions\Service\Order\OrderContainsSubscriptionProduct; class CreateSubscriptions implements ObserverInterface { /** - * @var Mollie + * @var MollieSubscriptionApi */ - private $mollieModel; + private $mollieSubscriptionApi; /** * @var OrderContainsSubscriptionProduct @@ -82,7 +82,7 @@ class CreateSubscriptions implements ObserverInterface public function __construct( Config $config, - Mollie $mollieModel, + MollieSubscriptionApi $mollieSubscriptionApi, OrderContainsSubscriptionProduct $orderContainsSubscriptionProduct, SubscriptionOptions $subscriptionOptions, SubscriptionToProductInterfaceFactory $subscriptionToProductFactory, @@ -93,7 +93,7 @@ public function __construct( SendNotificationEmail $sendCustomerNotificationEmail ) { $this->config = $config; - $this->mollieModel = $mollieModel; + $this->mollieSubscriptionApi = $mollieSubscriptionApi; $this->orderContainsSubscriptionProduct = $orderContainsSubscriptionProduct; $this->subscriptionOptions = $subscriptionOptions; $this->subscriptionToProductFactory = $subscriptionToProductFactory; @@ -119,7 +119,7 @@ public function execute(Observer $observer) return; } - $this->mollieApi = $this->mollieModel->getMollieApi($order->getStoreId()); + $this->mollieApi = $this->mollieSubscriptionApi->loadByStore($order->getStoreId()); $payment = $this->getPayment($order); $subscriptions = $this->subscriptionOptions->forOrder($order); diff --git a/Service/Email/SubscriptionToProductEmailVariables.php b/Service/Email/SubscriptionToProductEmailVariables.php index 1400835..c62651a 100644 --- a/Service/Email/SubscriptionToProductEmailVariables.php +++ b/Service/Email/SubscriptionToProductEmailVariables.php @@ -10,15 +10,15 @@ use Magento\Framework\Pricing\PriceCurrencyInterface; use Mollie\Api\MollieApiClient; use Mollie\Api\Resources\Customer; -use Mollie\Payment\Model\Mollie; use Mollie\Subscriptions\Api\Data\SubscriptionToProductInterface; +use Mollie\Subscriptions\Service\Mollie\MollieSubscriptionApi; class SubscriptionToProductEmailVariables { /** - * @var Mollie + * @var MollieSubscriptionApi */ - private $mollie; + private $mollieSubscriptionApi; /** * @var Customer[] @@ -41,11 +41,11 @@ class SubscriptionToProductEmailVariables private $apiToStore = []; public function __construct( - Mollie $mollie, + MollieSubscriptionApi $mollieSubscriptionApi, ProductRepositoryInterface $productRepository, PriceCurrencyInterface $priceCurrency ) { - $this->mollie = $mollie; + $this->mollieSubscriptionApi = $mollieSubscriptionApi; $this->productRepository = $productRepository; $this->priceCurrency = $priceCurrency; } @@ -99,7 +99,7 @@ private function getApiForStore($storeId): MollieApiClient return $this->apiToStore[$storeId]; } - $this->apiToStore[$storeId] = $this->mollie->getMollieApi($storeId); + $this->apiToStore[$storeId] = $this->mollieSubscriptionApi->loadByStore($storeId); return $this->apiToStore[$storeId]; } } diff --git a/Service/Mollie/MollieSubscriptionApi.php b/Service/Mollie/MollieSubscriptionApi.php new file mode 100644 index 0000000..b8f1bda --- /dev/null +++ b/Service/Mollie/MollieSubscriptionApi.php @@ -0,0 +1,24 @@ +addVersionString('MagentoSubscription'); + + return $client; + } + + public function loadByStore(int $storeId = null): \Mollie\Api\MollieApiClient + { + $client = parent::loadByStore($storeId); + $client->addVersionString('MagentoSubscription'); + + return $client; + } +} diff --git a/Test/Integration/Service/Email/SubscriptionToProductEmailVariablesTest.php b/Test/Integration/Service/Email/SubscriptionToProductEmailVariablesTest.php index 042279b..3c9b68e 100644 --- a/Test/Integration/Service/Email/SubscriptionToProductEmailVariablesTest.php +++ b/Test/Integration/Service/Email/SubscriptionToProductEmailVariablesTest.php @@ -9,6 +9,7 @@ use Mollie\Payment\Test\Integration\IntegrationTestCase; use Mollie\Subscriptions\Api\Data\SubscriptionToProductInterface; use Mollie\Subscriptions\Service\Email\SubscriptionToProductEmailVariables; +use Mollie\Subscriptions\Service\Mollie\MollieSubscriptionApi; class SubscriptionToProductEmailVariablesTest extends IntegrationTestCase { @@ -39,12 +40,12 @@ public function get($id, array $parameters = []) } }; - $mollieMock = $this->createMock(Mollie::class); - $mollieMock->method('getMollieApi')->willReturn($client); + $mollieSubscriptionApiMock = $this->createMock(MollieSubscriptionApi::class); + $mollieSubscriptionApiMock->method('loadByStore')->willReturn($client); /** @var SubscriptionToProductEmailVariables $instance */ $instance = $this->objectManager->create(SubscriptionToProductEmailVariables::class, [ - 'mollie' => $mollieMock, + 'mollieSubscriptionApi' => $mollieSubscriptionApiMock, ]); $model1 = $this->objectManager->create(SubscriptionToProductInterface::class); From ccf2969c19e96bc6c81a2b000f768dcb0c99b466 Mon Sep 17 00:00:00 2001 From: Michiel Gerritsen Date: Thu, 1 Jun 2023 13:06:13 +0200 Subject: [PATCH 03/13] Improvement: Add order create test for the webhook --- Controller/Api/Webhook.php | 1 + .../Controller/Api/WebhookTest.php | 125 ++++++++++++++++++ 2 files changed, 126 insertions(+) diff --git a/Controller/Api/Webhook.php b/Controller/Api/Webhook.php index 453ddd3..8a040f4 100644 --- a/Controller/Api/Webhook.php +++ b/Controller/Api/Webhook.php @@ -229,6 +229,7 @@ private function formatAddress(\Magento\Customer\Api\Data\AddressInterface $cust $address->setVatId($customerAddress->getVatId()); $address->setSuffix($customerAddress->getSuffix()); $address->setPrefix($customerAddress->getPrefix()); + $address->setRegionId($customerAddress->getRegionId()); return $address; } diff --git a/Test/Integration/Controller/Api/WebhookTest.php b/Test/Integration/Controller/Api/WebhookTest.php index f7f6f00..03d7b8f 100644 --- a/Test/Integration/Controller/Api/WebhookTest.php +++ b/Test/Integration/Controller/Api/WebhookTest.php @@ -2,13 +2,25 @@ namespace Mollie\Subscriptions\Test\Integration\Controller\Api; +use Magento\Customer\Api\AddressRepositoryInterface; +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Framework\Api\SearchCriteriaBuilder; use Magento\Framework\Encryption\Encryptor; +use Magento\Sales\Api\OrderRepositoryInterface; use Magento\TestFramework\Request; use Magento\TestFramework\TestCase\AbstractController as ControllerTestCase; use Mollie\Api\Endpoints\PaymentEndpoint; +use Mollie\Api\Endpoints\SubscriptionEndpoint; use Mollie\Api\Exceptions\ApiException; +use Mollie\Api\MollieApiClient; +use Mollie\Api\Resources\Payment; +use Mollie\Api\Resources\Subscription; +use Mollie\Payment\Api\Data\MollieCustomerInterface; +use Mollie\Payment\Api\MollieCustomerRepositoryInterface; use Mollie\Payment\Model\Mollie; use Mollie\Payment\Test\Fakes\FakeEncryptor; +use Mollie\Payment\Test\Integration\MolliePaymentBuilder; +use Mollie\Subscriptions\Service\Mollie\MollieSubscriptionApi; class WebhookTest extends ControllerTestCase { @@ -40,4 +52,117 @@ public function testAcceptsPost() $this->assert404NotFound(); } + + /** + * @magentoDataFixture Magento/Customer/_files/customer_with_addresses.php + * @magentoDataFixture Magento/Catalog/_files/product_simple.php + * @magentoConfigFixture default_store mollie_subscriptions/general/shipping_method flatrate_flatrate + */ + public function testPlacesOrderFromTransaction(): void + { + $transactionId = 'tr_testtransaction'; + + $this->createMollieCustomer(); + $api = $this->getApi($transactionId); + + $mollieSubscriptionApi = $this->createMock(MollieSubscriptionApi::class); + $mollieSubscriptionApi->method('loadByStore')->willReturn($api); + $this->_objectManager->addSharedInstance($mollieSubscriptionApi, MollieSubscriptionApi::class); + + // Check how many orders there are before the webhook is called + $ordersCount = count($this->getOrderIdsByTransactionId($transactionId)); + + $mollieMock = $this->createMock(Mollie::class); + $mollieMock->method('processTransactionForOrder'); + $this->_objectManager->addSharedInstance($mollieMock, Mollie::class); + + $this->dispatch('mollie-subscriptions/api/webhook?id=' . $transactionId); + $this->assertEquals(200, $this->getResponse()->getStatusCode()); + + $orders = $this->getOrderIdsByTransactionId($transactionId); + $this->assertSame($ordersCount + 1, count($orders)); + } + + private function createMollieCustomer(): void + { + /** @var CustomerRepositoryInterface $customerRepository */ + $customerRepository = $this->_objectManager->get(CustomerRepositoryInterface::class); + $magentoCustomer = $customerRepository->get('customer_with_addresses@test.com'); + $address = $magentoCustomer->getAddresses()[0]; + $address->setRegionId(1); + + // The fixture does not set the default billing and shipping address correct, so fix that. + $magentoCustomer->setDefaultBilling($address->getId()); + $magentoCustomer->setDefaultShipping($address->getId()); + $customerRepository->save($magentoCustomer); + + // Save the Mollie customer + /** @var MollieCustomerInterface $customer */ + $customer = $this->_objectManager->create(MollieCustomerInterface::class); + $customer->setMollieCustomerId('cst_testcustomer'); + $customer->setCustomerId($magentoCustomer->getId()); + + /** @var MollieCustomerRepositoryInterface $repository */ + $repository = $this->_objectManager->get(MollieCustomerRepositoryInterface::class); + $repository->save($customer); + } + + private function getOrderIdsByTransactionId(string $transactionId): array + { + /** @var OrderRepositoryInterface $repository */ + $repository = $this->_objectManager->get(OrderRepositoryInterface::class); + + /** @var SearchCriteriaBuilder $criteria */ + $criteria = $this->_objectManager->get(SearchCriteriaBuilder::class); + $criteria->addFilter('mollie_transaction_id', $transactionId); + + $list = $repository->getList($criteria->create()); + + return $list->getItems(); + } + + public function getSubscription(): Subscription + { + /** @var Subscription $subscription */ + $subscription = $this->_objectManager->get(Subscription::class); + $subscription->customerId = 'cst_testcustomer'; + $subscription->metadata = new \stdClass(); + $subscription->metadata->sku = 'simple'; + return $subscription; + } + + public function getPayment(string $transactionId): Payment + { + $molliePaymentBuilder = $this->_objectManager->get(MolliePaymentBuilder::class); + $molliePaymentBuilder->setMethod('ideal'); + $payment = $molliePaymentBuilder->build(); + + $payment->id = $transactionId; + $payment->customerId = 'cst_testcustomer'; + $payment->_links = new \stdClass(); + $payment->_links->subscription = new \stdClass(); + $payment->_links->subscription->href = 'https://example.com/mollie/subscriptions/sub_testsubscription'; + + return $payment; + } + + public function getApi(string $transactionId): MollieApiClient + { + $subscription = $this->getSubscription(); + + $subscriptionsEndpointMock = $this->createMock(SubscriptionEndpoint::class); + $subscriptionsEndpointMock->method('getForId')->willReturn($subscription); + + $payment = $this->getPayment($transactionId); + + $paymentEndpointMock = $this->createMock(PaymentEndpoint::class); + $paymentEndpointMock->method('get')->willReturn($payment); + + /** @var Mollie $mollie */ + $api = $this->createMock(MollieApiClient::class); + $api->method('performHttpCallToFullUrl')->willReturn($subscription); + $api->payments = $paymentEndpointMock; + $api->subscriptions = $subscriptionsEndpointMock; + return $api; + } } From b77fcd0e387ffc0ce3bd523724fc7095cb521eaf Mon Sep 17 00:00:00 2001 From: Michiel Gerritsen Date: Thu, 8 Jun 2023 12:21:52 +0200 Subject: [PATCH 04/13] Improvement: Better webhook handling --- Controller/Api/Webhook.php | 42 +++++++++++++++--- .../Controller/Api/WebhookTest.php | 44 ++++++++++++++++++- 2 files changed, 80 insertions(+), 6 deletions(-) diff --git a/Controller/Api/Webhook.php b/Controller/Api/Webhook.php index 8a040f4..08c6c53 100644 --- a/Controller/Api/Webhook.php +++ b/Controller/Api/Webhook.php @@ -29,7 +29,9 @@ use Mollie\Api\Resources\Subscription; use Mollie\Payment\Api\MollieCustomerRepositoryInterface; use Mollie\Payment\Logger\MollieLogger; +use Mollie\Payment\Model\Client\Payments; use Mollie\Payment\Model\Mollie; +use Mollie\Payment\Service\Mollie\Order\LinkTransactionToOrder; use Mollie\Payment\Service\Mollie\ValidateMetadata; use Mollie\Payment\Service\Order\SendOrderEmails; use Mollie\Subscriptions\Config; @@ -118,6 +120,11 @@ class Webhook extends Action implements CsrfAwareActionInterface */ private $validateMetadata; + /** + * @var LinkTransactionToOrder + */ + private $linkTransactionToOrder; + public function __construct( Context $context, Config $config, @@ -134,7 +141,8 @@ public function __construct( MollieLogger $mollieLogger, SendOrderEmails $sendOrderEmails, RetryUsingOtherStoreViews $retryUsingOtherStoreViews, - ValidateMetadata $validateMetadata + ValidateMetadata $validateMetadata, + LinkTransactionToOrder $linkTransactionToOrder ) { parent::__construct($context); @@ -153,6 +161,7 @@ public function __construct( $this->sendOrderEmails = $sendOrderEmails; $this->retryUsingOtherStoreViews = $retryUsingOtherStoreViews; $this->validateMetadata = $validateMetadata; + $this->linkTransactionToOrder = $linkTransactionToOrder; } public function execute() @@ -172,7 +181,7 @@ public function execute() if ($orders = $this->mollie->getOrderIdsByTransactionId($id)) { foreach ($orders as $orderId) { - $this->mollie->processTransaction($orderId, 'webhook'); + $this->mollie->processTransaction($orderId, Payments::TRANSACTION_TYPE_SUBSCRIPTION); } return $this->returnOkResponse(); @@ -182,7 +191,14 @@ public function execute() $molliePayment = $this->getPayment($id); $subscription = $this->api->subscriptions->getForId($molliePayment->customerId, $molliePayment->subscriptionId); - $customerId = $this->mollieCustomerRepository->getByMollieCustomerId($molliePayment->customerId)->getCustomerId(); + $mollieCustomer = $this->mollieCustomerRepository->getByMollieCustomerId($molliePayment->customerId); + if (!$mollieCustomer) { + throw new \Exception( + 'Mollie customer with ID ' . $molliePayment->customerId . ' not found in database' + ); + } + + $customerId = $mollieCustomer->getCustomerId(); $customer = $this->customerRepository->getById($customerId); $cart = $this->getCart($customer); @@ -201,10 +217,12 @@ public function execute() $order->getPayment()->setAdditionalInformation('subscription_created', $subscription->createdAt); $this->orderRepository->save($order); - $this->mollie->processTransactionForOrder($order, 'webhook'); + $this->linkTransactionToOrder->execute($molliePayment->id, $order); + + $this->mollie->processTransactionForOrder($order, Payments::TRANSACTION_TYPE_SUBSCRIPTION); return $this->returnOkResponse(); } catch (ApiException $exception) { - $this->mollieLogger->addInfoLog('ApiException occured while checking transaction', [ + $this->mollieLogger->addErrorLog('ApiException occured while checking transaction', [ 'id' => $id, 'exception' => $exception->__toString() ]); @@ -253,6 +271,20 @@ private function setShippingAddress(CustomerInterface $customer, CartInterface $ $shippingAddress->setCollectShippingRates(true); $shippingAddress->collectShippingRates(); $shippingAddress->setShippingMethod($this->config->getShippingMethod()); + + // There are no rates available. Switch to the first available shipping method. + if ($shippingAddress->getShippingRateByCode($this->config->getShippingMethod()) === false && + count($shippingAddress->getShippingRatesCollection()->getItems()) > 0 + ) { + $newMethod = $shippingAddress->getShippingRatesCollection()->getFirstItem()->getCode(); + $shippingAddress->setShippingMethod($newMethod); + + $this->mollieLogger->addInfoLog( + 'subscriptions', + 'No rates available for ' . $this->config->getShippingMethod() . + ', switched to ' . $newMethod + ); + } } private function getCart(CustomerInterface $customer): CartInterface diff --git a/Test/Integration/Controller/Api/WebhookTest.php b/Test/Integration/Controller/Api/WebhookTest.php index 03d7b8f..a9e4697 100644 --- a/Test/Integration/Controller/Api/WebhookTest.php +++ b/Test/Integration/Controller/Api/WebhookTest.php @@ -2,10 +2,10 @@ namespace Mollie\Subscriptions\Test\Integration\Controller\Api; -use Magento\Customer\Api\AddressRepositoryInterface; use Magento\Customer\Api\CustomerRepositoryInterface; use Magento\Framework\Api\SearchCriteriaBuilder; use Magento\Framework\Encryption\Encryptor; +use Magento\Sales\Api\Data\OrderInterface; use Magento\Sales\Api\OrderRepositoryInterface; use Magento\TestFramework\Request; use Magento\TestFramework\TestCase\AbstractController as ControllerTestCase; @@ -83,6 +83,37 @@ public function testPlacesOrderFromTransaction(): void $this->assertSame($ordersCount + 1, count($orders)); } + /** + * @magentoDataFixture Magento/Sales/_files/order.php + * @magentoDataFixture Magento/Customer/_files/customer_with_addresses.php + * @magentoDataFixture Magento/Catalog/_files/product_simple.php + * @magentoConfigFixture default_store mollie_subscriptions/general/shipping_method flatrate_flatrate + */ + public function testDoesNotCreateMultipleOrders(): void + { + $transactionId = 'tr_testtransaction'; + + $order = $this->loadOrderById('100000001'); + + $mollieSubscriptionApiMock = $this->createMock(MollieSubscriptionApi::class); + $mollieSubscriptionApiMock->expects($spy = $this->any())->method('loadByStore'); + + $this->_objectManager->addSharedInstance($mollieSubscriptionApiMock, MollieSubscriptionApi::class); + + $mollieMock = $this->createMock(Mollie::class); + $mollieMock->method('processTransactionForOrder'); + $mollieMock->method('getOrderIdsByTransactionId')->willReturn([$order]); + $this->_objectManager->addSharedInstance($mollieMock, Mollie::class); + + $this->dispatch('mollie-subscriptions/api/webhook?id=' . $transactionId); + $this->assertEquals(200, $this->getResponse()->getStatusCode()); + + $this->dispatch('mollie-subscriptions/api/webhook?id=' . $transactionId); + $this->assertEquals(200, $this->getResponse()->getStatusCode()); + + $this->assertEquals(0, $spy->getInvocationCount()); + } + private function createMollieCustomer(): void { /** @var CustomerRepositoryInterface $customerRepository */ @@ -165,4 +196,15 @@ public function getApi(string $transactionId): MollieApiClient $api->subscriptions = $subscriptionsEndpointMock; return $api; } + + private function loadOrderById($orderId): OrderInterface + { + $repository = $this->_objectManager->get(OrderRepositoryInterface::class); + $builder = $this->_objectManager->create(SearchCriteriaBuilder::class); + $searchCriteria = $builder->addFilter('increment_id', $orderId, 'eq')->create(); + + $orderList = $repository->getList($searchCriteria)->getItems(); + + return array_shift($orderList); + } } From 305fabe164567d9cc88f1be5bb1e407c44394a35 Mon Sep 17 00:00:00 2001 From: Michiel Gerritsen Date: Mon, 12 Jun 2023 08:54:44 +0200 Subject: [PATCH 05/13] Bugfix: Use the correct config path --- Config.php | 2 +- Test/Integration/ConfigTest.php | 35 +++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 Test/Integration/ConfigTest.php diff --git a/Config.php b/Config.php index 6f5bdde..d3e0516 100755 --- a/Config.php +++ b/Config.php @@ -356,6 +356,6 @@ public function getCustomerCancelNotificationTemplate($storeId = null, $scope = */ public function disableNewOrderConfirmation($storeId = null, $scope = ScopeInterface::SCOPE_STORE): bool { - return $this->getFlag(static::XML_PATH_EMAILS_ENABLE_ADMIN_CANCEL_NOTIFICATION, $storeId, $scope); + return $this->getFlag(static::XML_PATH_DISABLE_NEW_ORDER_CONFIRMATION, $storeId, $scope); } } diff --git a/Test/Integration/ConfigTest.php b/Test/Integration/ConfigTest.php new file mode 100644 index 0000000..cf0d542 --- /dev/null +++ b/Test/Integration/ConfigTest.php @@ -0,0 +1,35 @@ +objectManager->create(Config::class); + + $this->assertTrue($instance->disableNewOrderConfirmation()); + } + + /** + * @magentoConfigFixture default_store mollie_subscriptions/emails/disable_new_order_confirmation 0 + * @return void + */ + public function testDisableNewOrderConfirmationWhenDisabled(): void + { + /** @var Config $instance */ + $instance = $this->objectManager->create(Config::class); + + $this->assertFalse($instance->disableNewOrderConfirmation()); + } +} From 5f0cccd0bebde4e5577d4fc0aa1f04013d7e8f1d Mon Sep 17 00:00:00 2001 From: Michiel Gerritsen Date: Mon, 12 Jun 2023 10:25:07 +0200 Subject: [PATCH 06/13] Bugfix: Save and set the product quantity #54 --- Controller/Api/Webhook.php | 6 ++--- Service/Mollie/SubscriptionOptions.php | 6 +++-- .../Mollie/SubscriptionOptionsTest.php | 25 +++++++++++++++++++ 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/Controller/Api/Webhook.php b/Controller/Api/Webhook.php index aa8ba58..12d7d2c 100644 --- a/Controller/Api/Webhook.php +++ b/Controller/Api/Webhook.php @@ -177,7 +177,7 @@ public function execute() $customer = $this->customerRepository->getById($customerId); $cart = $this->getCart($customer); - $this->addProduct($molliePayment, $cart); + $this->addProduct($molliePayment, $cart, $subscription['metadata']['quantity'] ?? 1); $cart->setBillingAddress($this->formatAddress($this->addressRepository->getById($customer->getDefaultBilling()))); $this->setShippingAddress($customer, $cart); @@ -224,14 +224,14 @@ private function formatAddress(\Magento\Customer\Api\Data\AddressInterface $cust return $address; } - private function addProduct(Payment $mollieOrder, CartInterface $cart) + private function addProduct(Payment $mollieOrder, CartInterface $cart, float $quantity) { /** @var Subscription $subscription */ $subscription = $this->api->performHttpCallToFullUrl(MollieApiClient::HTTP_GET, $mollieOrder->_links->subscription->href); $sku = $subscription->metadata->sku; $product = $this->productRepository->get($sku); - $cart->addProduct($product); + $cart->addProduct($product, $quantity); } private function setShippingAddress(CustomerInterface $customer, CartInterface $cart) diff --git a/Service/Mollie/SubscriptionOptions.php b/Service/Mollie/SubscriptionOptions.php index ede24ad..9265ac1 100644 --- a/Service/Mollie/SubscriptionOptions.php +++ b/Service/Mollie/SubscriptionOptions.php @@ -172,8 +172,10 @@ private function addDescription(): void private function addMetadata(): void { $product = $this->orderItem->getProduct(); - - $this->options['metadata'] = ['sku' => $product->getSku()]; + $this->options['metadata'] = [ + 'sku' => $product->getSku(), + 'quantity' => $this->orderItem->getQtyOrdered(), + ]; } private function addWebhookUrl(): void diff --git a/Test/Integration/Service/Mollie/SubscriptionOptionsTest.php b/Test/Integration/Service/Mollie/SubscriptionOptionsTest.php index f53219f..5a69146 100644 --- a/Test/Integration/Service/Mollie/SubscriptionOptionsTest.php +++ b/Test/Integration/Service/Mollie/SubscriptionOptionsTest.php @@ -211,6 +211,31 @@ public function testAddsTheWebhookUrl() $this->assertStringContainsString('___store/default', $subscription->toArray()['webhookUrl']); } + /** + * @magentoDataFixture Magento/Sales/_files/order.php + */ + public function testAddsTheQuantity() + { + $order = $this->loadOrder('100000001'); + $items = $order->getItems(); + + /** @var OrderItemInterface $orderItem */ + $orderItem = array_shift($items); + $this->setOptionIdOnOrderItem($orderItem, 'weekly-finite'); + $this->setTheSubscriptionOnTheProduct($orderItem->getProduct()); + + $orderItem->setQtyOrdered(2); + + /** @var SubscriptionOptions $instance */ + $instance = $this->objectManager->create(SubscriptionOptions::class); + $result = $instance->forOrder($order); + + $this->assertCount(1, $result); + $subscription = $result[0]; + + $this->assertEquals(2, $subscription->toArray()['metadata']['quantity']); + } + /** * @dataProvider addsTheStartDate * @magentoDataFixture Magento/Sales/_files/order.php From b8237057912684e8e6bb29d115ffa6b1f4925ea6 Mon Sep 17 00:00:00 2001 From: Michiel Gerritsen Date: Mon, 12 Jun 2023 10:31:31 +0200 Subject: [PATCH 07/13] Access the metadata correct --- Controller/Api/Webhook.php | 2 +- Test/Integration/Controller/Api/WebhookTest.php | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Controller/Api/Webhook.php b/Controller/Api/Webhook.php index fc8743d..1f79d4f 100644 --- a/Controller/Api/Webhook.php +++ b/Controller/Api/Webhook.php @@ -202,7 +202,7 @@ public function execute() $customer = $this->customerRepository->getById($customerId); $cart = $this->getCart($customer); - $this->addProduct($molliePayment, $cart, $subscription['metadata']['quantity'] ?? 1); + $this->addProduct($molliePayment, $cart, $subscription->metadata->quantity ?? 1); $cart->setBillingAddress($this->formatAddress($this->addressRepository->getById($customer->getDefaultBilling()))); $this->setShippingAddress($customer, $cart); diff --git a/Test/Integration/Controller/Api/WebhookTest.php b/Test/Integration/Controller/Api/WebhookTest.php index a9e4697..9a0e6dc 100644 --- a/Test/Integration/Controller/Api/WebhookTest.php +++ b/Test/Integration/Controller/Api/WebhookTest.php @@ -152,7 +152,7 @@ private function getOrderIdsByTransactionId(string $transactionId): array return $list->getItems(); } - public function getSubscription(): Subscription + private function getSubscription(): Subscription { /** @var Subscription $subscription */ $subscription = $this->_objectManager->get(Subscription::class); @@ -162,7 +162,7 @@ public function getSubscription(): Subscription return $subscription; } - public function getPayment(string $transactionId): Payment + private function getPayment(string $transactionId): Payment { $molliePaymentBuilder = $this->_objectManager->get(MolliePaymentBuilder::class); $molliePaymentBuilder->setMethod('ideal'); @@ -177,7 +177,7 @@ public function getPayment(string $transactionId): Payment return $payment; } - public function getApi(string $transactionId): MollieApiClient + private function getApi(string $transactionId): MollieApiClient { $subscription = $this->getSubscription(); From b05ad8ef46756323f77301a577a23572b4f493c1 Mon Sep 17 00:00:00 2001 From: Michiel Gerritsen Date: Mon, 12 Jun 2023 10:32:08 +0200 Subject: [PATCH 08/13] Improvement: Use retry for setup:di:compile --- .github/workflows/phpstan.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/phpstan.yml b/.github/workflows/phpstan.yml index 0d0a92c..f11ab6d 100644 --- a/.github/workflows/phpstan.yml +++ b/.github/workflows/phpstan.yml @@ -28,7 +28,7 @@ jobs: - name: Install Mollie and Mollie Subscription extensions and run setup:di:compile run: | docker exec magento-project-community-edition php bin/magento module:enable Mollie_Payment Mollie_Subscriptions - docker exec magento-project-community-edition php bin/magento setup:di:compile + docker exec magento-project-community-edition ./retry "php bin/magento setup:di:compile" - name: Run PHPStan run: docker exec magento-project-community-edition /bin/bash -c "./vendor/bin/phpstan analyse --debug -c /data/extensions/mollie-magento2-subscriptions/phpstan.neon /data/extensions/mollie-magento2-subscriptions" From 99fec80e67fa4d19176e010bb81901f0ef4cee38 Mon Sep 17 00:00:00 2001 From: Michiel Gerritsen Date: Mon, 12 Jun 2023 11:36:35 +0200 Subject: [PATCH 09/13] Feature: Added translations --- i18n/de_DE.csv | 138 +++++++++++++++++++++++++++++++++++++++++ i18n/en_US.csv | 149 ++++++++++++++++++++++++++++++++++++++++----- i18n/en_US_raw.csv | 25 -------- i18n/es_ES.csv | 136 +++++++++++++++++++++++++++++++++++++++++ i18n/fr_FR.csv | 136 +++++++++++++++++++++++++++++++++++++++++ i18n/nl_NL.csv | 136 +++++++++++++++++++++++++++++++++++++++++ 6 files changed, 680 insertions(+), 40 deletions(-) create mode 100644 i18n/de_DE.csv delete mode 100644 i18n/en_US_raw.csv create mode 100644 i18n/es_ES.csv create mode 100644 i18n/fr_FR.csv create mode 100644 i18n/nl_NL.csv diff --git a/i18n/de_DE.csv b/i18n/de_DE.csv new file mode 100644 index 0000000..1904da8 --- /dev/null +++ b/i18n/de_DE.csv @@ -0,0 +1,138 @@ +"Check last 100 debug log records","Überprüfen Sie die letzten 100 Debug-Protokolleinträge" +"Check last 100 error log records","Überprüfen Sie die letzten 100 Fehlerprotokolleinträge" +"Run Self-test","Selbsttest durchführen" +"Check for latest versions","Nach neuesten Versionen suchen" +"Please select","Bitte auswählen" +Day(s),Tag(e) +Week(s),Woche(n) +Month(s),Monat(e) +Times,Zeiten +Infinite,Unendlich +Enabled,Aktiviert +Disabled,Deaktiviert +"Log is empty","Protokoll ist leer" +"Unable to cancel subscription: %1","Abonnement kann nicht gekündigt werden: %1" +"Subscription with ID ""%1"" has been cancelled","Abonnement mit der ID ""%1"" wurde gekündigt" +"No id provided","Keine ID angegeben" +"Please check the Mollie logs for more information","Bitte überprüfen Sie die Mollie-Protokolle für weitere Informationen" +"Unable to cancel subscription","Abonnement kann nicht gekündigt werden" +"My Subscriptions","Meine Abonnements" +"We are unable to restart the subscription","Wir können das Abonnement nicht neu starten" +"Unable to change the price for subscription ""%1""","Preis für Abonnement ""%1"" kann nicht geändert werden" +"Subscription ""%1"" canceled, deleted database record with ID ""%2""","Abonnement ""%1"" gekündigt, Datenbankeintrag mit ID ""%2"" gelöscht" +"Updated subscription ""%1"" to price ""%2""","Abonnement ""%1"" auf Preis ""%2"" aktualisiert" +"Use Config Settings","Konfigurationseinstellungen verwenden" +"Cron settings can't be saved","Cron-Einstellungen können nicht gespeichert werden" +"Could not save the subscriptionToProduct: %1","subscriptionToProduct: %1 konnte nicht gespeichert werden" +"subscription_to_product with id ""%1"" does not exist.","subscription_to_product mit der ID ""%1"" existiert nicht." +"Subscription for customer %1 and product %2 not found","Abonnement für Kunde %1 und Produkt %2 nicht gefunden" +"Could not delete the subscription_to_product: %1","subscription_to_product: %1 konnte nicht gelöscht werden" +"Please select a subscription before adding the product to your cart.","Bitte wählen Sie ein Abonnement aus, bevor Sie das Produkt in Ihren Warenkorb legen." +"You already have a subscription to this product.","Sie haben bereits ein Abonnement für dieses Produkt." +"Exception while adding the subscription buttons:","Ausnahme beim Hinzufügen der Abonnement-Schaltflächen:" +"You already have a subscription to ""%1"". This product has been removed from your cart.","Sie haben bereits ein Abonnement für ""%1"". Dieses Produkt wurde aus Ihrem Warenkorb entfernt." +"Subscription with ID ""%1"" is not active anymore, deleting record with ID ""%2""","Abonnement mit der ID ""%1"" ist nicht mehr aktiv, Eintrag mit der ID ""%2"" wird gelöscht" +"Payment with ID %1 not found in any store view","Zahlung mit der ID %1 in keiner Shop-Ansicht gefunden" +"Every day","Jeden Tag" +"Every %1 days","Jeden %1 Tag(e)" +"Every week","Jede Woche" +"Every %1 weeks","Jede %1 Woche(n)" +"Every month","Jeden Monat" +"Every %1 months","Jeden %1 Monat(e)" +"Every %1 %2","Jeden %1 %2" +Cancel,Abbrechen +Delete,Löschen +"Are you sure you want to delete this record?","Sind Sie sicher, dass Sie diesen Eintrag löschen möchten?" +"Manual & Support","Handbuch & Support" +"You have no active subscriptions.","Sie haben keine aktiven Abonnements." +Orders,Bestellungen +ID,ID +Date,Datum +Status,Status +Description,Beschreibung +Amount,Betrag +Action,Aktion +Total,Gesamt +Actions,Aktionen +"Are you sure you want to delete the subscription with ID %1?","Sind Sie sicher, dass Sie das Abonnement mit der ID %1 löschen möchten?" +Restart,Neustart +"One Time Purchase","Einmaliger Kauf" +Default,Standard +Title,Titel +"Repeat Every","Wiederholen Jeden" +Type,Typ +"Repeat Times","Wiederholungszeiten" +"Repeat Type","Wiederholungstyp" +"Add Subscription type","Abonnementtyp hinzufügen" +"Previous Page","Vorherige Seite" +"Next Page","Nächste Seite" +"Subscription canceled","Abonnement gekündigt" +"Hello,","Hallo," +"The customer %customerName has canceled a subscription with the description %subscription_description and ID %subscription_id.","Der Kunde %customerName hat ein Abonnement mit der Beschreibung %subscription_description und der ID %subscription_id gekündigt." +"Thanks again,","Nochmals vielen Dank," +%store_name,%store_name +"Dear %name,","Sehr geehrter %name," +"We confirm that you canceled the subscription with ID %subscription_id.","Wir bestätigen, dass Sie das Abonnement mit der ID %subscription_id gekündigt haben." +"New subscription","Neues Abonnement" +"The customer %customerName has started a new subscription with the description %description and ID %subscription_id.","Der Kunde %customerName hat ein neues Abonnement mit der Beschreibung %description und der ID %subscription_id gestartet." +"Your new subscription","Ihr neues Abonnement" +"Good news! We confirm your subscription to %product_name. The next order will be sent on its way to you on %nextPaymentDate.","Gute Nachrichten! Wir bestätigen Ihr Abonnement für %product_name. Die nächste Bestellung wird Ihnen am %nextPaymentDate zugesandt." +"Upcoming subscription renewal from %store_name","Bevorstehende Abonnementverlängerung von %store_name" +"We are very excited to get your %product_name subscription ready for %nextPaymentDate!","Wir freuen uns sehr, Ihr Abonnement für %product_name für den %nextPaymentDate vorzubereiten!" +"This email is a reminder that a subscription fee of %amount_value will be charged on the same date.","Diese E-Mail ist eine Erinnerung daran, dass am selben Datum eine Abonnementgebühr von %amount_value berechnet wird." +"If you would like to pause or cancel your subscription or make any other changes, you can always do so by logging into your %store_name account.","Wenn Sie Ihr Abonnement pausieren oder kündigen oder andere Änderungen vornehmen möchten, können Sie dies jederzeit tun, indem Sie sich in Ihr %store_name Konto einloggen." +"All our best,","Alles Gute," +"Subscription restarted","Abonnement neu gestartet" +"The customer %customerName has re-started a subscription with the description %subscriptionDescription.","Der K + +unde %customerName hat ein Abonnement mit der Beschreibung %subscriptionDescription neu gestartet." +"We confirm that you restarted the subscription on %product_name.","Wir bestätigen, dass Sie das Abonnement für %product_name neu gestartet haben." +Passed,Bestanden +Failed,Fehlgeschlagen +"Great, you are using the latest version.","Großartig, Sie verwenden die neueste Version." +"There is a new version available (%1) see .","Es ist eine neue Version verfügbar (%1) siehe ." +"last 100 debug log lines","letzte 100 Debug-Protokollzeilen" +"download as .txt file","als .txt-Datei herunterladen" +ok,ok +"last 100 error log records","letzte 100 Fehlerprotokolleinträge" +Self-test,Selbsttest +"Show more.","Mehr anzeigen." +"Show less.","Weniger anzeigen." +"Not all payments methods are available when ordering subscription products.","Nicht alle Zahlungsmethoden sind verfügbar, wenn Sie Abonnementprodukte bestellen." +"View active Mollie subscriptions","Aktive Mollie-Abonnements anzeigen" +"Mollie Subscriptions","Mollie Abonnements" +"View active subscriptions","Aktive Abonnements anzeigen" +Subscriptions,Abonnements +Branding,Markenbildung +"Debug & Logging","Debug & Protokollierung" +Logging,Protokollierung +"Debug Mode","Debug-Modus" +Emails,E-Mails +"Disable new order confirmation","Bestätigung für neue Bestellung deaktivieren" +"Send admin email on new subscription?","Admin-E-Mail bei neuem Abonnement senden?" +"Subscription notification template for admin","Abonnement-Benachrichtigungsvorlage für Admin" +"Send customer email on new subscription?","Kunden-E-Mail bei neuem Abonnement senden?" +"Subscription notification template for customer","Abonnement-Benachrichtigungsvorlage für Kunden" +"Send admin email on restart of a subscription?","Admin-E-Mail bei Neustart eines Abonnements senden?" +"Send customer email on restart of a subscription?","Kunden-E-Mail bei Neustart eines Abonnements senden?" +"Restart subscription notification template for customer","Neustart-Abonnement-Benachrichtigungsvorlage für Kunden" +"Send admin email on subscription cancel?","Admin-E-Mail bei Kündigung eines Abonnements senden?" +"Cancel subscription notification template for admin","Abonnement-Kündigungsvorlage für Admin" +"Send customer email on subscription cancel?","Kunden-E-Mail bei Kündigung eines Abonnements senden?" +"Cancel subscription notification template for customer","Abonnement-Kündigungsvorlage für Kunden" +General,Allgemein +Version,Version +"Shipping method","Versandmethode" +"Allow one-time purchase for products?","Einmaligen Kauf für Produkte erlauben?" +"Pre-payment reminder email","Vorauszahlungserinnerungs-E-Mail" +"Email template","E-Mail-Vorlage" +"Send BCC to","BCC senden an" +"Optional Comma separated list of emailaddresses. Leave empty to disable.","Optional Kommagetrennte Liste von E-Mail-Adressen. Leer lassen, um zu deaktivieren." +"Days before reminder","Tage vor Erinnerung" +"Cron time","Cron-Zeit" +"All Store Views","Alle Shop-Ansichten" +"Store View","Shop-Ansicht" +"First Payment Date","Erstes Zahlungsdatum" +"Customer Name","Kundenname" +"Next Payment at","Nächste Zahlung am" +"Pre-Payment Reminder","Vorauszahlungserinnerung" diff --git a/i18n/en_US.csv b/i18n/en_US.csv index ca244f9..d49c99e 100755 --- a/i18n/en_US.csv +++ b/i18n/en_US.csv @@ -1,17 +1,136 @@ -"ok" ,"OK" -"download as .txt file" ,"DOWNLOAD AS .TXT FILE" -"last 100 debug log lines" ,"Last 100 Debug Log Lines" -"last 100 error log records" ,"Last 100 Error Log Records" -"test passed" ,"Test Passed" -"test failed" ,"Test Failed" -"Great, you are using the latest version." ,"Great, you are using the latest version." -"There is a new version available (%1) see .","There is a new version available (%1) see ." -"Selftest" ,"Selftest" -"Changelog" ,"Changelog" +"Check last 100 debug log records","Check last 100 debug log records" +"Check last 100 error log records","Check last 100 error log records" +"Run Self-test","Run Self-test" +"Check for latest versions","Check for latest versions" +"Please select","Please select" +Day(s),Day(s) +Week(s),Week(s) +Month(s),Month(s) +Times,Times +Infinite,Infinite +Enabled,Enabled +Disabled,Disabled +"Log is empty","Log is empty" +"Unable to cancel subscription: %1","Unable to cancel subscription: %1" +"Subscription with ID ""%1"" has been cancelled","Subscription with ID ""%1"" has been cancelled" +"No id provided","No id provided" +"Please check the Mollie logs for more information","Please check the Mollie logs for more information" +"Unable to cancel subscription","Unable to cancel subscription" +"My Subscriptions","My Subscriptions" +"We are unable to restart the subscription","We are unable to restart the subscription" +"Unable to change the price for subscription ""%1""","Unable to change the price for subscription ""%1""" +"Subscription ""%1"" canceled, deleted database record with ID ""%2""","Subscription ""%1"" canceled, deleted database record with ID ""%2""" +"Updated subscription ""%1"" to price ""%2""","Updated subscription ""%1"" to price ""%2""" +"Use Config Settings","Use Config Settings" +"Cron settings can't be saved","Cron settings can't be saved" +"Could not save the subscriptionToProduct: %1","Could not save the subscriptionToProduct: %1" +"subscription_to_product with id ""%1"" does not exist.","subscription_to_product with id ""%1"" does not exist." +"Subscription for customer %1 and product %2 not found","Subscription for customer %1 and product %2 not found" +"Could not delete the subscription_to_product: %1","Could not delete the subscription_to_product: %1" +"Please select a subscription before adding the product to your cart.","Please select a subscription before adding the product to your cart." +"You already have a subscription to this product.","You already have a subscription to this product." +"Exception while adding the subscription buttons:","Exception while adding the subscription buttons:" +"You already have a subscription to ""%1"". This product has been removed from your cart.","You already have a subscription to ""%1"". This product has been removed from your cart." +"Subscription with ID ""%1"" is not active anymore, deleting record with ID ""%2""","Subscription with ID ""%1"" is not active anymore, deleting record with ID ""%2""" +"Payment with ID %1 not found in any store view","Payment with ID %1 not found in any store view" +"Every day","Every day" +"Every %1 days","Every %1 days" +"Every week","Every week" +"Every %1 weeks","Every %1 weeks" +"Every month","Every month" +"Every %1 months","Every %1 months" +"Every %1 %2","Every %1 %2" +Cancel,Cancel +Delete,Delete +"Are you sure you want to delete this record?","Are you sure you want to delete this record?" +"Manual & Support","Manual & Support" +"You have no active subscriptions.","You have no active subscriptions." +Orders,Orders +ID,ID +Date,Date +Status,Status +Description,Description +Amount,Amount +Action,Action +Total,Total +Actions,Actions +"Are you sure you want to delete the subscription with ID %1?","Are you sure you want to delete the subscription with ID %1?" +Restart,Restart +"One Time Purchase","One Time Purchase" +Default,Default +Title,Title +"Repeat Every","Repeat Every" +Type,Type +"Repeat Times","Repeat Times" +"Repeat Type","Repeat Type" +"Add Subscription type","Add Subscription type" +"Previous Page","Previous Page" +"Next Page","Next Page" +"Subscription canceled","Subscription canceled" +"Hello,","Hello," +"The customer %customerName has canceled a subscription with the description %subscription_description and ID %subscription_id.","The customer %customerName has canceled a subscription with the description %subscription_description and ID %subscription_id." +"Thanks again,","Thanks again," +%store_name,%store_name +"Dear %name,","Dear %name," +"We confirm that you canceled the subscription with ID %subscription_id.","We confirm that you canceled the subscription with ID %subscription_id." +"New subscription","New subscription" +"The customer %customerName has started a new subscription with the description %description and ID %subscription_id.","The customer %customerName has started a new subscription with the description %description and ID %subscription_id." +"Your new subscription","Your new subscription" +"Good news! We confirm your subscription to %product_name. The next order will be sent on its way to you on %nextPaymentDate.","Good news! We confirm your subscription to %product_name. The next order will be sent on its way to you on %nextPaymentDate." +"Upcoming subscription renewal from %store_name","Upcoming subscription renewal from %store_name" +"We are very excited to get your %product_name subscription ready for %nextPaymentDate!","We are very excited to get your %product_name subscription ready for %nextPaymentDate!" +"This email is a reminder that a subscription fee of %amount_value will be charged on the same date.","This email is a reminder that a subscription fee of %amount_value will be charged on the same date." +"If you would like to pause or cancel your subscription or make any other changes, you can always do so by logging into your %store_name account.","If you would like to pause or cancel your subscription or make any other changes, you can always do so by logging into your %store_name account." +"All our best,","All our best," +"Subscription restarted","Subscription restarted" +"The customer %customerName has re-started a subscription with the description %subscriptionDescription.","The customer %customerName has re-started a subscription with the description %subscriptionDescription." +"We confirm that you restarted the subscription on %product_name.","We confirm that you restarted the subscription on %product_name." +Passed,Passed +Failed,Failed +"Great, you are using the latest version.","Great, you are using the latest version." +"There is a new version available (%1) see .","There is a new version available (%1) see ." +"last 100 debug log lines","last 100 debug log lines" +"download as .txt file","download as .txt file" +ok,ok +"last 100 error log records","last 100 error log records" +Self-test,Self-test "Show more.","Show more." "Show less.","Show less." -"Are you sure you want to delete the subscription with ID %1?","Are you sure you want to delete the subscription with ID %1?" -"Subscription with ID "%1" has been cancelled","Subscription with ID "%1" has been cancelled" -"My Subscriptions","My Subscriptions" -"Restart","Restart" -"The subscription has been restarted successfully","The subscription has been restarted successfully" +"Not all payments methods are available when ordering subscription products.","Not all payments methods are available when ordering subscription products." +"View active Mollie subscriptions","View active Mollie subscriptions" +"Mollie Subscriptions","Mollie Subscriptions" +"View active subscriptions","View active subscriptions" +Subscriptions,Subscriptions +Branding,Branding +"Debug & Logging","Debug & Logging" +Logging,Logging +"Debug Mode","Debug Mode" +Emails,Emails +"Disable new order confirmation","Disable new order confirmation" +"Send admin email on new subscription?","Send admin email on new subscription?" +"Subscription notification template for admin","Subscription notification template for admin" +"Send customer email on new subscription?","Send customer email on new subscription?" +"Subscription notification template for customer","Subscription notification template for customer" +"Send admin email on restart of a subscription?","Send admin email on restart of a subscription?" +"Send customer email on restart of a subscription?","Send customer email on restart of a subscription?" +"Restart subscription notification template for customer","Restart subscription notification template for customer" +"Send admin email on subscription cancel?","Send admin email on subscription cancel?" +"Cancel subscription notification template for admin","Cancel subscription notification template for admin" +"Send customer email on subscription cancel?","Send customer email on subscription cancel?" +"Cancel subscription notification template for customer","Cancel subscription notification template for customer" +General,General +Version,Version +"Shipping method","Shipping method" +"Allow one-time purchase for products?","Allow one-time purchase for products?" +"Pre-payment reminder email","Pre-payment reminder email" +"Email template","Email template" +"Send BCC to","Send BCC to" +"Optional Comma separated list of emailaddresses. Leave empty to disable.","Optional Comma separated list of emailaddresses. Leave empty to disable." +"Days before reminder","Days before reminder" +"Cron time","Cron time" +"All Store Views","All Store Views" +"Store View","Store View" +"First Payment Date","First Payment Date" +"Customer Name","Customer Name" +"Next Payment at","Next Payment at" +"Pre-Payment Reminder","Pre-Payment Reminder" diff --git a/i18n/en_US_raw.csv b/i18n/en_US_raw.csv deleted file mode 100644 index ff98640..0000000 --- a/i18n/en_US_raw.csv +++ /dev/null @@ -1,25 +0,0 @@ -"Check last 100 debug log records","Check last 100 debug log records" -"Check last 100 error log records","Check last 100 error log records" -"Run Selftest","Run Selftest" -"Check for latest versions","Check for latest versions" -"Manual & Support","Manual & Support" -"test passed","test passed" -"test failed","test failed" -"Great, you are using the latest version.","Great, you are using the latest version." -"There is a new version available (%1) see .","There is a new version available (%1) see ." -"last 100 debug log lines","last 100 debug log lines" -"download as .txt file","download as .txt file" -ok,ok -"last 100 error log records","last 100 error log records" -Selftest,Selftest -"Show more.","Show more." -"Show less.","Show less." -Magmodules,Magmodules -Dummy,Dummy -Branding,Branding -"Debug & Logging","Debug & Logging" -Logging,Logging -"Debug Mode","Debug Mode" -General,General -Version,Version -Enabled,Enabled diff --git a/i18n/es_ES.csv b/i18n/es_ES.csv new file mode 100644 index 0000000..7b78e72 --- /dev/null +++ b/i18n/es_ES.csv @@ -0,0 +1,136 @@ +"Check last 100 debug log records","Verifica los últimos 100 registros del log de depuración" +"Check last 100 error log records","Verifica los últimos 100 registros del log de errores" +"Run Self-test","Ejecutar autoprueba" +"Check for latest versions","Comprueba las últimas versiones" +"Please select","Por favor selecciona" +Day(s),Día(s) +Week(s),Semana(s) +Month(s),Mes(es) +Times,Veces +Infinite,Infinito +Enabled,Habilitado +Disabled,Deshabilitado +"Log is empty","El registro está vacío" +"Unable to cancel subscription: %1","No se puede cancelar la suscripción: %1" +"Subscription with ID ""%1"" has been cancelled","La suscripción con ID ""%1"" ha sido cancelada" +"No id provided","No se proporcionó id" +"Please check the Mollie logs for more information","Por favor, consulta los registros de Mollie para más información" +"Unable to cancel subscription","No se puede cancelar la suscripción" +"My Subscriptions","Mis Suscripciones" +"We are unable to restart the subscription","No podemos reiniciar la suscripción" +"Unable to change the price for subscription ""%1""","No se puede cambiar el precio de la suscripción ""%1""" +"Subscription ""%1"" canceled, deleted database record with ID ""%2""","Suscripción ""%1"" cancelada, se eliminó el registro de la base de datos con ID ""%2""" +"Updated subscription ""%1"" to price ""%2""","Actualizada la suscripción ""%1"" al precio ""%2""" +"Use Config Settings","Usar Configuraciones de Configuración" +"Cron settings can't be saved","No se pueden guardar las configuraciones de Cron" +"Could not save the subscriptionToProduct: %1","No se pudo guardar la subscriptionToProduct: %1" +"subscription_to_product with id ""%1"" does not exist.","subscription_to_product con id ""%1"" no existe." +"Subscription for customer %1 and product %2 not found","No se encontró la suscripción para el cliente %1 y el producto %2" +"Could not delete the subscription_to_product: %1","No se pudo eliminar la subscription_to_product: %1" +"Please select a subscription before adding the product to your cart.","Por favor, selecciona una suscripción antes de agregar el producto a tu carrito." +"You already have a subscription to this product.","Ya tienes una suscripción a este producto." +"Exception while adding the subscription buttons:","Excepción al agregar los botones de suscripción:" +"You already have a subscription to ""%1"". This product has been removed from your cart.","Ya tienes una suscripción a ""%1"". Este producto ha sido eliminado de tu carrito." +"Subscription with ID ""%1"" is not active anymore, deleting record with ID ""%2""","La suscripción con ID ""%1"" ya no está activa, eliminando registro con ID ""%2""" +"Payment with ID %1 not found in any store view","Pago con ID %1 no encontrado en ninguna vista de tienda" +"Every day","Cada día" +"Every %1 days","Cada %1 días" +"Every week","Cada semana" +"Every %1 weeks","Cada %1 semanas" +"Every month","Cada mes" +"Every %1 months","Cada %1 meses" +"Every %1 %2","Cada %1 %2" +Cancel,Cancelar +Delete,Borrar +"Are you sure you want to delete this record?","¿Estás seguro de que quieres borrar este registro?" +"Manual & Support","Manual y Soporte" +"You have no active subscriptions.","No tienes suscripciones activas." +Orders,Órdenes +ID,ID +Date,Fecha +Status,Estado +Description,Descripción +Amount,Cantidad +Action,Acción +Total,Total +Actions,Acciones +"Are you sure you want to delete the subscription with ID %1?","¿Estás seguro de que quieres eliminar la suscripción con ID %1?" +Restart,Reiniciar +"One Time Purchase","Compra única" +Default,Defecto +Title,Título +"Repeat Every","Repetir Cada" +Type,Tipo +"Repeat Times","Veces de Repetición" +"Repeat Type","Tipo de Repetición" +"Add Subscription type","Agregar tipo de suscripción" +"Previous Page","Página anterior" +"Next Page","Página siguiente" +"Subscription canceled","Suscripción cancelada" +"Hello,","Hola," +"The customer %customerName has canceled a subscription with the description %subscription_description and ID %subscription_id.","El cliente %customerName ha cancelado una suscripción con la descripción %subscription_description y ID %subscription_id." +"Thanks again,","Gracias de nuevo," +%store_name,%store_name +"Dear %name,","Estimado %name," +"We confirm that you canceled the subscription with ID %subscription_id.","Confirmamos que cancelaste la suscripción con ID %subscription_id." +"New subscription","Nueva suscripción" +"The customer %customerName has started a new subscription with the description %description and ID %subscription_id.","El cliente %customerName ha comenzado una nueva suscripción con la descripción %description y ID %subscription_id." +"Your new subscription","Tu nueva suscripción" +"Good news! We confirm your subscription to %product_name. The next order will be sent on its way to you on %nextPaymentDate.","¡Buenas noticias! Confirmamos tu suscripción a %product_name. El próximo pedido se te enviará el %nextPaymentDate." +"Upcoming subscription renewal from %store_name","Próxima renovación de suscripción de %store_name" +"We are very excited to get your %product_name subscription ready for %nextPaymentDate!","¡Estamos muy emocionados de preparar tu suscripción a %product_name para el %nextPaymentDate!" +"This email is a reminder that a subscription fee of %amount_value will be charged on the same date.","Este correo electrónico es un recordatorio de que se cobrará una tarifa de suscripción de %amount_value en la misma fecha." +"If you would like to pause or cancel your subscription or make any other changes, you can always do so by logging into your %store_name account.","Si desea pausar o cancelar su suscripción o realizar cualquier otro cambio, siempre puede hacerlo iniciando sesión en su cuenta de %store_name." +"All our best,","Todo lo mejor," +"Subscription restarted","Suscripción reiniciada" +"The customer %customerName has re-started a subscription with the description %subscriptionDescription.","El cliente %customerName ha reiniciado una suscripción con la descripción %subscriptionDescription." +"We confirm that you restarted the subscription on %product_name.","Confirmamos que reiniciaste la suscripción en %product_name." +Passed,Aprobado +Failed,Fallado +"Great, you are using the latest version.","Genial, estás usando la última versión." +"There is a new version available (%1) see .","Hay una nueva versión disponible (%1) ver ." +"last 100 debug log lines","últimas 100 líneas del registro de depuración" +"download as .txt file","descargar como archivo .txt" +ok,ok +"last 100 error log records","últimos 100 registros de error" +Self-test,Autoevaluación +"Show more.","Mostrar más." +"Show less.","Mostrar menos." +"Not all payments methods are available when ordering subscription products.","No todos los métodos de pago están disponibles al ordenar productos de suscripción." +"View active Mollie subscriptions","Ver suscripciones activas de Mollie" +"Mollie Subscriptions","Suscripciones Mollie" +"View active subscriptions","Ver suscripciones activas" +Subscriptions,Suscripciones +Branding,Marca +"Debug & Logging","Depuración y Registro" +Logging,Registro +"Debug Mode","Modo de depuración" +Emails,Correos electrónicos +"Disable new order confirmation","Desactivar confirmación de nuevo pedido" +"Send admin email on new subscription?","¿Enviar correo electrónico al administrador en nueva suscripción?" +"Subscription notification template for admin","Plantilla de notificación de suscripción para el administrador" +"Send customer email on new subscription?","¿Enviar correo electrónico al cliente en nueva suscripción?" +"Subscription notification template for customer","Plantilla de notificación de suscripción para el cliente" +"Send admin email on restart of a subscription?","¿Enviar correo electrónico al administrador al reiniciar una suscripción?" +"Send customer email on restart of a subscription?","¿Enviar correo electrónico al cliente al reiniciar una suscripción?" +"Restart subscription notification template for customer","Plantilla de notificación de reinicio de suscripción para el cliente" +"Send admin email on subscription cancel?","¿Enviar correo electrónico al administrador al cancelar una suscripción?" +"Cancel subscription notification template for admin","Plantilla de notificación de cancelación de suscripción para el administrador" +"Send customer email on subscription cancel?","¿Enviar correo electrónico al cliente al cancelar una suscripción?" +"Cancel subscription notification template for customer","Plantilla de notificación de cancelación de suscripción para el cliente" +General,General +Version,Versión +"Shipping method","Método de envío" +"Allow one-time purchase for products?","¿Permitir compra única para productos?" +"Pre-payment reminder email","Correo electrónico de recordatorio de prepago" +"Email template","Plantilla de correo electrónico" +"Send BCC to","Enviar BCC a" +"Optional Comma separated list of emailaddresses. Leave empty to disable.","Opcional Lista de direcciones de correo electrónico separadas por comas. Dejar en blanco para desactivar." +"Days before reminder","Días antes del recordatorio" +"Cron time","Hora del cron" +"All Store Views","Todas las vistas de la tienda" +"Store View","Vista de la tienda" +"First Payment Date","Fecha del primer pago" +"Customer Name","Nombre del cliente" +"Next Payment at","Próximo pago en" +"Pre-Payment Reminder","Recordatorio de prepago" diff --git a/i18n/fr_FR.csv b/i18n/fr_FR.csv new file mode 100644 index 0000000..31e8216 --- /dev/null +++ b/i18n/fr_FR.csv @@ -0,0 +1,136 @@ +"Check last 100 debug log records","Vérifiez les 100 derniers enregistrements du journal de débogage" +"Check last 100 error log records","Vérifiez les 100 derniers enregistrements du journal des erreurs" +"Run Self-test","Exécutez le test d'auto-contrôle" +"Check for latest versions","Vérifiez les dernières versions" +"Please select","Veuillez sélectionner" +Day(s),Jour(s) +Week(s),Semaine(s) +Month(s),Mois +Times,Fois +Infinite,Infini +Enabled,Activé +Disabled,Désactivé +"Log is empty","Le journal est vide" +"Unable to cancel subscription: %1","Impossible d'annuler l'abonnement : %1" +"Subscription with ID ""%1"" has been cancelled","L'abonnement avec l'ID ""%1"" a été annulé" +"No id provided","Aucun id fourni" +"Please check the Mollie logs for more information","Veuillez consulter les journaux de Mollie pour plus d'informations" +"Unable to cancel subscription","Impossible d'annuler l'abonnement" +"My Subscriptions","Mes abonnements" +"We are unable to restart the subscription","Nous ne pouvons pas redémarrer l'abonnement" +"Unable to change the price for subscription ""%1""","Impossible de changer le prix de l'abonnement ""%1""" +"Subscription ""%1"" canceled, deleted database record with ID ""%2""","Abonnement ""%1"" annulé, enregistrement de la base de données avec l'ID ""%2"" supprimé" +"Updated subscription ""%1"" to price ""%2""","Abonnement ""%1"" mis à jour au prix ""%2""" +"Use Config Settings","Utiliser les paramètres de configuration" +"Cron settings can't be saved","Les paramètres de Cron ne peuvent pas être sauvegardés" +"Could not save the subscriptionToProduct: %1","Impossible de sauvegarder l'abonnementAuProduit : %1" +"subscription_to_product with id ""%1"" does not exist.","abonnement_au_produit avec l'id ""%1"" n'existe pas." +"Subscription for customer %1 and product %2 not found","Abonnement pour le client %1 et le produit %2 non trouvé" +"Could not delete the subscription_to_product: %1","Impossible de supprimer l'abonnement_au_produit : %1" +"Please select a subscription before adding the product to your cart.","Veuillez sélectionner un abonnement avant d'ajouter le produit à votre panier." +"You already have a subscription to this product.","Vous avez déjà un abonnement à ce produit." +"Exception while adding the subscription buttons:","Exception lors de l'ajout des boutons d'abonnement :" +"You already have a subscription to ""%1"". This product has been removed from your cart.","Vous avez déjà un abonnement à ""%1"". Ce produit a été retiré de votre panier." +"Subscription with ID ""%1"" is not active anymore, deleting record with ID ""%2""","L'abonnement avec l'ID ""%1"" n'est plus actif, suppression de l'enregistrement avec l'ID ""%2""" +"Payment with ID %1 not found in any store view","Paiement avec l'ID %1 non trouvé dans aucune vue de magasin" +"Every day","Tous les jours" +"Every %1 days","Tous les %1 jours" +"Every week","Chaque semaine" +"Every %1 weeks","Toutes les %1 semaines" +"Every month","Chaque mois" +"Every %1 months","Tous les %1 mois" +"Every %1 %2","Chaque %1 %2" +Cancel,Annuler +Delete,Supprimer +"Are you sure you want to delete this record?","Êtes-vous sûr de vouloir supprimer cet enregistrement?" +"Manual & Support","Manuel & Support" +"You have no active subscriptions.","Vous n'avez pas d'abonnements actifs." +Orders,Commandes +ID,ID +Date,Date +Status,Statut +Description,Description +Amount,Montant +Action,Action +Total,Total +Actions,Actions +"Are you sure you want to delete the subscription with ID %1?","Êtes-vous sûr de vouloir supprimer l'abonnement avec l'ID %1?" +Restart,Redémarrer +"One Time Purchase","Achat unique" +Default,Défaut +Title,Titre +"Repeat Every","Répéter chaque" +Type,Type +"Repeat Times","Répéter les fois" +"Repeat Type","Type de répétition" +"Add Subscription type","Ajouter un type d'abonnement" +"Previous Page","Page précédente" +"Next Page","Page suivante" +"Subscription canceled","Abonnement annulé" +"Hello,","Bonjour," +"The customer %customerName has canceled a subscription with the description %subscription_description and ID %subscription_id.","Le client %customerName a annulé un abonnement avec la description %subscription_description et l'ID %subscription_id." +"Thanks again,","Merci encore," +%store_name,%store_name +"Dear %name,","Cher %name," +"We confirm that you canceled the subscription with ID %subscription_id.","Nous confirmons que vous avez annulé l'abonnement avec l'ID %subscription_id." +"New subscription","Nouvel abonnement" +"The customer %customerName has started a new subscription with the description %description and ID %subscription_id.","Le client %customerName a commencé un nouvel abonnement avec la description %description et l'ID %subscription_id." +"Your new subscription","Votre nouvel abonnement" +"Good news! We confirm your subscription to %product_name. The next order will be sent on its way to you on %nextPaymentDate.","Bonne nouvelle ! Nous confirmons votre abonnement à %product_name. La prochaine commande vous sera envoyée le %nextPaymentDate." +"Upcoming subscription renewal from %store_name","Prochain renouvellement d'abonnement de %store_name" +"We are very excited to get your %product_name subscription ready for %nextPaymentDate!","Nous sommes très enthousiastes à l'idée de préparer votre abonnement à %product_name pour le %nextPaymentDate !" +"This email is a reminder that a subscription fee of %amount_value will be charged on the same date.","Cet e-mail est un rappel qu'un frais d'abonnement de %amount_value sera facturé à la même date." +"If you would like to pause or cancel your subscription or make any other changes, you can always do so by logging into your %store_name account.","Si vous souhaitez mettre en pause ou annuler votre abonnement ou apporter d'autres modifications, vous pouvez toujours le faire en vous connectant à votre compte %store_name." +"All our best,","Tous nos meilleurs vœux," +"Subscription restarted","Abonnement redémarré" +"The customer %customerName has re-started a subscription with the description %subscriptionDescription.","Le client %customerName a redémarré un abonnement avec la description %subscriptionDescription." +"We confirm that you restarted the subscription on %product_name.","Nous confirmons que vous avez redémarré l'abonnement sur %product_name." +Passed,Réussi +Failed,Échoué +"Great, you are using the latest version.","Super, vous utilisez la dernière version." +"There is a new version available (%1) see .","Il y a une nouvelle version disponible (%1) voir ." +"last 100 debug log lines","dernières 100 lignes du journal de débogage" +"download as .txt file","télécharger en fichier .txt" +ok,ok +"last 100 error log records","dernières 100 lignes du journal d'erreurs" +Self-test,Auto-test +"Show more.","Montrer plus." +"Show less.","Montrer moins." +"Not all payments methods are available when ordering subscription products.","Tous les modes de paiement ne sont pas disponibles lors de la commande de produits d'abonnement." +"View active Mollie subscriptions","Voir les abonnements Mollie actifs" +"Mollie Subscriptions","Abonnements Mollie" +"View active subscriptions","Voir les abonnements actifs" +Subscriptions,Abonnements +Branding,Marque +"Debug & Logging","Débogage & Journalisation" +Logging,Journalisation +"Debug Mode","Mode débogage" +Emails,Emails +"Disable new order confirmation","Désactiver la confirmation de nouvelle commande" +"Send admin email on new subscription?","Envoyer un e-mail à l'administrateur lors d'un nouvel abonnement ?" +"Subscription notification template for admin","Modèle de notification d'abonnement pour l'administrateur" +"Send customer email on new subscription?","Envoyer un e-mail au client lors d'un nouvel abonnement ?" +"Subscription notification template for customer","Modèle de notification d'abonnement pour le client" +"Send admin email on restart of a subscription?","Envoyer un e-mail à l'administrateur lors du redémarrage d'un abonnement ?" +"Send customer email on restart of a subscription?","Envoyer un e-mail au client lors du redémarrage d'un abonnement ?" +"Restart subscription notification template for customer","Modèle de notification de redémarrage d'abonnement pour le client" +"Send admin email on subscription cancel?","Envoyer un e-mail à l'administrateur lors de l'annulation d'un abonnement ?" +"Cancel subscription notification template for admin","Modèle de notification d'annulation d'abonnement pour l'administrateur" +"Send customer email on subscription cancel?","Envoyer un e-mail au client lors de l'annulation d'un abonnement ?" +"Cancel subscription notification template for customer","Modèle de notification d'annulation d'abonnement pour le client" +General,Général +Version,Version +"Shipping method","Méthode d'expédition" +"Allow one-time purchase for products?","Autoriser l'achat unique pour les produits ?" +"Pre-payment reminder email","Email de rappel de prépaiement" +"Email template","Modèle d'email" +"Send BCC to","Envoyer une copie à" +"Optional Comma separated list of emailaddresses. Leave empty to disable.","Optionnel Liste d'adresses e-mail séparées par des virgules. Laisser vide pour désactiver." +"Days before reminder","Jours avant le rappel" +"Cron time","Heure du Cron" +"All Store Views","Toutes les vues de magasin" +"Store View","Vue de magasin" +"First Payment Date","Date du premier paiement" +"Customer Name","Nom du client" +"Next Payment at","Prochain paiement à" +"Pre-Payment Reminder","Rappel de prépaiement" diff --git a/i18n/nl_NL.csv b/i18n/nl_NL.csv new file mode 100644 index 0000000..25dd7e6 --- /dev/null +++ b/i18n/nl_NL.csv @@ -0,0 +1,136 @@ +"Check last 100 debug log records","Controleer de laatste 100 debug log records" +"Check last 100 error log records","Controleer de laatste 100 foutlogboekrecords" +"Run Self-test","Voer zelftest uit" +"Check for latest versions","Controleer op laatste versies" +"Please select","Selecteer alstublieft" +Day(s),Dag(en) +Week(s),Week(en) +Month(s),Maand(en) +Times,Keer +Infinite,Oneindig +Enabled,Ingeschakeld +Disabled,Uitgeschakeld +"Log is empty","Log is leeg" +"Unable to cancel subscription: %1","Kan abonnement niet annuleren: %1" +"Subscription with ID ""%1"" has been cancelled","Abonnement met ID ""%1"" is geannuleerd" +"No id provided","Geen id verstrekt" +"Please check the Mollie logs for more information","Controleer de Mollie-logs voor meer informatie" +"Unable to cancel subscription","Kan abonnement niet annuleren" +"My Subscriptions","Mijn Abonnementen" +"We are unable to restart the subscription","We kunnen het abonnement niet opnieuw starten" +"Unable to change the price for subscription ""%1""","Kan de prijs voor abonnement ""%1"" niet wijzigen" +"Subscription ""%1"" canceled, deleted database record with ID ""%2""","Abonnement ""%1"" geannuleerd, database record met ID ""%2"" verwijderd" +"Updated subscription ""%1"" to price ""%2""","Abonnement ""%1"" bijgewerkt naar prijs ""%2""" +"Use Config Settings","Gebruik Config Instellingen" +"Cron settings can't be saved","Cron-instellingen kunnen niet worden opgeslagen" +"Could not save the subscriptionToProduct: %1","Kon de subscriptionToProduct niet opslaan: %1" +"subscription_to_product with id ""%1"" does not exist.","subscription_to_product met id ""%1"" bestaat niet." +"Subscription for customer %1 and product %2 not found","Abonnement voor klant %1 en product %2 niet gevonden" +"Could not delete the subscription_to_product: %1","Kon de subscriptionToProduct niet verwijderen: %1" +"Please select a subscription before adding the product to your cart.","Selecteer een abonnement voordat u het product aan uw winkelwagen toevoegt." +"You already have a subscription to this product.","U heeft al een abonnement op dit product." +"Exception while adding the subscription buttons:","Uitzondering bij het toevoegen van de abonnementsknoppen:" +"You already have a subscription to ""%1"". This product has been removed from your cart.","U heeft al een abonnement op ""%1"". Dit product is uit uw winkelwagen verwijderd." +"Subscription with ID ""%1"" is not active anymore, deleting record with ID ""%2""","Abonnement met ID ""%1"" is niet meer actief, record met ID ""%2"" verwijderen" +"Payment with ID %1 not found in any store view","Betaling met ID %1 niet gevonden in enig winkeloverzicht" +"Every day","Elke dag" +"Every %1 days","Elke %1 dagen" +"Every week","Elke week" +"Every %1 weeks","Elke %1 weken" +"Every month","Elke maand" +"Every %1 months","Elke %1 maanden" +"Every %1 %2","Elke %1 %2" +Cancel,Annuleren +Delete,Verwijderen +"Are you sure you want to delete this record?","Weet u zeker dat u dit record wilt verwijderen?" +"Manual & Support","Handleiding & Ondersteuning" +"You have no active subscriptions.","U heeft geen actieve abonnementen." +Orders,Bestellingen +ID,ID +Date,Datum +Status,Status +Description,Omschrijving +Amount,Bedrag +Action,Actie +Total,Totaal +Actions,Acties +"Are you sure you want to delete the subscription with ID %1?","Weet u zeker dat u het abonnement met ID %1 wilt verwijderen?" +Restart,Herstarten +"One Time Purchase","Eenmalige Aankoop" +Default,Standaard +Title,Titel +"Repeat Every","Herhaal Elke" +Type,Type +"Repeat Times","Herhaal Keer" +"Repeat Type","Herhaal Type" +"Add Subscription type","Voeg Abonnementstype toe" +"Previous Page","Vorige Pagina" +"Next Page","Volgende Pagina" +"Subscription canceled","Abonnement geannuleerd" +"Hello,","Hallo," +"The customer %customerName has canceled a subscription with the description %subscription_description and ID %subscription_id.","De klant %customerName heeft een abonnement geannuleerd met de beschrijving %subscription_description en ID %subscription_id." +"Thanks again,","Nogmaals bedankt," +%store_name,%store_name +"Dear %name,","Beste %name," +"We confirm that you canceled the subscription with ID %subscription_id.","We bevestigen dat u het abonnement met ID %subscription_id heeft geannuleerd." +"New subscription","Nieuw abonnement" +"The customer %customerName has started a new subscription with the description %description and ID %subscription_id.","De klant %customerName heeft een nieuw abonnement gestart met de beschrijving %description en ID %subscription_id." +"Your new subscription","Uw nieuw abonnement" +"Good news! We confirm your subscription to %product_name. The next order will be sent on its way to you on %nextPaymentDate.","Goed nieuws! We bevestigen uw abonnement op %product_name. De volgende bestelling wordt op %nextPaymentDate naar u verzonden." +"Upcoming subscription renewal from %store_name","Aankomende abonnementsvernieuwing van %store_name" +"We are very excited to get your %product_name subscription ready for %nextPaymentDate!","We zijn erg enthousiast om uw abonnement op %product_name klaar te maken voor %nextPaymentDate!" +"This email is a reminder that a subscription fee of %amount_value will be charged on the same date.","Deze e-mail is een herinnering dat op dezelfde datum een abonnementsvergoeding van %amount_value in rekening wordt gebracht." +"If you would like to pause or cancel your subscription or make any other changes, you can always do so by logging into your %store_name account.","Als u uw abonnement wilt pauzeren of annuleren of andere wijzigingen wilt aanbrengen, kunt u dat altijd doen door in te loggen op uw %store_name account." +"All our best,","Al onze beste," +"Subscription restarted","Abonnement herstart" +"The customer %customerName has re-started a subscription with the description %subscriptionDescription.","De klant %customerName heeft een abonnement herstart met de beschrijving %subscriptionDescription." +"We confirm that you restarted the subscription on %product_name.","We bevestigen dat u het abonnement op %product_name hebt herstart." +Passed,Geslaagd +Failed,Mislukt +"Great, you are using the latest version.","Geweldig, u gebruikt de nieuwste versie." +"There is a new version available (%1) see .","Er is een nieuwe versie beschikbaar (%1) zie ." +"last 100 debug log lines","laatste 100 debug log regels" +"download as .txt file","download als .txt bestand" +ok,ok +"last 100 error log records","laatste 100 foutlogboekrecords" +Self-test,Zelftest +"Show more.","Toon meer." +"Show less.","Toon minder." +"Not all payments methods are available when ordering subscription products.","Niet alle betaalmethoden zijn beschikbaar bij het bestellen van abonnementsproducten." +"View active Mollie subscriptions","Bekijk actieve Mollie-abonnementen" +"Mollie Subscriptions","Mollie Abonnementen" +"View active subscriptions","Bekijk actieve abonnementen" +Subscriptions,Abonnementen +Branding,Branding +"Debug & Logging","Debug & Logging" +Logging,Loggen +"Debug Mode","Debugmodus" +Emails,Emails +"Disable new order confirmation","Nieuwe bestelbevestiging uitschakelen" +"Send admin email on new subscription?","Admin e-mail sturen bij nieuw abonnement?" +"Subscription notification template for admin","Abonnementsmeldingssjabloon voor beheerder" +"Send customer email on new subscription?","Klant e-mail sturen bij nieuw abonnement?" +"Subscription notification template for customer","Abonnementsmeldingssjabloon voor klant" +"Send admin email on restart of a subscription?","Beheerder e-mail sturen bij herstarten van een abonnement?" +"Send customer email on restart of a subscription?","Klant e-mail sturen bij herstarten van een abonnement?" +"Restart subscription notification template for customer","Herstart abonnement meldingssjabloon voor klant" +"Send admin email on subscription cancel?","Beheerder e-mail sturen bij annulering van abonnement?" +"Cancel subscription notification template for admin","Annuleer abonnement meldingssjabloon voor beheerder" +"Send customer email on subscription cancel?","Klant e-mail sturen bij annulering van abonnement?" +"Cancel subscription notification template for customer","Annuleer abonnement meldingssjabloon voor klant" +General,Algemeen +Version,Versie +"Shipping method","Verzendmethode" +"Allow one-time purchase for products?","Eenmalige aankoop voor producten toestaan?" +"Pre-payment reminder email","Herinneringsemail voor vooruitbetaling" +"Email template","E-mail sjabloon" +"Send BCC to","Stuur BCC naar" +"Optional Comma separated list of emailaddresses. Leave empty to disable.","Optioneel Lijst met e-mailadressen gescheiden door komma's. Laat leeg om uit te schakelen." +"Days before reminder","Dagen voor herinnering" +"Cron time","Cron tijd" +"All Store Views","Alle winkelweergaven" +"Store View","Winkelweergave" +"First Payment Date","Eerste betalingsdatum" +"Customer Name","Klantnaam" +"Next Payment at","Volgende betaling op" +"Pre-Payment Reminder","Herinnering voorafgaande betaling" From 75fa97e3fb321039c4d4730eb51a988ed71315aa Mon Sep 17 00:00:00 2001 From: Michiel Gerritsen Date: Mon, 12 Jun 2023 14:40:40 +0200 Subject: [PATCH 10/13] Feature: Add order comment with subscription ID --- Controller/Api/Webhook.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Controller/Api/Webhook.php b/Controller/Api/Webhook.php index 1f79d4f..0d9858b 100644 --- a/Controller/Api/Webhook.php +++ b/Controller/Api/Webhook.php @@ -33,6 +33,7 @@ use Mollie\Payment\Model\Mollie; use Mollie\Payment\Service\Mollie\Order\LinkTransactionToOrder; use Mollie\Payment\Service\Mollie\ValidateMetadata; +use Mollie\Payment\Service\Order\OrderCommentHistory; use Mollie\Payment\Service\Order\SendOrderEmails; use Mollie\Subscriptions\Config; use Mollie\Subscriptions\Service\Mollie\MollieSubscriptionApi; @@ -125,6 +126,11 @@ class Webhook extends Action implements CsrfAwareActionInterface */ private $linkTransactionToOrder; + /** + * @var OrderCommentHistory + */ + private $orderCommentHistory; + public function __construct( Context $context, Config $config, @@ -142,7 +148,8 @@ public function __construct( SendOrderEmails $sendOrderEmails, RetryUsingOtherStoreViews $retryUsingOtherStoreViews, ValidateMetadata $validateMetadata, - LinkTransactionToOrder $linkTransactionToOrder + LinkTransactionToOrder $linkTransactionToOrder, + OrderCommentHistory $orderCommentHistory ) { parent::__construct($context); @@ -162,6 +169,7 @@ public function __construct( $this->retryUsingOtherStoreViews = $retryUsingOtherStoreViews; $this->validateMetadata = $validateMetadata; $this->linkTransactionToOrder = $linkTransactionToOrder; + $this->orderCommentHistory = $orderCommentHistory; } public function execute() @@ -217,6 +225,8 @@ public function execute() $order->getPayment()->setAdditionalInformation('subscription_created', $subscription->createdAt); $this->orderRepository->save($order); + $this->orderCommentHistory->add($order, __('Order created by Mollie subscription %1', $molliePayment->id)); + $this->linkTransactionToOrder->execute($molliePayment->id, $order); $this->mollie->processTransactionForOrder($order, Payments::TRANSACTION_TYPE_SUBSCRIPTION); From e573148783e73e6d5706d28560aaaba0b8823b05 Mon Sep 17 00:00:00 2001 From: Marvin Besselsen Date: Tue, 13 Jun 2023 09:05:09 +0200 Subject: [PATCH 11/13] Upped minimum requirements for mollie/magento2 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 2ce919f..e6de49b 100755 --- a/composer.json +++ b/composer.json @@ -9,7 +9,7 @@ ], "require": { "magento/framework": ">=102.0.0", - "mollie/magento2": ">=2.25.0", + "mollie/magento2": ">=2.27.0", "beberlei/assert": "*", "ext-json": "*" }, From 6ff8429d1239c24e233e5f07c2210dc699bb552d Mon Sep 17 00:00:00 2001 From: Marvin Besselsen Date: Tue, 13 Jun 2023 09:05:37 +0200 Subject: [PATCH 12/13] Version bump --- composer.json | 2 +- etc/config.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index e6de49b..cb80b21 100755 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "mollie/magento2-subscriptions", "description": "Mollie subscriptions extension for Magento 2", "type": "magento2-module", - "version": "1.10.3", + "version": "1.11.0", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/etc/config.xml b/etc/config.xml index af0e0cb..84b0538 100755 --- a/etc/config.xml +++ b/etc/config.xml @@ -10,7 +10,7 @@ - v1.10.3 + v1.11.0 0 0 From 390bd41301ff275f72d3d19883d3887b6eab8533 Mon Sep 17 00:00:00 2001 From: Michiel Gerritsen Date: Tue, 13 Jun 2023 09:27:18 +0200 Subject: [PATCH 13/13] Test fix --- Test/Integration/Controller/Api/WebhookTest.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Test/Integration/Controller/Api/WebhookTest.php b/Test/Integration/Controller/Api/WebhookTest.php index 9a0e6dc..05836d7 100644 --- a/Test/Integration/Controller/Api/WebhookTest.php +++ b/Test/Integration/Controller/Api/WebhookTest.php @@ -35,8 +35,7 @@ public function testAcceptsPost() $paymentEndpointMock->method('get')->willThrowException(new ApiException('Invalid transaction (Test)')); /** @var Mollie $mollie */ - $mollie = $this->_objectManager->get(Mollie::class); - $api = $mollie->getMollieApi(); + $api = new MollieApiClient(); $api->payments = $paymentEndpointMock; $mollieMock = $this->createMock(Mollie::class);